Skip to content

Commit f59b03c

Browse files
asyncLizcopybara-github
authored andcommitted
feat(checkbox): support :state(checked) and :state(indeterminate)
PiperOrigin-RevId: 694362061
1 parent 223b88d commit f59b03c

File tree

3 files changed

+86
-10
lines changed

3 files changed

+86
-10
lines changed

checkbox/internal/checkbox.ts

+20-3
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,11 @@ import {
2323
getValidityAnchor,
2424
mixinConstraintValidation,
2525
} from '../../labs/behaviors/constraint-validation.js';
26+
import {
27+
hasState,
28+
mixinCustomStateSet,
29+
toggleState,
30+
} from '../../labs/behaviors/custom-state-set.js';
2631
import {mixinElementInternals} from '../../labs/behaviors/element-internals.js';
2732
import {
2833
getFormState,
@@ -34,7 +39,7 @@ import {CheckboxValidator} from '../../labs/behaviors/validators/checkbox-valida
3439
// Separate variable needed for closure.
3540
const checkboxBaseClass = mixinDelegatesAria(
3641
mixinConstraintValidation(
37-
mixinFormAssociated(mixinElementInternals(LitElement)),
42+
mixinFormAssociated(mixinCustomStateSet(mixinElementInternals(LitElement))),
3843
),
3944
);
4045

@@ -59,14 +64,26 @@ export class Checkbox extends checkboxBaseClass {
5964
/**
6065
* Whether or not the checkbox is selected.
6166
*/
62-
@property({type: Boolean}) checked = false;
67+
@property({type: Boolean})
68+
get checked(): boolean {
69+
return this[hasState]('checked');
70+
}
71+
set checked(checked: boolean) {
72+
this[toggleState]('checked', checked);
73+
}
6374

6475
/**
6576
* Whether or not the checkbox is indeterminate.
6677
*
6778
* https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/checkbox#indeterminate_state_checkboxes
6879
*/
69-
@property({type: Boolean}) indeterminate = false;
80+
@property({type: Boolean})
81+
get indeterminate(): boolean {
82+
return this[hasState]('indeterminate');
83+
}
84+
set indeterminate(indeterminate: boolean) {
85+
this[toggleState]('indeterminate', indeterminate);
86+
}
7087

7188
/**
7289
* When true, require the checkbox to be selected when participating in

checkbox/internal/checkbox_test.ts

+59-7
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,31 @@ describe('checkbox', () => {
152152
expect(input.checked).toEqual(true);
153153
expect(harness.element.checked).toEqual(true);
154154
});
155+
156+
it('matches :state(checked) when true', async () => {
157+
// Arrange
158+
const {harness} = await setupTest();
159+
160+
// Act
161+
harness.element.checked = true;
162+
await env.waitForStability();
163+
164+
// Assert
165+
expect(harness.element.matches(':state(checked)'))
166+
.withContext("element.matches(':state(checked)')")
167+
.toBeTrue();
168+
});
169+
170+
it('does not match :state(checked) when false', async () => {
171+
// Arrange
172+
// Act
173+
const {harness} = await setupTest();
174+
175+
// Assert
176+
expect(harness.element.matches(':state(checked)'))
177+
.withContext("element.matches(':state(checked)')")
178+
.toBeFalse();
179+
});
155180
});
156181

157182
describe('indeterminate', () => {
@@ -169,6 +194,31 @@ describe('checkbox', () => {
169194
expect(input.indeterminate).toEqual(false);
170195
expect(input.getAttribute('aria-checked')).not.toEqual('mixed');
171196
});
197+
198+
it('matches :state(indeterminate) when true', async () => {
199+
// Arrange
200+
const {harness} = await setupTest();
201+
202+
// Act
203+
harness.element.indeterminate = true;
204+
await env.waitForStability();
205+
206+
// Assert
207+
expect(harness.element.matches(':state(indeterminate)'))
208+
.withContext("element.matches(':state(indeterminate)')")
209+
.toBeTrue();
210+
});
211+
212+
it('does not match :state(indeterminate) when false', async () => {
213+
// Arrange
214+
// Act
215+
const {harness} = await setupTest();
216+
217+
// Assert
218+
expect(harness.element.matches(':state(indeterminate)'))
219+
.withContext("element.matches(':state(indeterminate)')")
220+
.toBeFalse();
221+
});
172222
});
173223

174224
describe('disabled', () => {
@@ -186,13 +236,15 @@ describe('checkbox', () => {
186236

187237
describe('form submission', () => {
188238
async function setupFormTest(propsInit: Partial<Checkbox> = {}) {
189-
return await setupTest(html` <form>
190-
<md-test-checkbox
191-
.checked=${propsInit.checked === true}
192-
.disabled=${propsInit.disabled === true}
193-
.name=${propsInit.name ?? ''}
194-
.value=${propsInit.value ?? ''}></md-test-checkbox>
195-
</form>`);
239+
return await setupTest(
240+
html`<form>
241+
<md-test-checkbox
242+
.checked=${propsInit.checked === true}
243+
.disabled=${propsInit.disabled === true}
244+
.name=${propsInit.name ?? ''}
245+
.value=${propsInit.value ?? ''}></md-test-checkbox>
246+
</form>`,
247+
);
196248
}
197249

198250
it('does not submit if not checked', async () => {

docs/components/checkbox.md

+7
Original file line numberDiff line numberDiff line change
@@ -209,3 +209,10 @@ Token | Default value
209209
<!-- mdformat on(autogenerated might break rendering in catalog) -->
210210

211211
<!-- auto-generated API docs end -->
212+
213+
#### States
214+
215+
| State | Description |
216+
| --- | --- |
217+
| `:state(checked)` | Matches when the checkbox is checked. |
218+
| `:state(indeterminate)` | Matches when the checkbox is indeterminate. |

0 commit comments

Comments
 (0)