Skip to content

Commit 671ad82

Browse files
crisbetotinayuangao
authored andcommitted
fix(menu): panel positions not changing if position is updated after first open (#11707)
Fixes the menu's panel position not being accurate if it's `xPosition` or `yPosition` is updated after it has been opened for the first time. Fixes #11668.
1 parent 6c9c3fe commit 671ad82

File tree

2 files changed

+67
-32
lines changed

2 files changed

+67
-32
lines changed

src/lib/menu/menu-trigger.ts

Lines changed: 26 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,7 @@ export class MatMenuTrigger implements AfterContentInit, OnDestroy {
196196
}
197197

198198
const overlayRef = this._createOverlay();
199+
this._setPosition(overlayRef.getConfig().positionStrategy as FlexibleConnectedPositionStrategy);
199200
overlayRef.attach(this._portal);
200201

201202
if (this.menu.lazyContent) {
@@ -350,7 +351,9 @@ export class MatMenuTrigger implements AfterContentInit, OnDestroy {
350351
*/
351352
private _getOverlayConfig(): OverlayConfig {
352353
return new OverlayConfig({
353-
positionStrategy: this._getPosition(),
354+
positionStrategy: this._overlay.position()
355+
.flexibleConnectedTo(this._element)
356+
.withTransformOriginOn('.mat-menu-panel'),
354357
hasBackdrop: this.menu.hasBackdrop == null ? !this.triggersSubmenu() : this.menu.hasBackdrop,
355358
backdropClass: this.menu.backdropClass || 'cdk-overlay-transparent-backdrop',
356359
scrollStrategy: this._scrollStrategy(),
@@ -375,11 +378,11 @@ export class MatMenuTrigger implements AfterContentInit, OnDestroy {
375378
}
376379

377380
/**
378-
* This method builds the position strategy for the overlay, so the menu is properly connected
379-
* to the trigger.
380-
* @returns ConnectedPositionStrategy
381+
* Sets the appropriate positions on a position strategy
382+
* so the overlay connects with the trigger correctly.
383+
* @param positionStrategy Strategy whose position to update.
381384
*/
382-
private _getPosition(): FlexibleConnectedPositionStrategy {
385+
private _setPosition(positionStrategy: FlexibleConnectedPositionStrategy) {
383386
let [originX, originFallbackX]: HorizontalConnectionPos[] =
384387
this.menu.xPosition === 'before' ? ['end', 'start'] : ['start', 'end'];
385388

@@ -401,27 +404,24 @@ export class MatMenuTrigger implements AfterContentInit, OnDestroy {
401404
originFallbackY = overlayFallbackY === 'top' ? 'bottom' : 'top';
402405
}
403406

404-
return this._overlay.position()
405-
.flexibleConnectedTo(this._element)
406-
.withTransformOriginOn('.mat-menu-panel')
407-
.withPositions([
408-
{originX, originY, overlayX, overlayY, offsetY},
409-
{originX: originFallbackX, originY, overlayX: overlayFallbackX, overlayY, offsetY},
410-
{
411-
originX,
412-
originY: originFallbackY,
413-
overlayX,
414-
overlayY: overlayFallbackY,
415-
offsetY: -offsetY
416-
},
417-
{
418-
originX: originFallbackX,
419-
originY: originFallbackY,
420-
overlayX: overlayFallbackX,
421-
overlayY: overlayFallbackY,
422-
offsetY: -offsetY
423-
}
424-
]);
407+
positionStrategy.withPositions([
408+
{originX, originY, overlayX, overlayY, offsetY},
409+
{originX: originFallbackX, originY, overlayX: overlayFallbackX, overlayY, offsetY},
410+
{
411+
originX,
412+
originY: originFallbackY,
413+
overlayX,
414+
overlayY: overlayFallbackY,
415+
offsetY: -offsetY
416+
},
417+
{
418+
originX: originFallbackX,
419+
originY: originFallbackY,
420+
overlayX: overlayFallbackX,
421+
overlayY: overlayFallbackY,
422+
offsetY: -offsetY
423+
}
424+
]);
425425
}
426426

427427
/** Cleans up the active subscriptions. */

src/lib/menu/menu.spec.ts

Lines changed: 41 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -512,27 +512,28 @@ describe('MatMenu', () => {
512512

513513
describe('positions', () => {
514514
let fixture: ComponentFixture<PositionedMenu>;
515-
let panel: HTMLElement;
515+
let trigger: HTMLElement;
516516

517517
beforeEach(() => {
518518
fixture = createComponent(PositionedMenu);
519519
fixture.detectChanges();
520520

521-
const trigger = fixture.componentInstance.triggerEl.nativeElement;
521+
trigger = fixture.componentInstance.triggerEl.nativeElement;
522522

523523
// Push trigger to the bottom edge of viewport,so it has space to open "above"
524524
trigger.style.position = 'fixed';
525525
trigger.style.top = '600px';
526526

527527
// Push trigger to the right, so it has space to open "before"
528528
trigger.style.left = '100px';
529+
});
529530

531+
it('should append mat-menu-before if the x position is changed', () => {
530532
fixture.componentInstance.trigger.openMenu();
531533
fixture.detectChanges();
532-
panel = overlayContainerElement.querySelector('.mat-menu-panel') as HTMLElement;
533-
});
534534

535-
it('should append mat-menu-before if the x position is changed', () => {
535+
const panel = overlayContainerElement.querySelector('.mat-menu-panel') as HTMLElement;
536+
536537
expect(panel.classList).toContain('mat-menu-before');
537538
expect(panel.classList).not.toContain('mat-menu-after');
538539

@@ -544,6 +545,11 @@ describe('MatMenu', () => {
544545
});
545546

546547
it('should append mat-menu-above if the y position is changed', () => {
548+
fixture.componentInstance.trigger.openMenu();
549+
fixture.detectChanges();
550+
551+
const panel = overlayContainerElement.querySelector('.mat-menu-panel') as HTMLElement;
552+
547553
expect(panel.classList).toContain('mat-menu-above');
548554
expect(panel.classList).not.toContain('mat-menu-below');
549555

@@ -564,12 +570,41 @@ describe('MatMenu', () => {
564570
newFixture.detectChanges();
565571
newFixture.componentInstance.trigger.openMenu();
566572
newFixture.detectChanges();
567-
panel = overlayContainerElement.querySelector('.mat-menu-panel') as HTMLElement;
573+
const panel = overlayContainerElement.querySelector('.mat-menu-panel') as HTMLElement;
568574

569575
expect(panel.classList).toContain('mat-menu-below');
570576
expect(panel.classList).toContain('mat-menu-after');
571577
});
572578

579+
it('should be able to update the position after the first open', () => {
580+
trigger.style.position = 'fixed';
581+
trigger.style.top = '200px';
582+
583+
fixture.componentInstance.yPosition = 'above';
584+
fixture.detectChanges();
585+
586+
fixture.componentInstance.trigger.openMenu();
587+
fixture.detectChanges();
588+
589+
let panel = overlayContainerElement.querySelector('.mat-menu-panel') as HTMLElement;
590+
591+
expect(Math.floor(panel.getBoundingClientRect().bottom))
592+
.toBe(Math.floor(trigger.getBoundingClientRect().bottom), 'Expected menu to open above');
593+
594+
fixture.componentInstance.trigger.closeMenu();
595+
fixture.detectChanges();
596+
597+
fixture.componentInstance.yPosition = 'below';
598+
fixture.detectChanges();
599+
600+
fixture.componentInstance.trigger.openMenu();
601+
fixture.detectChanges();
602+
panel = overlayContainerElement.querySelector('.mat-menu-panel') as HTMLElement;
603+
604+
expect(Math.floor(panel.getBoundingClientRect().top))
605+
.toBe(Math.floor(trigger.getBoundingClientRect().top), 'Expected menu to open below');
606+
});
607+
573608
});
574609

575610
describe('fallback positions', () => {

0 commit comments

Comments
 (0)