Skip to content

Commit 18779fd

Browse files
committed
fix(menu): account for menu padding different from the default
Currently we use the hardcoded value of the menu padding to offset the panel so the two menu triggers align. Since the value is in TS, the logic breaks down if the user has set their own padding. These changes add some logic to compute the padding the first time a sub-menu is opened. Fixes #16167.
1 parent 612a738 commit 18779fd

File tree

3 files changed

+56
-5
lines changed

3 files changed

+56
-5
lines changed

src/material-experimental/mdc-menu/menu.spec.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1657,6 +1657,26 @@ describe('MatMenu', () => {
16571657
expect(Math.round(triggerRect.top)).toBe(Math.round(panelRect.top) + MENU_PANEL_TOP_PADDING);
16581658
}));
16591659

1660+
it('should account for custom padding when offsetting the sub-menu', () => {
1661+
compileTestComponent();
1662+
instance.rootTriggerEl.nativeElement.style.position = 'fixed';
1663+
instance.rootTriggerEl.nativeElement.style.left = '75px';
1664+
instance.rootTriggerEl.nativeElement.style.top = '75px';
1665+
instance.rootTrigger.openMenu();
1666+
fixture.detectChanges();
1667+
1668+
// Change the padding to something different from the default.
1669+
(overlay.querySelector('.mat-mdc-menu-content') as HTMLElement).style.padding = '15px 0';
1670+
1671+
instance.levelOneTrigger.openMenu();
1672+
fixture.detectChanges();
1673+
1674+
const triggerRect = overlay.querySelector('#level-one-trigger')!.getBoundingClientRect();
1675+
const panelRect = overlay.querySelectorAll('.cdk-overlay-pane')[1].getBoundingClientRect();
1676+
1677+
expect(Math.round(triggerRect.top)).toBe(Math.round(panelRect.top) + 15);
1678+
});
1679+
16601680
it('should close all of the menus when an item is clicked', fakeAsync(() => {
16611681
compileTestComponent();
16621682
instance.rootTriggerEl.nativeElement.click();

src/material/menu/menu-trigger.ts

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -58,9 +58,6 @@ export const MAT_MENU_SCROLL_STRATEGY_FACTORY_PROVIDER = {
5858
useFactory: MAT_MENU_SCROLL_STRATEGY_FACTORY,
5959
};
6060

61-
/** Default top padding of the menu panel. */
62-
export const MENU_PANEL_TOP_PADDING = 8;
63-
6461
/** Options for binding a passive event listener. */
6562
const passiveEventListenerOptions = normalizePassiveListenerOptions({passive: true});
6663

@@ -90,6 +87,12 @@ export class MatMenuTrigger implements AfterContentInit, OnDestroy {
9087
private _menuCloseSubscription = Subscription.EMPTY;
9188
private _scrollStrategy: () => ScrollStrategy;
9289

90+
/**
91+
* Cached value of the padding of the parent menu panel.
92+
* Used to offset sub-menus to compensate for the padding.
93+
*/
94+
private _parentInnerPadding: number | undefined;
95+
9396
/**
9497
* Handles touch start events on the trigger.
9598
* Needs to be an arrow function so we can easily use addEventListener and removeEventListener.
@@ -448,11 +451,18 @@ export class MatMenuTrigger implements AfterContentInit, OnDestroy {
448451
let offsetY = 0;
449452

450453
if (this.triggersSubmenu()) {
454+
// console.log(this._parentMenu.items.first._getHostElement().offsetTop);
451455
// When the menu is a sub-menu, it should always align itself
452456
// to the edges of the trigger, instead of overlapping it.
453457
overlayFallbackX = originX = this.menu.xPosition === 'before' ? 'start' : 'end';
454458
originFallbackX = overlayX = originX === 'end' ? 'start' : 'end';
455-
offsetY = overlayY === 'bottom' ? MENU_PANEL_TOP_PADDING : -MENU_PANEL_TOP_PADDING;
459+
460+
if (this._parentInnerPadding == null) {
461+
const firstSibling = this._parentMenu.items.first;
462+
this._parentInnerPadding = firstSibling ? firstSibling._getHostElement().offsetTop : 0;
463+
}
464+
465+
offsetY = overlayY === 'bottom' ? this._parentInnerPadding : -this._parentInnerPadding;
456466
} else if (!this.menu.overlapTrigger) {
457467
originY = overlayY === 'top' ? 'bottom' : 'top';
458468
originFallbackY = overlayFallbackY === 'top' ? 'bottom' : 'top';

src/material/menu/menu.spec.ts

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ import {
2828
MenuPositionY,
2929
MatMenuItem,
3030
} from './index';
31-
import {MENU_PANEL_TOP_PADDING, MAT_MENU_SCROLL_STRATEGY} from './menu-trigger';
31+
import {MAT_MENU_SCROLL_STRATEGY} from './menu-trigger';
3232
import {MatRipple} from '@angular/material/core';
3333
import {
3434
dispatchKeyboardEvent,
@@ -44,6 +44,7 @@ import {Subject} from 'rxjs';
4444
import {ScrollDispatcher} from '@angular/cdk/scrolling';
4545
import {FocusMonitor} from '@angular/cdk/a11y';
4646

47+
const MENU_PANEL_TOP_PADDING = 8;
4748

4849
describe('MatMenu', () => {
4950
let overlayContainer: OverlayContainer;
@@ -1647,6 +1648,26 @@ describe('MatMenu', () => {
16471648
expect(Math.round(triggerRect.top)).toBe(Math.round(panelRect.top) + MENU_PANEL_TOP_PADDING);
16481649
}));
16491650

1651+
it('should account for custom padding when offsetting the sub-menu', () => {
1652+
compileTestComponent();
1653+
instance.rootTriggerEl.nativeElement.style.position = 'fixed';
1654+
instance.rootTriggerEl.nativeElement.style.left = '75px';
1655+
instance.rootTriggerEl.nativeElement.style.top = '75px';
1656+
instance.rootTrigger.openMenu();
1657+
fixture.detectChanges();
1658+
1659+
// Change the padding to something different from the default.
1660+
(overlay.querySelector('.mat-menu-content') as HTMLElement).style.padding = '15px 0';
1661+
1662+
instance.levelOneTrigger.openMenu();
1663+
fixture.detectChanges();
1664+
1665+
const triggerRect = overlay.querySelector('#level-one-trigger')!.getBoundingClientRect();
1666+
const panelRect = overlay.querySelectorAll('.cdk-overlay-pane')[1].getBoundingClientRect();
1667+
1668+
expect(Math.round(triggerRect.top)).toBe(Math.round(panelRect.top) + 15);
1669+
});
1670+
16501671
it('should close all of the menus when an item is clicked', fakeAsync(() => {
16511672
compileTestComponent();
16521673
instance.rootTriggerEl.nativeElement.click();

0 commit comments

Comments
 (0)