diff --git a/src/lib/menu/menu-directive.ts b/src/lib/menu/menu-directive.ts index bc1a0c35c0ec..e4edc8d6e801 100644 --- a/src/lib/menu/menu-directive.ts +++ b/src/lib/menu/menu-directive.ts @@ -20,6 +20,8 @@ import {FocusKeyManager} from '../core/a11y/focus-key-manager'; import {MdMenuPanel} from './menu-panel'; import {Subscription} from 'rxjs/Subscription'; import {transformMenu, fadeInItems} from './menu-animations'; +import {ESCAPE} from '../core/keyboard/keycodes'; + @Component({ moduleId: module.id, @@ -75,17 +77,6 @@ export class MdMenu implements AfterContentInit, MdMenuPanel, OnDestroy { /** Whether the menu should overlap its trigger. */ @Input() overlapTrigger = true; - ngAfterContentInit() { - this._keyManager = new FocusKeyManager(this.items).withWrap(); - this._tabSubscription = this._keyManager.tabOut.subscribe(() => this._emitCloseEvent()); - } - - ngOnDestroy() { - if (this._tabSubscription) { - this._tabSubscription.unsubscribe(); - } - } - /** * This method takes classes set on the host md-menu element and applies them on the * menu template that displays in the overlay container. Otherwise, it's difficult @@ -104,6 +95,28 @@ export class MdMenu implements AfterContentInit, MdMenuPanel, OnDestroy { /** Event emitted when the menu is closed. */ @Output() close = new EventEmitter(); + ngAfterContentInit() { + this._keyManager = new FocusKeyManager(this.items).withWrap(); + this._tabSubscription = this._keyManager.tabOut.subscribe(() => this._emitCloseEvent()); + } + + ngOnDestroy() { + if (this._tabSubscription) { + this._tabSubscription.unsubscribe(); + } + } + + /** Handle a keyboard event from the menu, delegating to the appropriate action. */ + _handleKeydown(event: KeyboardEvent) { + switch (event.keyCode) { + case ESCAPE: + this._emitCloseEvent(); + return; + default: + this._keyManager.onKeydown(event); + } + } + /** * Focus the first item in the menu. This method is used by the menu trigger * to focus the first item when the menu is opened by the ENTER key. diff --git a/src/lib/menu/menu.html b/src/lib/menu/menu.html index 989bf1f56acb..10972059e8c5 100644 --- a/src/lib/menu/menu.html +++ b/src/lib/menu/menu.html @@ -1,5 +1,5 @@ -
diff --git a/src/lib/menu/menu.spec.ts b/src/lib/menu/menu.spec.ts index 4f5c79ed2b79..18e327753363 100644 --- a/src/lib/menu/menu.spec.ts +++ b/src/lib/menu/menu.spec.ts @@ -18,9 +18,11 @@ import { MenuPositionY } from './index'; import {OverlayContainer} from '../core/overlay/overlay-container'; -import {ViewportRuler} from '../core/overlay/position/viewport-ruler'; import {Dir, LayoutDirection} from '../core/rtl/dir'; import {extendObject} from '../core/util/object-extend'; +import {ESCAPE} from '../core/keyboard/keycodes'; +import {dispatchKeyboardEvent} from '../core/testing/dispatch-events'; + describe('MdMenu', () => { let overlayContainerElement: HTMLElement; @@ -80,6 +82,18 @@ describe('MdMenu', () => { expect(overlayContainerElement.textContent).toBe(''); }); + it('should close the menu when pressing escape', () => { + const fixture = TestBed.createComponent(SimpleMenu); + fixture.detectChanges(); + fixture.componentInstance.trigger.openMenu(); + + const panel = overlayContainerElement.querySelector('.mat-menu-panel'); + dispatchKeyboardEvent(panel, 'keydown', ESCAPE); + fixture.detectChanges(); + + expect(overlayContainerElement.textContent).toBe(''); + }); + it('should open a custom menu', () => { const fixture = TestBed.createComponent(CustomMenu); fixture.detectChanges();