mirror of
https://github.com/odoo/owl.git
synced 2025-10-06 19:59:41 +07:00
[IMP] tags: introduce xml tag
Big change! This commit introduces an xml function tag to easily define inline templates. This is a pretty big change toward single file owl components Part of #284
This commit is contained in:
@@ -1406,14 +1406,14 @@ exports[`t-slot directive can define and call slots 2`] = `
|
||||
let c2 = [], p2 = {key:2};
|
||||
var vn2 = h('div', p2, c2);
|
||||
c1.push(vn2);
|
||||
const slot3 = this.slots[context.__owl__.slotId + '_' + 'header'];
|
||||
const slot3 = this.constructor.slots[context.__owl__.slotId + '_' + 'header'];
|
||||
if (slot3) {
|
||||
slot3.call(this, context.__owl__.parent, Object.assign({}, extra, {parentNode: c2, vars: extra.vars, parent: owner}));
|
||||
}
|
||||
let c4 = [], p4 = {key:4};
|
||||
var vn4 = h('div', p4, c4);
|
||||
c1.push(vn4);
|
||||
const slot5 = this.slots[context.__owl__.slotId + '_' + 'footer'];
|
||||
const slot5 = this.constructor.slots[context.__owl__.slotId + '_' + 'footer'];
|
||||
if (slot5) {
|
||||
slot5.call(this, context.__owl__.parent, Object.assign({}, extra, {parentNode: c4, vars: extra.vars, parent: owner}));
|
||||
}
|
||||
@@ -1557,7 +1557,7 @@ exports[`t-slot directive slots are rendered with proper context, part 2 1`] = `
|
||||
let c2 = [], p2 = {key:2,attrs:{href: _1}};
|
||||
var vn2 = h('a', p2, c2);
|
||||
result = vn2;
|
||||
const slot3 = this.slots[context.__owl__.slotId + '_' + 'default'];
|
||||
const slot3 = this.constructor.slots[context.__owl__.slotId + '_' + 'default'];
|
||||
if (slot3) {
|
||||
slot3.call(this, context.__owl__.parent, Object.assign({}, extra, {parentNode: c2, vars: extra.vars, parent: owner}));
|
||||
}
|
||||
@@ -1661,7 +1661,7 @@ exports[`t-slot directive slots are rendered with proper context, part 3 1`] = `
|
||||
let c2 = [], p2 = {key:2,attrs:{href: _1}};
|
||||
var vn2 = h('a', p2, c2);
|
||||
result = vn2;
|
||||
const slot3 = this.slots[context.__owl__.slotId + '_' + 'default'];
|
||||
const slot3 = this.constructor.slots[context.__owl__.slotId + '_' + 'default'];
|
||||
if (slot3) {
|
||||
slot3.call(this, context.__owl__.parent, Object.assign({}, extra, {parentNode: c2, vars: extra.vars, parent: owner}));
|
||||
}
|
||||
|
||||
+217
-244
@@ -1,5 +1,6 @@
|
||||
import { Component, Env } from "../../src/component/component";
|
||||
import { QWeb } from "../../src/qweb/qweb";
|
||||
import { xml } from "../../src/tags";
|
||||
import { EventBus } from "../../src/core/event_bus";
|
||||
import {
|
||||
makeDeferred,
|
||||
@@ -119,11 +120,11 @@ describe("basic widget properties", () => {
|
||||
});
|
||||
|
||||
test("widget style and classname", async () => {
|
||||
env.qweb.addTemplate(
|
||||
"StyledWidget",
|
||||
`<div style="font-weight:bold;" class="some-class">world</div>`
|
||||
);
|
||||
class StyledWidget extends Widget {}
|
||||
class StyledWidget extends Widget {
|
||||
static template = xml`
|
||||
<div style="font-weight:bold;" class="some-class">world</div>
|
||||
`;
|
||||
}
|
||||
const widget = new StyledWidget(env);
|
||||
await widget.mount(fixture);
|
||||
expect(fixture.innerHTML).toBe(`<div style="font-weight:bold;" class="some-class">world</div>`);
|
||||
@@ -164,19 +165,17 @@ describe("basic widget properties", () => {
|
||||
test("reconciliation alg is not confused in some specific situation", async () => {
|
||||
// in this test, we set t-key to 4 because it was in conflict with the
|
||||
// template id corresponding to the first child.
|
||||
env.qweb.addTemplates(`
|
||||
<templates>
|
||||
<div t-name="Parent">
|
||||
<Child />
|
||||
<Child t-key="4"/>
|
||||
</div>
|
||||
<span t-name="Child">child</span>
|
||||
</templates>
|
||||
`);
|
||||
|
||||
class Child extends Component<any, any, any> {}
|
||||
class Child extends Component<any, any, any> {
|
||||
static template = xml`<span>child</span>`;
|
||||
}
|
||||
|
||||
class Parent extends Component<any, any, any> {
|
||||
static template = xml`
|
||||
<div>
|
||||
<Child />
|
||||
<Child t-key="4"/>
|
||||
</div>
|
||||
`;
|
||||
static components = { Child };
|
||||
}
|
||||
|
||||
@@ -226,7 +225,6 @@ describe("lifecycle hooks", () => {
|
||||
|
||||
test("willStart hook is called on subwidget", async () => {
|
||||
let ok = false;
|
||||
env.qweb.addTemplate("ParentWidget", `<div><t t-component="child"/></div>`);
|
||||
class ChildWidget extends Widget {
|
||||
async willStart() {
|
||||
ok = true;
|
||||
@@ -234,6 +232,7 @@ describe("lifecycle hooks", () => {
|
||||
}
|
||||
|
||||
class ParentWidget extends Widget {
|
||||
static template = xml`<div><t t-component="child"/></div>`;
|
||||
static components = { child: ChildWidget };
|
||||
}
|
||||
const widget = new ParentWidget(env);
|
||||
@@ -244,8 +243,6 @@ describe("lifecycle hooks", () => {
|
||||
test("mounted hook is called on subcomponents, in proper order", async () => {
|
||||
const steps: any[] = [];
|
||||
|
||||
env.qweb.addTemplate("ParentWidget", `<div><t t-component="child"/></div>`);
|
||||
|
||||
class ChildWidget extends Widget {
|
||||
mounted() {
|
||||
expect(document.body.contains(this.el)).toBe(true);
|
||||
@@ -254,7 +251,8 @@ describe("lifecycle hooks", () => {
|
||||
}
|
||||
|
||||
class ParentWidget extends Widget {
|
||||
static components = { child: ChildWidget };
|
||||
static template = xml`<div><ChildWidget /></div>`
|
||||
static components = { ChildWidget };
|
||||
mounted() {
|
||||
steps.push("parent:mounted");
|
||||
}
|
||||
@@ -267,12 +265,6 @@ describe("lifecycle hooks", () => {
|
||||
test("mounted hook is called on subsubcomponents, in proper order", async () => {
|
||||
const steps: any[] = [];
|
||||
|
||||
env.qweb.addTemplate(
|
||||
"ParentWidget",
|
||||
`<div><t t-if="state.flag"><t t-component="child"/></t></div>`
|
||||
);
|
||||
env.qweb.addTemplate("ChildWidget", `<div><t t-component="childchild"/></div>`);
|
||||
|
||||
class ChildChildWidget extends Widget {
|
||||
mounted() {
|
||||
steps.push("childchild:mounted");
|
||||
@@ -283,6 +275,7 @@ describe("lifecycle hooks", () => {
|
||||
}
|
||||
|
||||
class ChildWidget extends Widget {
|
||||
static template = xml`<div><t t-component="childchild"/></div>`;
|
||||
static components = { childchild: ChildChildWidget };
|
||||
mounted() {
|
||||
steps.push("child:mounted");
|
||||
@@ -293,6 +286,7 @@ describe("lifecycle hooks", () => {
|
||||
}
|
||||
|
||||
class ParentWidget extends Widget {
|
||||
static template = xml`<div><t t-if="state.flag"><t t-component="child"/></t></div>`;
|
||||
static components = { child: ChildWidget };
|
||||
state = { flag: false };
|
||||
mounted() {
|
||||
@@ -435,12 +429,7 @@ describe("lifecycle hooks", () => {
|
||||
|
||||
test("components are unmounted and destroyed if no longer in DOM", async () => {
|
||||
let steps: string[] = [];
|
||||
env.qweb.addTemplate(
|
||||
"ParentWidget",
|
||||
`<div>
|
||||
<t t-if="state.ok"><t t-component="child"/></t>
|
||||
</div>`
|
||||
);
|
||||
|
||||
class ChildWidget extends Widget {
|
||||
constructor(parent) {
|
||||
super(parent);
|
||||
@@ -457,8 +446,13 @@ describe("lifecycle hooks", () => {
|
||||
}
|
||||
}
|
||||
class ParentWidget extends Widget {
|
||||
static template = xml`
|
||||
<div>
|
||||
<t t-if="state.ok"><ChildWidget /></t>
|
||||
</div>
|
||||
`;
|
||||
static components = { ChildWidget };
|
||||
state = { ok: true };
|
||||
static components = { child: ChildWidget };
|
||||
}
|
||||
|
||||
const widget = new ParentWidget(env);
|
||||
@@ -471,8 +465,9 @@ describe("lifecycle hooks", () => {
|
||||
|
||||
test("components are unmounted and destroyed if no longer in DOM, even after updateprops", async () => {
|
||||
let childUnmounted = false;
|
||||
env.qweb.addTemplate("ChildWidget", `<span><t t-esc="props.n"/></span>`);
|
||||
|
||||
class ChildWidget extends Widget {
|
||||
static template = xml`<span><t t-esc="props.n"/></span>`;
|
||||
willUnmount() {
|
||||
childUnmounted = true;
|
||||
}
|
||||
@@ -481,16 +476,14 @@ describe("lifecycle hooks", () => {
|
||||
}
|
||||
}
|
||||
|
||||
env.qweb.addTemplate(
|
||||
"ParentWidget",
|
||||
`
|
||||
<div>
|
||||
<div t-if="state.flag">
|
||||
<ChildWidget n="state.n"/>
|
||||
</div>
|
||||
</div>`
|
||||
);
|
||||
class ParentWidget extends Widget {
|
||||
static template = xml`
|
||||
<div>
|
||||
<div t-if="state.flag">
|
||||
<ChildWidget n="state.n"/>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
static components = { ChildWidget };
|
||||
state = { n: 0, flag: true };
|
||||
increment() {
|
||||
@@ -515,7 +508,7 @@ describe("lifecycle hooks", () => {
|
||||
|
||||
test("hooks are called in proper order in widget creation/destruction", async () => {
|
||||
let steps: string[] = [];
|
||||
env.qweb.addTemplate("ParentWidget", `<div><t t-component="child"/></div>`);
|
||||
|
||||
class ChildWidget extends Widget {
|
||||
constructor(parent) {
|
||||
super(parent);
|
||||
@@ -532,6 +525,7 @@ describe("lifecycle hooks", () => {
|
||||
}
|
||||
}
|
||||
class ParentWidget extends Widget {
|
||||
static template = xml`<div><t t-component="child"/></div>`;
|
||||
static components = { child: ChildWidget };
|
||||
constructor(parent) {
|
||||
super(parent);
|
||||
@@ -614,13 +608,13 @@ describe("lifecycle hooks", () => {
|
||||
test("patched hook is called after updateProps", async () => {
|
||||
let n = 0;
|
||||
|
||||
env.qweb.addTemplate("Parent", '<div><Child a="state.a"/></div>');
|
||||
class TestWidget extends Widget {
|
||||
patched() {
|
||||
n++;
|
||||
}
|
||||
}
|
||||
class Parent extends Widget {
|
||||
static template = xml`<div><Child a="state.a"/></div>`;
|
||||
state = { a: 1 };
|
||||
static components = { Child: TestWidget };
|
||||
}
|
||||
@@ -654,17 +648,18 @@ describe("lifecycle hooks", () => {
|
||||
|
||||
test("shouldUpdate hook prevent rerendering", async () => {
|
||||
let shouldUpdate = false;
|
||||
env.qweb.addTemplate("Parent", `<div><Child val="state.val"/></div>`);
|
||||
class TestWidget extends Widget {
|
||||
static template = xml`<div><t t-esc="props.val"/></div>`;
|
||||
shouldUpdate() {
|
||||
return shouldUpdate;
|
||||
}
|
||||
}
|
||||
class Parent extends Widget {
|
||||
state = { val: 42 };
|
||||
static template = xml`<div><Child val="state.val"/></div>`;
|
||||
static components = { Child: TestWidget };
|
||||
state = { val: 42 };
|
||||
}
|
||||
env.qweb.addTemplate("TestWidget", `<div><t t-esc="props.val"/></div>`);
|
||||
|
||||
const widget = new Parent(env);
|
||||
await widget.mount(fixture);
|
||||
expect(fixture.innerHTML).toBe("<div><div>42</div></div>");
|
||||
@@ -680,15 +675,6 @@ describe("lifecycle hooks", () => {
|
||||
test("sub widget (inside sub node): hooks are correctly called", async () => {
|
||||
let created = false;
|
||||
let mounted = false;
|
||||
env.qweb.addTemplate(
|
||||
"ParentWidget",
|
||||
`
|
||||
<div>
|
||||
<t t-if="state.flag">
|
||||
<div><t t-component="child"/></div>
|
||||
</t>
|
||||
</div>`
|
||||
);
|
||||
|
||||
class ChildWidget extends Widget {
|
||||
constructor(parent, props) {
|
||||
@@ -700,6 +686,13 @@ describe("lifecycle hooks", () => {
|
||||
}
|
||||
}
|
||||
class ParentWidget extends Widget {
|
||||
static template = xml`
|
||||
<div>
|
||||
<t t-if="state.flag">
|
||||
<div><t t-component="child"/></div>
|
||||
</t>
|
||||
</div>
|
||||
`;
|
||||
static components = { child: ChildWidget };
|
||||
state = { flag: false };
|
||||
}
|
||||
@@ -716,13 +709,7 @@ describe("lifecycle hooks", () => {
|
||||
|
||||
test("willPatch/patched hook", async () => {
|
||||
const steps: string[] = [];
|
||||
env.qweb.addTemplate(
|
||||
"ParentWidget",
|
||||
`
|
||||
<div>
|
||||
<t t-component="child" v="state.n"/>
|
||||
</div>`
|
||||
);
|
||||
|
||||
class ChildWidget extends Widget {
|
||||
willPatch() {
|
||||
steps.push("child:willPatch");
|
||||
@@ -732,6 +719,11 @@ describe("lifecycle hooks", () => {
|
||||
}
|
||||
}
|
||||
class ParentWidget extends Widget {
|
||||
static template = xml`
|
||||
<div>
|
||||
<t t-component="child" v="state.n"/>
|
||||
</div>
|
||||
`;
|
||||
static components = { child: ChildWidget };
|
||||
state = { n: 1 };
|
||||
willPatch() {
|
||||
@@ -763,13 +755,6 @@ describe("lifecycle hooks", () => {
|
||||
// we make sure here that willPatch/patched is only called if widget is in
|
||||
// dom, mounted
|
||||
const steps: string[] = [];
|
||||
env.qweb.addTemplates(`
|
||||
<templates>
|
||||
<div t-name="ParentWidget">
|
||||
<ChildWidget t-if="state.flag" v="state.n" t-keepalive="1"/>
|
||||
</div>
|
||||
</templates>
|
||||
`);
|
||||
|
||||
class ChildWidget extends Widget {
|
||||
willPatch() {
|
||||
@@ -786,6 +771,11 @@ describe("lifecycle hooks", () => {
|
||||
}
|
||||
}
|
||||
class ParentWidget extends Widget {
|
||||
static template = xml`
|
||||
<div>
|
||||
<ChildWidget t-if="state.flag" v="state.n" t-keepalive="1"/>
|
||||
</div>
|
||||
`;
|
||||
static components = { ChildWidget };
|
||||
state = { n: 1, flag: true };
|
||||
}
|
||||
@@ -793,7 +783,7 @@ describe("lifecycle hooks", () => {
|
||||
const widget = new ParentWidget(env);
|
||||
await widget.mount(fixture);
|
||||
|
||||
expect(env.qweb.templates.ParentWidget.fn.toString()).toMatchSnapshot();
|
||||
expect(env.qweb.templates[ParentWidget.template].fn.toString()).toMatchSnapshot();
|
||||
expect(steps).toEqual(["child:mounted"]);
|
||||
widget.state.flag = false;
|
||||
await nextTick();
|
||||
@@ -865,21 +855,20 @@ describe("destroy method", () => {
|
||||
});
|
||||
|
||||
test("destroying a widget before being mounted", async () => {
|
||||
env.qweb.addTemplates(`
|
||||
<templates>
|
||||
<div t-name="Parent" t-on-some-event="doStuff">
|
||||
<Child />
|
||||
</div>
|
||||
<span t-name="Child">
|
||||
<GrandChild t-if="state.flag" val="something"/>
|
||||
<button t-on-click="doSomething">click</button>
|
||||
</span>
|
||||
<span t-name="GrandChild">
|
||||
<t t-esc="props.val.val"/>
|
||||
</span>
|
||||
</templates>`);
|
||||
class GrandChild extends Component<any, any, any> {}
|
||||
class GrandChild extends Component<any, any, any> {
|
||||
static template = xml`
|
||||
<span>
|
||||
<t t-esc="props.val.val"/>
|
||||
</span>
|
||||
`;
|
||||
}
|
||||
class Child extends Component<any, any, any> {
|
||||
static template = xml`
|
||||
<span>
|
||||
<GrandChild t-if="state.flag" val="something"/>
|
||||
<button t-on-click="doSomething">click</button>
|
||||
</span>
|
||||
`;
|
||||
static components = { GrandChild };
|
||||
state = { val: 33, flag: false };
|
||||
doSomething() {
|
||||
@@ -892,6 +881,11 @@ describe("destroy method", () => {
|
||||
}
|
||||
}
|
||||
class Parent extends Component<any, any, any> {
|
||||
static template = xml`
|
||||
<div t-on-some-event="doStuff">
|
||||
<Child />
|
||||
</div>
|
||||
`;
|
||||
static components = { Child };
|
||||
state = { p: 1 };
|
||||
doStuff() {
|
||||
@@ -985,14 +979,12 @@ describe("composition", () => {
|
||||
|
||||
test("t-refs are bound at proper timing", async () => {
|
||||
expect.assertions(2);
|
||||
env.qweb.addTemplate(
|
||||
"ParentWidget",
|
||||
`
|
||||
class ParentWidget extends Widget {
|
||||
static template = xml`
|
||||
<div>
|
||||
<t t-foreach="state.list" t-as="elem" t-ref="child" t-key="elem" t-component="Widget"/>
|
||||
</div>`
|
||||
);
|
||||
class ParentWidget extends Widget {
|
||||
</div>
|
||||
`;
|
||||
static components = { Widget };
|
||||
state = { list: <any>[] };
|
||||
willPatch() {
|
||||
@@ -3055,8 +3047,8 @@ describe("t-slot directive", () => {
|
||||
);
|
||||
expect(env.qweb.templates.Parent.fn.toString()).toMatchSnapshot();
|
||||
expect(env.qweb.templates.Dialog.fn.toString()).toMatchSnapshot();
|
||||
expect(env.qweb.slots["1_header"].toString()).toMatchSnapshot();
|
||||
expect(env.qweb.slots["1_footer"].toString()).toMatchSnapshot();
|
||||
expect(QWeb.slots["1_header"].toString()).toMatchSnapshot();
|
||||
expect(QWeb.slots["1_footer"].toString()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test("slots are rendered with proper context", async () => {
|
||||
@@ -3092,7 +3084,7 @@ describe("t-slot directive", () => {
|
||||
expect(fixture.innerHTML).toBe(
|
||||
'<div><span class="counter">1</span><span><button>do something</button></span></div>'
|
||||
);
|
||||
expect(env.qweb.slots["1_footer"].toString()).toMatchSnapshot();
|
||||
expect(QWeb.slots["1_footer"].toString()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test("slots are rendered with proper context, part 2", async () => {
|
||||
@@ -3130,7 +3122,7 @@ describe("t-slot directive", () => {
|
||||
expect(fixture.innerHTML).toBe(
|
||||
'<div><u><li><a href="/user/1">User Aaron</a></li><li><a href="/user/2">User Mathieu</a></li></u></div>'
|
||||
);
|
||||
expect(env.qweb.slots["1_default"].toString()).toMatchSnapshot();
|
||||
expect(QWeb.slots["1_default"].toString()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test("slots are rendered with proper context, part 3", async () => {
|
||||
@@ -3169,7 +3161,7 @@ describe("t-slot directive", () => {
|
||||
expect(fixture.innerHTML).toBe(
|
||||
'<div><u><li><a href="/user/1">User Aaron</a></li><li><a href="/user/2">User Mathieu</a></li></u></div>'
|
||||
);
|
||||
expect(env.qweb.slots["1_default"].toString()).toMatchSnapshot();
|
||||
expect(QWeb.slots["1_default"].toString()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test("slots are rendered with proper context, part 4", async () => {
|
||||
@@ -3202,7 +3194,7 @@ describe("t-slot directive", () => {
|
||||
app.state.user.name = "David";
|
||||
await nextTick();
|
||||
expect(fixture.innerHTML).toBe('<div><a href="/user/1">User David</a></div>');
|
||||
expect(env.qweb.slots["1_default"].toString()).toMatchSnapshot();
|
||||
expect(QWeb.slots["1_default"].toString()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test("refs are properly bound in slots", async () => {
|
||||
@@ -3238,7 +3230,7 @@ describe("t-slot directive", () => {
|
||||
expect(fixture.innerHTML).toBe(
|
||||
'<div><span class="counter">1</span><span><button>do something</button></span></div>'
|
||||
);
|
||||
expect(env.qweb.slots["1_footer"].toString()).toMatchSnapshot();
|
||||
expect(QWeb.slots["1_footer"].toString()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test("content is the default slot", async () => {
|
||||
@@ -3260,7 +3252,7 @@ describe("t-slot directive", () => {
|
||||
await parent.mount(fixture);
|
||||
|
||||
expect(fixture.innerHTML).toBe("<div><div><span>sts rocks</span></div></div>");
|
||||
expect(env.qweb.slots["1_default"].toString()).toMatchSnapshot();
|
||||
expect(QWeb.slots["1_default"].toString()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test("default slot work with text nodes", async () => {
|
||||
@@ -3280,7 +3272,7 @@ describe("t-slot directive", () => {
|
||||
await parent.mount(fixture);
|
||||
|
||||
expect(fixture.innerHTML).toBe("<div><div>sts rocks</div></div>");
|
||||
expect(env.qweb.slots["1_default"].toString()).toMatchSnapshot();
|
||||
expect(QWeb.slots["1_default"].toString()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test("multiple roots are allowed in a named slot", async () => {
|
||||
@@ -3305,7 +3297,7 @@ describe("t-slot directive", () => {
|
||||
await parent.mount(fixture);
|
||||
|
||||
expect(fixture.innerHTML).toBe("<div><div><span>sts</span><span>rocks</span></div></div>");
|
||||
expect(env.qweb.slots["1_content"].toString()).toMatchSnapshot();
|
||||
expect(QWeb.slots["1_content"].toString()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test("multiple roots are allowed in a default slot", async () => {
|
||||
@@ -3328,7 +3320,7 @@ describe("t-slot directive", () => {
|
||||
await parent.mount(fixture);
|
||||
|
||||
expect(fixture.innerHTML).toBe("<div><div><span>sts</span><span>rocks</span></div></div>");
|
||||
expect(env.qweb.slots["1_default"].toString()).toMatchSnapshot();
|
||||
expect(QWeb.slots["1_default"].toString()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test("missing slots are ignored", async () => {
|
||||
@@ -3599,17 +3591,16 @@ describe("t-model directive", () => {
|
||||
});
|
||||
|
||||
test("on a select, initial state", async () => {
|
||||
env.qweb.addTemplates(`
|
||||
<templates>
|
||||
<div t-name="SomeComponent">
|
||||
<select t-model="color">
|
||||
<option value="">Please select one</option>
|
||||
<option value="red">Red</option>
|
||||
<option value="blue">Blue</option>
|
||||
</select>
|
||||
</div>
|
||||
</templates>`);
|
||||
class SomeComponent extends Widget {
|
||||
static template = xml`
|
||||
<div>
|
||||
<select t-model="color">
|
||||
<option value="">Please select one</option>
|
||||
<option value="red">Red</option>
|
||||
<option value="blue">Blue</option>
|
||||
</select>
|
||||
</div>
|
||||
`;
|
||||
state = { color: "red" };
|
||||
}
|
||||
const comp = new SomeComponent(env);
|
||||
@@ -3619,15 +3610,13 @@ describe("t-model directive", () => {
|
||||
});
|
||||
|
||||
test("on a sub state key", async () => {
|
||||
env.qweb.addTemplates(`
|
||||
<templates>
|
||||
<div t-name="SomeComponent">
|
||||
<input t-model="something.text"/>
|
||||
<span><t t-esc="state.something.text"/></span>
|
||||
</div>
|
||||
</templates>`);
|
||||
|
||||
class SomeComponent extends Widget {
|
||||
static template = xml`
|
||||
<div>
|
||||
<input t-model="something.text"/>
|
||||
<span><t t-esc="state.something.text"/></span>
|
||||
</div>
|
||||
`;
|
||||
state = { something: { text: "" } };
|
||||
}
|
||||
const comp = new SomeComponent(env);
|
||||
@@ -3639,18 +3628,17 @@ describe("t-model directive", () => {
|
||||
await editInput(input, "test");
|
||||
expect(comp.state.something.text).toBe("test");
|
||||
expect(fixture.innerHTML).toBe("<div><input><span>test</span></div>");
|
||||
expect(env.qweb.templates.SomeComponent.fn.toString()).toMatchSnapshot();
|
||||
expect(env.qweb.templates[SomeComponent.template].fn.toString()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test(".lazy modifier", async () => {
|
||||
env.qweb.addTemplates(`
|
||||
<templates>
|
||||
<div t-name="SomeComponent">
|
||||
class SomeComponent extends Widget {
|
||||
static template = xml`
|
||||
<div>
|
||||
<input t-model.lazy="text"/>
|
||||
<span><t t-esc="state.text"/></span>
|
||||
</div>
|
||||
</templates>`);
|
||||
class SomeComponent extends Widget {
|
||||
`;
|
||||
state = { text: "" };
|
||||
}
|
||||
const comp = new SomeComponent(env);
|
||||
@@ -3668,18 +3656,17 @@ describe("t-model directive", () => {
|
||||
await nextTick();
|
||||
expect(comp.state.text).toBe("test");
|
||||
expect(fixture.innerHTML).toBe("<div><input><span>test</span></div>");
|
||||
expect(env.qweb.templates.SomeComponent.fn.toString()).toMatchSnapshot();
|
||||
expect(env.qweb.templates[SomeComponent.template].fn.toString()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test(".trim modifier", async () => {
|
||||
env.qweb.addTemplates(`
|
||||
<templates>
|
||||
<div t-name="SomeComponent">
|
||||
<input t-model.trim="text"/>
|
||||
<span><t t-esc="state.text"/></span>
|
||||
</div>
|
||||
</templates>`);
|
||||
class SomeComponent extends Widget {
|
||||
static template = xml`
|
||||
<div t-name="SomeComponent">
|
||||
<input t-model.trim="text"/>
|
||||
<span><t t-esc="state.text"/></span>
|
||||
</div>
|
||||
`;
|
||||
state = { text: "" };
|
||||
}
|
||||
const comp = new SomeComponent(env);
|
||||
@@ -3692,14 +3679,13 @@ describe("t-model directive", () => {
|
||||
});
|
||||
|
||||
test(".number modifier", async () => {
|
||||
env.qweb.addTemplates(`
|
||||
<templates>
|
||||
<div t-name="SomeComponent">
|
||||
<input t-model.number="number"/>
|
||||
<span><t t-esc="state.number"/></span>
|
||||
</div>
|
||||
</templates>`);
|
||||
class SomeComponent extends Widget {
|
||||
static template = xml`
|
||||
<div>
|
||||
<input t-model.number="number"/>
|
||||
<span><t t-esc="state.number"/></span>
|
||||
</div>
|
||||
`;
|
||||
state = { number: 0 };
|
||||
}
|
||||
const comp = new SomeComponent(env);
|
||||
@@ -3732,15 +3718,14 @@ describe("environment and plugins", () => {
|
||||
|
||||
test("plugin works as expected", async () => {
|
||||
somePlugin(env);
|
||||
env.qweb.addTemplates(`
|
||||
<templates>
|
||||
<div t-name="App">
|
||||
class App extends Widget {
|
||||
static template=xml`
|
||||
<div>
|
||||
<t t-if="env.someFlag">Red</t>
|
||||
<t t-else="1">Blue</t>
|
||||
</div>
|
||||
</templates>
|
||||
`);
|
||||
class App extends Widget {}
|
||||
`;
|
||||
}
|
||||
|
||||
const app = new App(env);
|
||||
await app.mount(fixture);
|
||||
@@ -4086,18 +4071,19 @@ describe("top level sub widgets", () => {
|
||||
});
|
||||
|
||||
test("can select a sub widget ", async () => {
|
||||
env.qweb.addTemplates(`
|
||||
<templates>
|
||||
<t t-name="Parent">
|
||||
<t t-if="env.flag"><Child /></t>
|
||||
<t t-if="!env.flag"><OtherChild /></t>
|
||||
</t>
|
||||
<span t-name="Child">CHILD 1</span>
|
||||
<div t-name="OtherChild">CHILD 2</div>
|
||||
</templates>`);
|
||||
class Child extends Widget {}
|
||||
class OtherChild extends Widget {}
|
||||
class Child extends Widget {
|
||||
static template = xml`<span>CHILD 1</span>`;
|
||||
}
|
||||
class OtherChild extends Widget {
|
||||
static template = xml`<div>CHILD 2</div>`;
|
||||
}
|
||||
class Parent extends Widget {
|
||||
static template = xml`
|
||||
<t>
|
||||
<t t-if="env.flag"><Child /></t>
|
||||
<t t-if="!env.flag"><OtherChild /></t>
|
||||
</t>
|
||||
`;
|
||||
static components = { Child, OtherChild };
|
||||
}
|
||||
(<any>env).flag = true;
|
||||
@@ -4110,22 +4096,23 @@ describe("top level sub widgets", () => {
|
||||
await parent.mount(fixture);
|
||||
expect(fixture.innerHTML).toBe("<div>CHILD 2</div>");
|
||||
|
||||
expect(env.qweb.templates.Parent.fn.toString()).toMatchSnapshot();
|
||||
expect(env.qweb.templates[Parent.template].fn.toString()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test("can select a sub widget, part 2", async () => {
|
||||
env.qweb.addTemplates(`
|
||||
<templates>
|
||||
<t t-name="Parent">
|
||||
<t t-if="state.flag"><Child /></t>
|
||||
<t t-if="!state.flag"><OtherChild /></t>
|
||||
</t>
|
||||
<span t-name="Child">CHILD 1</span>
|
||||
<div t-name="OtherChild">CHILD 2</div>
|
||||
</templates>`);
|
||||
class Child extends Widget {}
|
||||
class OtherChild extends Widget {}
|
||||
class Child extends Widget {
|
||||
static template = xml`<span>CHILD 1</span>`;
|
||||
}
|
||||
class OtherChild extends Widget {
|
||||
static template = xml`<div>CHILD 2</div>`;
|
||||
}
|
||||
class Parent extends Widget {
|
||||
static template = xml`
|
||||
<t>
|
||||
<t t-if="state.flag"><Child /></t>
|
||||
<t t-if="!state.flag"><OtherChild /></t>
|
||||
</t>
|
||||
`;
|
||||
state = { flag: true };
|
||||
static components = { Child, OtherChild };
|
||||
}
|
||||
@@ -4140,13 +4127,9 @@ describe("top level sub widgets", () => {
|
||||
|
||||
describe("unmounting and remounting", () => {
|
||||
test("widget can be unmounted and remounted", async () => {
|
||||
env.qweb.addTemplates(`
|
||||
<templates>
|
||||
<div t-name="MyWidget">Hey</div>
|
||||
</templates>`);
|
||||
|
||||
const steps: string[] = [];
|
||||
class MyWidget extends Widget {
|
||||
static template = xml`<div>Hey</div>`;
|
||||
async willStart() {
|
||||
steps.push("willstart");
|
||||
}
|
||||
@@ -4173,13 +4156,9 @@ describe("unmounting and remounting", () => {
|
||||
});
|
||||
|
||||
test("widget can be mounted twice without ill effect", async () => {
|
||||
env.qweb.addTemplates(`
|
||||
<templates>
|
||||
<div t-name="MyWidget">Hey</div>
|
||||
</templates>`);
|
||||
|
||||
const steps: string[] = [];
|
||||
class MyWidget extends Widget {
|
||||
static template = xml`<div>Hey</div>`;
|
||||
async willStart() {
|
||||
steps.push("willstart");
|
||||
}
|
||||
@@ -4200,16 +4179,11 @@ describe("unmounting and remounting", () => {
|
||||
|
||||
test("state changes in willUnmount do not trigger rerender", async () => {
|
||||
const steps: string[] = [];
|
||||
env.qweb.addTemplates(`
|
||||
<templates>
|
||||
<div t-name="Parent">
|
||||
<Child t-if="state.flag" val="state.val"/>
|
||||
</div>
|
||||
<span t-name="Child"><t t-esc="props.val"/><t t-esc="state.n"/></span>
|
||||
</templates>
|
||||
`);
|
||||
|
||||
class Child extends Widget {
|
||||
static template = xml`
|
||||
<span><t t-esc="props.val"/><t t-esc="state.n"/></span>
|
||||
`;
|
||||
state = { n: 2 };
|
||||
__render(a, b, c, d) {
|
||||
steps.push("render");
|
||||
@@ -4228,6 +4202,11 @@ describe("unmounting and remounting", () => {
|
||||
}
|
||||
}
|
||||
class Parent extends Widget {
|
||||
static template = xml`
|
||||
<div>
|
||||
<Child t-if="state.flag" val="state.val"/>
|
||||
</div>
|
||||
`;
|
||||
static components = { Child };
|
||||
state = { val: 1, flag: true };
|
||||
}
|
||||
@@ -4243,13 +4222,10 @@ describe("unmounting and remounting", () => {
|
||||
});
|
||||
|
||||
test("state changes in willUnmount will be applied on remount", async () => {
|
||||
env.qweb.addTemplates(`
|
||||
<templates>
|
||||
<div t-name="TestWidget"><t t-esc="state.val"/></div>
|
||||
</templates>
|
||||
`);
|
||||
|
||||
class TestWidget extends Widget {
|
||||
static template = xml`
|
||||
<div><t t-esc="state.val"/></div>
|
||||
`;
|
||||
state = { val: 1 };
|
||||
willUnmount() {
|
||||
this.state.val = 3;
|
||||
@@ -4271,15 +4247,14 @@ describe("unmounting and remounting", () => {
|
||||
|
||||
describe("dynamic root nodes", () => {
|
||||
test("template with t-if, part 1", async () => {
|
||||
env.qweb.addTemplates(`
|
||||
<templates>
|
||||
<t t-name="TestWidget">
|
||||
<t t-if="true"><span>hey</span></t>
|
||||
<t t-if="false"><div>abc</div></t>
|
||||
</t>
|
||||
</templates>
|
||||
`);
|
||||
class TestWidget extends Widget {}
|
||||
class TestWidget extends Widget {
|
||||
static template = xml`
|
||||
<t>
|
||||
<t t-if="true"><span>hey</span></t>
|
||||
<t t-if="false"><div>abc</div></t>
|
||||
</t>
|
||||
`;
|
||||
}
|
||||
|
||||
const widget = new TestWidget(env);
|
||||
await widget.mount(fixture);
|
||||
@@ -4288,15 +4263,14 @@ describe("dynamic root nodes", () => {
|
||||
});
|
||||
|
||||
test("template with t-if, part 2", async () => {
|
||||
env.qweb.addTemplates(`
|
||||
<templates>
|
||||
<t t-name="TestWidget">
|
||||
<t t-if="false"><span>hey</span></t>
|
||||
<t t-if="true"><div>abc</div></t>
|
||||
</t>
|
||||
</templates>
|
||||
`);
|
||||
class TestWidget extends Widget {}
|
||||
class TestWidget extends Widget {
|
||||
static template = xml`
|
||||
<t>
|
||||
<t t-if="false"><span>hey</span></t>
|
||||
<t t-if="true"><div>abc</div></t>
|
||||
</t>
|
||||
`;
|
||||
}
|
||||
|
||||
const widget = new TestWidget(env);
|
||||
await widget.mount(fixture);
|
||||
@@ -4305,15 +4279,13 @@ describe("dynamic root nodes", () => {
|
||||
});
|
||||
|
||||
test("switching between sub branches dynamically", async () => {
|
||||
env.qweb.addTemplates(`
|
||||
<templates>
|
||||
<t t-name="TestWidget">
|
||||
<t t-if="state.flag"><span>hey</span></t>
|
||||
<t t-if="!state.flag"><div>abc</div></t>
|
||||
</t>
|
||||
</templates>
|
||||
`);
|
||||
class TestWidget extends Widget {
|
||||
static template = xml`
|
||||
<t>
|
||||
<t t-if="state.flag"><span>hey</span></t>
|
||||
<t t-if="!state.flag"><div>abc</div></t>
|
||||
</t>
|
||||
`;
|
||||
state = { flag: true };
|
||||
}
|
||||
|
||||
@@ -4328,19 +4300,19 @@ describe("dynamic root nodes", () => {
|
||||
});
|
||||
|
||||
test("switching between sub components dynamically", async () => {
|
||||
env.qweb.addTemplates(`
|
||||
<templates>
|
||||
<t t-name="ChildA"><span>hey</span></t>
|
||||
<t t-name="ChildB"><div>abc</div></t>
|
||||
<t t-name="TestWidget">
|
||||
<t t-if="state.flag"><ChildA/></t>
|
||||
<t t-if="!state.flag"><ChildB/></t>
|
||||
</t>
|
||||
</templates>
|
||||
`);
|
||||
class ChildA extends Widget {}
|
||||
class ChildB extends Widget {}
|
||||
class ChildA extends Widget {
|
||||
static template = xml`<span>hey</span>`;
|
||||
}
|
||||
class ChildB extends Widget {
|
||||
static template = xml`<div>abc</div>`;
|
||||
}
|
||||
class TestWidget extends Widget {
|
||||
static template = xml`
|
||||
<t>
|
||||
<t t-if="state.flag"><ChildA/></t>
|
||||
<t t-if="!state.flag"><ChildB/></t>
|
||||
</t>
|
||||
`;
|
||||
static components = { ChildA, ChildB };
|
||||
state = { flag: true };
|
||||
}
|
||||
@@ -4359,17 +4331,13 @@ describe("dynamic root nodes", () => {
|
||||
describe("dynamic t-props", () => {
|
||||
test("basic use", async () => {
|
||||
expect.assertions(4);
|
||||
env.qweb.addTemplates(`
|
||||
<templates>
|
||||
<span t-name="Child">
|
||||
<t t-esc="props.a + props.b"/>
|
||||
</span>
|
||||
<div t-name="Parent">
|
||||
<Child t-props="some.obj"/>
|
||||
</div>
|
||||
</templates>
|
||||
`);
|
||||
|
||||
class Child extends Widget {
|
||||
static template = xml`
|
||||
<span>
|
||||
<t t-esc="props.a + props.b"/>
|
||||
</span>
|
||||
`;
|
||||
constructor(parent, props) {
|
||||
super(parent, props);
|
||||
expect(props).toEqual({ a: 1, b: 2 });
|
||||
@@ -4377,6 +4345,11 @@ describe("dynamic t-props", () => {
|
||||
}
|
||||
}
|
||||
class Parent extends Widget {
|
||||
static template = xml`
|
||||
<div>
|
||||
<Child t-props="some.obj"/>
|
||||
</div>
|
||||
`;
|
||||
static components = { Child };
|
||||
|
||||
some = { obj: { a: 1, b: 2 } };
|
||||
@@ -4386,6 +4359,6 @@ describe("dynamic t-props", () => {
|
||||
await widget.mount(fixture);
|
||||
|
||||
expect(fixture.innerHTML).toBe("<div><span>3</span></div>");
|
||||
expect(env.qweb.templates.Parent.fn.toString()).toMatchSnapshot();
|
||||
expect(env.qweb.templates[Parent.template].fn.toString()).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
+22
-1
@@ -5,6 +5,27 @@ import "../src/qweb/base_directives";
|
||||
import "../src/qweb/extensions";
|
||||
import "../src/component/directive";
|
||||
|
||||
// Some static cleanup
|
||||
let nextSlotId;
|
||||
let slots;
|
||||
let nextId;
|
||||
let TEMPLATES;
|
||||
|
||||
beforeEach(() => {
|
||||
nextSlotId = QWeb.nextSlotId;
|
||||
slots = Object.assign({}, QWeb.slots);
|
||||
nextId = QWeb.nextId;
|
||||
TEMPLATES = Object.assign({}, QWeb.TEMPLATES);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
QWeb.nextSlotId = nextSlotId;
|
||||
QWeb.slots = slots;
|
||||
QWeb.nextId = nextId;
|
||||
QWeb.TEMPLATES = TEMPLATES;
|
||||
});
|
||||
|
||||
// helpers
|
||||
export function nextMicroTick(): Promise<void> {
|
||||
return Promise.resolve();
|
||||
}
|
||||
@@ -82,7 +103,7 @@ export function renderToString(qweb: QWeb, t: string, context: EvalContext = {})
|
||||
const node = renderToDOM(qweb, t, context);
|
||||
const result = node instanceof Text ? node.textContent! : node.outerHTML;
|
||||
if (result !== qweb.renderToString(t, context)) {
|
||||
throw new Error("HTML string returned by renderToString helper does not match QWeb render");
|
||||
throw new Error("HTML string returned by renderToString helper does not match QWeb render");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Component } from "../../src/component/component";
|
||||
import { Link, LINK_TEMPLATE_NAME } from "../../src/router/Link";
|
||||
import { Link } from "../../src/router/Link";
|
||||
import { RouterEnv } from "../../src/router/Router";
|
||||
import { makeTestEnv, makeTestFixture, nextTick } from "../helpers";
|
||||
import { TestRouter } from "./TestRouter";
|
||||
@@ -50,7 +50,7 @@ describe("Link component", () => {
|
||||
'<div><a href="/about" class="router-link-active">About</a></div>'
|
||||
);
|
||||
|
||||
expect(env.qweb.templates[LINK_TEMPLATE_NAME].fn.toString()).toMatchSnapshot();
|
||||
expect(env.qweb.templates[Link.template].fn.toString()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test("do not redirect if right clicking", async () => {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Component } from "../../src/component/component";
|
||||
import { RouterEnv } from "../../src/router/Router";
|
||||
import { RouteComponent, ROUTE_COMPONENT_TEMPLATE_NAME } from "../../src/router/RouteComponent";
|
||||
import { RouteComponent } from "../../src/router/RouteComponent";
|
||||
import { makeTestEnv, makeTestFixture, nextTick } from "../helpers";
|
||||
import { TestRouter } from "./TestRouter";
|
||||
|
||||
@@ -52,7 +52,7 @@ describe("RouteComponent", () => {
|
||||
await router.navigate({ to: "users" });
|
||||
await nextTick();
|
||||
expect(fixture.innerHTML).toBe("<div><span>Users</span></div>");
|
||||
expect(env.qweb.templates[ROUTE_COMPONENT_TEMPLATE_NAME].fn.toString()).toMatchSnapshot();
|
||||
expect(env.qweb.templates[RouteComponent.template].fn.toString()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test("can render parameterized route", async () => {
|
||||
|
||||
@@ -12,11 +12,11 @@ exports[`Link component can render simple cases 1`] = `
|
||||
var vn3 = h('a', p3, c3);
|
||||
result = vn3;
|
||||
if (!context['navigate']) {
|
||||
throw new Error('Missing handler \\\\'' + 'navigate' + \`\\\\' when evaluating template '__owl__-router-link'\`)
|
||||
throw new Error('Missing handler \\\\'' + 'navigate' + \`\\\\' when evaluating template '__template__1'\`)
|
||||
}
|
||||
extra.handlers['click' + 3] = extra.handlers['click' + 3] || context['navigate'].bind(owner);
|
||||
p3.on['click'] = extra.handlers['click' + 3];
|
||||
const slot4 = this.slots[context.__owl__.slotId + '_' + 'default'];
|
||||
const slot4 = this.constructor.slots[context.__owl__.slotId + '_' + 'default'];
|
||||
if (slot4) {
|
||||
slot4.call(this, context.__owl__.parent, Object.assign({}, extra, {parentNode: c3, vars: extra.vars, parent: owner}));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user