Skip to content

Commit

Permalink
fix(autocomplete): closing parent overlay when pressing escpe (#13413)
Browse files Browse the repository at this point in the history
Currently we only listen for keydown events on the autocomplete trigger, however because the escape handler doesn't go through the `OverlayKeyboardDispatcher`, it means parent overlay will still receive the event, causing unintended side effects like the parent dialog closing when escape is pressed on an autocomplete. These changes switch the closing actions to go through the `OverlayRef.keydownEvents()`.
  • Loading branch information
crisbeto authored and vivian-hu-zz committed Oct 4, 2018
1 parent a620fca commit 8dfd2ee
Show file tree
Hide file tree
Showing 2 changed files with 17 additions and 16 deletions.
19 changes: 12 additions & 7 deletions src/lib/autocomplete/autocomplete-trigger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -365,13 +365,7 @@ export class MatAutocompleteTrigger implements ControlValueAccessor, OnDestroy {
event.preventDefault();
}

// Close when pressing ESCAPE or ALT + UP_ARROW, based on the a11y guidelines.
// See: https://www.w3.org/TR/wai-aria-practices-1.1/#textbox-keyboard-interaction
if (this.panelOpen && (keyCode === ESCAPE || (keyCode === UP_ARROW && event.altKey))) {
this._resetActiveItem();
this._closeKeyEventStream.next();
event.stopPropagation();
} else if (this.activeOption && keyCode === ENTER && this.panelOpen) {
if (this.activeOption && keyCode === ENTER && this.panelOpen) {
this.activeOption._selectViaInteraction();
this._resetActiveItem();
event.preventDefault();
Expand Down Expand Up @@ -574,6 +568,17 @@ export class MatAutocompleteTrigger implements ControlValueAccessor, OnDestroy {
this._portal = new TemplatePortal(this.autocomplete.template, this._viewContainerRef);
this._overlayRef = this._overlay.create(this._getOverlayConfig());

// Use the `keydownEvents` in order to take advantage of
// the overlay event targeting provided by the CDK overlay.
this._overlayRef.keydownEvents().subscribe(event => {
// Close when pressing ESCAPE or ALT + UP_ARROW, based on the a11y guidelines.
// See: https://www.w3.org/TR/wai-aria-practices-1.1/#textbox-keyboard-interaction
if (event.keyCode === ESCAPE || (event.keyCode === UP_ARROW && event.altKey)) {
this._resetActiveItem();
this._closeKeyEventStream.next();
}
});

if (this._viewportRuler) {
this._viewportSubscription = this._viewportRuler.change().subscribe(() => {
if (this.panelOpen && this._overlayRef) {
Expand Down
14 changes: 5 additions & 9 deletions src/lib/autocomplete/autocomplete.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
dispatchKeyboardEvent,
MockNgZone,
typeInElement,
dispatchEvent,
} from '@angular/cdk/testing';
import {
ChangeDetectionStrategy,
Expand Down Expand Up @@ -1043,8 +1044,6 @@ describe('MatAutocomplete', () => {

it('should close the panel when pressing escape', fakeAsync(() => {
const trigger = fixture.componentInstance.trigger;
const escapeEvent = createKeyboardEvent('keydown', ESCAPE);
const stopPropagationSpy = spyOn(escapeEvent, 'stopPropagation').and.callThrough();

input.focus();
flush();
Expand All @@ -1053,12 +1052,11 @@ describe('MatAutocomplete', () => {
expect(document.activeElement).toBe(input, 'Expected input to be focused.');
expect(trigger.panelOpen).toBe(true, 'Expected panel to be open.');

trigger._handleKeydown(escapeEvent);
dispatchKeyboardEvent(document.body, 'keydown', ESCAPE);
fixture.detectChanges();

expect(document.activeElement).toBe(input, 'Expected input to continue to be focused.');
expect(trigger.panelOpen).toBe(false, 'Expected panel to be closed.');
expect(stopPropagationSpy).toHaveBeenCalled();
}));

it('should prevent the default action when pressing escape', fakeAsync(() => {
Expand All @@ -1080,7 +1078,7 @@ describe('MatAutocomplete', () => {
expect(document.activeElement).toBe(input, 'Expected input to be focused.');
expect(trigger.panelOpen).toBe(true, 'Expected panel to be open.');

trigger._handleKeydown(upArrowEvent);
dispatchEvent(document.body, upArrowEvent);
fixture.detectChanges();

expect(document.activeElement).toBe(input, 'Expected input to continue to be focused.');
Expand Down Expand Up @@ -1125,7 +1123,7 @@ describe('MatAutocomplete', () => {
// from crashing when trying to stringify the option if the test fails.
expect(!!trigger.activeOption).toBe(true, 'Expected to find an active option.');

trigger._handleKeydown(createKeyboardEvent('keydown', ESCAPE));
dispatchKeyboardEvent(document.body, 'keydown', ESCAPE);
tick();

expect(!!trigger.activeOption).toBe(false, 'Expected no active options.');
Expand Down Expand Up @@ -1782,10 +1780,8 @@ describe('MatAutocomplete', () => {
});

it('should close the panel when pressing escape', () => {
const escapeEvent = createKeyboardEvent('keydown', ESCAPE);

expect(closingActionSpy).not.toHaveBeenCalled();
trigger._handleKeydown(escapeEvent);
dispatchKeyboardEvent(document.body, 'keydown', ESCAPE);
expect(closingActionSpy).toHaveBeenCalledWith(null);
});
});
Expand Down

0 comments on commit 8dfd2ee

Please sign in to comment.