[FIX] web: correct default container (usePosition)

Since [1] positioning of popovers targeting an element contained in an
iframe is permitted.

This commit will ensure the popper's element positioning is correct in
the following case: have a popper
- outside an iframe
- targeting an element that is inside it
- and no container element has been given (default is used)

**Before this commit**
The default container that is used is the target's owner document
element, a.k.a. the iframe's html element.

**After this commit**
The default container that is used is now the popper's owner document.

** Side notes **
This bug has been found when working for the following taskid-3603843
It is required for this task and will get fw-ported through the master
branch.

[1]: d6afa9f325

closes odoo/odoo#161568

X-original-commit: e664b2367f482aa6edc492db16ec92ffff1ac0a1
Signed-off-by: Francois Georis (fge) <fge@odoo.com>
Signed-off-by: Bruno Boi (boi) <boi@odoo.com>
This commit is contained in:
Bruno Boi (boi)
2024-04-12 11:26:46 +00:00
parent bb0cb28962
commit 351f894ed9
2 changed files with 73 additions and 1 deletions
+1 -1
View File
@@ -122,7 +122,7 @@ function getBestPosition(popper, target, { container, margin, position }, iframe
// Retrieve container
if (!container) {
container = target.ownerDocument.documentElement;
container = popper.ownerDocument.documentElement;
} else if (typeof container === "function") {
container = container();
}
@@ -555,6 +555,78 @@ QUnit.test("iframe: both popper and target inside", async (assert) => {
assert.strictEqual(popperBox.left, onPositionedArgs.solution.left);
});
QUnit.test("iframe: default container is the popper owner's document", async (assert) => {
assert.expect(1);
// Prepare an outer iframe, that will hold the popper element
let def = makeDeferred();
const outerIframe = document.createElement("iframe");
Object.assign(outerIframe.style, { height: "450px", width: "450px" });
outerIframe.onload = def.resolve;
getFixture().prepend(outerIframe);
registerCleanup(() => outerIframe.remove());
await def;
Object.assign(outerIframe.contentDocument.body.style, {
...CONTAINER_STYLE,
margin: "0",
justifyContent: "flex-start",
});
def = makeDeferred();
const iframeSheet = outerIframe.contentDocument.createElement("style");
iframeSheet.onload = def.resolve;
iframeSheet.textContent = `
#popper {
background-color: plum;
height: 100px;
width: 100px;
}
`;
outerIframe.contentDocument.head.appendChild(iframeSheet);
await def; // wait for the iframe's stylesheet to be loaded
// Prepare the inner iframe, that will hold the target element
def = makeDeferred();
const innerIframe = document.createElement("iframe");
innerIframe.srcdoc = `<div id="target" />`;
Object.assign(innerIframe.style, {
height: "300px",
width: "120px",
marginLeft: "10px",
});
innerIframe.onload = def.resolve;
outerIframe.contentDocument.body.appendChild(innerIframe);
await def;
Object.assign(innerIframe.contentDocument.body.style, {
...FLEXBOX_STYLE,
height: "300px",
width: "120px",
margin: "0",
});
// Prepare the target element
const target = innerIframe.contentDocument.getElementById("target");
Object.assign(target.style, TARGET_STYLE);
// Mount the popper component and check its position
class Popper extends Component {
static template = xml`<div id="popper" t-ref="popper" />`;
setup() {
usePosition("popper", () => target, {
position: "top-start",
onPositioned: (_, { direction, variant }) => {
assert.strictEqual(`${direction}-${variant}`, "top-start");
// the style setup in this test leaves enough space in the inner iframe
// for the popper to be positioned at top-middle, but this is exactly
// what we want to avoid: the popper's base container should not be the
// inner iframe, but the outer iframe, so the popper should be positioned
// at top-start.
},
});
}
}
await mount(Popper, outerIframe.contentDocument.body);
});
QUnit.test("popper as child of another", async (assert) => {
class Child extends Component {
static template = /* xml */ xml`