Skip to content

Commit 4681e43

Browse files
committed
fix(autocomplete): handle escape key
* Closes the autocomplete panel when pressing escape. * Switches the autocomplete unit tests to using the common utility for creating fake keyboard events.
1 parent e2f67f5 commit 4681e43

File tree

2 files changed

+32
-19
lines changed

2 files changed

+32
-19
lines changed

src/lib/autocomplete/autocomplete-trigger.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import {PositionStrategy} from '../core/overlay/position/position-strategy';
1919
import {ConnectedPositionStrategy} from '../core/overlay/position/connected-position-strategy';
2020
import {Observable} from 'rxjs/Observable';
2121
import {MdOptionSelectionChange, MdOption} from '../core/option/option';
22-
import {ENTER, UP_ARROW, DOWN_ARROW} from '../core/keyboard/keycodes';
22+
import {ENTER, UP_ARROW, DOWN_ARROW, ESCAPE} from '../core/keyboard/keycodes';
2323
import {Dir} from '../core/rtl/dir';
2424
import {MdInputContainer} from '../input/input-container';
2525
import {ScrollDispatcher} from '../core/overlay/scroll/scroll-dispatcher';
@@ -231,7 +231,9 @@ export class MdAutocompleteTrigger implements ControlValueAccessor, OnDestroy {
231231
}
232232

