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;