[IMP] portal: add support for .closest modifier

It is sometimes useful in practice to be able to configure the portal so
that it looks for a target as close as possible as the portal location.
For example, in odoo, a portal may be set up in a form view that should
target the current form view. But if the form view itself is in a
dialog, it may fail, because it may find a valid target in the form view that is located
underneath.

With this commit, we add a .closest modifier to the `t-portal`
directive.
This commit is contained in:
Géry Debongnie
2023-05-08 11:33:50 +02:00
parent ba20267151
commit 0a5a1e61dd
7 changed files with 99 additions and 11 deletions
+11
View File
@@ -1998,6 +1998,7 @@ describe("qweb parser", () => {
test("t-portal", async () => {
expect(parse(`<t t-portal="target">Content</t>`)).toEqual({
type: ASTType.TPortal,
isClosest: false,
target: "target",
content: { type: ASTType.Text, value: "Content" },
});
@@ -2008,6 +2009,7 @@ describe("qweb parser", () => {
condition: "condition",
content: {
content: { type: ASTType.Text, value: "Content" },
isClosest: false,
target: "target",
type: ASTType.TPortal,
},
@@ -2016,4 +2018,13 @@ describe("qweb parser", () => {
type: ASTType.TIf,
});
});
test("t-portal with .closest", async () => {
expect(parse(`<t t-portal.closest="target">Content</t>`)).toEqual({
type: ASTType.TPortal,
isClosest: true,
target: "target",
content: { type: ASTType.Text, value: "Content" },
});
});
});
@@ -999,3 +999,26 @@ exports[`Portal: UI/UX focus is kept across re-renders 2`] = `
}
}"
`;
exports[`portal .closest suffix basic use of .suffix 1`] = `
"function anonymous(app, bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
const Portal = app.Portal;
const comp1 = app.createComponent(null, false, true, false, false);
let block2 = createBlock(\`<p class=\\"target\\">far target</p>\`);
let block3 = createBlock(\`<div><p class=\\"target\\">close target</p><block-child-0/></div>\`);
function slot1(ctx, node, key = \\"\\") {
return text(\`portal content\`);
}
return function template(ctx, node, key = \\"\\") {
const b2 = block2();
const b5 = comp1({target: '.target',isClosest: true,slots: {'default': {__render: slot1.bind(this), __ctx: ctx}}}, key + \`__1\`, node, ctx, Portal);
const b3 = block3([], [b5]);
return multi([b2, b3]);
}
}"
`;
+18
View File
@@ -1028,3 +1028,21 @@ describe("Portal: Props validation", () => {
expect(error!.message).toBe(`invalid portal target`);
});
});
describe("portal .closest suffix", () => {
test("basic use of .suffix", async () => {
class Parent extends Component {
static template = xml`
<p class="target">far target</p>
<div>
<p class="target">close target</p>
<t t-portal.closest="'.target'">portal content</t>
</div>`;
}
await mount(Parent, fixture);
expect(fixture.innerHTML).toBe(
'<p class="target">far target</p><div><p class="target">close targetportal content</p></div>'
);
});
});