From dbcb921d54608adc95dc124635d2973312928687 Mon Sep 17 00:00:00 2001 From: Kristiyan Kostadinov Date: Thu, 14 Nov 2024 18:55:18 +0100 Subject: [PATCH] fix(material/menu): handle keyboard events through dispatcher (#29997) Currently `mat-menu` handles it keyboard events in the template, however this ignores the overlay's stacking context which can capture some events that it shouldn't. These changes switch the menu to handling the events through the common dispatcher instead. Fixes #29996. (cherry picked from commit 42f6a4ac45b115784a155c886dff28b100d9604b) --- src/material/menu/menu-trigger.ts | 10 +++++----- src/material/menu/menu.html | 1 - src/material/menu/menu.spec.ts | 5 +---- src/material/menu/menu.ts | 4 ---- src/material/menu/testing/menu-harness.ts | 3 +-- 5 files changed, 7 insertions(+), 16 deletions(-) diff --git a/src/material/menu/menu-trigger.ts b/src/material/menu/menu-trigger.ts index a0c0afc187e2..d7f85f2a22d1 100644 --- a/src/material/menu/menu-trigger.ts +++ b/src/material/menu/menu-trigger.ts @@ -412,11 +412,11 @@ export class MatMenuTrigger implements AfterContentInit, OnDestroy { config.positionStrategy as FlexibleConnectedPositionStrategy, ); this._overlayRef = this._overlay.create(config); - - // Consume the `keydownEvents` in order to prevent them from going to another overlay. - // Ideally we'd also have our keyboard event logic in here, however doing so will - // break anybody that may have implemented the `MatMenuPanel` themselves. - this._overlayRef.keydownEvents().subscribe(); + this._overlayRef.keydownEvents().subscribe(event => { + if (this.menu instanceof MatMenu) { + this.menu._handleKeydown(event); + } + }); } return this._overlayRef; diff --git a/src/material/menu/menu.html b/src/material/menu/menu.html index 77f78f1d71b6..1c77f023b8b7 100644 --- a/src/material/menu/menu.html +++ b/src/material/menu/menu.html @@ -3,7 +3,6 @@ class="mat-mdc-menu-panel" [id]="panelId" [class]="_classList" - (keydown)="_handleKeydown($event)" (click)="closed.emit('click')" [@transformMenu]="_panelAnimationState" (@transformMenu.start)="_onAnimationStart($event)" diff --git a/src/material/menu/menu.spec.ts b/src/material/menu/menu.spec.ts index 8de50ba1cadd..fc7de98f0928 100644 --- a/src/material/menu/menu.spec.ts +++ b/src/material/menu/menu.spec.ts @@ -473,16 +473,13 @@ describe('MatMenu', () => { fixture.componentInstance.trigger.openMenu(); const panel = overlayContainerElement.querySelector('.mat-mdc-menu-panel')!; - const event = createKeyboardEvent('keydown', ESCAPE); - spyOn(event, 'stopPropagation').and.callThrough(); + const event = dispatchKeyboardEvent(panel, 'keydown', ESCAPE); - 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.ts b/src/material/menu/menu.ts index 809fe23c440c..e3ae2558567e 100644 --- a/src/material/menu/menu.ts +++ b/src/material/menu/menu.ts @@ -385,10 +385,6 @@ export class MatMenu implements AfterContentInit, MatMenuPanel, OnI 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(); } /** diff --git a/src/material/menu/testing/menu-harness.ts b/src/material/menu/testing/menu-harness.ts index 890ad26bcd0d..753aa8c676f9 100644 --- a/src/material/menu/testing/menu-harness.ts +++ b/src/material/menu/testing/menu-harness.ts @@ -12,7 +12,6 @@ import { HarnessLoader, HarnessPredicate, TestElement, - TestKey, } from '@angular/cdk/testing'; import {coerceBooleanProperty} from '@angular/cdk/coercion'; import {MenuHarnessFilters, MenuItemHarnessFilters} from './menu-harness-filters'; @@ -82,7 +81,7 @@ export class MatMenuHarness extends ContentContainerComponentHarness { async close(): Promise { const panel = await this._getMenuPanel(); if (panel) { - return panel.sendKeys(TestKey.ESCAPE); + return panel.click(); } }