Skip to content

Commit 549b86c

Browse files
committed
fix(autocomplete): emit closing action for escape keydown event
1 parent 8a23157 commit 549b86c

File tree

2 files changed

+72
-2
lines changed

2 files changed

+72
-2
lines changed

src/lib/autocomplete/autocomplete-trigger.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ import {MdAutocomplete} from './autocomplete';
3737
import {PositionStrategy} from '../core/overlay/position/position-strategy';
3838
import {ConnectedPositionStrategy} from '../core/overlay/position/connected-position-strategy';
3939
import {Observable} from 'rxjs/Observable';
40+
import {Subject} from 'rxjs/Subject';
4041
import {MdOptionSelectionChange, MdOption} from '../core/option/option';
4142
import {ENTER, UP_ARROW, DOWN_ARROW, ESCAPE} from '../core/keyboard/keycodes';
4243
import {Directionality} from '../core/bidi/index';
@@ -131,6 +132,9 @@ export class MdAutocompleteTrigger implements ControlValueAccessor, OnDestroy {
131132
/** The subscription for closing actions (some are bound to document). */
132133
private _closingActionsSubscription: Subscription;
133134

135+
/** Stream of escape keyboard events */
136+
private _escapeEventStream = new Subject<KeyboardEvent>();
137+
134138
/** View -> model callback called when value changes */
135139
_onChange: (value: any) => void = () => {};
136140

@@ -224,6 +228,7 @@ export class MdAutocompleteTrigger implements ControlValueAccessor, OnDestroy {
224228
return merge(
225229
this.optionSelections,
226230
this.autocomplete._keyManager.tabOut,
231+
this._escapeEventStream,
227232
this._outsideClickStream
228233
);
229234
}
@@ -297,7 +302,7 @@ export class MdAutocompleteTrigger implements ControlValueAccessor, OnDestroy {
297302

298303
_handleKeydown(event: KeyboardEvent): void {
299304
if (event.keyCode === ESCAPE && this.panelOpen) {
300-
this.closePanel();
305+
this._escapeEventStream.next(event);
301306
} else if (this.activeOption && event.keyCode === ENTER && this.panelOpen) {
302307
this.activeOption._selectViaInteraction();
303308
event.preventDefault();

src/lib/autocomplete/autocomplete.spec.ts

Lines changed: 66 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import {OverlayContainer} from '../core/overlay/overlay-container';
2121
import {MdInputModule} from '../input/index';
2222
import {Directionality, Direction} from '../core/bidi/index';
2323
import {Subscription} from 'rxjs/Subscription';
24-
import {ENTER, DOWN_ARROW, SPACE, UP_ARROW, ESCAPE} from '../core/keyboard/keycodes';
24+
import {ENTER, DOWN_ARROW, SPACE, UP_ARROW, ESCAPE, TAB} from '../core/keyboard/keycodes';
2525
import {MdOption} from '../core/option/option';
2626
import {MdInputContainer} from '../input/input-container';
2727
import {Observable} from 'rxjs/Observable';
@@ -1206,6 +1206,71 @@ describe('MdAutocomplete', () => {
12061206
}));
12071207
});
12081208

1209+
describe('panel closing', () => {
1210+
let fixture: ComponentFixture<SimpleAutocomplete>;
1211+
let input: HTMLInputElement;
1212+
let trigger: MdAutocompleteTrigger;
1213+
let closingActionSpy: jasmine.Spy;
1214+
let closingActionsSub: Subscription;
1215+
1216+
beforeEach(() => {
1217+
fixture = TestBed.createComponent(SimpleAutocomplete);
1218+
fixture.detectChanges();
1219+
1220+
input = fixture.debugElement.query(By.css('input')).nativeElement;
1221+
1222+
fixture.componentInstance.trigger.openPanel();
1223+
fixture.detectChanges();
1224+
1225+
trigger = fixture.componentInstance.trigger;
1226+
closingActionSpy = jasmine.createSpy('closing action listener');
1227+
closingActionsSub = trigger.panelClosingActions.subscribe(closingActionSpy);
1228+
});
1229+
1230+
afterEach(() => {
1231+
closingActionsSub.unsubscribe();
1232+
});
1233+
1234+
it('should emit panel close event when clicking away', async(() => {
1235+
fixture.whenStable().then(() => {
1236+
expect(closingActionSpy).not.toHaveBeenCalled();
1237+
dispatchFakeEvent(document, 'click');
1238+
expect(closingActionSpy).toHaveBeenCalled();
1239+
});
1240+
}));
1241+
1242+
it('should emit panel close event when tabbing out', async(() => {
1243+
const tabEvent = createKeyboardEvent('keydown', TAB);
1244+
input.focus();
1245+
1246+
fixture.whenStable().then(() => {
1247+
expect(closingActionSpy).not.toHaveBeenCalled();
1248+
trigger._handleKeydown(tabEvent);
1249+
expect(closingActionSpy).toHaveBeenCalled();
1250+
});
1251+
}));
1252+
1253+
it('should emit panel close event when selecting an option', async(() => {
1254+
fixture.whenStable().then(() => {
1255+
const option = overlayContainerElement.querySelector('md-option') as HTMLElement;
1256+
1257+
expect(closingActionSpy).not.toHaveBeenCalled();
1258+
option.click();
1259+
expect(closingActionSpy).toHaveBeenCalled();
1260+
});
1261+
}));
1262+
1263+
it('should close the panel when pressing escape', async(() => {
1264+
const escapeEvent = createKeyboardEvent('keydown', ESCAPE);
1265+
1266+
fixture.whenStable().then(() => {
1267+
expect(closingActionSpy).not.toHaveBeenCalled();
1268+
trigger._handleKeydown(escapeEvent);
1269+
expect(closingActionSpy).toHaveBeenCalled();
1270+
});
1271+
}));
1272+
});
1273+
12091274
describe('without mdInput', () => {
12101275
let fixture: ComponentFixture<AutocompleteWithNativeInput>;
12111276

0 commit comments

Comments
 (0)