diff --git a/packages/main/cypress/specs/Popover.cy.tsx b/packages/main/cypress/specs/Popover.cy.tsx index ef436f5fbb7f..ea135cca3c6d 100644 --- a/packages/main/cypress/specs/Popover.cy.tsx +++ b/packages/main/cypress/specs/Popover.cy.tsx @@ -1969,4 +1969,265 @@ describe("Min Width via CSS", () => { expect(currentWidth).to.be.at.least(400); }); }); + + describe("Resize Alignment", () => { + it("should keep left edge aligned with opener when resizing from right with placement Bottom and horizontalAlign Start", () => { + cy.mount( + <> + + +
Content
+
+ + ); + + cy.get("[ui5-popover]").invoke("prop", "open", true); + + let initialPopoverLeft: number; + let openerLeft: number; + + cy.get("#btnResizeBottom").then($opener => { + openerLeft = $opener[0].getBoundingClientRect().left; + }); + + cy.get("[ui5-popover]").then($popover => { + initialPopoverLeft = $popover[0].getBoundingClientRect().left; + }); + + cy.get("[ui5-popover]") + .shadow() + .find(".ui5-popover-resize-handle") + .should("be.visible") + .trigger("mousedown", { button: 0 }) + .trigger("mousemove", { clientX: 100, clientY: 0 }) + .trigger("mouseup"); + + cy.get("[ui5-popover]").then($popover => { + const finalPopoverLeft = $popover[0].getBoundingClientRect().left; + expect(Math.abs(finalPopoverLeft - initialPopoverLeft)).to.be.lessThan(2); + expect(Math.abs(finalPopoverLeft - openerLeft)).to.be.lessThan(2); + }); + }); + + it("should keep right edge aligned with opener when resizing from right with placement Bottom and horizontalAlign End", () => { + cy.mount( + <> + + +
Content
+
+ + ); + + cy.get("[ui5-popover]").invoke("prop", "open", true); + + let initialPopoverRight: number; + let openerRight: number; + + cy.get("#btnResizeBottomEnd").then($opener => { + openerRight = $opener[0].getBoundingClientRect().right; + }); + + cy.get("[ui5-popover]").then($popover => { + initialPopoverRight = $popover[0].getBoundingClientRect().right; + }); + + cy.get("[ui5-popover]") + .shadow() + .find(".ui5-popover-resize-handle") + .should("be.visible") + .trigger("mousedown", { button: 0 }) + .trigger("mousemove", { clientX: -100, clientY: 0 }) + .trigger("mouseup"); + + cy.get("[ui5-popover]").then($popover => { + const finalPopoverRight = $popover[0].getBoundingClientRect().right; + expect(Math.abs(finalPopoverRight - initialPopoverRight)).to.be.lessThan(2); + expect(Math.abs(finalPopoverRight - openerRight)).to.be.lessThan(2); + }); + }); + + it("should keep top edge aligned with opener when resizing from bottom with placement End and verticalAlign Top", () => { + cy.mount( + <> + + +
Content
+
+ + ); + + cy.get("[ui5-popover]").invoke("prop", "open", true); + + let initialPopoverTop: number; + let openerTop: number; + + cy.get("#btnResizeEnd").then($opener => { + openerTop = $opener[0].getBoundingClientRect().top; + }); + + cy.get("[ui5-popover]").then($popover => { + initialPopoverTop = $popover[0].getBoundingClientRect().top; + }); + + cy.get("[ui5-popover]") + .shadow() + .find(".ui5-popover-resize-handle") + .should("be.visible") + .trigger("mousedown", { button: 0 }) + .trigger("mousemove", { clientX: 0, clientY: 100 }) + .trigger("mouseup"); + + cy.get("[ui5-popover]").then($popover => { + const finalPopoverTop = $popover[0].getBoundingClientRect().top; + expect(Math.abs(finalPopoverTop - initialPopoverTop)).to.be.lessThan(2); + expect(Math.abs(finalPopoverTop - openerTop)).to.be.lessThan(2); + }); + }); + + it("should keep bottom edge aligned with opener when resizing from bottom with placement End and verticalAlign Bottom", () => { + cy.mount( + <> + + +
Content
+
+ + ); + + cy.get("[ui5-popover]").invoke("prop", "open", true); + + let initialPopoverBottom: number; + let openerBottom: number; + + cy.get("#btnResizeEndBottom").then($opener => { + openerBottom = $opener[0].getBoundingClientRect().bottom; + }); + + cy.get("[ui5-popover]").then($popover => { + initialPopoverBottom = $popover[0].getBoundingClientRect().bottom; + }); + + cy.get("[ui5-popover]") + .shadow() + .find(".ui5-popover-resize-handle") + .should("be.visible") + .trigger("mousedown", { button: 0 }) + .trigger("mousemove", { clientX: 0, clientY: -100 }) + .trigger("mouseup"); + + cy.get("[ui5-popover]").then($popover => { + const finalPopoverBottom = $popover[0].getBoundingClientRect().bottom; + expect(Math.abs(finalPopoverBottom - initialPopoverBottom)).to.be.lessThan(2); + expect(Math.abs(finalPopoverBottom - openerBottom)).to.be.lessThan(2); + }); + }); + + it("should keep left edge aligned when resizing from right with placement Top and horizontalAlign Start", () => { + cy.mount( + <> + + +
Content
+
+ + ); + + cy.get("[ui5-popover]").invoke("prop", "open", true); + + let initialPopoverLeft: number; + + cy.get("[ui5-popover]").then($popover => { + initialPopoverLeft = $popover[0].getBoundingClientRect().left; + }); + + cy.get("[ui5-popover]") + .shadow() + .find(".ui5-popover-resize-handle") + .should("be.visible") + .trigger("mousedown", { button: 0 }) + .trigger("mousemove", { clientX: 100, clientY: 0 }) + .trigger("mouseup"); + + cy.get("[ui5-popover]").then($popover => { + const finalPopoverLeft = $popover[0].getBoundingClientRect().left; + expect(Math.abs(finalPopoverLeft - initialPopoverLeft)).to.be.lessThan(2); + }); + }); + + it("should keep top edge aligned when resizing from bottom with placement Start and verticalAlign Top", () => { + cy.mount( + <> + + +
Content
+
+ + ); + + cy.get("[ui5-popover]").invoke("prop", "open", true); + + let initialPopoverTop: number; + + cy.get("[ui5-popover]").then($popover => { + initialPopoverTop = $popover[0].getBoundingClientRect().top; + }); + + cy.get("[ui5-popover]") + .shadow() + .find(".ui5-popover-resize-handle") + .should("be.visible") + .trigger("mousedown", { button: 0 }) + .trigger("mousemove", { clientX: 0, clientY: 100 }) + .trigger("mouseup"); + + cy.get("[ui5-popover]").then($popover => { + const finalPopoverTop = $popover[0].getBoundingClientRect().top; + expect(Math.abs(finalPopoverTop - initialPopoverTop)).to.be.lessThan(2); + }); + }); + + it("should maintain alignment during multiple resize operations", () => { + cy.mount( + <> + + +
Content
+
+ + ); + + cy.get("[ui5-popover]").invoke("prop", "open", true); + + let initialPopoverLeft: number; + + cy.get("[ui5-popover]").then($popover => { + initialPopoverLeft = $popover[0].getBoundingClientRect().left; + }); + + cy.get("[ui5-popover]") + .shadow() + .find(".ui5-popover-resize-handle") + .trigger("mousedown", { button: 0 }) + .trigger("mousemove", { clientX: 100, clientY: 0 }) + .trigger("mouseup"); + + cy.get("[ui5-popover]").then($popover => { + const afterFirstResize = $popover[0].getBoundingClientRect().left; + expect(Math.abs(afterFirstResize - initialPopoverLeft)).to.be.lessThan(2); + }); + + cy.get("[ui5-popover]") + .shadow() + .find(".ui5-popover-resize-handle") + .trigger("mousedown", { button: 0 }) + .trigger("mousemove", { clientX: -50, clientY: 0 }) + .trigger("mouseup"); + + cy.get("[ui5-popover]").then($popover => { + const afterSecondResize = $popover[0].getBoundingClientRect().left; + expect(Math.abs(afterSecondResize - initialPopoverLeft)).to.be.lessThan(2); + }); + }); + }); }); diff --git a/packages/main/src/PopoverResize.ts b/packages/main/src/PopoverResize.ts index e41a5d60edb0..2edbbf3f59a3 100644 --- a/packages/main/src/PopoverResize.ts +++ b/packages/main/src/PopoverResize.ts @@ -287,6 +287,11 @@ class PopoverResize { const isResizingFromTop = resizeHandlePlacement === ResizeHandlePlacement.TopLeft || resizeHandlePlacement === ResizeHandlePlacement.TopRight; + const opener = popover.getOpenerHTMLElement(popover.opener); + const openerRect = opener?.getBoundingClientRect(); + const actualPlacement = openerRect ? popover.getActualPlacement(openerRect) : popover.actualPlacement; + const isVerticalPlacement = actualPlacement === PopoverActualPlacement.Top || actualPlacement === PopoverActualPlacement.Bottom; + // Calculate width changes if (isResizingFromLeft) { // Resizing from left edge - width increases when moving left (negative delta) @@ -326,7 +331,11 @@ class PopoverResize { maxWidthFromRight, ); - this._currentDeltaX = (initialBoundingRect.width - newWidth) / 2; + if (isVerticalPlacement) { + this._currentDeltaX = 0; + } else { + this._currentDeltaX = (initialBoundingRect.width - newWidth) / 2; + } } // Calculate height changes @@ -368,7 +377,13 @@ class PopoverResize { maxHeightFromBottom, ); - this._currentDeltaY = (initialBoundingRect.height - newHeight) / 2; + const isHorizontalPlacement = actualPlacement === PopoverActualPlacement.Left || actualPlacement === PopoverActualPlacement.Right; + + if (isHorizontalPlacement) { + this._currentDeltaY = 0; + } else { + this._currentDeltaY = (initialBoundingRect.height - newHeight) / 2; + } } this._currentDeltaX += this._totalDeltaX || 0;