diff --git a/src/dev-app/menu/menu-demo.html b/src/dev-app/menu/menu-demo.html index d4cc3801ed01..8f383a08775c 100644 --- a/src/dev-app/menu/menu-demo.html +++ b/src/dev-app/menu/menu-demo.html @@ -105,9 +105,7 @@
-

- Position x: before -

+

Position x: before

-

- Position y: above -

+

Position y: above

-

- Position x: before, overlapTrigger: true -

+

Position x: before, overlapTrigger: true

-

- Position y: above, overlapTrigger: true -

+

Position y: above, overlapTrigger: true

+
+

disabledInteractive (should not open)

+ + + + + + @for (item of items; track item) { + + } + +
diff --git a/src/material/menu/BUILD.bazel b/src/material/menu/BUILD.bazel index 4fb81fba46b5..9b8ab63420f1 100644 --- a/src/material/menu/BUILD.bazel +++ b/src/material/menu/BUILD.bazel @@ -111,6 +111,7 @@ ng_project( "//src/cdk/overlay", "//src/cdk/scrolling", "//src/cdk/testing/private", + "//src/material/button", "//src/material/core", ], ) diff --git a/src/material/menu/menu-trigger-base.ts b/src/material/menu/menu-trigger-base.ts index efdf997a3b88..1fd74d7e3d30 100644 --- a/src/material/menu/menu-trigger-base.ts +++ b/src/material/menu/menu-trigger-base.ts @@ -22,6 +22,7 @@ import { } from '@angular/cdk/overlay'; import {TemplatePortal} from '@angular/cdk/portal'; import { + booleanAttribute, ChangeDetectorRef, Directive, ElementRef, @@ -29,6 +30,7 @@ import { inject, InjectionToken, Injector, + input, NgZone, OnDestroy, ViewContainerRef, @@ -139,6 +141,9 @@ export abstract class MatMenuTriggerBase implements OnDestroy { } private _menuInternal: MatMenuPanel | null = null; + /** Whether the host component is disabled. Needed to correctly handle disabledInteractive. */ + readonly triggerIsDisabled = input(false, {alias: 'disabled', transform: booleanAttribute}); + /** Event emitted when the associated menu is opened. */ abstract menuOpened: EventEmitter; @@ -191,6 +196,10 @@ export abstract class MatMenuTriggerBase implements OnDestroy { /** Internal method to open menu providing option to auto focus on first item. */ protected _openMenu(autoFocus: boolean): void { + if (this.triggerIsDisabled()) { + return; + } + const menu = this._menu; if (this._menuOpen || !menu) { diff --git a/src/material/menu/menu.spec.ts b/src/material/menu/menu.spec.ts index 28a0d50fd10e..14c1ecb99d97 100644 --- a/src/material/menu/menu.spec.ts +++ b/src/material/menu/menu.spec.ts @@ -42,6 +42,7 @@ import { provideFakeDirectionality, } from '../../cdk/testing/private'; import {MATERIAL_ANIMATIONS, MatRipple} from '../core'; +import {MatButton} from '@angular/material/button'; import {MatMenu, MatMenuItem} from './index'; import { MAT_MENU_DEFAULT_OPTIONS, @@ -1279,6 +1280,23 @@ describe('MatMenu', () => { })); }); + it('does not open if the trigger element is disabled (including disabledInteractive)', fakeAsync(() => { + const fixture = TestBed.createComponent(DisabledMenu); + fixture.detectChanges(); + + const trigger = fixture.componentInstance.triggerEl.nativeElement; + trigger.click(); + fixture.detectChanges(); + tick(500); + expect(overlayContainerElement.querySelector('.mat-mdc-menu-panel [mat-menu-item]')).toBeNull(); + + dispatchKeyboardEvent(trigger, 'keydown', ENTER); + trigger.click(); + fixture.detectChanges(); + tick(500); + expect(overlayContainerElement.querySelector('.mat-mdc-menu-panel [mat-menu-item]')).toBeNull(); + })); + describe('positions', () => { let fixture: ComponentFixture; let trigger: HTMLElement; @@ -2634,6 +2652,20 @@ class SimpleMenu { }) class SimpleMenuOnPush extends SimpleMenu {} +@Component({ + template: ` + + + + + `, + imports: [MatButton, MatMenuTrigger, MatMenu, MatMenuItem], +}) +class DisabledMenu { + @ViewChild('triggerEl', {read: ElementRef}) triggerEl!: ElementRef; +} + @Component({ template: ` @@ -2666,7 +2698,7 @@ interface TestableMenu { class OverlapMenu implements TestableMenu { @Input() overlapTrigger: boolean = false; @ViewChild(MatMenuTrigger) trigger!: MatMenuTrigger; - @ViewChild('triggerEl') triggerEl!: ElementRef; + @ViewChild('triggerEl', {read: ElementRef}) triggerEl!: ElementRef; } @Component({