diff --git a/src/lib/menu/menu-trigger.ts b/src/lib/menu/menu-trigger.ts index c06485356677..9bceb8282e80 100644 --- a/src/lib/menu/menu-trigger.ts +++ b/src/lib/menu/menu-trigger.ts @@ -43,8 +43,8 @@ import {MdMenuItem} from './menu-item'; import {MdMenuPanel} from './menu-panel'; import {MenuPositionX, MenuPositionY} from './menu-positions'; import {throwMdMenuMissingError} from './menu-errors'; -import {merge} from 'rxjs/observable/merge'; import {of as observableOf} from 'rxjs/observable/of'; +import {merge} from 'rxjs/observable/merge'; import {Subscription} from 'rxjs/Subscription'; /** Injection token that determines the scroll handling while the menu is open. */ @@ -153,7 +153,7 @@ export class MdMenuTrigger implements AfterViewInit, OnDestroy { this._checkMenu(); this.menu.close.subscribe(reason => { - this._destroyMenu(); + this.closeMenu(); // If a click closed the menu, we should close the entire chain of nested menus. if (reason === 'click' && this._parentMenu) { @@ -205,9 +205,7 @@ export class MdMenuTrigger implements AfterViewInit, OnDestroy { openMenu(): void { if (!this._menuOpen) { this._createOverlay().attach(this._portal); - this._closeSubscription = this._menuClosingActions().subscribe(() => { - this.menu.close.emit(); - }); + this._closeSubscription = this._menuClosingActions().subscribe(() => this.menu.close.emit()); this._initMenu(); if (this.menu instanceof MdMenu) { @@ -218,20 +216,11 @@ export class MdMenuTrigger implements AfterViewInit, OnDestroy { /** Closes the menu. */ closeMenu(): void { - this.menu.close.emit(); - } - - /** Focuses the menu trigger. */ - focus() { - this._element.nativeElement.focus(); - } - - /** Closes the menu and does the necessary cleanup. */ - private _destroyMenu() { if (this._overlayRef && this.menuOpen) { this._resetMenu(); this._overlayRef.detach(); this._closeSubscription.unsubscribe(); + this.menu.close.emit(); if (this.menu instanceof MdMenu) { this.menu._resetAnimation(); @@ -239,6 +228,11 @@ export class MdMenuTrigger implements AfterViewInit, OnDestroy { } } + /** Focuses the menu trigger. */ + focus() { + this._element.nativeElement.focus(); + } + /** * This method sets the menu state to open and focuses the first item if * the menu was opened via the keyboard. @@ -406,11 +400,11 @@ export class MdMenuTrigger implements AfterViewInit, OnDestroy { /** Returns a stream that emits whenever an action that should close the menu occurs. */ private _menuClosingActions() { const backdrop = this._overlayRef!.backdropClick(); - const parentClose = this._parentMenu ? this._parentMenu.close : observableOf(); + const parentClose = this._parentMenu ? this._parentMenu.close : observableOf(null); const hover = this._parentMenu ? RxChain.from(this._parentMenu.hover()) .call(filter, active => active !== this._menuItemInstance) .call(filter, () => this._menuOpen) - .result() : observableOf(); + .result() : observableOf(null); return merge(backdrop, parentClose, hover); } diff --git a/src/lib/menu/menu.spec.ts b/src/lib/menu/menu.spec.ts index d92db276b1cc..a9e8086aaa5f 100644 --- a/src/lib/menu/menu.spec.ts +++ b/src/lib/menu/menu.spec.ts @@ -98,7 +98,7 @@ describe('MdMenu', () => { expect(overlayContainerElement.textContent).toBe(''); })); - it('should close the menu when pressing ESCAPE', fakeAsync(() => { + it('should close the menu when pressing escape', fakeAsync(() => { const fixture = TestBed.createComponent(SimpleMenu); fixture.detectChanges(); fixture.componentInstance.trigger.openMenu(); @@ -494,40 +494,26 @@ describe('MdMenu', () => { menuItem.click(); fixture.detectChanges(); - expect(fixture.componentInstance.closeCallback).toHaveBeenCalledWith('click'); - expect(fixture.componentInstance.closeCallback).toHaveBeenCalledTimes(1); + expect(fixture.componentInstance.closeCallback).toHaveBeenCalled(); }); it('should emit a close event when the backdrop is clicked', () => { - const backdrop = overlayContainerElement - .querySelector('.cdk-overlay-backdrop') as HTMLElement; + const backdrop = overlayContainerElement.querySelector('.cdk-overlay-backdrop'); backdrop.click(); fixture.detectChanges(); - expect(fixture.componentInstance.closeCallback).toHaveBeenCalledWith(undefined); - expect(fixture.componentInstance.closeCallback).toHaveBeenCalledTimes(1); - }); - - it('should emit an event when pressing ESCAPE', () => { - const menu = overlayContainerElement.querySelector('.mat-menu-panel') as HTMLElement; - - dispatchKeyboardEvent(menu, 'keydown', ESCAPE); - fixture.detectChanges(); - - expect(fixture.componentInstance.closeCallback).toHaveBeenCalledWith('keydown'); - expect(fixture.componentInstance.closeCallback).toHaveBeenCalledTimes(1); + expect(fixture.componentInstance.closeCallback).toHaveBeenCalled(); }); it('should complete the callback when the menu is destroyed', () => { - const emitCallback = jasmine.createSpy('emit callback'); - const completeCallback = jasmine.createSpy('complete callback'); + let emitCallback = jasmine.createSpy('emit callback'); + let completeCallback = jasmine.createSpy('complete callback'); fixture.componentInstance.menu.close.subscribe(emitCallback, null, completeCallback); fixture.destroy(); - expect(emitCallback).toHaveBeenCalledWith(undefined); - expect(emitCallback).toHaveBeenCalledTimes(1); + expect(emitCallback).toHaveBeenCalled(); expect(completeCallback).toHaveBeenCalled(); }); }); @@ -1009,9 +995,6 @@ describe('MdMenu', () => { tick(500); expect(overlay.querySelectorAll('.mat-menu-panel').length).toBe(0, 'Expected no open menus'); - expect(instance.rootCloseCallback).toHaveBeenCalledTimes(1); - expect(instance.levelOneCloseCallback).toHaveBeenCalledTimes(1); - expect(instance.levelTwoCloseCallback).toHaveBeenCalledTimes(1); })); it('should toggle a nested menu when its trigger is added after init', fakeAsync(() => { @@ -1076,7 +1059,7 @@ describe('MdMenu default overrides', () => { @Component({ template: ` - + @@ -1169,7 +1152,7 @@ class CustomMenu { [mdMenuTriggerFor]="levelTwo" #alternateTrigger="mdMenuTrigger">Toggle alternate menu - + - + - + @@ -1209,15 +1192,12 @@ class NestedMenu { @ViewChild('rootTrigger') rootTrigger: MdMenuTrigger; @ViewChild('rootTriggerEl') rootTriggerEl: ElementRef; @ViewChild('alternateTrigger') alternateTrigger: MdMenuTrigger; - readonly rootCloseCallback = jasmine.createSpy('root menu closed callback'); @ViewChild('levelOne') levelOneMenu: MdMenu; @ViewChild('levelOneTrigger') levelOneTrigger: MdMenuTrigger; - readonly levelOneCloseCallback = jasmine.createSpy('level one menu closed callback'); @ViewChild('levelTwo') levelTwoMenu: MdMenu; @ViewChild('levelTwoTrigger') levelTwoTrigger: MdMenuTrigger; - readonly levelTwoCloseCallback = jasmine.createSpy('level one menu closed callback'); @ViewChild('lazy') lazyMenu: MdMenu; @ViewChild('lazyTrigger') lazyTrigger: MdMenuTrigger;