Skip to content

Commit 3b7fe64

Browse files
crisbetommalerba
authored andcommitted
fix(menu): not closing sibling sub-menus when hovering over disabled items (#10396)
* Fixes sub-menus not being closed when the user hovers over a disabled sibling menu item. * Fixes the sub-menu arrow not being greyed out for disabled items.
1 parent 0e21443 commit 3b7fe64

File tree

4 files changed

+58
-7
lines changed

4 files changed

+58
-7
lines changed

src/lib/menu/_menu-theme.scss

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@
1616
color: mat-color($foreground, 'text');
1717

1818
&[disabled] {
19-
color: mat-color($foreground, 'disabled');
19+
&, &::after {
20+
color: mat-color($foreground, 'disabled');
21+
}
2022
}
2123
}
2224

src/lib/menu/menu-item.ts

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ export const _MatMenuItemMixinBase = mixinDisableRipple(mixinDisabled(MatMenuIte
4747
'[attr.aria-disabled]': 'disabled.toString()',
4848
'[attr.disabled]': 'disabled || null',
4949
'(click)': '_checkDisabled($event)',
50-
'(mouseenter)': '_emitHoverEvent()',
50+
'(mouseenter)': '_handleMouseEnter()',
5151
},
5252
changeDetection: ChangeDetectionStrategy.OnPush,
5353
encapsulation: ViewEncapsulation.None,
@@ -121,10 +121,8 @@ export class MatMenuItem extends _MatMenuItemMixinBase
121121
}
122122

123123
/** Emits to the hover stream. */
124-
_emitHoverEvent() {
125-
if (!this.disabled) {
126-
this._hovered.next(this);
127-
}
124+
_handleMouseEnter() {
125+
this._hovered.next(this);
128126
}
129127

130128
/** Gets the label to be used when determining whether the option should be focused. */

src/lib/menu/menu-trigger.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ export class MatMenuTrigger implements AfterContentInit, OnDestroy {
152152
if (this.triggersSubmenu()) {
153153
// Subscribe to changes in the hovered item in order to toggle the panel.
154154
this._hoverSubscription = this._parentMenu._hovered()
155-
.pipe(filter(active => active === this._menuItemInstance))
155+
.pipe(filter(active => active === this._menuItemInstance && !active.disabled))
156156
.subscribe(() => {
157157
this._openedByMouse = true;
158158
this.openMenu();

src/lib/menu/menu.spec.ts

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -910,6 +910,57 @@ describe('MatMenu', () => {
910910
.toBe(1, 'Expected one open menu');
911911
}));
912912

913+
it('should close submenu when hovering over disabled sibling item', fakeAsync(() => {
914+
compileTestComponent();
915+
instance.rootTriggerEl.nativeElement.click();
916+
fixture.detectChanges();
917+
tick(500);
918+
919+
const items = fixture.debugElement.queryAll(By.directive(MatMenuItem));
920+
921+
dispatchFakeEvent(items[0].nativeElement, 'mouseenter');
922+
fixture.detectChanges();
923+
tick(500);
924+
925+
expect(overlay.querySelectorAll('.mat-menu-panel').length)
926+
.toBe(2, 'Expected two open menus');
927+
928+
items[1].componentInstance.disabled = true;
929+
fixture.detectChanges();
930+
931+
// Invoke the handler directly since the fake events are flaky on disabled elements.
932+
items[1].componentInstance._handleMouseEnter();
933+
fixture.detectChanges();
934+
tick(500);
935+
936+
expect(overlay.querySelectorAll('.mat-menu-panel').length)
937+
.toBe(1, 'Expected one open menu');
938+
}));
939+
940+
it('should not open submenu when hovering over disabled trigger', fakeAsync(() => {
941+
compileTestComponent();
942+
instance.rootTriggerEl.nativeElement.click();
943+
fixture.detectChanges();
944+
tick(500);
945+
946+
expect(overlay.querySelectorAll('.mat-menu-panel').length)
947+
.toBe(1, 'Expected one open menu');
948+
949+
const item = fixture.debugElement.query(By.directive(MatMenuItem));
950+
951+
item.componentInstance.disabled = true;
952+
fixture.detectChanges();
953+
954+
// Invoke the handler directly since the fake events are flaky on disabled elements.
955+
item.componentInstance._handleMouseEnter();
956+
fixture.detectChanges();
957+
tick(500);
958+
959+
expect(overlay.querySelectorAll('.mat-menu-panel').length)
960+
.toBe(1, 'Expected to remain at one open menu');
961+
}));
962+
963+
913964
it('should open a nested menu when its trigger is clicked', () => {
914965
compileTestComponent();
915966
instance.rootTriggerEl.nativeElement.click();

0 commit comments

Comments
 (0)