From 76055a4eefae87b5a20507dd56d2e806f08da30d Mon Sep 17 00:00:00 2001 From: Kristiyan Kostadinov Date: Thu, 4 Jan 2018 21:59:37 +0200 Subject: [PATCH] fix(select): support using shift + arrow key to toggle items in a multi-select (#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. --- src/lib/select/select.spec.ts | 62 +++++++++++++++++++++++++++++++++++ src/lib/select/select.ts | 8 +++++ 2 files changed, 70 insertions(+) diff --git a/src/lib/select/select.spec.ts b/src/lib/select/select.spec.ts index 490ba33ac4fc..a932b10da025 100644 --- a/src/lib/select/select.spec.ts +++ b/src/lib/select/select.spec.ts @@ -403,6 +403,68 @@ describe('MatSelect', () => { .toBe(false, 'Expected panel to stay closed.'); })); + it('should toggle the next option when pressing shift + DOWN_ARROW on a multi-select', + fakeAsync(() => { + fixture.destroy(); + + const multiFixture = TestBed.createComponent(MultiSelect); + const event = createKeyboardEvent('keydown', DOWN_ARROW); + Object.defineProperty(event, 'shiftKey', {get: () => true}); + + multiFixture.detectChanges(); + select = multiFixture.debugElement.query(By.css('mat-select')).nativeElement; + + multiFixture.componentInstance.select.open(); + multiFixture.detectChanges(); + flush(); + + expect(multiFixture.componentInstance.select.value).toBeFalsy(); + + dispatchEvent(select, event); + multiFixture.detectChanges(); + + expect(multiFixture.componentInstance.select.value).toEqual(['pizza-1']); + + dispatchEvent(select, event); + multiFixture.detectChanges(); + + expect(multiFixture.componentInstance.select.value).toEqual(['pizza-1', 'tacos-2']); + })); + + it('should toggle the previous option when pressing shift + UP_ARROW on a multi-select', + fakeAsync(() => { + fixture.destroy(); + + const multiFixture = TestBed.createComponent(MultiSelect); + const event = createKeyboardEvent('keydown', UP_ARROW); + Object.defineProperty(event, 'shiftKey', {get: () => true}); + + multiFixture.detectChanges(); + select = multiFixture.debugElement.query(By.css('mat-select')).nativeElement; + + multiFixture.componentInstance.select.open(); + multiFixture.detectChanges(); + flush(); + + // Move focus down first. + for (let i = 0; i < 5; i++) { + dispatchKeyboardEvent(select, 'keydown', DOWN_ARROW); + multiFixture.detectChanges(); + } + + expect(multiFixture.componentInstance.select.value).toBeFalsy(); + + dispatchEvent(select, event); + multiFixture.detectChanges(); + + expect(multiFixture.componentInstance.select.value).toEqual(['chips-4']); + + dispatchEvent(select, event); + multiFixture.detectChanges(); + + expect(multiFixture.componentInstance.select.value).toEqual(['sandwich-3', 'chips-4']); + })); + it('should prevent the default action when pressing space', fakeAsync(() => { const event = dispatchKeyboardEvent(select, 'keydown', SPACE); expect(event.defaultPrevented).toBe(true); diff --git a/src/lib/select/select.ts b/src/lib/select/select.ts index 0cee44c76514..b2d73ab3de77 100644 --- a/src/lib/select/select.ts +++ b/src/lib/select/select.ts @@ -662,7 +662,15 @@ export class MatSelect extends _MatSelectMixinBase implements AfterContentInit, event.preventDefault(); this._keyManager.activeItem._selectViaInteraction(); } else { + const isArrowKey = keyCode === DOWN_ARROW || keyCode === UP_ARROW; + const previouslyFocusedIndex = this._keyManager.activeItemIndex; + this._keyManager.onKeydown(event); + + if (this._multiple && isArrowKey && event.shiftKey && this._keyManager.activeItem && + this._keyManager.activeItemIndex !== previouslyFocusedIndex) { + this._keyManager.activeItem._selectViaInteraction(); + } } }