[FIX] web: compute containing block layout in position hook
How to reproduce: - create a list embedded view in Knowledge (/item list) - in the control panel click on "Filters" Current behavior: - the dropdown is not correctly positioned Expected behavior: - the dropdown should be positioned around the button Fix: `getBestPosition` uses dimensions and layout positionning based on the viewport, but `reposition` modifies the style properties `top` and `left` of the element, which are relative to the closest containing block. Since in that case the style position is `fixed`, there are a number of factor that can influence the containing block, as specified in: https://developer.mozilla.org/en-US/docs/Web/CSS/Containing_block#identifying_the_containing_block We can easily compute the layout offset caused by the containing block just after resetting the popper top and left to `0px` and take it into account for the final value. Task-3267555 closes odoo/odoo#118018 Signed-off-by: Bruno Boi (boi) <boi@odoo.com>
This commit is contained in:
@@ -83,7 +83,9 @@ const DEFAULTS = {
|
||||
* @param {HTMLElement} reference
|
||||
* @param {HTMLElement} popper
|
||||
* @param {Options} options
|
||||
* @returns {PositioningSolution} the best positioning solution
|
||||
* @returns {PositioningSolution} the best positioning solution, relative to
|
||||
* the containing block of the popper.
|
||||
* => can be applied to popper.style.(top|left)
|
||||
*/
|
||||
function getBestPosition(reference, popper, { container, margin, position }) {
|
||||
// Retrieve directions and variants
|
||||
@@ -172,7 +174,12 @@ function getBestPosition(reference, popper, { container, margin, position }) {
|
||||
left: directionValue,
|
||||
};
|
||||
return {
|
||||
...positioning,
|
||||
// Subtract the offsets of the containing block (relative to the
|
||||
// viewport). It can be done like that because the style top and
|
||||
// left were reset to 0px in `reposition`
|
||||
// https://developer.mozilla.org/en-US/docs/Web/CSS/Containing_block#identifying_the_containing_block
|
||||
top: positioning.top - popBox.top,
|
||||
left: positioning.left - popBox.left,
|
||||
direction: DIRECTIONS[d],
|
||||
variant: VARIANTS[v],
|
||||
};
|
||||
|
||||
@@ -150,6 +150,43 @@ QUnit.test("has no effect when component is destroyed", async (assert) => {
|
||||
);
|
||||
});
|
||||
|
||||
QUnit.test("is positioned relative to its containing block", async (assert) => {
|
||||
const fixtureBox = getFixture().getBoundingClientRect();
|
||||
// offset the container
|
||||
const margin = 15;
|
||||
container.style.margin = `${margin}px`;
|
||||
let pos1, pos2;
|
||||
let TestComp = getTestComponent({
|
||||
onPositioned: (el, pos) => {
|
||||
pos1 = pos;
|
||||
},
|
||||
});
|
||||
let popper = await mount(TestComp, container);
|
||||
|
||||
const popBox1 = document.getElementById("popper").getBoundingClientRect();
|
||||
destroy(popper);
|
||||
document.getElementById("reference").remove();
|
||||
|
||||
// make container the containing block instead of the viewport
|
||||
container.style.contain = "layout";
|
||||
|
||||
TestComp = getTestComponent({
|
||||
onPositioned: (el, pos) => {
|
||||
pos2 = pos;
|
||||
},
|
||||
});
|
||||
popper = await mount(TestComp, container);
|
||||
const popBox2 = document.getElementById("popper").getBoundingClientRect();
|
||||
destroy(popper);
|
||||
|
||||
// best positions are not the same relative to their containing block
|
||||
assert.equal(pos1.top, pos2.top + margin + fixtureBox.top);
|
||||
assert.equal(pos1.left, pos2.left + margin + fixtureBox.left);
|
||||
// best positions are the same relative to the viewport
|
||||
assert.equal(popBox1.top, popBox2.top);
|
||||
assert.equal(popBox1.left, popBox2.left);
|
||||
});
|
||||
|
||||
function getPositionTest(position, positionToCheck) {
|
||||
return async (assert) => {
|
||||
assert.expect(2);
|
||||
|
||||
Reference in New Issue
Block a user