Skip to content

Commit cef9436

Browse files
committed
fix(material/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 5fc655b commit cef9436

File tree

4 files changed

+64
-4
lines changed

4 files changed

+64
-4
lines changed

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

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2213,6 +2213,26 @@ describe('MDC-based MatMenu', () => {
22132213
expect(Math.round(triggerRect.top)).toBe(Math.round(panelRect.top) + MENU_PANEL_TOP_PADDING);
22142214
}));
22152215

2216+
it('should account for custom padding when offsetting the sub-menu', () => {
2217+
compileTestComponent();
2218+
instance.rootTriggerEl.nativeElement.style.position = 'fixed';
2219+
instance.rootTriggerEl.nativeElement.style.left = '75px';
2220+
instance.rootTriggerEl.nativeElement.style.top = '75px';
2221+
instance.rootTrigger.openMenu();
2222+
fixture.detectChanges();
2223+
2224+
// Change the padding to something different from the default.
2225+
(overlay.querySelector('.mat-mdc-menu-content') as HTMLElement).style.padding = '15px 0';
2226+
2227+
instance.levelOneTrigger.openMenu();
2228+
fixture.detectChanges();
2229+
2230+
const triggerRect = overlay.querySelector('#level-one-trigger')!.getBoundingClientRect();
2231+
const panelRect = overlay.querySelectorAll('.cdk-overlay-pane')[1].getBoundingClientRect();
2232+
2233+
expect(Math.round(triggerRect.top)).toBe(Math.round(panelRect.top) + 15);
2234+
});
2235+
22162236
it('should close all of the menus when an item is clicked', fakeAsync(() => {
22172237
compileTestComponent();
22182238
instance.rootTriggerEl.nativeElement.click();

src/material/menu/menu-trigger.ts

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,11 @@ export const MAT_MENU_SCROLL_STRATEGY_FACTORY_PROVIDER = {
6565
useFactory: MAT_MENU_SCROLL_STRATEGY_FACTORY,
6666
};
6767

68-
/** Default top padding of the menu panel. */
68+
/**
69+
* Default top padding of the menu panel.
70+
* @deprecated No longer being used. Will be removed.
71+
* @breaking-change 15.0.0
72+
*/
6973
export const MENU_PANEL_TOP_PADDING = 8;
7074

7175
/** Options for binding a passive event listener. */
@@ -98,6 +102,12 @@ export abstract class _MatMenuTriggerBase implements AfterContentInit, OnDestroy
98102
*/
99103
private _parentMaterialMenu: _MatMenuBase | undefined;
100104

105+
/**
106+
* Cached value of the padding of the parent menu panel.
107+
* Used to offset sub-menus to compensate for the padding.
108+
*/
109+
private _parentInnerPadding: number | undefined;
110+
101111
/**
102112
* Handles touch start events on the trigger.
103113
* Needs to be an arrow function so we can easily use addEventListener and removeEventListener.
@@ -511,7 +521,15 @@ export abstract class _MatMenuTriggerBase implements AfterContentInit, OnDestroy
511521
// to the edges of the trigger, instead of overlapping it.
512522
overlayFallbackX = originX = menu.xPosition === 'before' ? 'start' : 'end';
513523
originFallbackX = overlayX = originX === 'end' ? 'start' : 'end';
514-
offsetY = overlayY === 'bottom' ? MENU_PANEL_TOP_PADDING : -MENU_PANEL_TOP_PADDING;
524+
525+
if (this._parentMaterialMenu) {
526+
if (this._parentInnerPadding == null) {
527+
const firstSibling = this._parentMaterialMenu.items.first;
528+
this._parentInnerPadding = firstSibling ? firstSibling._getHostElement().offsetTop : 0;
529+
}
530+
531+
offsetY = overlayY === 'bottom' ? this._parentInnerPadding : -this._parentInnerPadding;
532+
}
515533
} else if (!menu.overlapTrigger) {
516534
originY = overlayY === 'top' ? 'bottom' : 'top';
517535
originFallbackY = overlayFallbackY === 'top' ? 'bottom' : 'top';

src/material/menu/menu.spec.ts

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,9 @@ import {
5252
MenuPositionX,
5353
MenuPositionY,
5454
} from './index';
55-
import {MAT_MENU_SCROLL_STRATEGY, MENU_PANEL_TOP_PADDING} from './menu-trigger';
55+
import {MAT_MENU_SCROLL_STRATEGY} from './menu-trigger';
56+
57+
const MENU_PANEL_TOP_PADDING = 8;
5658

5759
describe('MatMenu', () => {
5860
let overlayContainerElement: HTMLElement;
@@ -2204,6 +2206,26 @@ describe('MatMenu', () => {
22042206
expect(Math.round(triggerRect.top)).toBe(Math.round(panelRect.top) + MENU_PANEL_TOP_PADDING);
22052207
}));
22062208

2209+
it('should account for custom padding when offsetting the sub-menu', () => {
2210+
compileTestComponent();
2211+
instance.rootTriggerEl.nativeElement.style.position = 'fixed';
2212+
instance.rootTriggerEl.nativeElement.style.left = '75px';
2213+
instance.rootTriggerEl.nativeElement.style.top = '75px';
2214+
instance.rootTrigger.openMenu();
2215+
fixture.detectChanges();
2216+
2217+
// Change the padding to something different from the default.
2218+
(overlay.querySelector('.mat-menu-content') as HTMLElement).style.padding = '15px 0';
2219+
2220+
instance.levelOneTrigger.openMenu();
2221+
fixture.detectChanges();
2222+
2223+
const triggerRect = overlay.querySelector('#level-one-trigger')!.getBoundingClientRect();
2224+
const panelRect = overlay.querySelectorAll('.cdk-overlay-pane')[1].getBoundingClientRect();
2225+
2226+
expect(Math.round(triggerRect.top)).toBe(Math.round(panelRect.top) + 15);
2227+
});
2228+
22072229
it('should close all of the menus when an item is clicked', fakeAsync(() => {
22082230
compileTestComponent();
22092231
instance.rootTriggerEl.nativeElement.click();

tools/public_api_guard/material/menu.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -321,7 +321,7 @@ export abstract class _MatMenuTriggerBase implements AfterContentInit, OnDestroy
321321
static ɵfac: i0.ɵɵFactoryDeclaration<_MatMenuTriggerBase, [null, null, null, null, { optional: true; }, { optional: true; self: true; }, { optional: true; }, null, null]>;
322322
}
323323

324-
// @public
324+
// @public @deprecated
325325
const MENU_PANEL_TOP_PADDING = 8;
326326

327327
// @public

0 commit comments

Comments
 (0)