Skip to content

Commit 147a354

Browse files
authored
fix(material/chips): don't remove aria-selected from deselected options (#25748)
For chip-listbox, 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 87e17aa commit 147a354

File tree

3 files changed

+23
-15
lines changed

3 files changed

+23
-15
lines changed

src/material/chips/chip-listbox.spec.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -513,19 +513,19 @@ describe('MDC-based MatChipListbox', () => {
513513
'.mdc-evolution-chip__action--primary',
514514
);
515515

516-
expect(getAriaSelected()).toEqual([null, null, null]);
516+
expect(getAriaSelected()).toEqual(['false', 'false', 'false']);
517517

518518
primaryActions[1].click();
519519
fixture.detectChanges();
520-
expect(getAriaSelected()).toEqual([null, 'true', null]);
520+
expect(getAriaSelected()).toEqual(['false', 'true', 'false']);
521521

522522
primaryActions[2].click();
523523
fixture.detectChanges();
524-
expect(getAriaSelected()).toEqual([null, null, 'true']);
524+
expect(getAriaSelected()).toEqual(['false', 'false', 'true']);
525525

526526
primaryActions[0].click();
527527
fixture.detectChanges();
528-
expect(getAriaSelected()).toEqual(['true', null, null]);
528+
expect(getAriaSelected()).toEqual(['true', 'false', 'false']);
529529
}));
530530

531531
it('should set `aria-selected` based on the selection state in multi-selection mode', fakeAsync(() => {
@@ -548,23 +548,23 @@ describe('MDC-based MatChipListbox', () => {
548548
'.mdc-evolution-chip__action--primary',
549549
);
550550

551-
expect(getAriaSelected()).toEqual([null, null, null]);
551+
expect(getAriaSelected()).toEqual(['false', 'false', 'false']);
552552

553553
primaryActions[1].click();
554554
fixture.detectChanges();
555-
expect(getAriaSelected()).toEqual([null, 'true', null]);
555+
expect(getAriaSelected()).toEqual(['false', 'true', 'false']);
556556

557557
primaryActions[2].click();
558558
fixture.detectChanges();
559-
expect(getAriaSelected()).toEqual([null, 'true', 'true']);
559+
expect(getAriaSelected()).toEqual(['false', 'true', 'true']);
560560

561561
primaryActions[0].click();
562562
fixture.detectChanges();
563563
expect(getAriaSelected()).toEqual(['true', 'true', 'true']);
564564

565565
primaryActions[1].click();
566566
fixture.detectChanges();
567-
expect(getAriaSelected()).toEqual(['true', null, 'true']);
567+
expect(getAriaSelected()).toEqual(['true', 'false', 'true']);
568568
}));
569569
});
570570

src/material/chips/chip-option.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,7 @@ describe('MDC-based Option Chips', () => {
233233
});
234234

235235
it('should have correct aria-selected in single selection mode', () => {
236-
expect(primaryAction.hasAttribute('aria-selected')).toBe(false);
236+
expect(primaryAction.getAttribute('aria-selected')).toBe('false');
237237

238238
testComponent.selected = true;
239239
fixture.detectChanges();

src/material/chips/chip-option.ts

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -106,13 +106,21 @@ export class MatChipOption extends MatChip implements OnInit {
106106
}
107107
private _selected = false;
108108

109-
/** The ARIA selected applied to the chip. */
109+
/**
110+
* The ARIA selected applied to the chip. Conforms to WAI ARIA best practices for listbox
111+
* interaction patterns.
112+
*
113+
* From [WAI ARIA Listbox authoring practices guide](
114+
* https://www.w3.org/WAI/ARIA/apg/patterns/listbox/):
115+
* "If any options are selected, each selected option has either aria-selected or aria-checked
116+
* set to true. All options that are selectable but not selected have either aria-selected or
117+
* aria-checked set to false."
118+
*
119+
* Set `aria-selected="false"` on not-selected listbox options that are selectable to fix
120+
* VoiceOver reading every option as "selected" (#25736).
121+
*/
110122
get ariaSelected(): string | null {
111-
// Remove the `aria-selected` when the chip is deselected in single-selection mode, because
112-
// it adds noise to NVDA users where "not selected" will be read out for each chip.
113-
return this.selectable && (this._chipListMultiple || this.selected)
114-
? this.selected.toString()
115-
: null;
123+
return this.selectable ? this.selected.toString() : null;
116124
}
117125

118126
/** The unstyled chip selector for this component. */

0 commit comments

Comments
 (0)