diff --git a/src/lib/menu/menu-directive.ts b/src/lib/menu/menu-directive.ts index 3863103b3f4..9ff5f9aeb15 100644 --- a/src/lib/menu/menu-directive.ts +++ b/src/lib/menu/menu-directive.ts @@ -196,8 +196,8 @@ export class MatMenu implements OnInit, AfterContentInit, MatMenuPanel, OnDestro set classList(classes: string) { this.panelClass = classes; } /** Event emitted when the menu is closed. */ - @Output() readonly closed: EventEmitter = - new EventEmitter(); + @Output() readonly closed: EventEmitter = + new EventEmitter(); /** * Event emitted when the menu is closed. @@ -217,7 +217,7 @@ export class MatMenu implements OnInit, AfterContentInit, MatMenuPanel, OnDestro ngAfterContentInit() { this._keyManager = new FocusKeyManager(this.items).withWrap().withTypeAhead(); - this._tabSubscription = this._keyManager.tabOut.subscribe(() => this.close.emit('keydown')); + this._tabSubscription = this._keyManager.tabOut.subscribe(() => this.close.emit('tab')); } ngOnDestroy() { diff --git a/src/lib/menu/menu-panel.ts b/src/lib/menu/menu-panel.ts index 9e140a1d819..344841a81f5 100644 --- a/src/lib/menu/menu-panel.ts +++ b/src/lib/menu/menu-panel.ts @@ -21,7 +21,7 @@ export interface MatMenuPanel { yPosition: MenuPositionY; overlapTrigger: boolean; templateRef: TemplateRef; - close: EventEmitter; + close: EventEmitter; parentMenu?: MatMenuPanel | undefined; direction?: Direction; focusFirstItem: (origin?: FocusOrigin) => void; diff --git a/src/lib/menu/menu-trigger.ts b/src/lib/menu/menu-trigger.ts index ea5fc92a6ff..46e42754ddc 100644 --- a/src/lib/menu/menu-trigger.ts +++ b/src/lib/menu/menu-trigger.ts @@ -155,7 +155,7 @@ export class MatMenuTrigger implements AfterContentInit, OnDestroy { this._destroyMenu(); // If a click closed the menu, we should close the entire chain of nested menus. - if (reason === 'click' && this._parentMenu) { + if ((reason === 'click' || reason === 'tab') && this._parentMenu) { this._parentMenu.closed.emit(reason); } }); diff --git a/src/lib/menu/menu.spec.ts b/src/lib/menu/menu.spec.ts index 55676f8d6fd..39209f5fbd5 100644 --- a/src/lib/menu/menu.spec.ts +++ b/src/lib/menu/menu.spec.ts @@ -15,7 +15,7 @@ import { } from '@angular/core'; import {Direction, Directionality} from '@angular/cdk/bidi'; import {OverlayContainer, Overlay} from '@angular/cdk/overlay'; -import {ESCAPE, LEFT_ARROW, RIGHT_ARROW} from '@angular/cdk/keycodes'; +import {ESCAPE, LEFT_ARROW, RIGHT_ARROW, TAB} from '@angular/cdk/keycodes'; import { MAT_MENU_DEFAULT_OPTIONS, MatMenu, @@ -1168,6 +1168,28 @@ describe('MatMenu', () => { expect(overlay.querySelectorAll('.mat-menu-panel').length).toBe(0, 'Expected no open menus'); })); + it('should close all of the menus when the user tabs away', fakeAsync(() => { + compileTestComponent(); + instance.rootTriggerEl.nativeElement.click(); + fixture.detectChanges(); + + instance.levelOneTrigger.openMenu(); + fixture.detectChanges(); + + instance.levelTwoTrigger.openMenu(); + fixture.detectChanges(); + + const menus = overlay.querySelectorAll('.mat-menu-panel'); + + expect(menus.length).toBe(3, 'Expected three open menus'); + + dispatchKeyboardEvent(menus[menus.length - 1], 'keydown', TAB); + fixture.detectChanges(); + tick(500); + + expect(overlay.querySelectorAll('.mat-menu-panel').length).toBe(0, 'Expected no open menus'); + })); + it('should set a class on the menu items that trigger a sub-menu', () => { compileTestComponent(); instance.rootTrigger.openMenu();