Skip to content

Commit

Permalink
fix(autocomplete): close panel using alt + up arrow (angular#9341)
Browse files Browse the repository at this point in the history
[Based on the accessibility guidelines](https://www.w3.org/TR/wai-aria-practices-1.1/#textbox-keyboard-interaction), the autocomplete panel can be closed using the alt + up arrow keyboard combo.
  • Loading branch information
crisbeto authored and jelbourn committed Jan 29, 2018
1 parent 0b5806b commit a1ad82b
Show file tree
Hide file tree
Showing 2 changed files with 27 additions and 6 deletions.
14 changes: 8 additions & 6 deletions src/lib/autocomplete/autocomplete-trigger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,8 +129,8 @@ export class MatAutocompleteTrigger implements ControlValueAccessor, OnDestroy {
/** The subscription for closing actions (some are bound to document). */
private _closingActionsSubscription: Subscription;

/** Stream of escape keyboard events. */
private _escapeEventStream = new Subject<void>();
/** Stream of keyboard events that can close the panel. */
private _closeKeyEventStream = new Subject<void>();

/** View -> model callback called when value changes */
_onChange: (value: any) => void = () => {};
Expand All @@ -152,7 +152,7 @@ export class MatAutocompleteTrigger implements ControlValueAccessor, OnDestroy {

ngOnDestroy() {
this._destroyPanel();
this._escapeEventStream.complete();
this._closeKeyEventStream.complete();
}

/* Whether or not the autocomplete panel is open. */
Expand Down Expand Up @@ -194,7 +194,7 @@ export class MatAutocompleteTrigger implements ControlValueAccessor, OnDestroy {
return merge(
this.optionSelections,
this.autocomplete._keyManager.tabOut.pipe(filter(() => this._panelOpen)),
this._escapeEventStream,
this._closeKeyEventStream,
this._outsideClickStream,
this._overlayRef ?
this._overlayRef.detachments().pipe(filter(() => this._panelOpen)) :
Expand Down Expand Up @@ -289,9 +289,11 @@ export class MatAutocompleteTrigger implements ControlValueAccessor, OnDestroy {
_handleKeydown(event: KeyboardEvent): void {
const keyCode = event.keyCode;

if (keyCode === ESCAPE && this.panelOpen) {
// Close when pressing ESCAPE or ALT + UP_ARROW, based on the a11y guidelines.
// See: https://www.w3.org/TR/wai-aria-practices-1.1/#textbox-keyboard-interaction
if (this.panelOpen && (keyCode === ESCAPE || (keyCode === UP_ARROW && event.altKey))) {
this._resetActiveItem();
this._escapeEventStream.next();
this._closeKeyEventStream.next();
event.stopPropagation();
} else if (this.activeOption && keyCode === ENTER && this.panelOpen) {
this.activeOption._selectViaInteraction();
Expand Down
19 changes: 19 additions & 0 deletions src/lib/autocomplete/autocomplete.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -902,6 +902,25 @@ describe('MatAutocomplete', () => {
expect(stopPropagationSpy).toHaveBeenCalled();
}));

it('should close the panel when pressing ALT + UP_ARROW', fakeAsync(() => {
const trigger = fixture.componentInstance.trigger;
const upArrowEvent = createKeyboardEvent('keydown', UP_ARROW);
Object.defineProperty(upArrowEvent, 'altKey', {get: () => true});

input.focus();
flush();
fixture.detectChanges();

expect(document.activeElement).toBe(input, 'Expected input to be focused.');
expect(trigger.panelOpen).toBe(true, 'Expected panel to be open.');

trigger._handleKeydown(upArrowEvent);
fixture.detectChanges();

expect(document.activeElement).toBe(input, 'Expected input to continue to be focused.');
expect(trigger.panelOpen).toBe(false, 'Expected panel to be closed.');
}));

it('should close the panel when tabbing away from a trigger without results', fakeAsync(() => {
fixture.componentInstance.states = [];
fixture.componentInstance.filteredStates = [];
Expand Down

0 comments on commit a1ad82b

Please sign in to comment.