Skip to content

Commit ebbf97e

Browse files
crisbetojelbourn
authored andcommitted
fix(chips): remove aria-selected from deselected chip in single selection mode (#15634)
Along the same lines as #15617 since the setup between `mat-select` and `mat-chip-list` fairly similar. Removes the `aria-selected` from deselected chips in single selection mode in order to reduce the amount of noise for screen reader users.
1 parent 073e542 commit ebbf97e

File tree

4 files changed

+31
-8
lines changed

4 files changed

+31
-8
lines changed

src/lib/chips/chip-list.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,7 @@ export class MatChipList extends _MatChipListMixinBase implements MatFormFieldCo
175175
get multiple(): boolean { return this._multiple; }
176176
set multiple(value: boolean) {
177177
this._multiple = coerceBooleanProperty(value);
178+
this._syncChipsState();
178179
}
179180
private _multiple: boolean = false;
180181

@@ -267,7 +268,7 @@ export class MatChipList extends _MatChipListMixinBase implements MatFormFieldCo
267268
get disabled(): boolean { return this.ngControl ? !!this.ngControl.disabled : this._disabled; }
268269
set disabled(value: boolean) {
269270
this._disabled = coerceBooleanProperty(value);
270-
this._syncChipsDisabledState();
271+
this._syncChipsState();
271272
}
272273
protected _disabled: boolean = false;
273274

@@ -371,7 +372,7 @@ export class MatChipList extends _MatChipListMixinBase implements MatFormFieldCo
371372
// Since this happens after the content has been
372373
// checked, we need to defer it to the next tick.
373374
Promise.resolve().then(() => {
374-
this._syncChipsDisabledState();
375+
this._syncChipsState();
375376
});
376377
}
377378

@@ -781,11 +782,12 @@ export class MatChipList extends _MatChipListMixinBase implements MatFormFieldCo
781782
return this.chips.some(chip => chip._hasFocus);
782783
}
783784

784-
/** Syncs the list's disabled state with the individual chips. */
785-
private _syncChipsDisabledState() {
785+
/** Syncs the list's state with the individual chips. */
786+
private _syncChipsState() {
786787
if (this.chips) {
787788
this.chips.forEach(chip => {
788789
chip.disabled = this._disabled;
790+
chip._chipListMultiple = this.multiple;
789791
});
790792
}
791793
}

src/lib/chips/chip.spec.ts

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
import {Directionality} from '@angular/cdk/bidi';
22
import {BACKSPACE, DELETE, SPACE} from '@angular/cdk/keycodes';
33
import {createKeyboardEvent, dispatchFakeEvent} from '@angular/cdk/testing';
4-
import {Component, DebugElement} from '@angular/core';
4+
import {Component, DebugElement, ViewChild} from '@angular/core';
55
import {async, ComponentFixture, TestBed} from '@angular/core/testing';
66
import {MAT_RIPPLE_GLOBAL_OPTIONS, RippleGlobalOptions} from '@angular/material/core';
77
import {By} from '@angular/platform-browser';
88
import {Subject} from 'rxjs';
9-
import {MatChip, MatChipEvent, MatChipSelectionChange, MatChipsModule} from './index';
9+
import {MatChip, MatChipEvent, MatChipSelectionChange, MatChipsModule, MatChipList} from './index';
1010

1111

1212
describe('Chips', () => {
@@ -257,14 +257,27 @@ describe('Chips', () => {
257257
expect(testComponent.chipSelectionChange).toHaveBeenCalledWith(CHIP_DESELECTED_EVENT);
258258
});
259259

260-
it('should have correct aria-selected', () => {
260+
it('should have correct aria-selected in single selection mode', () => {
261+
expect(chipNativeElement.hasAttribute('aria-selected')).toBe(false);
262+
263+
testComponent.selected = true;
264+
fixture.detectChanges();
265+
266+
expect(chipNativeElement.getAttribute('aria-selected')).toBe('true');
267+
});
268+
269+
it('should have the correct aria-selected in multi-selection mode', () => {
270+
testComponent.chipList.multiple = true;
271+
fixture.detectChanges();
272+
261273
expect(chipNativeElement.getAttribute('aria-selected')).toBe('false');
262274

263275
testComponent.selected = true;
264276
fixture.detectChanges();
265277

266278
expect(chipNativeElement.getAttribute('aria-selected')).toBe('true');
267279
});
280+
268281
});
269282

270283
describe('when selectable is false', () => {
@@ -390,6 +403,7 @@ describe('Chips', () => {
390403
</mat-chip-list>`
391404
})
392405
class SingleChip {
406+
@ViewChild(MatChipList) chipList: MatChipList;
393407
disabled: boolean = false;
394408
name: string = 'Test';
395409
color: string = 'primary';

src/lib/chips/chip.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,9 @@ export class MatChip extends _MatChipMixinBase implements FocusableOption, OnDes
145145
/** Whether the chip list is selectable */
146146
chipListSelectable: boolean = true;
147147

148+
/** Whether the chip list is in multi-selection mode. */
149+
_chipListMultiple: boolean = false;
150+
148151
/** The chip avatar */
149152
@ContentChild(MatChipAvatar) avatar: MatChipAvatar;
150153

@@ -218,7 +221,10 @@ export class MatChip extends _MatChipMixinBase implements FocusableOption, OnDes
218221

219222
/** The ARIA selected applied to the chip. */
220223
get ariaSelected(): string | null {
221-
return this.selectable ? this.selected.toString() : null;
224+
// Remove the `aria-selected` when the chip is deselected in single-selection mode, because
225+
// it adds noise to NVDA users where "not selected" will be read out for each chip.
226+
return this.selectable && (this._chipListMultiple || this.selected) ?
227+
this.selected.toString() : null;
222228
}
223229

224230
constructor(public _elementRef: ElementRef,

tools/public_api_guard/lib/chips.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ export declare const _MatChipMixinBase: CanColorCtor & CanDisableRippleCtor & Ca
55
export declare const MAT_CHIPS_DEFAULT_OPTIONS: InjectionToken<MatChipsDefaultOptions>;
66

77
export declare class MatChip extends _MatChipMixinBase implements FocusableOption, OnDestroy, CanColor, CanDisable, CanDisableRipple, RippleTarget {
8+
_chipListMultiple: boolean;
89
_elementRef: ElementRef;
910
_hasFocus: boolean;
1011
readonly _onBlur: Subject<MatChipEvent>;

0 commit comments

Comments
 (0)