Skip to content

Commit eae2620

Browse files
authored
fix(cdk/menu): don't prevent default enter and space actions (#25591)
Fixes that the CDK menu trigger and menu item were preventing the default action on enter and space key presses. Presumably this was done to avoid form submissions, but it can also prevent anchors from being used as menu items. These changes avoid submissions by setting the `type` attribute on `button` elements. Fixes #25584.
1 parent 1b0265c commit eae2620

File tree

5 files changed

+32
-4
lines changed

5 files changed

+32
-4
lines changed

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@ describe('MenuItem', () => {
3838
expect(nativeButton.getAttribute('role')).toBe('menuitem');
3939
});
4040

41+
it('should a type on the button', () => {
42+
expect(nativeButton.getAttribute('type')).toBe('button');
43+
});
44+
4145
it('should coerce the disabled property', () => {
4246
(menuItem as any).disabled = '';
4347
expect(menuItem.disabled).toBeTrue();

src/cdk/menu/menu-item.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ export class CdkMenuItem implements FocusableOption, FocusableElement, Toggler,
5353
protected readonly _dir = inject(Directionality, InjectFlags.Optional);
5454

5555
/** The menu's native DOM host element. */
56-
readonly _elementRef = inject(ElementRef);
56+
readonly _elementRef: ElementRef<HTMLElement> = inject(ElementRef);
5757

5858
/** The Angular zone. */
5959
protected _ngZone = inject(NgZone);
@@ -109,6 +109,7 @@ export class CdkMenuItem implements FocusableOption, FocusableElement, Toggler,
109109

110110
constructor() {
111111
this._setupMouseEnter();
112+
this._setType();
112113

113114
if (this._isStandaloneItem()) {
114115
this._tabindex = 0;
@@ -197,7 +198,6 @@ export class CdkMenuItem implements FocusableOption, FocusableElement, Toggler,
197198
case SPACE:
198199
case ENTER:
199200
if (!hasModifierKey(event)) {
200-
event.preventDefault();
201201
this.trigger({keepOpen: event.keyCode === SPACE && !this.closeOnSpacebarTrigger});
202202
}
203203
break;
@@ -298,4 +298,14 @@ export class CdkMenuItem implements FocusableOption, FocusableElement, Toggler,
298298
private _isParentVertical() {
299299
return this._parentMenu?.orientation === 'vertical';
300300
}
301+
302+
/** Sets the `type` attribute of the menu item. */
303+
private _setType() {
304+
const element = this._elementRef.nativeElement;
305+
306+
if (element.nodeName === 'BUTTON' && !element.getAttribute('type')) {
307+
// Prevent form submissions.
308+
element.setAttribute('type', 'button');
309+
}
310+
}
301311
}

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,10 @@ describe('MenuTrigger', () => {
3434
expect(menuItemElement.getAttribute('role')).toBe('menuitem');
3535
});
3636

37+
it('should set a type on the trigger', () => {
38+
expect(menuItemElement.getAttribute('type')).toBe('button');
39+
});
40+
3741
it('should set the aria disabled attribute', () => {
3842
expect(menuItemElement.getAttribute('aria-disabled')).toBeNull();
3943

src/cdk/menu/menu-trigger.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ export class CdkMenuTrigger extends CdkMenuTriggerBase implements OnDestroy {
8484
this._subscribeToMenuStackClosed();
8585
this._subscribeToMouseEnter();
8686
this._subscribeToMenuStackHasFocus();
87+
this._setType();
8788
}
8889

8990
/** Toggle the attached menu. */
@@ -130,7 +131,6 @@ export class CdkMenuTrigger extends CdkMenuTriggerBase implements OnDestroy {
130131
case SPACE:
131132
case ENTER:
132133
if (!hasModifierKey(event)) {
133-
event.preventDefault();
134134
this.toggle();
135135
this.childMenu?.focusFirstItem('keyboard');
136136
}
@@ -326,4 +326,14 @@ export class CdkMenuTrigger extends CdkMenuTriggerBase implements OnDestroy {
326326
this._elementRef.nativeElement.setAttribute('role', 'button');
327327
}
328328
}
329+
330+
/** Sets thte `type` attribute of the trigger. */
331+
private _setType() {
332+
const element = this._elementRef.nativeElement;
333+
334+
if (element.nodeName === 'BUTTON' && !element.getAttribute('type')) {
335+
// Prevents form submissions.
336+
element.setAttribute('type', 'button');
337+
}
338+
}
329339
}

tools/public_api_guard/cdk/menu.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ export class CdkMenuItem implements FocusableOption, FocusableElement, Toggler,
122122
protected readonly _dir: Directionality | null;
123123
get disabled(): boolean;
124124
set disabled(value: BooleanInput);
125-
readonly _elementRef: ElementRef<any>;
125+
readonly _elementRef: ElementRef<HTMLElement>;
126126
focus(): void;
127127
getLabel(): string;
128128
getMenu(): Menu | undefined;

0 commit comments

Comments
 (0)