From 38aeb865b73de83c9aa7ef50fdcbd35d1301ba10 Mon Sep 17 00:00:00 2001 From: Kristiyan Kostadinov Date: Mon, 7 Jun 2021 23:03:35 +0200 Subject: [PATCH] fix(material/menu): not interrupting keyboard events to other overlays (#22856) For historical reasons, `mat-menu` doesn't use the same keyboard event dispatcher as the other overlays. To work around it, previously we added a dummy subscription so that the menu would still show up in the overlay keyboard stack. This works for most events, but it breaks down for the escape key, because closing the menu removes it from the stack immediately, allowing the event to bubble up to the document and be dispatched to the next overlay in the stack. These changes resolve the issue by adding a `stopPropagation` call. Fixes #22694. (cherry picked from commit aeecb3ccbde1e679766c7bcc2ba4b2e483c27ac2) --- src/material-experimental/mdc-menu/menu.spec.ts | 2 ++ src/material/menu/menu.spec.ts | 2 ++ src/material/menu/menu.ts | 5 +++++ 3 files changed, 9 insertions(+) diff --git a/src/material-experimental/mdc-menu/menu.spec.ts b/src/material-experimental/mdc-menu/menu.spec.ts index 2a6aa5200c3a..d4bb4ff2f65e 100644 --- a/src/material-experimental/mdc-menu/menu.spec.ts +++ b/src/material-experimental/mdc-menu/menu.spec.ts @@ -437,12 +437,14 @@ describe('MDC-based MatMenu', () => { const panel = overlayContainerElement.querySelector('.mat-mdc-menu-panel')!; const event = createKeyboardEvent('keydown', ESCAPE); + spyOn(event, 'stopPropagation').and.callThrough(); dispatchEvent(panel, event); fixture.detectChanges(); tick(500); expect(overlayContainerElement.textContent).toBe(''); expect(event.defaultPrevented).toBe(true); + expect(event.stopPropagation).toHaveBeenCalled(); })); it('should not close the menu when pressing ESCAPE with a modifier', fakeAsync(() => { diff --git a/src/material/menu/menu.spec.ts b/src/material/menu/menu.spec.ts index b368ccbcb984..21c77c9aa6cb 100644 --- a/src/material/menu/menu.spec.ts +++ b/src/material/menu/menu.spec.ts @@ -436,6 +436,7 @@ describe('MatMenu', () => { const panel = overlayContainerElement.querySelector('.mat-menu-panel')!; const event = createKeyboardEvent('keydown', ESCAPE); + spyOn(event, 'stopPropagation').and.callThrough(); dispatchEvent(panel, event); fixture.detectChanges(); @@ -443,6 +444,7 @@ describe('MatMenu', () => { expect(overlayContainerElement.textContent).toBe(''); expect(event.defaultPrevented).toBe(true); + expect(event.stopPropagation).toHaveBeenCalled(); })); it('should not close the menu when pressing ESCAPE with a modifier', fakeAsync(() => { diff --git a/src/material/menu/menu.ts b/src/material/menu/menu.ts index 6f17ff03512b..f4e20d4a62ca 100644 --- a/src/material/menu/menu.ts +++ b/src/material/menu/menu.ts @@ -341,7 +341,12 @@ export class _MatMenuBase implements AfterContentInit, MatMenuPanel } manager.onKeydown(event); + return; } + + // Don't allow the event to propagate if we've already handled it, or it may + // end up reaching other overlays that were opened earlier (see #22694). + event.stopPropagation(); } /**