fix(autocomplete): emit closing action for escape keydown event (#6250)

This commit is contained in:
Will Howell
2017-10-02 20:14:50 -04:00
committed by Kara
parent b5ada20301
commit f4673a59af
2 changed files with 72 additions and 1 deletions
+7 -1
View File
@@ -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';
@@ -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 = () => {};
@@ -145,6 +149,7 @@ export class MatAutocompleteTrigger implements ControlValueAccessor, OnDestroy {
ngOnDestroy() {
this._destroyPanel();
this._escapeEventStream.complete();
}
/* Whether or not the autocomplete panel is open. */
@@ -186,6 +191,7 @@ export class MatAutocompleteTrigger implements ControlValueAccessor, OnDestroy {
return merge(
this.optionSelections,
this.autocomplete._keyManager.tabOut,
this._escapeEventStream,
this._outsideClickStream
);
}
@@ -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();
+65
View File
@@ -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>;