Skip to content

Commit f4673a5

Browse files
willshowellkara
authored andcommitted
fix(autocomplete): emit closing action for escape keydown event (#6250)
1 parent b5ada20 commit f4673a5

File tree

2 files changed

+72
-1
lines changed

2 files changed

+72
-1
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 {MatOption, MatOptionSelectionChange} from '@angular/material/core';
3838
import {MatFormField} 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';
@@ -125,6 +126,9 @@ export class MatAutocompleteTrigger implements ControlValueAccessor, OnDestroy {
125126
/** The subscription for closing actions (some are bound to document). */
126127
private _closingActionsSubscription: Subscription;
127128

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

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

146150
ngOnDestroy() {
147151
this._destroyPanel();
152+
this._escapeEventStream.complete();
148153
}
149154

150155
/* Whether or not the autocomplete panel is open. */
@@ -186,6 +191,7 @@ export class MatAutocompleteTrigger implements ControlValueAccessor, OnDestroy {
186191
return merge(
187192
this.optionSelections,
188193
this.autocomplete._keyManager.tabOut,
194+
this._escapeEventStream,
189195
this._outsideClickStream
190196
);
191197
}
@@ -262,7 +268,7 @@ export class MatAutocompleteTrigger implements ControlValueAccessor, OnDestroy {
262268

263269
if (keyCode === ESCAPE && this.panelOpen) {
264270
this._resetActiveItem();
265-
this.closePanel();
271+
this._escapeEventStream.next();
266272
event.stopPropagation();
267273
} else if (this.activeOption && keyCode === ENTER && this.panelOpen) {
268274
this.activeOption._selectViaInteraction();

src/lib/autocomplete/autocomplete.spec.ts

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1403,6 +1403,71 @@ describe('MatAutocomplete', () => {
14031403
}));
14041404
});
14051405

1406+
describe('panel closing', () => {
1407+
let fixture: ComponentFixture<SimpleAutocomplete>;
1408+
let input: HTMLInputElement;
1409+
let trigger: MatAutocompleteTrigger;
1410+
let closingActionSpy: jasmine.Spy;
1411+
let closingActionsSub: Subscription;
1412+
1413+
beforeEach(() => {
1414+
fixture = TestBed.createComponent(SimpleAutocomplete);
1415+
fixture.detectChanges();
1416+
1417+
input = fixture.debugElement.query(By.css('input')).nativeElement;
1418+
1419+
fixture.componentInstance.trigger.openPanel();
1420+
fixture.detectChanges();
1421+
1422+
trigger = fixture.componentInstance.trigger;
1423+
closingActionSpy = jasmine.createSpy('closing action listener');
1424+
closingActionsSub = trigger.panelClosingActions.subscribe(closingActionSpy);
1425+
});
1426+
1427+
afterEach(() => {
1428+
closingActionsSub.unsubscribe();
1429+
});
1430+
1431+
it('should emit panel close event when clicking away', async(() => {
1432+
fixture.whenStable().then(() => {
1433+
expect(closingActionSpy).not.toHaveBeenCalled();
1434+
dispatchFakeEvent(document, 'click');
1435+
expect(closingActionSpy).toHaveBeenCalled();
1436+
});
1437+
}));
1438+
1439+
it('should emit panel close event when tabbing out', async(() => {
1440+
const tabEvent = createKeyboardEvent('keydown', TAB);
1441+
input.focus();
1442+
1443+
fixture.whenStable().then(() => {
1444+
expect(closingActionSpy).not.toHaveBeenCalled();
1445+
trigger._handleKeydown(tabEvent);
1446+
expect(closingActionSpy).toHaveBeenCalled();
1447+
});
1448+
}));
1449+
1450+
it('should emit panel close event when selecting an option', async(() => {
1451+
fixture.whenStable().then(() => {
1452+
const option = overlayContainerElement.querySelector('mat-option') as HTMLElement;
1453+
1454+
expect(closingActionSpy).not.toHaveBeenCalled();
1455+
option.click();
1456+
expect(closingActionSpy).toHaveBeenCalled();
1457+
});
1458+
}));
1459+
1460+
it('should close the panel when pressing escape', async(() => {
1461+
const escapeEvent = createKeyboardEvent('keydown', ESCAPE);
1462+
1463+
fixture.whenStable().then(() => {
1464+
expect(closingActionSpy).not.toHaveBeenCalled();
1465+
trigger._handleKeydown(escapeEvent);
1466+
expect(closingActionSpy).toHaveBeenCalled();
1467+
});
1468+
}));
1469+
});
1470+
14061471
describe('without matInput', () => {
14071472
let fixture: ComponentFixture<AutocompleteWithNativeInput>;
14081473

0 commit comments

Comments
 (0)