Skip to content

Commit

Permalink
fix(menu): set proper focus origin when the user starts using the key…
Browse files Browse the repository at this point in the history
…board after opening with the mouse (#11000)

Sets the focus origin of the menu keyboard manager to `keyboard` when the user presses some of the navigational keys. This ensures that focus highlighting will work when opening using the mouse and then navigating with the keyboard.

Fixes #10980.
  • Loading branch information
crisbeto authored and andrewseguin committed Apr 26, 2018
1 parent a292d31 commit ec3e3e7
Show file tree
Hide file tree
Showing 2 changed files with 37 additions and 3 deletions.
10 changes: 8 additions & 2 deletions src/lib/menu/menu-directive.ts
Expand Up @@ -9,7 +9,7 @@
import {FocusKeyManager, FocusOrigin} from '@angular/cdk/a11y';
import {Direction} from '@angular/cdk/bidi';
import {coerceBooleanProperty} from '@angular/cdk/coercion';
import {ESCAPE, LEFT_ARROW, RIGHT_ARROW} from '@angular/cdk/keycodes';
import {ESCAPE, LEFT_ARROW, RIGHT_ARROW, DOWN_ARROW, UP_ARROW} from '@angular/cdk/keycodes';
import {
AfterContentInit,
ChangeDetectionStrategy,
Expand Down Expand Up @@ -243,7 +243,9 @@ export class MatMenu implements OnInit, AfterContentInit, MatMenuPanel, OnDestro

/** Handle a keyboard event from the menu, delegating to the appropriate action. */
_handleKeydown(event: KeyboardEvent) {
switch (event.keyCode) {
const keyCode = event.keyCode;

switch (keyCode) {
case ESCAPE:
this.closed.emit('keydown');
event.stopPropagation();
Expand All @@ -259,6 +261,10 @@ export class MatMenu implements OnInit, AfterContentInit, MatMenuPanel, OnDestro
}
break;
default:
if (keyCode === UP_ARROW || keyCode === DOWN_ARROW) {
this._keyManager.setFocusOrigin('keyboard');
}

this._keyManager.onKeydown(event);
}
}
Expand Down
30 changes: 29 additions & 1 deletion src/lib/menu/menu.spec.ts
Expand Up @@ -17,7 +17,7 @@ import {
} from '@angular/core';
import {Direction, Directionality} from '@angular/cdk/bidi';
import {OverlayContainer, Overlay} from '@angular/cdk/overlay';
import {ESCAPE, LEFT_ARROW, RIGHT_ARROW, TAB} from '@angular/cdk/keycodes';
import {ESCAPE, LEFT_ARROW, RIGHT_ARROW, DOWN_ARROW, TAB} from '@angular/cdk/keycodes';
import {
MAT_MENU_DEFAULT_OPTIONS,
MatMenu,
Expand Down Expand Up @@ -332,6 +332,34 @@ describe('MatMenu', () => {
expect(trigger.menuOpen).toBe(false);
}));

it('should switch to keyboard focus when using the keyboard after opening using the mouse',
fakeAsync(() => {
const fixture = createComponent(SimpleMenu, [], [FakeIcon]);

fixture.detectChanges();
fixture.componentInstance.triggerEl.nativeElement.click();
fixture.detectChanges();

const panel = document.querySelector('.mat-menu-panel')! as HTMLElement;
const items: HTMLElement[] =
Array.from(panel.querySelectorAll('.mat-menu-panel [mat-menu-item]'));

items.forEach(item => patchElementFocus(item));

tick(500);
tick();
fixture.detectChanges();
expect(items.some(item => item.classList.contains('cdk-keyboard-focused'))).toBe(false);

dispatchKeyboardEvent(panel, 'keydown', DOWN_ARROW);
fixture.detectChanges();
tick();

// We skip to the third item, because the second one is disabled.
expect(items[2].classList).toContain('cdk-focused');
expect(items[2].classList).toContain('cdk-keyboard-focused');
}));

describe('lazy rendering', () => {
it('should be able to render the menu content lazily', fakeAsync(() => {
const fixture = createComponent(SimpleLazyMenu);
Expand Down

0 comments on commit ec3e3e7

Please sign in to comment.