Skip to content

Commit 188ff25

Browse files
committed
fix(cdk/menu): disabled behavior fixes for menu item (#31721)
Fixes the following issues with the disabled behavior of the CDK menu item: * The item wasn't receiving focus like specified in the spec. * There wasn't a proper way to style disabled items which led users to use `aria-disabled`. * Clicking the disaled item didn't prevent the default behavior. Fixes #31179. (cherry picked from commit d61e35d)
1 parent bdab800 commit 188ff25

File tree

4 files changed

+46
-3
lines changed

4 files changed

+46
-3
lines changed

goldens/cdk/menu/index.api.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,7 @@ export class CdkMenuItem implements FocusableOption, FocusableElement, Toggler,
138138
getLabel(): string;
139139
getMenu(): Menu | undefined;
140140
getMenuTrigger(): CdkMenuTrigger | null;
141+
protected _handleClick(event: MouseEvent): void;
141142
get hasMenu(): boolean;
142143
isMenuOpen(): boolean;
143144
// (undocumented)

src/cdk/menu/menu-base.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,11 @@ export abstract class CdkMenuBase
196196

197197
/** Setup the FocusKeyManager with the correct orientation for the menu. */
198198
private _setKeyManager() {
199-
this.keyManager = new FocusKeyManager(this.items).withWrap().withTypeAhead().withHomeAndEnd();
199+
this.keyManager = new FocusKeyManager(this.items)
200+
.withWrap()
201+
.withTypeAhead()
202+
.withHomeAndEnd()
203+
.skipPredicate(() => false);
200204

201205
if (this.orientation === 'horizontal') {
202206
this.keyManager.withHorizontalOrientation(this.dir?.value || 'ltr');

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

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import {Component, Type} from '@angular/core';
22
import {ComponentFixture, TestBed} from '@angular/core/testing';
3-
import {dispatchKeyboardEvent} from '../testing/private';
3+
import {dispatchFakeEvent, dispatchKeyboardEvent} from '../testing/private';
44
import {By} from '@angular/platform-browser';
55
import {ENTER} from '../keycodes';
66
import {CdkMenuModule} from './menu-module';
@@ -44,6 +44,33 @@ describe('MenuItem', () => {
4444
expect(nativeButton.hasAttribute('aria-disabled')).toBeFalse();
4545
});
4646

47+
it('should toggle a class when the item is disabled', () => {
48+
expect(nativeButton.classList).not.toContain('cdk-menu-item-disabled');
49+
50+
menuItem.disabled = true;
51+
fixture.changeDetectorRef.markForCheck();
52+
fixture.detectChanges();
53+
54+
expect(nativeButton.classList).toContain('cdk-menu-item-disabled');
55+
56+
menuItem.disabled = false;
57+
fixture.changeDetectorRef.markForCheck();
58+
fixture.detectChanges();
59+
60+
expect(nativeButton.classList).not.toContain('cdk-menu-item-disabled');
61+
});
62+
63+
it('should prevent the default click action when clicking on a disabled button', () => {
64+
menuItem.disabled = true;
65+
fixture.changeDetectorRef.markForCheck();
66+
fixture.detectChanges();
67+
68+
const event = dispatchFakeEvent(nativeButton, 'click');
69+
fixture.detectChanges();
70+
71+
expect(event.defaultPrevented).toBe(true);
72+
});
73+
4774
it('should not have a menu', () => {
4875
expect(menuItem.hasMenu).toBeFalse();
4976
});

src/cdk/menu/menu-item.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,12 @@ import {eventDispatchesNativeClick} from './event-detection';
4040
host: {
4141
'role': 'menuitem',
4242
'class': 'cdk-menu-item',
43+
'[class.cdk-menu-item-disabled]': 'disabled',
4344
'[tabindex]': '_tabindex',
4445
'[attr.aria-disabled]': 'disabled || null',
4546
'(blur)': '_resetTabIndex()',
4647
'(focus)': '_setTabIndex()',
47-
'(click)': 'trigger()',
48+
'(click)': '_handleClick($event)',
4849
'(keydown)': '_onKeydown($event)',
4950
},
5051
})
@@ -181,6 +182,16 @@ export class CdkMenuItem implements FocusableOption, FocusableElement, Toggler,
181182
}
182183
}
183184

185+
/** Handles click events on the item. */
186+
protected _handleClick(event: MouseEvent) {
187+
if (this.disabled) {
188+
event.preventDefault();
189+
event.stopPropagation();
190+
} else {
191+
this.trigger();
192+
}
193+
}
194+
184195
/**
185196
* Handles keyboard events for the menu item, specifically either triggering the user defined
186197
* callback or opening/closing the current menu based on whether the left or right arrow key was

0 commit comments

Comments
 (0)