Skip to content

Commit 2c192d0

Browse files
crisbetojelbourn
authored andcommitted
fix(autocomplete): close panel using alt + up arrow (#9341)
[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.
1 parent 4e8aa52 commit 2c192d0

File tree

2 files changed

+27
-6
lines changed

2 files changed

+27
-6
lines changed

src/lib/autocomplete/autocomplete-trigger.ts

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -129,8 +129,8 @@ export class MatAutocompleteTrigger implements ControlValueAccessor, OnDestroy {
129129
/** The subscription for closing actions (some are bound to document). */
130130
private _closingActionsSubscription: Subscription;
131131

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

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

153153
ngOnDestroy() {
154154
this._destroyPanel();
155-
this._escapeEventStream.complete();
155+
this._closeKeyEventStream.complete();
156156
}
157157

158158
/** Whether or not the autocomplete panel is open. */
@@ -194,7 +194,7 @@ export class MatAutocompleteTrigger implements ControlValueAccessor, OnDestroy {
194194
return merge(
195195
this.optionSelections,
196196
this.autocomplete._keyManager.tabOut.pipe(filter(() => this._panelOpen)),
197-
this._escapeEventStream,
197+
this._closeKeyEventStream,
198198
this._outsideClickStream,
199199
this._overlayRef ?
200200
this._overlayRef.detachments().pipe(filter(() => this._panelOpen)) :
@@ -289,9 +289,11 @@ export class MatAutocompleteTrigger implements ControlValueAccessor, OnDestroy {
289289
_handleKeydown(event: KeyboardEvent): void {
290290
const keyCode = event.keyCode;
291291

292-
if (keyCode === ESCAPE && this.panelOpen) {
292+
// Close when pressing ESCAPE or ALT + UP_ARROW, based on the a11y guidelines.
293+
// See: https://www.w3.org/TR/wai-aria-practices-1.1/#textbox-keyboard-interaction
294+
if (this.panelOpen && (keyCode === ESCAPE || (keyCode === UP_ARROW && event.altKey))) {
293295
this._resetActiveItem();
294-
this._escapeEventStream.next();
296+
this._closeKeyEventStream.next();
295297
event.stopPropagation();
296298
} else if (this.activeOption && keyCode === ENTER && this.panelOpen) {
297299
this.activeOption._selectViaInteraction();

src/lib/autocomplete/autocomplete.spec.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -902,6 +902,25 @@ describe('MatAutocomplete', () => {
902902
expect(stopPropagationSpy).toHaveBeenCalled();
903903
}));
904904

905+
it('should close the panel when pressing ALT + UP_ARROW', fakeAsync(() => {
906+
const trigger = fixture.componentInstance.trigger;
907+
const upArrowEvent = createKeyboardEvent('keydown', UP_ARROW);
908+
Object.defineProperty(upArrowEvent, 'altKey', {get: () => true});
909+
910+
input.focus();
911+
flush();
912+
fixture.detectChanges();
913+
914+
expect(document.activeElement).toBe(input, 'Expected input to be focused.');
915+
expect(trigger.panelOpen).toBe(true, 'Expected panel to be open.');
916+
917+
trigger._handleKeydown(upArrowEvent);
918+
fixture.detectChanges();
919+
920+
expect(document.activeElement).toBe(input, 'Expected input to continue to be focused.');
921+
expect(trigger.panelOpen).toBe(false, 'Expected panel to be closed.');
922+
}));
923+
905924
it('should close the panel when tabbing away from a trigger without results', fakeAsync(() => {
906925
fixture.componentInstance.states = [];
907926
fixture.componentInstance.filteredStates = [];

0 commit comments

Comments
 (0)