diff --git a/goldens/cdk/menu/index.api.md b/goldens/cdk/menu/index.api.md index 43399c063e66..bc07c92e333e 100644 --- a/goldens/cdk/menu/index.api.md +++ b/goldens/cdk/menu/index.api.md @@ -138,6 +138,7 @@ export class CdkMenuItem implements FocusableOption, FocusableElement, Toggler, getLabel(): string; getMenu(): Menu | undefined; getMenuTrigger(): CdkMenuTrigger | null; + protected _handleClick(event: MouseEvent): void; get hasMenu(): boolean; isMenuOpen(): boolean; // (undocumented) diff --git a/src/cdk/menu/menu-base.ts b/src/cdk/menu/menu-base.ts index 6c050c81868b..42fae274aac6 100644 --- a/src/cdk/menu/menu-base.ts +++ b/src/cdk/menu/menu-base.ts @@ -196,7 +196,11 @@ export abstract class CdkMenuBase /** Setup the FocusKeyManager with the correct orientation for the menu. */ private _setKeyManager() { - this.keyManager = new FocusKeyManager(this.items).withWrap().withTypeAhead().withHomeAndEnd(); + this.keyManager = new FocusKeyManager(this.items) + .withWrap() + .withTypeAhead() + .withHomeAndEnd() + .skipPredicate(() => false); if (this.orientation === 'horizontal') { this.keyManager.withHorizontalOrientation(this.dir?.value || 'ltr'); diff --git a/src/cdk/menu/menu-item.spec.ts b/src/cdk/menu/menu-item.spec.ts index 698e331ba72a..1eade63e3cc3 100644 --- a/src/cdk/menu/menu-item.spec.ts +++ b/src/cdk/menu/menu-item.spec.ts @@ -1,6 +1,6 @@ import {Component, Type} from '@angular/core'; import {ComponentFixture, TestBed} from '@angular/core/testing'; -import {dispatchKeyboardEvent} from '../testing/private'; +import {dispatchFakeEvent, dispatchKeyboardEvent} from '../testing/private'; import {By} from '@angular/platform-browser'; import {ENTER} from '../keycodes'; import {CdkMenuModule} from './menu-module'; @@ -44,6 +44,33 @@ describe('MenuItem', () => { expect(nativeButton.hasAttribute('aria-disabled')).toBeFalse(); }); + it('should toggle a class when the item is disabled', () => { + expect(nativeButton.classList).not.toContain('cdk-menu-item-disabled'); + + menuItem.disabled = true; + fixture.changeDetectorRef.markForCheck(); + fixture.detectChanges(); + + expect(nativeButton.classList).toContain('cdk-menu-item-disabled'); + + menuItem.disabled = false; + fixture.changeDetectorRef.markForCheck(); + fixture.detectChanges(); + + expect(nativeButton.classList).not.toContain('cdk-menu-item-disabled'); + }); + + it('should prevent the default click action when clicking on a disabled button', () => { + menuItem.disabled = true; + fixture.changeDetectorRef.markForCheck(); + fixture.detectChanges(); + + const event = dispatchFakeEvent(nativeButton, 'click'); + fixture.detectChanges(); + + expect(event.defaultPrevented).toBe(true); + }); + it('should not have a menu', () => { expect(menuItem.hasMenu).toBeFalse(); }); diff --git a/src/cdk/menu/menu-item.ts b/src/cdk/menu/menu-item.ts index 58ed42e5b5d7..45ecaf9822f2 100644 --- a/src/cdk/menu/menu-item.ts +++ b/src/cdk/menu/menu-item.ts @@ -40,11 +40,12 @@ import {eventDispatchesNativeClick} from './event-detection'; host: { 'role': 'menuitem', 'class': 'cdk-menu-item', + '[class.cdk-menu-item-disabled]': 'disabled', '[tabindex]': '_tabindex', '[attr.aria-disabled]': 'disabled || null', '(blur)': '_resetTabIndex()', '(focus)': '_setTabIndex()', - '(click)': 'trigger()', + '(click)': '_handleClick($event)', '(keydown)': '_onKeydown($event)', }, }) @@ -181,6 +182,16 @@ export class CdkMenuItem implements FocusableOption, FocusableElement, Toggler, } } + /** Handles click events on the item. */ + protected _handleClick(event: MouseEvent) { + if (this.disabled) { + event.preventDefault(); + event.stopPropagation(); + } else { + this.trigger(); + } + } + /** * Handles keyboard events for the menu item, specifically either triggering the user defined * callback or opening/closing the current menu based on whether the left or right arrow key was