diff --git a/src/lib/autocomplete/autocomplete-trigger.ts b/src/lib/autocomplete/autocomplete-trigger.ts index 89e8d3487b90..9d1b1241f660 100644 --- a/src/lib/autocomplete/autocomplete-trigger.ts +++ b/src/lib/autocomplete/autocomplete-trigger.ts @@ -7,7 +7,7 @@ */ import {Directionality} from '@angular/cdk/bidi'; -import {DOWN_ARROW, ENTER, ESCAPE, UP_ARROW} from '@angular/cdk/keycodes'; +import {DOWN_ARROW, ENTER, ESCAPE, UP_ARROW, TAB} from '@angular/cdk/keycodes'; import { ConnectedPositionStrategy, Overlay, @@ -270,19 +270,21 @@ export class MdAutocompleteTrigger implements ControlValueAccessor, OnDestroy { } _handleKeydown(event: KeyboardEvent): void { - if (event.keyCode === ESCAPE && this.panelOpen) { + const keyCode = event.keyCode; + + if (keyCode === ESCAPE && this.panelOpen) { this._resetActiveItem(); this.closePanel(); event.stopPropagation(); - } else if (this.activeOption && event.keyCode === ENTER && this.panelOpen) { + } else if (this.activeOption && keyCode === ENTER && this.panelOpen) { this.activeOption._selectViaInteraction(); this._resetActiveItem(); event.preventDefault(); } else { const prevActiveItem = this.autocomplete._keyManager.activeItem; - const isArrowKey = event.keyCode === UP_ARROW || event.keyCode === DOWN_ARROW; + const isArrowKey = keyCode === UP_ARROW || keyCode === DOWN_ARROW; - if (this.panelOpen) { + if (this.panelOpen || keyCode === TAB) { this.autocomplete._keyManager.onKeydown(event); } else if (isArrowKey) { this.openPanel(); diff --git a/src/lib/autocomplete/autocomplete.spec.ts b/src/lib/autocomplete/autocomplete.spec.ts index e4886f42dd1d..c99c9865eb6b 100644 --- a/src/lib/autocomplete/autocomplete.spec.ts +++ b/src/lib/autocomplete/autocomplete.spec.ts @@ -1,9 +1,14 @@ import {Direction, Directionality} from '@angular/cdk/bidi'; -import {DOWN_ARROW, ENTER, ESCAPE, SPACE, UP_ARROW} from '@angular/cdk/keycodes'; +import {DOWN_ARROW, ENTER, ESCAPE, SPACE, UP_ARROW, TAB} from '@angular/cdk/keycodes'; import {OverlayContainer} from '@angular/cdk/overlay'; import {map, RxChain, startWith} from '@angular/cdk/rxjs'; import {ScrollDispatcher} from '@angular/cdk/scrolling'; -import {createKeyboardEvent, dispatchFakeEvent, typeInElement} from '@angular/cdk/testing'; +import { + createKeyboardEvent, + dispatchKeyboardEvent, + dispatchFakeEvent, + typeInElement, +} from '@angular/cdk/testing'; import { ChangeDetectionStrategy, Component, @@ -944,6 +949,26 @@ describe('MdAutocomplete', () => { }); })); + it('should close the panel when tabbing away from a trigger without results', async(() => { + const trigger = fixture.componentInstance.trigger; + + fixture.componentInstance.states = []; + fixture.componentInstance.filteredStates = []; + fixture.detectChanges(); + input.focus(); + + fixture.whenStable().then(() => { + expect(overlayContainerElement.querySelector('.mat-autocomplete-panel')) + .toBeTruthy('Expected panel to be rendered.'); + + dispatchKeyboardEvent(input, 'keydown', TAB); + fixture.detectChanges(); + + expect(overlayContainerElement.querySelector('.mat-autocomplete-panel')) + .toBeFalsy('Expected panel to be removed.'); + }); + })); + it('should reset the active option when closing with the escape key', fakeAsync(() => { const trigger = fixture.componentInstance.trigger;