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.
This commit is contained in:
Kristiyan Kostadinov
2018-06-20 19:55:31 +02:00
committed by tinayuangao
parent f59cf5954c
commit 50ea837250
2 changed files with 68 additions and 33 deletions
+26 -26
View File
@@ -196,6 +196,7 @@ export class MatMenuTrigger implements AfterContentInit, OnDestroy {
}
const overlayRef = this._createOverlay();
this._setPosition(overlayRef.getConfig().positionStrategy as FlexibleConnectedPositionStrategy);
overlayRef.attach(this._portal);
if (this.menu.lazyContent) {
@@ -350,7 +351,9 @@ export class MatMenuTrigger implements AfterContentInit, OnDestroy {
*/
private _getOverlayConfig(): OverlayConfig {
return new OverlayConfig({
positionStrategy: this._getPosition(),
positionStrategy: this._overlay.position()
.flexibleConnectedTo(this._element)
.withTransformOriginOn('.mat-menu-panel'),
hasBackdrop: this.menu.hasBackdrop == null ? !this.triggersSubmenu() : this.menu.hasBackdrop,
backdropClass: this.menu.backdropClass || 'cdk-overlay-transparent-backdrop',
scrollStrategy: this._scrollStrategy(),
@@ -375,11 +378,11 @@ export class MatMenuTrigger implements AfterContentInit, OnDestroy {
}
/**
* This method builds the position strategy for the overlay, so the menu is properly connected
* to the trigger.
* @returns ConnectedPositionStrategy
* Sets the appropriate positions on a position strategy
* so the overlay connects with the trigger correctly.
* @param positionStrategy Strategy whose position to update.
*/
private _getPosition(): FlexibleConnectedPositionStrategy {
private _setPosition(positionStrategy: FlexibleConnectedPositionStrategy) {
let [originX, originFallbackX]: HorizontalConnectionPos[] =
this.menu.xPosition === 'before' ? ['end', 'start'] : ['start', 'end'];
@@ -401,27 +404,24 @@ export class MatMenuTrigger implements AfterContentInit, OnDestroy {
originFallbackY = overlayFallbackY === 'top' ? 'bottom' : 'top';
}
return this._overlay.position()
.flexibleConnectedTo(this._element)
.withTransformOriginOn('.mat-menu-panel')
.withPositions([
{originX, originY, overlayX, overlayY, offsetY},
{originX: originFallbackX, originY, overlayX: overlayFallbackX, overlayY, offsetY},
{
originX,
originY: originFallbackY,
overlayX,
overlayY: overlayFallbackY,
offsetY: -offsetY
},
{
originX: originFallbackX,
originY: originFallbackY,
overlayX: overlayFallbackX,
overlayY: overlayFallbackY,
offsetY: -offsetY
}
]);
positionStrategy.withPositions([
{originX, originY, overlayX, overlayY, offsetY},
{originX: originFallbackX, originY, overlayX: overlayFallbackX, overlayY, offsetY},
{
originX,
originY: originFallbackY,
overlayX,
overlayY: overlayFallbackY,
offsetY: -offsetY
},
{
originX: originFallbackX,
originY: originFallbackY,
overlayX: overlayFallbackX,
overlayY: overlayFallbackY,
offsetY: -offsetY
}
]);
}
/** Cleans up the active subscriptions. */
+42 -7
View File
@@ -512,13 +512,13 @@ describe('MatMenu', () => {
describe('positions', () => {
let fixture: ComponentFixture<PositionedMenu>;
let panel: HTMLElement;
let trigger: HTMLElement;
beforeEach(() => {
fixture = createComponent(PositionedMenu);
fixture.detectChanges();
const trigger = fixture.componentInstance.triggerEl.nativeElement;
trigger = fixture.componentInstance.triggerEl.nativeElement;
// Push trigger to the bottom edge of viewport,so it has space to open "above"
trigger.style.position = 'fixed';
@@ -526,13 +526,14 @@ describe('MatMenu', () => {
// Push trigger to the right, so it has space to open "before"
trigger.style.left = '100px';
fixture.componentInstance.trigger.openMenu();
fixture.detectChanges();
panel = overlayContainerElement.querySelector('.mat-menu-panel') as HTMLElement;
});
it('should append mat-menu-before if the x position is changed', () => {
fixture.componentInstance.trigger.openMenu();
fixture.detectChanges();
const panel = overlayContainerElement.querySelector('.mat-menu-panel') as HTMLElement;
expect(panel.classList).toContain('mat-menu-before');
expect(panel.classList).not.toContain('mat-menu-after');
@@ -544,6 +545,11 @@ describe('MatMenu', () => {
});
it('should append mat-menu-above if the y position is changed', () => {
fixture.componentInstance.trigger.openMenu();
fixture.detectChanges();
const panel = overlayContainerElement.querySelector('.mat-menu-panel') as HTMLElement;
expect(panel.classList).toContain('mat-menu-above');
expect(panel.classList).not.toContain('mat-menu-below');
@@ -564,12 +570,41 @@ describe('MatMenu', () => {
newFixture.detectChanges();
newFixture.componentInstance.trigger.openMenu();
newFixture.detectChanges();
panel = overlayContainerElement.querySelector('.mat-menu-panel') as HTMLElement;
const panel = overlayContainerElement.querySelector('.mat-menu-panel') as HTMLElement;
expect(panel.classList).toContain('mat-menu-below');
expect(panel.classList).toContain('mat-menu-after');
});
it('should be able to update the position after the first open', () => {
trigger.style.position = 'fixed';
trigger.style.top = '200px';
fixture.componentInstance.yPosition = 'above';
fixture.detectChanges();
fixture.componentInstance.trigger.openMenu();
fixture.detectChanges();
let panel = overlayContainerElement.querySelector('.mat-menu-panel') as HTMLElement;
expect(Math.floor(panel.getBoundingClientRect().bottom))
.toBe(Math.floor(trigger.getBoundingClientRect().bottom), 'Expected menu to open above');
fixture.componentInstance.trigger.closeMenu();
fixture.detectChanges();
fixture.componentInstance.yPosition = 'below';
fixture.detectChanges();
fixture.componentInstance.trigger.openMenu();
fixture.detectChanges();
panel = overlayContainerElement.querySelector('.mat-menu-panel') as HTMLElement;
expect(Math.floor(panel.getBoundingClientRect().top))
.toBe(Math.floor(trigger.getBoundingClientRect().top), 'Expected menu to open below');
});
});
describe('fallback positions', () => {