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.
This commit is contained in:
Kristiyan Kostadinov
2018-01-23 16:53:53 +01:00
committed by Jeremy Elbourn
parent 4e8aa52d32
commit 2c192d0a57
2 changed files with 27 additions and 6 deletions
+8 -6
View File
@@ -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 = () => {};
@@ -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. */
@@ -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)) :
@@ -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();
+19
View File
@@ -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 = [];