Skip to content

Commit 7dfcf8f

Browse files
committed
fix(material/checkbox): fix ARIA semantics and use native DOM properties (#26710)
For the checkbox component, fix semantics so that a11y produces a state of "mixed" when both the `checked` and `indeterminate` Inputs to mat-checkbox are both true. Also, remove the use of aria-checked in favor of the native DOM properties for checked and indeterminate. Conform to [ARIA in HTML W3C input checkbox specification](https://www.w3.org/TR/html-aria/#el-input-checkbox). Fix a11y bug where checkbox renders the indeterminate visual state but accessibility tree has a state of "checked". Aligns with native checkbox, which produces the mixed state when indeterminate property is true, regardless of the value of the checked property. Some accessibility checkers produce an error when native checkbox has aria-checked on it. Remove aria-checked to fix errors. Does not make visual changes. Fix #26709 (cherry picked from commit 5c5617d)
1 parent 501d73e commit 7dfcf8f

File tree

6 files changed

+7
-30
lines changed

6 files changed

+7
-30
lines changed

src/material/checkbox/checkbox.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,13 @@
88
type="checkbox"
99
class="mdc-checkbox__native-control"
1010
[class.mdc-checkbox--selected]="checked"
11-
[attr.aria-checked]="_getAriaChecked()"
1211
[attr.aria-label]="ariaLabel || null"
1312
[attr.aria-labelledby]="ariaLabelledby"
1413
[attr.aria-describedby]="ariaDescribedby"
1514
[attr.name]="name"
1615
[attr.value]="value"
1716
[checked]="checked"
17+
[indeterminate]="indeterminate"
1818
[disabled]="disabled"
1919
[id]="inputId"
2020
[required]="required"

src/material/checkbox/checkbox.spec.ts

+6-9
Original file line numberDiff line numberDiff line change
@@ -96,19 +96,16 @@ describe('MDC-based MatCheckbox', () => {
9696
it('should add and remove indeterminate state', fakeAsync(() => {
9797
expect(inputElement.checked).toBe(false);
9898
expect(inputElement.indeterminate).toBe(false);
99-
expect(inputElement.getAttribute('aria-checked'))
100-
.withContext('Expect aria-checked to be false')
101-
.toBe('false');
10299

103100
testComponent.isIndeterminate = true;
104101
fixture.detectChanges();
105102
flush();
106103

107104
expect(inputElement.checked).toBe(false);
108105
expect(inputElement.indeterminate).toBe(true);
109-
expect(inputElement.getAttribute('aria-checked'))
110-
.withContext('Expect aria checked to be mixed for indeterminate checkbox')
111-
.toBe('mixed');
106+
expect(inputElement.hasAttribute('aria-checked'))
107+
.withContext('Expect aria-checked attribute to not be used')
108+
.toBe(false);
112109

113110
testComponent.isIndeterminate = false;
114111
fixture.detectChanges();
@@ -148,9 +145,9 @@ describe('MDC-based MatCheckbox', () => {
148145
expect(inputElement.indeterminate).toBe(true);
149146
expect(inputElement.checked).toBe(true);
150147
expect(testComponent.isIndeterminate).toBe(true);
151-
expect(inputElement.getAttribute('aria-checked'))
152-
.withContext('Expect aria checked to be true')
153-
.toBe('true');
148+
expect(inputElement.hasAttribute('aria-checked'))
149+
.withContext('Expect aria-checked attribute to not be used')
150+
.toBe(false);
154151

155152
inputElement.click();
156153
fixture.detectChanges();

src/material/checkbox/checkbox.ts

-8
Original file line numberDiff line numberDiff line change
@@ -311,14 +311,6 @@ export abstract class _MatCheckboxBase<E>
311311
this.disabled = isDisabled;
312312
}
313313

314-
_getAriaChecked(): 'true' | 'false' | 'mixed' {
315-
if (this.checked) {
316-
return 'true';
317-
}
318-
319-
return this.indeterminate ? 'mixed' : 'false';
320-
}
321-
322314
private _transitionCheckState(newState: TransitionCheckState) {
323315
let oldState = this._currentCheckState;
324316
let element = this._getAnimationTargetElement();

src/material/legacy-checkbox/checkbox.html

-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
[tabIndex]="tabIndex"
1313
[attr.aria-label]="ariaLabel || null"
1414
[attr.aria-labelledby]="ariaLabelledby"
15-
[attr.aria-checked]="_getAriaChecked()"
1615
[attr.aria-describedby]="ariaDescribedby"
1716
(change)="_onInteractionEvent($event)"
1817
(click)="_onInputClick($event)">

src/material/legacy-checkbox/checkbox.spec.ts

-9
Original file line numberDiff line numberDiff line change
@@ -73,19 +73,13 @@ describe('MatLegacyCheckbox', () => {
7373
expect(checkboxNativeElement.classList).not.toContain('mat-checkbox-checked');
7474
expect(inputElement.checked).toBe(false);
7575
expect(inputElement.indeterminate).toBe(false);
76-
expect(inputElement.getAttribute('aria-checked'))
77-
.withContext('Expect aria-checked to be false')
78-
.toBe('false');
7976

8077
testComponent.isIndeterminate = true;
8178
fixture.detectChanges();
8279

8380
expect(checkboxNativeElement.classList).toContain('mat-checkbox-indeterminate');
8481
expect(inputElement.checked).toBe(false);
8582
expect(inputElement.indeterminate).toBe(true);
86-
expect(inputElement.getAttribute('aria-checked'))
87-
.withContext('Expect aria checked to be mixed for indeterminate checkbox')
88-
.toBe('mixed');
8983

9084
testComponent.isIndeterminate = false;
9185
fixture.detectChanges();
@@ -125,9 +119,6 @@ describe('MatLegacyCheckbox', () => {
125119
expect(inputElement.indeterminate).toBe(true);
126120
expect(inputElement.checked).toBe(true);
127121
expect(testComponent.isIndeterminate).toBe(true);
128-
expect(inputElement.getAttribute('aria-checked'))
129-
.withContext('Expect aria checked to be true')
130-
.toBe('true');
131122

132123
inputElement.click();
133124
fixture.detectChanges();

tools/public_api_guard/material/checkbox.md

-2
Original file line numberDiff line numberDiff line change
@@ -94,8 +94,6 @@ export abstract class _MatCheckboxBase<E> extends _MatCheckboxMixinBase implemen
9494
abstract focus(origin?: FocusOrigin): void;
9595
protected abstract _getAnimationTargetElement(): HTMLElement | null;
9696
// (undocumented)
97-
_getAriaChecked(): 'true' | 'false' | 'mixed';
98-
// (undocumented)
9997
protected _handleInputClick(): void;
10098
id: string;
10199
get indeterminate(): boolean;

0 commit comments

Comments
 (0)