diff --git a/checkbox/internal/checkbox.ts b/checkbox/internal/checkbox.ts index 21a8ee03b5..653fae554d 100644 --- a/checkbox/internal/checkbox.ts +++ b/checkbox/internal/checkbox.ts @@ -23,6 +23,11 @@ import { getValidityAnchor, mixinConstraintValidation, } from '../../labs/behaviors/constraint-validation.js'; +import { + hasState, + mixinCustomStateSet, + toggleState, +} from '../../labs/behaviors/custom-state-set.js'; import {mixinElementInternals} from '../../labs/behaviors/element-internals.js'; import { getFormState, @@ -34,7 +39,7 @@ import {CheckboxValidator} from '../../labs/behaviors/validators/checkbox-valida // Separate variable needed for closure. const checkboxBaseClass = mixinDelegatesAria( mixinConstraintValidation( - mixinFormAssociated(mixinElementInternals(LitElement)), + mixinFormAssociated(mixinCustomStateSet(mixinElementInternals(LitElement))), ), ); @@ -59,14 +64,26 @@ export class Checkbox extends checkboxBaseClass { /** * Whether or not the checkbox is selected. */ - @property({type: Boolean}) checked = false; + @property({type: Boolean}) + get checked(): boolean { + return this[hasState]('checked'); + } + set checked(checked: boolean) { + this[toggleState]('checked', checked); + } /** * Whether or not the checkbox is indeterminate. * * https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/checkbox#indeterminate_state_checkboxes */ - @property({type: Boolean}) indeterminate = false; + @property({type: Boolean}) + get indeterminate(): boolean { + return this[hasState]('indeterminate'); + } + set indeterminate(indeterminate: boolean) { + this[toggleState]('indeterminate', indeterminate); + } /** * When true, require the checkbox to be selected when participating in diff --git a/checkbox/internal/checkbox_test.ts b/checkbox/internal/checkbox_test.ts index 61f368a332..a086a6c15c 100644 --- a/checkbox/internal/checkbox_test.ts +++ b/checkbox/internal/checkbox_test.ts @@ -152,6 +152,31 @@ describe('checkbox', () => { expect(input.checked).toEqual(true); expect(harness.element.checked).toEqual(true); }); + + it('matches :state(checked) when true', async () => { + // Arrange + const {harness} = await setupTest(); + + // Act + harness.element.checked = true; + await env.waitForStability(); + + // Assert + expect(harness.element.matches(':state(checked)')) + .withContext("element.matches(':state(checked)')") + .toBeTrue(); + }); + + it('does not match :state(checked) when false', async () => { + // Arrange + // Act + const {harness} = await setupTest(); + + // Assert + expect(harness.element.matches(':state(checked)')) + .withContext("element.matches(':state(checked)')") + .toBeFalse(); + }); }); describe('indeterminate', () => { @@ -169,6 +194,31 @@ describe('checkbox', () => { expect(input.indeterminate).toEqual(false); expect(input.getAttribute('aria-checked')).not.toEqual('mixed'); }); + + it('matches :state(indeterminate) when true', async () => { + // Arrange + const {harness} = await setupTest(); + + // Act + harness.element.indeterminate = true; + await env.waitForStability(); + + // Assert + expect(harness.element.matches(':state(indeterminate)')) + .withContext("element.matches(':state(indeterminate)')") + .toBeTrue(); + }); + + it('does not match :state(indeterminate) when false', async () => { + // Arrange + // Act + const {harness} = await setupTest(); + + // Assert + expect(harness.element.matches(':state(indeterminate)')) + .withContext("element.matches(':state(indeterminate)')") + .toBeFalse(); + }); }); describe('disabled', () => { @@ -186,13 +236,15 @@ describe('checkbox', () => { describe('form submission', () => { async function setupFormTest(propsInit: Partial = {}) { - return await setupTest(html`
- -
`); + return await setupTest( + html`
+ +
`, + ); } it('does not submit if not checked', async () => { diff --git a/docs/components/checkbox.md b/docs/components/checkbox.md index 2ae0332224..9ccf73eb2d 100644 --- a/docs/components/checkbox.md +++ b/docs/components/checkbox.md @@ -209,3 +209,10 @@ Token | Default value + +#### States + +| State | Description | +| --- | --- | +| `:state(checked)` | Matches when the checkbox is checked. | +| `:state(indeterminate)` | Matches when the checkbox is indeterminate. |