Skip to content

Commit

Permalink
fix(autocomplete): emit closing action for escape keydown event (#6250)
Browse files Browse the repository at this point in the history
  • Loading branch information
willshowell authored and kara committed Oct 3, 2017
1 parent b5ada20 commit f4673a5
Show file tree
Hide file tree
Showing 2 changed files with 72 additions and 1 deletion.
8 changes: 7 additions & 1 deletion src/lib/autocomplete/autocomplete-trigger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import {MatOption, MatOptionSelectionChange} from '@angular/material/core';
import {MatFormField} from '@angular/material/form-field';
import {DOCUMENT} from '@angular/platform-browser';
import {Observable} from 'rxjs/Observable';
import {Subject} from 'rxjs/Subject';
import {fromEvent} from 'rxjs/observable/fromEvent';
import {merge} from 'rxjs/observable/merge';
import {of as observableOf} from 'rxjs/observable/of';
Expand Down Expand Up @@ -125,6 +126,9 @@ export class MatAutocompleteTrigger implements ControlValueAccessor, OnDestroy {
/** The subscription for closing actions (some are bound to document). */
private _closingActionsSubscription: Subscription;

/** Stream of escape keyboard events. */
private _escapeEventStream = new Subject<void>();

/** View -> model callback called when value changes */
_onChange: (value: any) => void = () => {};

Expand All @@ -145,6 +149,7 @@ export class MatAutocompleteTrigger implements ControlValueAccessor, OnDestroy {

ngOnDestroy() {
this._destroyPanel();
this._escapeEventStream.complete();
}

/* Whether or not the autocomplete panel is open. */
Expand Down Expand Up @@ -186,6 +191,7 @@ export class MatAutocompleteTrigger implements ControlValueAccessor, OnDestroy {
return merge(
this.optionSelections,
this.autocomplete._keyManager.tabOut,
this._escapeEventStream,
this._outsideClickStream
);
}
Expand Down Expand Up @@ -262,7 +268,7 @@ export class MatAutocompleteTrigger implements ControlValueAccessor, OnDestroy {

if (keyCode === ESCAPE && this.panelOpen) {
this._resetActiveItem();
this.closePanel();
this._escapeEventStream.next();
event.stopPropagation();
} else if (this.activeOption && keyCode === ENTER && this.panelOpen) {
this.activeOption._selectViaInteraction();
Expand Down
65 changes: 65 additions & 0 deletions src/lib/autocomplete/autocomplete.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1403,6 +1403,71 @@ describe('MatAutocomplete', () => {
}));
});

describe('panel closing', () => {
let fixture: ComponentFixture<SimpleAutocomplete>;
let input: HTMLInputElement;
let trigger: MatAutocompleteTrigger;
let closingActionSpy: jasmine.Spy;
let closingActionsSub: Subscription;

beforeEach(() => {
fixture = TestBed.createComponent(SimpleAutocomplete);
fixture.detectChanges();

input = fixture.debugElement.query(By.css('input')).nativeElement;

fixture.componentInstance.trigger.openPanel();
fixture.detectChanges();

trigger = fixture.componentInstance.trigger;
closingActionSpy = jasmine.createSpy('closing action listener');
closingActionsSub = trigger.panelClosingActions.subscribe(closingActionSpy);
});

afterEach(() => {
closingActionsSub.unsubscribe();
});

it('should emit panel close event when clicking away', async(() => {
fixture.whenStable().then(() => {
expect(closingActionSpy).not.toHaveBeenCalled();
dispatchFakeEvent(document, 'click');
expect(closingActionSpy).toHaveBeenCalled();
});
}));

it('should emit panel close event when tabbing out', async(() => {
const tabEvent = createKeyboardEvent('keydown', TAB);
input.focus();

fixture.whenStable().then(() => {
expect(closingActionSpy).not.toHaveBeenCalled();
trigger._handleKeydown(tabEvent);
expect(closingActionSpy).toHaveBeenCalled();
});
}));

it('should emit panel close event when selecting an option', async(() => {
fixture.whenStable().then(() => {
const option = overlayContainerElement.querySelector('mat-option') as HTMLElement;

expect(closingActionSpy).not.toHaveBeenCalled();
option.click();
expect(closingActionSpy).toHaveBeenCalled();
});
}));

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

fixture.whenStable().then(() => {
expect(closingActionSpy).not.toHaveBeenCalled();
trigger._handleKeydown(escapeEvent);
expect(closingActionSpy).toHaveBeenCalled();
});
}));
});

describe('without matInput', () => {
let fixture: ComponentFixture<AutocompleteWithNativeInput>;

Expand Down

0 comments on commit f4673a5

Please sign in to comment.