Skip to content

Commit 975fe7e

Browse files
crisbetommalerba
authored andcommitted
fix(selection-list): toggle newly-focused item when pressing arrow key + shift (#10828)
[Based on the accessibility guidelines](https://www.w3.org/TR/wai-aria-practices-1.1/), multi-select listboxes can support toggling items by pressing the arrow keys + shift. These changes implement the behavior in the selection list.
1 parent 2ed5f7d commit 975fe7e

File tree

2 files changed

+61
-5
lines changed

2 files changed

+61
-5
lines changed

src/lib/list/selection-list.spec.ts

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,30 @@ describe('MatSelectionList without forms', () => {
233233
expect(manager.activeItemIndex).toEqual(1);
234234
});
235235

236+
it('should focus and toggle the next item when pressing SHIFT + UP_ARROW', () => {
237+
const manager = selectionList.componentInstance._keyManager;
238+
const upKeyEvent = createKeyboardEvent('keydown', UP_ARROW);
239+
Object.defineProperty(upKeyEvent, 'shiftKey', {get: () => true});
240+
241+
dispatchFakeEvent(listOptions[3].nativeElement, 'focus');
242+
expect(manager.activeItemIndex).toBe(3);
243+
244+
expect(listOptions[1].componentInstance.selected).toBe(false);
245+
expect(listOptions[2].componentInstance.selected).toBe(false);
246+
247+
selectionList.componentInstance._keydown(upKeyEvent);
248+
fixture.detectChanges();
249+
250+
expect(listOptions[1].componentInstance.selected).toBe(false);
251+
expect(listOptions[2].componentInstance.selected).toBe(true);
252+
253+
selectionList.componentInstance._keydown(upKeyEvent);
254+
fixture.detectChanges();
255+
256+
expect(listOptions[1].componentInstance.selected).toBe(true);
257+
expect(listOptions[2].componentInstance.selected).toBe(true);
258+
});
259+
236260
it('should focus next item when press DOWN ARROW', () => {
237261
const manager = selectionList.componentInstance._keyManager;
238262

@@ -245,6 +269,30 @@ describe('MatSelectionList without forms', () => {
245269
expect(manager.activeItemIndex).toEqual(3);
246270
});
247271

272+
it('should focus and toggle the next item when pressing SHIFT + DOWN_ARROW', () => {
273+
const manager = selectionList.componentInstance._keyManager;
274+
const downKeyEvent = createKeyboardEvent('keydown', DOWN_ARROW);
275+
Object.defineProperty(downKeyEvent, 'shiftKey', {get: () => true});
276+
277+
dispatchFakeEvent(listOptions[0].nativeElement, 'focus');
278+
expect(manager.activeItemIndex).toBe(0);
279+
280+
expect(listOptions[1].componentInstance.selected).toBe(false);
281+
expect(listOptions[2].componentInstance.selected).toBe(false);
282+
283+
selectionList.componentInstance._keydown(downKeyEvent);
284+
fixture.detectChanges();
285+
286+
expect(listOptions[1].componentInstance.selected).toBe(true);
287+
expect(listOptions[2].componentInstance.selected).toBe(false);
288+
289+
selectionList.componentInstance._keydown(downKeyEvent);
290+
fixture.detectChanges();
291+
292+
expect(listOptions[1].componentInstance.selected).toBe(true);
293+
expect(listOptions[2].componentInstance.selected).toBe(true);
294+
});
295+
248296
it('should be able to focus the first item when pressing HOME', () => {
249297
const manager = selectionList.componentInstance._keyManager;
250298
expect(manager.activeItemIndex).toBe(-1);

src/lib/list/selection-list.ts

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
import {FocusableOption, FocusKeyManager} from '@angular/cdk/a11y';
1010
import {coerceBooleanProperty} from '@angular/cdk/coercion';
1111
import {SelectionModel} from '@angular/cdk/collections';
12-
import {SPACE, ENTER, HOME, END} from '@angular/cdk/keycodes';
12+
import {SPACE, ENTER, HOME, END, UP_ARROW, DOWN_ARROW} from '@angular/cdk/keycodes';
1313
import {
1414
AfterContentInit,
1515
Attribute,
@@ -370,7 +370,11 @@ export class MatSelectionList extends _MatSelectionListMixinBase implements Focu
370370

371371
/** Passes relevant key presses to our key manager. */
372372
_keydown(event: KeyboardEvent) {
373-
switch (event.keyCode) {
373+
const keyCode = event.keyCode;
374+
const manager = this._keyManager;
375+
const previousFocusIndex = manager.activeItemIndex;
376+
377+
switch (keyCode) {
374378
case SPACE:
375379
case ENTER:
376380
if (!this.disabled) {
@@ -382,12 +386,16 @@ export class MatSelectionList extends _MatSelectionListMixinBase implements Focu
382386
break;
383387
case HOME:
384388
case END:
385-
event.keyCode === HOME ? this._keyManager.setFirstItemActive() :
386-
this._keyManager.setLastItemActive();
389+
keyCode === HOME ? manager.setFirstItemActive() : manager.setLastItemActive();
387390
event.preventDefault();
388391
break;
389392
default:
390-
this._keyManager.onKeydown(event);
393+
manager.onKeydown(event);
394+
}
395+
396+
if ((keyCode === UP_ARROW || keyCode === DOWN_ARROW) && event.shiftKey &&
397+
manager.activeItemIndex !== previousFocusIndex) {
398+
this._toggleSelectOnFocusedOption();
391399
}
392400
}
393401

0 commit comments

Comments
 (0)