Skip to content

Commit 0c7ce7a

Browse files
crisbetojelbourn
authored andcommittedDec 3, 2018
fix(list): don't handle events when modifier key is pressed (#14313)
Doesn't `preventDefault` and handle ENTER/SPACE/HOME/END keyboard events when a modifier is pressed, in order to avoid clashing with native OS shortcuts.
1 parent b00b3bb commit 0c7ce7a

File tree

2 files changed

+69
-7
lines changed

2 files changed

+69
-7
lines changed
 

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

+48
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,26 @@ describe('MatSelectionList without forms', () => {
196196
expect(ENTER_EVENT.defaultPrevented).toBe(true);
197197
});
198198

199+
it('should not be able to toggle an item when pressing a modifier key', () => {
200+
const testListItem = listOptions[1].nativeElement as HTMLElement;
201+
const selectList =
202+
selectionList.injector.get<MatSelectionList>(MatSelectionList).selectedOptions;
203+
204+
expect(selectList.selected.length).toBe(0);
205+
206+
[ENTER, SPACE].forEach(key => {
207+
const event = createKeyboardEvent('keydown', key, testListItem);
208+
Object.defineProperty(event, 'ctrlKey', { get: () => true });
209+
210+
dispatchFakeEvent(testListItem, 'focus');
211+
selectionList.componentInstance._keydown(event);
212+
fixture.detectChanges();
213+
expect(event.defaultPrevented).toBe(false);
214+
});
215+
216+
expect(selectList.selected.length).toBe(0);
217+
});
218+
199219
it('should not be able to toggle a disabled option using SPACE', () => {
200220
const testListItem = listOptions[1].nativeElement as HTMLElement;
201221
const selectionModel = selectionList.componentInstance.selectedOptions;
@@ -332,6 +352,20 @@ describe('MatSelectionList without forms', () => {
332352
expect(event.defaultPrevented).toBe(true);
333353
});
334354

355+
it('should not change focus when pressing HOME with a modifier key', () => {
356+
const manager = selectionList.componentInstance._keyManager;
357+
expect(manager.activeItemIndex).toBe(-1);
358+
359+
const event = createKeyboardEvent('keydown', HOME);
360+
Object.defineProperty(event, 'shiftKey', { get: () => true });
361+
362+
dispatchEvent(selectionList.nativeElement, event);
363+
fixture.detectChanges();
364+
365+
expect(manager.activeItemIndex).toBe(-1);
366+
expect(event.defaultPrevented).toBe(false);
367+
});
368+
335369
it('should focus the last item when pressing END', () => {
336370
const manager = selectionList.componentInstance._keyManager;
337371
expect(manager.activeItemIndex).toBe(-1);
@@ -343,6 +377,20 @@ describe('MatSelectionList without forms', () => {
343377
expect(event.defaultPrevented).toBe(true);
344378
});
345379

380+
it('should not change focus when pressing END with a modifier key', () => {
381+
const manager = selectionList.componentInstance._keyManager;
382+
expect(manager.activeItemIndex).toBe(-1);
383+
384+
const event = createKeyboardEvent('keydown', END);
385+
Object.defineProperty(event, 'shiftKey', { get: () => true });
386+
387+
dispatchEvent(selectionList.nativeElement, event);
388+
fixture.detectChanges();
389+
390+
expect(manager.activeItemIndex).toBe(-1);
391+
expect(event.defaultPrevented).toBe(false);
392+
});
393+
346394
it('should select all items using ctrl + a', () => {
347395
const event = createKeyboardEvent('keydown', A, selectionList.nativeElement);
348396
Object.defineProperty(event, 'ctrlKey', {get: () => true});

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

+21-7
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,16 @@
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, UP_ARROW, DOWN_ARROW, A} from '@angular/cdk/keycodes';
12+
import {
13+
SPACE,
14+
ENTER,
15+
HOME,
16+
END,
17+
UP_ARROW,
18+
DOWN_ARROW,
19+
A,
20+
hasModifierKey,
21+
} from '@angular/cdk/keycodes';
1322
import {
1423
AfterContentInit,
1524
Attribute,
@@ -417,21 +426,26 @@ export class MatSelectionList extends _MatSelectionListMixinBase implements Focu
417426
const keyCode = event.keyCode;
418427
const manager = this._keyManager;
419428
const previousFocusIndex = manager.activeItemIndex;
429+
const hasModifier = hasModifierKey(event);
420430

421431
switch (keyCode) {
422432
case SPACE:
423433
case ENTER:
424-
this._toggleFocusedOption();
425-
// Always prevent space from scrolling the page since the list has focus
426-
event.preventDefault();
434+
if (!hasModifier) {
435+
this._toggleFocusedOption();
436+
// Always prevent space from scrolling the page since the list has focus
437+
event.preventDefault();
438+
}
427439
break;
428440
case HOME:
429441
case END:
430-
keyCode === HOME ? manager.setFirstItemActive() : manager.setLastItemActive();
431-
event.preventDefault();
442+
if (!hasModifier) {
443+
keyCode === HOME ? manager.setFirstItemActive() : manager.setLastItemActive();
444+
event.preventDefault();
445+
}
432446
break;
433447
case A:
434-
if (event.ctrlKey) {
448+
if (hasModifierKey(event, 'ctrlKey')) {
435449
this.options.find(option => !option.selected) ? this.selectAll() : this.deselectAll();
436450
event.preventDefault();
437451
}

0 commit comments

Comments
 (0)
Please sign in to comment.