Skip to content

Commit

Permalink
fix(autocomplete): emit closing action for escape keydown event
Browse files Browse the repository at this point in the history
  • Loading branch information
willshowell committed Sep 25, 2017
1 parent 3571f68 commit c6cd992
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 2 deletions.
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 {MdOption, MdOptionSelectionChange} from '@angular/material/core';
import {MdFormField} 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 @@ -127,6 +128,9 @@ export class MdAutocompleteTrigger 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 Down Expand Up @@ -157,6 +161,7 @@ export class MdAutocompleteTrigger implements ControlValueAccessor, OnDestroy {

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

/* Whether or not the autocomplete panel is open. */
Expand Down Expand Up @@ -198,6 +203,7 @@ export class MdAutocompleteTrigger implements ControlValueAccessor, OnDestroy {
return merge(
this.optionSelections,
this.autocomplete._keyManager.tabOut,
this._escapeEventStream,
this._outsideClickStream
);
}
Expand Down Expand Up @@ -272,7 +278,7 @@ export class MdAutocompleteTrigger implements ControlValueAccessor, OnDestroy {
_handleKeydown(event: KeyboardEvent): void {
if (event.keyCode === ESCAPE && this.panelOpen) {
this._resetActiveItem();
this.closePanel();
this._escapeEventStream.next();
event.stopPropagation();
} else if (this.activeOption && event.keyCode === ENTER && this.panelOpen) {
this.activeOption._selectViaInteraction();
Expand Down
67 changes: 66 additions & 1 deletion src/lib/autocomplete/autocomplete.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {Direction, Directionality} from '@angular/cdk/bidi';
import {DOWN_ARROW, ENTER, ESCAPE, SPACE, UP_ARROW} from '@angular/cdk/keycodes';
import {DOWN_ARROW, ENTER, ESCAPE, SPACE, TAB, UP_ARROW} from '@angular/cdk/keycodes';
import {OverlayContainer} from '@angular/cdk/overlay';
import {map, RxChain, startWith} from '@angular/cdk/rxjs';
import {ScrollDispatcher} from '@angular/cdk/scrolling';
Expand Down Expand Up @@ -1359,6 +1359,71 @@ describe('MdAutocomplete', () => {
}));
});

describe('panel closing', () => {
let fixture: ComponentFixture<SimpleAutocomplete>;
let input: HTMLInputElement;
let trigger: MdAutocompleteTrigger;
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('md-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 mdInput', () => {
let fixture: ComponentFixture<AutocompleteWithNativeInput>;

Expand Down

0 comments on commit c6cd992

Please sign in to comment.