Skip to content

Commit 17e217a

Browse files
authored
fix(material/chips): aria-selected not reflecting selection state (#25742)
When the chip listbox is in single selection mode, it needs to remove `aria-selected` from the selected option and move it to the newly-selected one. The listbox was doing this already, but change detection wasn't being triggered which meant that the DOM was out of date.
1 parent 9769a5a commit 17e217a

File tree

2 files changed

+79
-8
lines changed

2 files changed

+79
-8
lines changed

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

Lines changed: 76 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import {
55
dispatchKeyboardEvent,
66
MockNgZone,
77
patchElementFocus,
8-
} from '../../cdk/testing/private';
8+
} from '@angular/cdk/testing/private';
99
import {
1010
Component,
1111
DebugElement,
@@ -412,11 +412,8 @@ describe('MDC-based MatChipListbox', () => {
412412
});
413413

414414
describe('selection logic', () => {
415-
beforeEach(() => {
416-
fixture = createComponent(BasicChipListbox);
417-
});
418-
419415
it('should remove selection if chip has been removed', fakeAsync(() => {
416+
fixture = createComponent(BasicChipListbox);
420417
const instanceChips = fixture.componentInstance.chips;
421418
const chipListbox = fixture.componentInstance.chipListbox;
422419
dispatchKeyboardEvent(primaryActions[0], 'keydown', SPACE);
@@ -439,6 +436,7 @@ describe('MDC-based MatChipListbox', () => {
439436
}));
440437

441438
it('should select an option that was added after initialization', () => {
439+
fixture = createComponent(BasicChipListbox);
442440
fixture.componentInstance.foods.push({viewValue: 'Potatoes', value: 'potatoes-8'});
443441
fixture.detectChanges();
444442

@@ -458,6 +456,7 @@ describe('MDC-based MatChipListbox', () => {
458456
});
459457

460458
it('should not select disabled chips', () => {
459+
fixture = createComponent(BasicChipListbox);
461460
const array = chips.toArray();
462461
dispatchKeyboardEvent(primaryActions[2], 'keydown', SPACE);
463462
fixture.detectChanges();
@@ -472,9 +471,6 @@ describe('MDC-based MatChipListbox', () => {
472471
});
473472

474473
it('should not select when is not selectable', fakeAsync(() => {
475-
fixture.destroy();
476-
TestBed.resetTestingModule();
477-
478474
const falsyFixture = createComponent(FalsyBasicChipListbox);
479475
falsyFixture.detectChanges();
480476
tick();
@@ -498,6 +494,78 @@ describe('MDC-based MatChipListbox', () => {
498494
.withContext('Expected first option not to be selected.')
499495
.toBe(false);
500496
}));
497+
498+
it('should set `aria-selected` based on the selection state in single selection mode', fakeAsync(() => {
499+
const getAriaSelected = () =>
500+
Array.from(primaryActions).map(action => action.getAttribute('aria-selected'));
501+
502+
fixture = createComponent(BasicChipListbox);
503+
// Use a shorter list so we can keep the assertions smaller
504+
fixture.componentInstance.foods = [
505+
{value: 'steak-0', viewValue: 'Steak'},
506+
{value: 'pizza-1', viewValue: 'Pizza'},
507+
{value: 'tacos-2', viewValue: 'Tacos'},
508+
];
509+
fixture.componentInstance.selectable = true;
510+
fixture.detectChanges();
511+
512+
primaryActions = chipListboxNativeElement.querySelectorAll<HTMLElement>(
513+
'.mdc-evolution-chip__action--primary',
514+
);
515+
516+
expect(getAriaSelected()).toEqual([null, null, null]);
517+
518+
primaryActions[1].click();
519+
fixture.detectChanges();
520+
expect(getAriaSelected()).toEqual([null, 'true', null]);
521+
522+
primaryActions[2].click();
523+
fixture.detectChanges();
524+
expect(getAriaSelected()).toEqual([null, null, 'true']);
525+
526+
primaryActions[0].click();
527+
fixture.detectChanges();
528+
expect(getAriaSelected()).toEqual(['true', null, null]);
529+
}));
530+
531+
it('should set `aria-selected` based on the selection state in multi-selection mode', fakeAsync(() => {
532+
const getAriaSelected = () =>
533+
Array.from(primaryActions).map(action => action.getAttribute('aria-selected'));
534+
535+
fixture = createComponent(MultiSelectionChipListbox);
536+
fixture.detectChanges();
537+
538+
// Use a shorter list so we can keep the assertions smaller
539+
fixture.componentInstance.foods = [
540+
{value: 'steak-0', viewValue: 'Steak'},
541+
{value: 'pizza-1', viewValue: 'Pizza'},
542+
{value: 'tacos-2', viewValue: 'Tacos'},
543+
];
544+
fixture.componentInstance.selectable = true;
545+
fixture.detectChanges();
546+
547+
primaryActions = chipListboxNativeElement.querySelectorAll<HTMLElement>(
548+
'.mdc-evolution-chip__action--primary',
549+
);
550+
551+
expect(getAriaSelected()).toEqual([null, null, null]);
552+
553+
primaryActions[1].click();
554+
fixture.detectChanges();
555+
expect(getAriaSelected()).toEqual([null, 'true', null]);
556+
557+
primaryActions[2].click();
558+
fixture.detectChanges();
559+
expect(getAriaSelected()).toEqual([null, 'true', 'true']);
560+
561+
primaryActions[0].click();
562+
fixture.detectChanges();
563+
expect(getAriaSelected()).toEqual(['true', 'true', 'true']);
564+
565+
primaryActions[1].click();
566+
fixture.detectChanges();
567+
expect(getAriaSelected()).toEqual(['true', null, 'true']);
568+
}));
501569
});
502570

503571
describe('chip list with chip input', () => {

src/material/chips/chip-option.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ export class MatChipOption extends MatChip implements OnInit {
9292
}
9393
set selectable(value: BooleanInput) {
9494
this._selectable = coerceBooleanProperty(value);
95+
this._changeDetectorRef.markForCheck();
9596
}
9697
protected _selectable: boolean = true;
9798

@@ -168,6 +169,8 @@ export class MatChipOption extends MatChip implements OnInit {
168169
selected: this.selected,
169170
});
170171
}
172+
173+
this._changeDetectorRef.markForCheck();
171174
}
172175
}
173176
}

0 commit comments

Comments
 (0)