Skip to content

Commit e134ecc

Browse files
committed
fix(autocomplete): emit closing action for escape keydown event
1 parent dcf3b27 commit e134ecc

File tree

2 files changed

+73
-2
lines changed

2 files changed

+73
-2
lines changed

src/lib/autocomplete/autocomplete-trigger.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ import {MdOption, MdOptionSelectionChange} from '@angular/material/core';
3838
import {MdFormField} from '@angular/material/form-field';
3939
import {DOCUMENT} from '@angular/platform-browser';
4040
import {Observable} from 'rxjs/Observable';
41+
import {Subject} from 'rxjs/Subject';
4142
import {fromEvent} from 'rxjs/observable/fromEvent';
4243
import {merge} from 'rxjs/observable/merge';
4344
import {of as observableOf} from 'rxjs/observable/of';
@@ -127,6 +128,9 @@ export class MdAutocompleteTrigger implements ControlValueAccessor, OnDestroy {
127128
/** The subscription for closing actions (some are bound to document). */
128129
private _closingActionsSubscription: Subscription;
129130

131+
/** Stream of escape keyboard events. */
132+
private _escapeEventStream = new Subject<void>();
133+
130134
/** View -> model callback called when value changes */
131135
_onChange: (value: any) => void = () => {};
132136

@@ -157,6 +161,7 @@ export class MdAutocompleteTrigger implements ControlValueAccessor, OnDestroy {
157161

158162
ngOnDestroy() {
159163
this._destroyPanel();
164+
this._escapeEventStream.complete();
160165
}
161166

162167
/* Whether or not the autocomplete panel is open. */
@@ -198,6 +203,7 @@ export class MdAutocompleteTrigger implements ControlValueAccessor, OnDestroy {
198203
return merge(
199204
this.optionSelections,
200205
this.autocomplete._keyManager.tabOut,
206+
this._escapeEventStream,
201207
this._outsideClickStream
202208
);
203209
}
@@ -272,7 +278,7 @@ export class MdAutocompleteTrigger implements ControlValueAccessor, OnDestroy {
272278
_handleKeydown(event: KeyboardEvent): void {
273279
if (event.keyCode === ESCAPE && this.panelOpen) {
274280
this._resetActiveItem();
275-
this.closePanel();
281+
this._escapeEventStream.next();
276282
event.stopPropagation();
277283
} else if (this.activeOption && event.keyCode === ENTER && this.panelOpen) {
278284
this.activeOption._selectViaInteraction();

src/lib/autocomplete/autocomplete.spec.ts

Lines changed: 66 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import {
2525
} from './index';
2626
import {MdInputModule} from '../input/index';
2727
import {Subscription} from 'rxjs/Subscription';
28-
import {DOWN_ARROW, ENTER, ESCAPE, SPACE, UP_ARROW} from '@angular/material/core';
28+
import {DOWN_ARROW, ENTER, ESCAPE, SPACE, TAB, UP_ARROW} from '@angular/material/core';
2929
import {MdOption} from '@angular/material/core';
3030
import {MdFormField, MdFormFieldModule} from '@angular/material/form-field';
3131
import {Observable} from 'rxjs/Observable';
@@ -1359,6 +1359,71 @@ describe('MdAutocomplete', () => {
13591359
}));
13601360
});
13611361

1362+
describe('panel closing', () => {
1363+
let fixture: ComponentFixture<SimpleAutocomplete>;
1364+
let input: HTMLInputElement;
1365+
let trigger: MdAutocompleteTrigger;
1366+
let closingActionSpy: jasmine.Spy;
1367+
let closingActionsSub: Subscription;
1368+
1369+
beforeEach(() => {
1370+
fixture = TestBed.createComponent(SimpleAutocomplete);
1371+
fixture.detectChanges();
1372+
1373+
input = fixture.debugElement.query(By.css('input')).nativeElement;
1374+
1375+
fixture.componentInstance.trigger.openPanel();
1376+
fixture.detectChanges();
1377+
1378+
trigger = fixture.componentInstance.trigger;
1379+
closingActionSpy = jasmine.createSpy('closing action listener');
1380+
closingActionsSub = trigger.panelClosingActions.subscribe(closingActionSpy);
1381+
});
1382+
1383+
afterEach(() => {
1384+
closingActionsSub.unsubscribe();
1385+
});
1386+
1387+
it('should emit panel close event when clicking away', async(() => {
1388+
fixture.whenStable().then(() => {
1389+
expect(closingActionSpy).not.toHaveBeenCalled();
1390+
dispatchFakeEvent(document, 'click');
1391+
expect(closingActionSpy).toHaveBeenCalled();
1392+
});
1393+
}));
1394+
1395+
it('should emit panel close event when tabbing out', async(() => {
1396+
const tabEvent = createKeyboardEvent('keydown', TAB);
1397+
input.focus();
1398+
1399+
fixture.whenStable().then(() => {
1400+
expect(closingActionSpy).not.toHaveBeenCalled();
1401+
trigger._handleKeydown(tabEvent);
1402+
expect(closingActionSpy).toHaveBeenCalled();
1403+
});
1404+
}));
1405+
1406+
it('should emit panel close event when selecting an option', async(() => {
1407+
fixture.whenStable().then(() => {
1408+
const option = overlayContainerElement.querySelector('md-option') as HTMLElement;
1409+
1410+
expect(closingActionSpy).not.toHaveBeenCalled();
1411+
option.click();
1412+
expect(closingActionSpy).toHaveBeenCalled();
1413+
});
1414+
}));
1415+
1416+
it('should close the panel when pressing escape', async(() => {
1417+
const escapeEvent = createKeyboardEvent('keydown', ESCAPE);
1418+
1419+
fixture.whenStable().then(() => {
1420+
expect(closingActionSpy).not.toHaveBeenCalled();
1421+
trigger._handleKeydown(escapeEvent);
1422+
expect(closingActionSpy).toHaveBeenCalled();
1423+
});
1424+
}));
1425+
});
1426+
13621427
describe('without mdInput', () => {
13631428
let fixture: ComponentFixture<AutocompleteWithNativeInput>;
13641429

0 commit comments

Comments
 (0)