Skip to content

Commit

Permalink
fix(material/autocomplete): don't block default arrow keys when using…
Browse files Browse the repository at this point in the history
… modifiers (#11987)

Currently we hijack all up/down arrow key events, however this interferes with keyboard shortcuts such as shift + up arrow for marking all of the text. These changes stop intercepting the arrow keys, if they're used with a modifier.

These changes also fix an issue where all the mocked out key events had the `metaKey` set to `true` on some browsers.
  • Loading branch information
crisbeto committed Feb 24, 2022
1 parent ca6b9fa commit 5825faf
Show file tree
Hide file tree
Showing 3 changed files with 32 additions and 3 deletions.
14 changes: 14 additions & 0 deletions src/material-experimental/mdc-autocomplete/autocomplete.spec.ts
Expand Up @@ -1470,6 +1470,20 @@ describe('MDC-based MatAutocomplete', () => {

expect(!!trigger.activeOption).withContext('Expected no active options.').toBe(false);
}));

it('should not prevent the default action when a modifier key is pressed', () => {
['metaKey', 'ctrlKey', 'altKey', 'shiftKey'].forEach(name => {
const event = createKeyboardEvent('keydown', DOWN_ARROW);
Object.defineProperty(event, name, {get: () => true});

fixture.componentInstance.trigger._handleKeydown(event);
fixture.detectChanges();

expect(event.defaultPrevented)
.withContext(`Expected autocompete not to block ${name} key`)
.toBe(false);
});
});
});

describe('option groups', () => {
Expand Down
7 changes: 4 additions & 3 deletions src/material/autocomplete/autocomplete-trigger.ts
Expand Up @@ -390,24 +390,25 @@ export abstract class _MatAutocompleteTriggerBase

_handleKeydown(event: KeyboardEvent): void {
const keyCode = event.keyCode;
const hasModifier = hasModifierKey(event);

// Prevent the default action on all escape key presses. This is here primarily to bring IE
// in line with other browsers. By default, pressing escape on IE will cause it to revert
// the input value to the one that it had on focus, however it won't dispatch any events
// which means that the model value will be out of sync with the view.
if (keyCode === ESCAPE && !hasModifierKey(event)) {
if (keyCode === ESCAPE && !hasModifier) {
event.preventDefault();
}

if (this.activeOption && keyCode === ENTER && this.panelOpen && !hasModifierKey(event)) {
if (this.activeOption && keyCode === ENTER && this.panelOpen && !hasModifier) {
this.activeOption._selectViaInteraction();
this._resetActiveItem();
event.preventDefault();
} else if (this.autocomplete) {
const prevActiveItem = this.autocomplete._keyManager.activeItem;
const isArrowKey = keyCode === UP_ARROW || keyCode === DOWN_ARROW;

if (this.panelOpen || keyCode === TAB) {
if (keyCode === TAB || (isArrowKey && !hasModifier && this.panelOpen)) {
this.autocomplete._keyManager.onKeydown(event);
} else if (isArrowKey && this._canOpen()) {
this.openPanel();
Expand Down
14 changes: 14 additions & 0 deletions src/material/autocomplete/autocomplete.spec.ts
Expand Up @@ -1453,6 +1453,20 @@ describe('MatAutocomplete', () => {

expect(!!trigger.activeOption).withContext('Expected no active options.').toBe(false);
}));

it('should not prevent the default action when a modifier key is pressed', () => {
['metaKey', 'ctrlKey', 'altKey', 'shiftKey'].forEach(name => {
const event = createKeyboardEvent('keydown', DOWN_ARROW);
Object.defineProperty(event, name, {get: () => true});

fixture.componentInstance.trigger._handleKeydown(event);
fixture.detectChanges();

expect(event.defaultPrevented)
.withContext(`Expected autocompete not to block ${name} key`)
.toBe(false);
});
});
});

describe('option groups', () => {
Expand Down

0 comments on commit 5825faf

Please sign in to comment.