Skip to content

Commit d2e11a8

Browse files
committed
fix(menu): not closing on escape when opened by mouse
Fixes `md-menu` not closing when escape is pressed, if it was opened by a mouse click. The old behavior seemed to only be targeted towards keyboard users, however the escape key can also be considered a convenience feature.
1 parent 12d6e96 commit d2e11a8

File tree

4 files changed

+22
-17
lines changed

4 files changed

+22
-17
lines changed

src/lib/menu/menu-directive.ts

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ import {FocusKeyManager} from '../core/a11y/focus-key-manager';
2020
import {MdMenuPanel} from './menu-panel';
2121
import {Subscription} from 'rxjs/Subscription';
2222
import {transformMenu, fadeInItems} from './menu-animations';
23-
import {ESCAPE} from '../core/keyboard/keycodes';
2423

2524

2625
@Component({
@@ -37,13 +36,15 @@ import {ESCAPE} from '../core/keyboard/keycodes';
3736
exportAs: 'mdMenu'
3837
})
3938
export class MdMenu implements AfterContentInit, MdMenuPanel, OnDestroy {
40-
private _keyManager: FocusKeyManager;
4139
private _xPosition: MenuPositionX = 'after';
4240
private _yPosition: MenuPositionY = 'below';
4341

4442
/** Subscription to tab events on the menu panel */
4543
private _tabSubscription: Subscription;
4644

45+
/** Manages keyboard events inside the panel. */
46+
_keyManager: FocusKeyManager;
47+
4748
/** Config object to be passed into the menu's ngClass */
4849
_classList: any = {};
4950

@@ -106,17 +107,6 @@ export class MdMenu implements AfterContentInit, MdMenuPanel, OnDestroy {
106107
}
107108
}
108109

109-
/** Handle a keyboard event from the menu, delegating to the appropriate action. */
110-
_handleKeydown(event: KeyboardEvent) {
111-
switch (event.keyCode) {
112-
case ESCAPE:
113-
this._emitCloseEvent();
114-
return;
115-
default:
116-
this._keyManager.onKeydown(event);
117-
}
118-
}
119-
120110
/**
121111
* Focus the first item in the menu. This method is used by the menu trigger
122112
* to focus the first item when the menu is opened by the ENTER key.

src/lib/menu/menu-trigger.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
Optional,
99
Output,
1010
ViewContainerRef,
11+
Renderer2,
1112
} from '@angular/core';
1213
import {MdMenuPanel} from './menu-panel';
1314
import {throwMdMenuMissingError} from './menu-errors';
@@ -24,6 +25,7 @@ import {
2425
VerticalConnectionPos,
2526
RepositionScrollStrategy,
2627
ScrollDispatcher,
28+
ESCAPE,
2729
} from '../core';
2830
import {Subscription} from 'rxjs/Subscription';
2931
import {MenuPositionX, MenuPositionY} from './menu-positions';
@@ -50,6 +52,7 @@ export class MdMenuTrigger implements AfterViewInit, OnDestroy {
5052
private _menuOpen: boolean = false;
5153
private _backdropSubscription: Subscription;
5254
private _positionSubscription: Subscription;
55+
private _globalKeydownHandler: () => void;
5356

5457
// tracking input type is necessary so it's possible to only auto-focus
5558
// the first item of the list when the menu is opened via the keyboard
@@ -81,7 +84,7 @@ export class MdMenuTrigger implements AfterViewInit, OnDestroy {
8184

8285
constructor(private _overlay: Overlay, private _element: ElementRef,
8386
private _viewContainerRef: ViewContainerRef, @Optional() private _dir: Dir,
84-
private _scrollDispatcher: ScrollDispatcher) { }
87+
private _scrollDispatcher: ScrollDispatcher, private _renderer: Renderer2) { }
8588

8689
ngAfterViewInit() {
8790
this._checkMenu();
@@ -104,6 +107,14 @@ export class MdMenuTrigger implements AfterViewInit, OnDestroy {
104107
this._createOverlay();
105108
this._overlayRef.attach(this._portal);
106109
this._subscribeToBackdrop();
110+
111+
this._globalKeydownHandler = this._renderer.listen('document', 'keydown',
112+
(event: KeyboardEvent) => {
113+
if (event.keyCode === ESCAPE) {
114+
this.closeMenu();
115+
}
116+
});
117+
107118
this._initMenu();
108119
}
109120
}
@@ -114,6 +125,11 @@ export class MdMenuTrigger implements AfterViewInit, OnDestroy {
114125
this._overlayRef.detach();
115126
this._backdropSubscription.unsubscribe();
116127
this._resetMenu();
128+
129+
if (this._globalKeydownHandler) {
130+
this._globalKeydownHandler();
131+
this._globalKeydownHandler = null;
132+
}
117133
}
118134
}
119135

src/lib/menu/menu.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<ng-template>
2-
<div class="mat-menu-panel" [ngClass]="_classList" (keydown)="_handleKeydown($event)"
2+
<div class="mat-menu-panel" [ngClass]="_classList" (keydown)="_keyManager.onKeydown($event)"
33
(click)="_emitCloseEvent()" [@transformMenu]="'showing'">
44
<div class="mat-menu-content" [@fadeInItems]="'showing'">
55
<ng-content></ng-content>

src/lib/menu/menu.spec.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,8 +87,7 @@ describe('MdMenu', () => {
8787
fixture.detectChanges();
8888
fixture.componentInstance.trigger.openMenu();
8989

90-
const panel = overlayContainerElement.querySelector('.mat-menu-panel');
91-
dispatchKeyboardEvent(panel, 'keydown', ESCAPE);
90+
dispatchKeyboardEvent(document, 'keydown', ESCAPE);
9291
fixture.detectChanges();
9392

9493
expect(overlayContainerElement.textContent).toBe('');

0 commit comments

Comments
 (0)