Skip to content

Commit

Permalink
fix(menu): improve a11y for screenreaders (#1715)
Browse files Browse the repository at this point in the history
  • Loading branch information
kara authored and jelbourn committed Nov 8, 2016
1 parent 15cd28b commit 267e323
Show file tree
Hide file tree
Showing 6 changed files with 37 additions and 18 deletions.
8 changes: 4 additions & 4 deletions src/demo-app/menu/menu-demo.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<p>You clicked on: {{ selected }}</p>

<md-toolbar>
<button md-icon-button [md-menu-trigger-for]="menu">
<button md-icon-button [md-menu-trigger-for]="menu" aria-label="Open basic menu">
<md-icon>more_vert</md-icon>
</button>
</md-toolbar>
Expand All @@ -17,7 +17,7 @@
<div class="menu-section">
<p> Clicking these will navigate:</p>
<md-toolbar>
<button md-icon-button [md-menu-trigger-for]="anchorMenu">
<button md-icon-button [md-menu-trigger-for]="anchorMenu" aria-label="Open anchor menu">
<md-icon>more_vert</md-icon>
</button>
</md-toolbar>
Expand All @@ -33,7 +33,7 @@
Position x: before
</p>
<md-toolbar class="end-icon">
<button md-icon-button [md-menu-trigger-for]="posXMenu">
<button md-icon-button [md-menu-trigger-for]="posXMenu" aria-label="Open x-positioned menu">
<md-icon>more_vert</md-icon>
</button>
</md-toolbar>
Expand All @@ -50,7 +50,7 @@
Position y: above
</p>
<md-toolbar>
<button md-icon-button [md-menu-trigger-for]="posYMenu">
<button md-icon-button [md-menu-trigger-for]="posYMenu" aria-label="Open y-positioned menu">
<md-icon>more_vert</md-icon>
</button>
</md-toolbar>
Expand Down
11 changes: 11 additions & 0 deletions src/lib/core/a11y/fake-mousedown.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@

/**
* Screenreaders will often fire fake mousedown events when a focusable element
* is activated using the keyboard. We can typically distinguish between these faked
* mousedown events and real mousedown events using the "buttons" property. While
* real mousedowns will indicate the mouse button that was pressed (e.g. "1" for
* the left mouse button), faked mousedowns will usually set the property value to 0.
*/
export function isFakeMousedownFromScreenReader(event: MouseEvent): boolean {
return event.buttons === 0;
}
1 change: 1 addition & 0 deletions src/lib/core/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ export {

export {FocusTrap} from './a11y/focus-trap';
export {InteractivityChecker} from './a11y/interactivity-checker';
export {isFakeMousedownFromScreenReader} from './a11y/fake-mousedown';

export {A11yModule} from './a11y/index';

Expand Down
6 changes: 5 additions & 1 deletion src/lib/menu/menu-item.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {MdFocusable} from '../core/a11y/list-key-manager';
host: {
'role': 'menuitem',
'(click)': '_checkDisabled($event)',
'tabindex': '-1'
'[attr.tabindex]': '_tabindex'
},
templateUrl: 'menu-item.html',
exportAs: 'mdMenuItem'
Expand Down Expand Up @@ -41,6 +41,10 @@ export class MdMenuItem implements MdFocusable {
return String(!!this.disabled);
}

get _tabindex() {
return this.disabled ? '-1' : '0';
}


_getHostElement(): HTMLElement {
return this._elementRef.nativeElement;
Expand Down
27 changes: 15 additions & 12 deletions src/lib/menu/menu-trigger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,7 @@ import {
import {MdMenuPanel} from './menu-panel';
import {MdMenuMissingError} from './menu-errors';
import {
ENTER,
SPACE,
isFakeMousedownFromScreenReader,
Overlay,
OverlayState,
OverlayRef,
Expand All @@ -32,8 +31,8 @@ import { Subscription } from 'rxjs/Subscription';
selector: '[md-menu-trigger-for]',
host: {
'aria-haspopup': 'true',
'(keydown)': '_handleKeydown($event)',
'(click)': 'toggleMenu()'
'(mousedown)': '_handleMousedown($event)',
'(click)': 'toggleMenu()',
},
exportAs: 'mdMenuTrigger'
})
Expand All @@ -45,7 +44,7 @@ export class MdMenuTrigger implements AfterViewInit, OnDestroy {

// tracking input type is necessary so it's possible to only auto-focus
// the first item of the list when the menu is opened via the keyboard
private _openedFromKeyboard: boolean = false;
private _openedByMouse: boolean = false;

@Input('md-menu-trigger-for') menu: MdMenuPanel;
@Output() onMenuOpen = new EventEmitter<void>();
Expand Down Expand Up @@ -118,7 +117,10 @@ export class MdMenuTrigger implements AfterViewInit, OnDestroy {
private _initMenu(): void {
this._setIsMenuOpen(true);

if (this._openedFromKeyboard) {
// Should only set focus if opened via the keyboard, so keyboard users can
// can easily navigate menu items. According to spec, mouse users should not
// see the focus style.
if (!this._openedByMouse) {
this.menu.focusFirstItem();
}
};
Expand All @@ -130,10 +132,12 @@ export class MdMenuTrigger implements AfterViewInit, OnDestroy {
private _resetMenu(): void {
this._setIsMenuOpen(false);

if (this._openedFromKeyboard) {
// Focus only needs to be reset to the host element if the menu was opened
// by the keyboard and manually shifted to the first menu item.
if (!this._openedByMouse) {
this.focus();
this._openedFromKeyboard = false;
}
this._openedByMouse = false;
}

// set state rather than toggle to support triggers sharing a menu
Expand Down Expand Up @@ -191,10 +195,9 @@ export class MdMenuTrigger implements AfterViewInit, OnDestroy {
);
}

// TODO: internal
_handleKeydown(event: KeyboardEvent): void {
if (event.keyCode === ENTER || event.keyCode === SPACE) {
this._openedFromKeyboard = true;
_handleMousedown(event: MouseEvent): void {
if (!isFakeMousedownFromScreenReader(event)) {
this._openedByMouse = true;
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/lib/menu/menu.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ class CustomMenuPanel implements MdMenuPanel {
positionY: MenuPositionY = 'below';
@ViewChild(TemplateRef) templateRef: TemplateRef<any>;
@Output() close = new EventEmitter<void>();
focusFirstItem: () => void;
focusFirstItem = () => {};
}

@Component({
Expand Down

0 comments on commit 267e323

Please sign in to comment.