From 422445e2863b627542a63981b20e9688f0c26819 Mon Sep 17 00:00:00 2001 From: Kristiyan Kostadinov Date: Thu, 2 Jan 2025 09:39:01 +0100 Subject: [PATCH] fix(cdk/menu): avoid re-opening the menu on enter Fixes that the CDK menu was re-opening immediately on enter presses on elements that aren't buttons or links. Fixes #30250. --- src/cdk/menu/menu-item.ts | 8 ++++++++ src/cdk/menu/menu-trigger.spec.ts | 23 ++++++++++++++++++++++- 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/src/cdk/menu/menu-item.ts b/src/cdk/menu/menu-item.ts index c203c2fc5633..1496720412af 100644 --- a/src/cdk/menu/menu-item.ts +++ b/src/cdk/menu/menu-item.ts @@ -193,6 +193,14 @@ export class CdkMenuItem implements FocusableOption, FocusableElement, Toggler, case ENTER: // Skip events that will trigger clicks so the handler doesn't get triggered twice. if (!hasModifierKey(event) && !eventDispatchesNativeClick(this._elementRef, event)) { + const nodeName = this._elementRef.nativeElement.nodeName; + + // Avoid repeat events on non-native elements (see #30250). Note that we don't do this + // on the native elements so we don't interfere with their behavior (see #26296). + if (nodeName !== 'A' && nodeName !== 'BUTTON') { + event.preventDefault(); + } + this.trigger({keepOpen: event.keyCode === SPACE && !this.closeOnSpacebarTrigger}); } break; diff --git a/src/cdk/menu/menu-trigger.spec.ts b/src/cdk/menu/menu-trigger.spec.ts index cc447dd7f0e7..d9b138a12cc8 100644 --- a/src/cdk/menu/menu-trigger.spec.ts +++ b/src/cdk/menu/menu-trigger.spec.ts @@ -465,6 +465,22 @@ describe('MenuTrigger', () => { expect(secondEvent.defaultPrevented).toBe(false); }); + it('should prevent the default action on enter presses on non-button/non-link triggers', () => { + fixture.componentInstance.useButtonTrigger = false; + fixture.changeDetectorRef.markForCheck(); + detectChanges(); + + const firstEvent = dispatchKeyboardEvent(nativeTrigger, 'keydown', ENTER); + detectChanges(); + expect(firstEvent.defaultPrevented).toBe(true); + expect(nativeMenus.length).toBe(2); + + const secondEvent = dispatchKeyboardEvent(nativeTrigger, 'keydown', ENTER); + detectChanges(); + expect(nativeMenus.length).toBe(1); + expect(secondEvent.defaultPrevented).toBe(true); + }); + it('should close the open menu on background click', () => { nativeTrigger.click(); detectChanges(); @@ -674,7 +690,11 @@ class TriggerOpensItsMenu { @Component({ template: ` - + @if (useButtonTrigger) { + + } @else { +
First
+ }
@@ -693,6 +713,7 @@ class StandaloneTriggerWithInlineMenu { @ViewChild('submenu_item', {read: ElementRef}) submenuItem?: ElementRef; @ViewChild('inline_item', {read: ElementRef}) nativeInlineItem: ElementRef; @ViewChildren(CdkMenu, {read: ElementRef}) nativeMenus: QueryList; + useButtonTrigger = true; } @Component({