Skip to content

Commit f198ec2

Browse files
committed
fix(material/core): don't remove aria-selected from deselected options
For mat-option, set `aria-selected="false"` on deselected options that are selectable. Conforms with [WAI ARIA Listbox authoring practices guide]( https://www.w3.org/WAI/ARIA/apg/patterns/listbox/), which says to always include aria-selected attribute on options that are selectable. Fix issue where voiceover reads every option as "selected" (#25736). Fix #25736
1 parent fd11f2b commit f198ec2

File tree

3 files changed

+20
-13
lines changed

3 files changed

+20
-13
lines changed

src/material/core/option/option.ts

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -196,13 +196,20 @@ export class _MatOptionBase<T = any> implements FocusableOption, AfterViewChecke
196196
}
197197

198198
/**
199-
* Gets the `aria-selected` value for the option. We explicitly omit the `aria-selected`
200-
* attribute from single-selection, unselected options. Including the `aria-selected="false"`
201-
* attributes adds a significant amount of noise to screen-reader users without providing useful
202-
* information.
199+
* The ARIA selected applied to the option. Conforms to WAI ARIA best practices for listbox
200+
* interaction patterns.
201+
*
202+
* From [WAI ARIA Listbox authoring practices guide](
203+
* https://www.w3.org/WAI/ARIA/apg/patterns/listbox/):
204+
* "If any options are selected, each selected option has either aria-selected or aria-checked
205+
* set to true. All options that are selectable but not selected have either aria-selected or
206+
* aria-checked set to false."
207+
*
208+
* Set `aria-selected="false"` on not-selected listbox options that are selectable to fix
209+
* VoiceOver reading every option as "selected" (#25736).
203210
*/
204211
_getAriaSelected(): boolean | null {
205-
return this.selected || (this.multiple ? false : null);
212+
return this.selected;
206213
}
207214

208215
/** Returns the correct tabindex for the option depending on disabled state. */

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1135,9 +1135,9 @@ describe('MatSelect', () => {
11351135
}));
11361136

11371137
it('should set aria-selected on each option for single select', fakeAsync(() => {
1138-
expect(options.every(option => !option.hasAttribute('aria-selected')))
1138+
expect(options.every(option => option.getAttribute('aria-selected') === 'false'))
11391139
.withContext(
1140-
'Expected all unselected single-select options not to have ' + 'aria-selected set.',
1140+
'Expected all unselected single-select options to have aria-selected="false" set.',
11411141
)
11421142
.toBe(true);
11431143

@@ -1152,9 +1152,9 @@ describe('MatSelect', () => {
11521152
.withContext('Expected selected single-select option to have aria-selected="true".')
11531153
.toEqual('true');
11541154
options.splice(1, 1);
1155-
expect(options.every(option => !option.hasAttribute('aria-selected')))
1155+
expect(options.every(option => option.getAttribute('aria-selected') === 'false'))
11561156
.withContext(
1157-
'Expected all unselected single-select options not to have ' + 'aria-selected set.',
1157+
'Expected all unselected single-select options to have aria-selected="false" set.',
11581158
)
11591159
.toBe(true);
11601160
}));

src/material/select/select.spec.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1168,9 +1168,9 @@ describe('MDC-based MatSelect', () => {
11681168
}));
11691169

11701170
it('should set aria-selected on each option for single select', fakeAsync(() => {
1171-
expect(options.every(option => !option.hasAttribute('aria-selected')))
1171+
expect(options.every(option => option.getAttribute('aria-selected') === 'false'))
11721172
.withContext(
1173-
'Expected all unselected single-select options not to have ' + 'aria-selected set.',
1173+
'Expected all unselected single-select options to have aria-selected="false" set.',
11741174
)
11751175
.toBe(true);
11761176

@@ -1187,9 +1187,9 @@ describe('MDC-based MatSelect', () => {
11871187
)
11881188
.toEqual('true');
11891189
options.splice(1, 1);
1190-
expect(options.every(option => !option.hasAttribute('aria-selected')))
1190+
expect(options.every(option => option.getAttribute('aria-selected') === 'false'))
11911191
.withContext(
1192-
'Expected all unselected single-select options not to have ' + 'aria-selected set.',
1192+
'Expected all unselected single-select options to have aria-selected="false" set.',
11931193
)
11941194
.toBe(true);
11951195
}));

0 commit comments

Comments
 (0)