Skip to content

Commit 3892e09

Browse files
authored
Revert "Revert "fix(menu): multiple close events for a single close" (#7036)"
This reverts commit dcfe515.
1 parent 881630f commit 3892e09

File tree

2 files changed

+48
-22
lines changed

2 files changed

+48
-22
lines changed

src/lib/menu/menu-trigger.ts

+17-11
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,8 @@ import {MdMenuItem} from './menu-item';
4343
import {MdMenuPanel} from './menu-panel';
4444
import {MenuPositionX, MenuPositionY} from './menu-positions';
4545
import {throwMdMenuMissingError} from './menu-errors';
46-
import {of as observableOf} from 'rxjs/observable/of';
4746
import {merge} from 'rxjs/observable/merge';
47+
import {of as observableOf} from 'rxjs/observable/of';
4848
import {Subscription} from 'rxjs/Subscription';
4949

5050
/** Injection token that determines the scroll handling while the menu is open. */
@@ -153,7 +153,7 @@ export class MdMenuTrigger implements AfterViewInit, OnDestroy {
153153
this._checkMenu();
154154

155155
this.menu.close.subscribe(reason => {
156-
this.closeMenu();
156+
this._destroyMenu();
157157

158158
// If a click closed the menu, we should close the entire chain of nested menus.
159159
if (reason === 'click' && this._parentMenu) {
@@ -205,7 +205,9 @@ export class MdMenuTrigger implements AfterViewInit, OnDestroy {
205205
openMenu(): void {
206206
if (!this._menuOpen) {
207207
this._createOverlay().attach(this._portal);
208-
this._closeSubscription = this._menuClosingActions().subscribe(() => this.menu.close.emit());
208+
this._closeSubscription = this._menuClosingActions().subscribe(() => {
209+
this.menu.close.emit();
210+
});
209211
this._initMenu();
210212

211213
if (this.menu instanceof MdMenu) {
@@ -216,23 +218,27 @@ export class MdMenuTrigger implements AfterViewInit, OnDestroy {
216218

217219
/** Closes the menu. */
218220
closeMenu(): void {
221+
this.menu.close.emit();
222+
}
223+
224+
/** Focuses the menu trigger. */
225+
focus() {
226+
this._element.nativeElement.focus();
227+
}
228+
229+
/** Closes the menu and does the necessary cleanup. */
230+
private _destroyMenu() {
219231
if (this._overlayRef && this.menuOpen) {
220232
this._resetMenu();
221233
this._overlayRef.detach();
222234
this._closeSubscription.unsubscribe();
223-
this.menu.close.emit();
224235

225236
if (this.menu instanceof MdMenu) {
226237
this.menu._resetAnimation();
227238
}
228239
}
229240
}
230241

231-
/** Focuses the menu trigger. */
232-
focus() {
233-
this._element.nativeElement.focus();
234-
}
235-
236242
/**
237243
* This method sets the menu state to open and focuses the first item if
238244
* the menu was opened via the keyboard.
@@ -400,11 +406,11 @@ export class MdMenuTrigger implements AfterViewInit, OnDestroy {
400406
/** Returns a stream that emits whenever an action that should close the menu occurs. */
401407
private _menuClosingActions() {
402408
const backdrop = this._overlayRef!.backdropClick();
403-
const parentClose = this._parentMenu ? this._parentMenu.close : observableOf(null);
409+
const parentClose = this._parentMenu ? this._parentMenu.close : observableOf();
404410
const hover = this._parentMenu ? RxChain.from(this._parentMenu.hover())
405411
.call(filter, active => active !== this._menuItemInstance)
406412
.call(filter, () => this._menuOpen)
407-
.result() : observableOf(null);
413+
.result() : observableOf();
408414

409415
return merge(backdrop, parentClose, hover);
410416
}

src/lib/menu/menu.spec.ts

+31-11
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ describe('MdMenu', () => {
9797
expect(overlayContainerElement.textContent).toBe('');
9898
}));
9999

100-
it('should close the menu when pressing escape', fakeAsync(() => {
100+
it('should close the menu when pressing ESCAPE', fakeAsync(() => {
101101
const fixture = TestBed.createComponent(SimpleMenu);
102102
fixture.detectChanges();
103103
fixture.componentInstance.trigger.openMenu();
@@ -493,26 +493,40 @@ describe('MdMenu', () => {
493493
menuItem.click();
494494
fixture.detectChanges();
495495

496-
expect(fixture.componentInstance.closeCallback).toHaveBeenCalled();
496+
expect(fixture.componentInstance.closeCallback).toHaveBeenCalledWith('click');
497+
expect(fixture.componentInstance.closeCallback).toHaveBeenCalledTimes(1);
497498
});
498499

499500
it('should emit a close event when the backdrop is clicked', () => {
500-
const backdrop = <HTMLElement>overlayContainerElement.querySelector('.cdk-overlay-backdrop');
501+
const backdrop = overlayContainerElement
502+
.querySelector('.cdk-overlay-backdrop') as HTMLElement;
501503

502504
backdrop.click();
503505
fixture.detectChanges();
504506

505-
expect(fixture.componentInstance.closeCallback).toHaveBeenCalled();
507+
expect(fixture.componentInstance.closeCallback).toHaveBeenCalledWith(undefined);
508+
expect(fixture.componentInstance.closeCallback).toHaveBeenCalledTimes(1);
509+
});
510+
511+
it('should emit an event when pressing ESCAPE', () => {
512+
const menu = overlayContainerElement.querySelector('.mat-menu-panel') as HTMLElement;
513+
514+
dispatchKeyboardEvent(menu, 'keydown', ESCAPE);
515+
fixture.detectChanges();
516+
517+
expect(fixture.componentInstance.closeCallback).toHaveBeenCalledWith('keydown');
518+
expect(fixture.componentInstance.closeCallback).toHaveBeenCalledTimes(1);
506519
});
507520

508521
it('should complete the callback when the menu is destroyed', () => {
509-
let emitCallback = jasmine.createSpy('emit callback');
510-
let completeCallback = jasmine.createSpy('complete callback');
522+
const emitCallback = jasmine.createSpy('emit callback');
523+
const completeCallback = jasmine.createSpy('complete callback');
511524

512525
fixture.componentInstance.menu.close.subscribe(emitCallback, null, completeCallback);
513526
fixture.destroy();
514527

515-
expect(emitCallback).toHaveBeenCalled();
528+
expect(emitCallback).toHaveBeenCalledWith(undefined);
529+
expect(emitCallback).toHaveBeenCalledTimes(1);
516530
expect(completeCallback).toHaveBeenCalled();
517531
});
518532
});
@@ -994,6 +1008,9 @@ describe('MdMenu', () => {
9941008
tick(500);
9951009

9961010
expect(overlay.querySelectorAll('.mat-menu-panel').length).toBe(0, 'Expected no open menus');
1011+
expect(instance.rootCloseCallback).toHaveBeenCalledTimes(1);
1012+
expect(instance.levelOneCloseCallback).toHaveBeenCalledTimes(1);
1013+
expect(instance.levelTwoCloseCallback).toHaveBeenCalledTimes(1);
9971014
}));
9981015

9991016
it('should toggle a nested menu when its trigger is added after init', fakeAsync(() => {
@@ -1044,7 +1061,7 @@ describe('MdMenu default overrides', () => {
10441061
@Component({
10451062
template: `
10461063
<button [mdMenuTriggerFor]="menu" #triggerEl>Toggle menu</button>
1047-
<md-menu class="custom-one custom-two" #menu="mdMenu" (close)="closeCallback()">
1064+
<md-menu class="custom-one custom-two" #menu="mdMenu" (close)="closeCallback($event)">
10481065
<button md-menu-item> Item </button>
10491066
<button md-menu-item disabled> Disabled </button>
10501067
</md-menu>
@@ -1137,7 +1154,7 @@ class CustomMenu {
11371154
[mdMenuTriggerFor]="levelTwo"
11381155
#alternateTrigger="mdMenuTrigger">Toggle alternate menu</button>
11391156
1140-
<md-menu #root="mdMenu">
1157+
<md-menu #root="mdMenu" (close)="rootCloseCallback($event)">
11411158
<button md-menu-item
11421159
id="level-one-trigger"
11431160
[mdMenuTriggerFor]="levelOne"
@@ -1150,7 +1167,7 @@ class CustomMenu {
11501167
#lazyTrigger="mdMenuTrigger">Three</button>
11511168
</md-menu>
11521169
1153-
<md-menu #levelOne="mdMenu">
1170+
<md-menu #levelOne="mdMenu" (close)="levelOneCloseCallback($event)">
11541171
<button md-menu-item>Four</button>
11551172
<button md-menu-item
11561173
id="level-two-trigger"
@@ -1159,7 +1176,7 @@ class CustomMenu {
11591176
<button md-menu-item>Six</button>
11601177
</md-menu>
11611178
1162-
<md-menu #levelTwo="mdMenu">
1179+
<md-menu #levelTwo="mdMenu" (close)="levelTwoCloseCallback($event)">
11631180
<button md-menu-item>Seven</button>
11641181
<button md-menu-item>Eight</button>
11651182
<button md-menu-item>Nine</button>
@@ -1177,12 +1194,15 @@ class NestedMenu {
11771194
@ViewChild('rootTrigger') rootTrigger: MdMenuTrigger;
11781195
@ViewChild('rootTriggerEl') rootTriggerEl: ElementRef;
11791196
@ViewChild('alternateTrigger') alternateTrigger: MdMenuTrigger;
1197+
readonly rootCloseCallback = jasmine.createSpy('root menu closed callback');
11801198

11811199
@ViewChild('levelOne') levelOneMenu: MdMenu;
11821200
@ViewChild('levelOneTrigger') levelOneTrigger: MdMenuTrigger;
1201+
readonly levelOneCloseCallback = jasmine.createSpy('level one menu closed callback');
11831202

11841203
@ViewChild('levelTwo') levelTwoMenu: MdMenu;
11851204
@ViewChild('levelTwoTrigger') levelTwoTrigger: MdMenuTrigger;
1205+
readonly levelTwoCloseCallback = jasmine.createSpy('level one menu closed callback');
11861206

11871207
@ViewChild('lazy') lazyMenu: MdMenu;
11881208
@ViewChild('lazyTrigger') lazyTrigger: MdMenuTrigger;

0 commit comments

Comments
 (0)