233233
_handleKeydown(event: KeyboardEvent): void {
234-
if (this.activeOption && event.keyCode === ENTER) {
234+
if (event.keyCode === ESCAPE && this.panelOpen) {
235+
this.closePanel();
236+
} else if (this.activeOption && event.keyCode === ENTER) {
235237
this.activeOption._selectViaInteraction();
236238
event.preventDefault();
237239
} else {

src/lib/autocomplete/autocomplete.spec.ts

Lines changed: 28 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,14 @@ import {MdInputModule} from '../input/index';
1616
import {Dir, LayoutDirection} from '../core/rtl/dir';
1717
import {FormControl, FormsModule, ReactiveFormsModule} from '@angular/forms';
1818
import {Subscription} from 'rxjs/Subscription';
19-
import {ENTER, DOWN_ARROW, SPACE, UP_ARROW, HOME, END} from '../core/keyboard/keycodes';
19+
import {ENTER, DOWN_ARROW, SPACE, UP_ARROW, HOME, END, ESCAPE} from '../core/keyboard/keycodes';
2020
import {MdOption} from '../core/option/option';
2121
import {MdAutocomplete} from './autocomplete';
2222
import {MdInputContainer} from '../input/input-container';
2323
import {Observable} from 'rxjs/Observable';
2424
import {Subject} from 'rxjs/Subject';
2525
import {dispatchFakeEvent} from '../core/testing/dispatch-events';
26+
import {createKeyboardEvent} from '../core/testing/event-objects';
2627
import {typeInElement} from '../core/testing/type-in-element';
2728
import {ScrollDispatcher} from '../core/overlay/scroll/scroll-dispatcher';
2829

@@ -535,8 +536,8 @@ describe('MdAutocomplete', () => {
535536
fixture.detectChanges();
536537

537538
input = fixture.debugElement.query(By.css('input')).nativeElement;
538-
DOWN_ARROW_EVENT = new MockKeyboardEvent(DOWN_ARROW) as KeyboardEvent;
539-
ENTER_EVENT = new MockKeyboardEvent(ENTER) as KeyboardEvent;
539+
DOWN_ARROW_EVENT = createKeyboardEvent('keydown', DOWN_ARROW);
540+
ENTER_EVENT = createKeyboardEvent('keydown', ENTER);
540541

541542
fixture.componentInstance.trigger.openPanel();
542543
fixture.detectChanges();
@@ -594,7 +595,7 @@ describe('MdAutocomplete', () => {
594595
const optionEls =
595596
overlayContainerElement.querySelectorAll('md-option') as NodeListOf<HTMLElement>;
596597

597-
const UP_ARROW_EVENT = new MockKeyboardEvent(UP_ARROW) as KeyboardEvent;
598+
const UP_ARROW_EVENT = createKeyboardEvent('keydown', UP_ARROW);
598599
fixture.componentInstance.trigger._handleKeydown(UP_ARROW_EVENT);
599600
tick();
600601
fixture.detectChanges();
@@ -668,7 +669,7 @@ describe('MdAutocomplete', () => {
668669
typeInElement('New', input);
669670
fixture.detectChanges();
670671

671-
const SPACE_EVENT = new MockKeyboardEvent(SPACE) as KeyboardEvent;
672+
const SPACE_EVENT = createKeyboardEvent('keydown', SPACE);
672673
fixture.componentInstance.trigger._handleKeydown(DOWN_ARROW_EVENT);
673674

674675
fixture.whenStable().then(() => {
@@ -748,7 +749,7 @@ describe('MdAutocomplete', () => {
748749
const scrollContainer =
749750
document.querySelector('.cdk-overlay-pane .mat-autocomplete-panel');
750751

751-
const UP_ARROW_EVENT = new MockKeyboardEvent(UP_ARROW) as KeyboardEvent;
752+
const UP_ARROW_EVENT = createKeyboardEvent('keydown', UP_ARROW);
752753
fixture.componentInstance.trigger._handleKeydown(UP_ARROW_EVENT);
753754
tick();
754755
fixture.detectChanges();
@@ -762,7 +763,7 @@ describe('MdAutocomplete', () => {
762763
const scrollContainer =
763764
document.querySelector('.cdk-overlay-pane .mat-autocomplete-panel');
764765

765-
const END_EVENT = new MockKeyboardEvent(END) as KeyboardEvent;
766+
const END_EVENT = createKeyboardEvent('keydown', END);
766767
fixture.componentInstance.trigger._handleKeydown(END_EVENT);
767768
tick();
768769
fixture.detectChanges();
@@ -779,14 +780,31 @@ describe('MdAutocomplete', () => {
779780
scrollContainer.scrollTop = 100;
780781
fixture.detectChanges();
781782

782-
const HOME_EVENT = new MockKeyboardEvent(HOME) as KeyboardEvent;
783+
const HOME_EVENT = createKeyboardEvent('keydown', HOME);
783784
fixture.componentInstance.trigger._handleKeydown(HOME_EVENT);
784785
tick();
785786
fixture.detectChanges();
786787

787788
expect(scrollContainer.scrollTop).toEqual(0, 'Expected panel to reveal the first option.');
788789
}));
789790

791+
it('should close the panel when pressing escape', async(() => {
792+
const trigger = fixture.componentInstance.trigger;
793+
const escapeEvent = createKeyboardEvent('keydown', ESCAPE);
794+
795+
input.focus();
796+
797+
fixture.whenStable().then(() => {
798+
expect(document.activeElement).toBe(input, 'Expected input to be focused.');
799+
expect(trigger.panelOpen).toBe(true, 'Expected panel to be open.');
800+
801+
trigger._handleKeydown(escapeEvent);
802+
803+
expect(document.activeElement).toBe(input, 'Expected input to continue to be focused.');
804+
expect(trigger.panelOpen).toBe(false, 'Expected panel to be closed.');
805+
});
806+
}));
807+
790808
});
791809

792810
describe('aria', () => {
@@ -832,7 +850,7 @@ describe('MdAutocomplete', () => {
832850
expect(input.hasAttribute('aria-activedescendant'))
833851
.toBe(false, 'Expected aria-activedescendant to be absent if no active item.');
834852

835-
const DOWN_ARROW_EVENT = new MockKeyboardEvent(DOWN_ARROW) as KeyboardEvent;
853+
const DOWN_ARROW_EVENT = createKeyboardEvent('keydown', DOWN_ARROW);
836854
fixture.componentInstance.trigger._handleKeydown(DOWN_ARROW_EVENT);
837855

838856
fixture.whenStable().then(() => {
@@ -1140,7 +1158,7 @@ describe('MdAutocomplete', () => {
11401158
tick();
11411159
fixture.detectChanges();
11421160

1143-
const DOWN_ARROW_EVENT = new MockKeyboardEvent(DOWN_ARROW) as KeyboardEvent;
1161+
const DOWN_ARROW_EVENT = createKeyboardEvent('keydown', DOWN_ARROW);
11441162
fixture.componentInstance.trigger._handleKeydown(DOWN_ARROW_EVENT);
11451163
tick();
11461164
fixture.detectChanges();
@@ -1418,10 +1436,3 @@ class AutocompleteWithNativeInput {
14181436
});
14191437
}
14201438
}
1421-
1422-
1423-
/** This is a mock keyboard event to test keyboard events in the autocomplete. */
1424-
class MockKeyboardEvent {
1425-
constructor(public keyCode: number) {}
1426-
preventDefault() {}
1427-
}

0 commit comments

Comments
 (0)