Skip to content

Commit 133b03f

Browse files
committed
fix(autocomplete): emit closing action for escape keydown event
1 parent 5967f6e commit 133b03f

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
event.stopPropagation();
302307
} else if (this.activeOption && event.keyCode === ENTER && this.panelOpen) {
303308
this.activeOption._selectViaInteraction();

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';
@@ -1209,6 +1209,71 @@ describe('MdAutocomplete', () => {
12091209
}));
12101210
});
12111211

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

0 commit comments

Comments
 (0)