Skip to content

Commit 52758be

Browse files
authored
fix(cdk/menu): avoid re-opening the menu on enter (#30263)
Fixes that the CDK menu was re-opening immediately on enter presses on elements that aren't buttons or links. Fixes #30250.
1 parent b961966 commit 52758be

File tree

2 files changed

+30
-1
lines changed

2 files changed

+30
-1
lines changed

src/cdk/menu/menu-item.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,14 @@ export class CdkMenuItem implements FocusableOption, FocusableElement, Toggler,
193193
case ENTER:
194194
// Skip events that will trigger clicks so the handler doesn't get triggered twice.
195195
if (!hasModifierKey(event) && !eventDispatchesNativeClick(this._elementRef, event)) {
196+
const nodeName = this._elementRef.nativeElement.nodeName;
197+
198+
// Avoid repeat events on non-native elements (see #30250). Note that we don't do this
199+
// on the native elements so we don't interfere with their behavior (see #26296).
200+
if (nodeName !== 'A' && nodeName !== 'BUTTON') {
201+
event.preventDefault();
202+
}
203+
196204
this.trigger({keepOpen: event.keyCode === SPACE && !this.closeOnSpacebarTrigger});
197205
}
198206
break;

src/cdk/menu/menu-trigger.spec.ts

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -465,6 +465,22 @@ describe('MenuTrigger', () => {
465465
expect(secondEvent.defaultPrevented).toBe(false);
466466
});
467467

468+
it('should prevent the default action on enter presses on non-button/non-link triggers', () => {
469+
fixture.componentInstance.useButtonTrigger = false;
470+
fixture.changeDetectorRef.markForCheck();
471+
detectChanges();
472+
473+
const firstEvent = dispatchKeyboardEvent(nativeTrigger, 'keydown', ENTER);
474+
detectChanges();
475+
expect(firstEvent.defaultPrevented).toBe(true);
476+
expect(nativeMenus.length).toBe(2);
477+
478+
const secondEvent = dispatchKeyboardEvent(nativeTrigger, 'keydown', ENTER);
479+
detectChanges();
480+
expect(nativeMenus.length).toBe(1);
481+
expect(secondEvent.defaultPrevented).toBe(true);
482+
});
483+
468484
it('should close the open menu on background click', () => {
469485
nativeTrigger.click();
470486
detectChanges();
@@ -674,7 +690,11 @@ class TriggerOpensItsMenu {
674690

675691
@Component({
676692
template: `
677-
<button cdkMenuItem [cdkMenuTriggerFor]="sub1">First</button>
693+
@if (useButtonTrigger) {
694+
<button cdkMenuItem [cdkMenuTriggerFor]="sub1">First</button>
695+
} @else {
696+
<div cdkMenuItem [cdkMenuTriggerFor]="sub1">First</div>
697+
}
678698
679699
<ng-template #sub1>
680700
<div cdkMenu>
@@ -693,6 +713,7 @@ class StandaloneTriggerWithInlineMenu {
693713
@ViewChild('submenu_item', {read: ElementRef}) submenuItem?: ElementRef<HTMLElement>;
694714
@ViewChild('inline_item', {read: ElementRef}) nativeInlineItem: ElementRef<HTMLElement>;
695715
@ViewChildren(CdkMenu, {read: ElementRef}) nativeMenus: QueryList<ElementRef>;
716+
useButtonTrigger = true;
696717
}
697718

698719
@Component({

0 commit comments

Comments
 (0)