Skip to content

Commit 76055a4

Browse files
crisbetojelbourn
authored andcommitted
fix(select): support using shift + arrow key to toggle items in a multi-select (angular#9037)
Based on the accessibility guidelines for a multi-selection listbox (https://www.w3.org/TR/wai-aria-practices-1.1/#Listbox), potentially we can support toggling the previous/next item if the user presses shift + up/down arrow.
1 parent 3e99bcc commit 76055a4

File tree

2 files changed

+70
-0
lines changed

2 files changed

+70
-0
lines changed

src/lib/select/select.spec.ts

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -403,6 +403,68 @@ describe('MatSelect', () => {
403403
.toBe(false, 'Expected panel to stay closed.');
404404
}));
405405

406+
it('should toggle the next option when pressing shift + DOWN_ARROW on a multi-select',
407+
fakeAsync(() => {
408+
fixture.destroy();
409+
410+
const multiFixture = TestBed.createComponent(MultiSelect);
411+
const event = createKeyboardEvent('keydown', DOWN_ARROW);
412+
Object.defineProperty(event, 'shiftKey', {get: () => true});
413+
414+
multiFixture.detectChanges();
415+
select = multiFixture.debugElement.query(By.css('mat-select')).nativeElement;
416+
417+
multiFixture.componentInstance.select.open();
418+
multiFixture.detectChanges();
419+
flush();
420+
421+
expect(multiFixture.componentInstance.select.value).toBeFalsy();
422+
423+
dispatchEvent(select, event);
424+
multiFixture.detectChanges();
425+
426+
expect(multiFixture.componentInstance.select.value).toEqual(['pizza-1']);
427+
428+
dispatchEvent(select, event);
429+
multiFixture.detectChanges();
430+
431+
expect(multiFixture.componentInstance.select.value).toEqual(['pizza-1', 'tacos-2']);
432+
}));
433+
434+
it('should toggle the previous option when pressing shift + UP_ARROW on a multi-select',
435+
fakeAsync(() => {
436+
fixture.destroy();
437+
438+
const multiFixture = TestBed.createComponent(MultiSelect);
439+
const event = createKeyboardEvent('keydown', UP_ARROW);
440+
Object.defineProperty(event, 'shiftKey', {get: () => true});
441+
442+
multiFixture.detectChanges();
443+
select = multiFixture.debugElement.query(By.css('mat-select')).nativeElement;
444+
445+
multiFixture.componentInstance.select.open();
446+
multiFixture.detectChanges();
447+
flush();
448+
449+
// Move focus down first.
450+
for (let i = 0; i < 5; i++) {
451+
dispatchKeyboardEvent(select, 'keydown', DOWN_ARROW);
452+
multiFixture.detectChanges();
453+
}
454+
455+
expect(multiFixture.componentInstance.select.value).toBeFalsy();
456+
457+
dispatchEvent(select, event);
458+
multiFixture.detectChanges();
459+
460+
expect(multiFixture.componentInstance.select.value).toEqual(['chips-4']);
461+
462+
dispatchEvent(select, event);
463+
multiFixture.detectChanges();
464+
465+
expect(multiFixture.componentInstance.select.value).toEqual(['sandwich-3', 'chips-4']);
466+
}));
467+
406468
it('should prevent the default action when pressing space', fakeAsync(() => {
407469
const event = dispatchKeyboardEvent(select, 'keydown', SPACE);
408470
expect(event.defaultPrevented).toBe(true);

src/lib/select/select.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -662,7 +662,15 @@ export class MatSelect extends _MatSelectMixinBase implements AfterContentInit,
662662
event.preventDefault();
663663
this._keyManager.activeItem._selectViaInteraction();
664664
} else {
665+
const isArrowKey = keyCode === DOWN_ARROW || keyCode === UP_ARROW;
666+
const previouslyFocusedIndex = this._keyManager.activeItemIndex;
667+
665668
this._keyManager.onKeydown(event);
669+
670+
if (this._multiple && isArrowKey && event.shiftKey && this._keyManager.activeItem &&
671+
this._keyManager.activeItemIndex !== previouslyFocusedIndex) {
672+
this._keyManager.activeItem._selectViaInteraction();
673+
}
666674
}
667675
}
668676

0 commit comments

Comments
 (0)