[IMP] component: simplify constructor API

It now takes two arguments: parent (optional, only for non-root
components) and props (optional). In the case of the root component,
the env is taken from the config (config.defaultEnv). If it doesn't
exists, the default env is created on the fly.

Closes #306
This commit is contained in:
Aaron Bohy
2019-10-14 15:09:17 +02:00
committed by Géry Debongnie
parent 9f93da4765
commit 9106c19066
14 changed files with 406 additions and 386 deletions
+12 -20
View File
@@ -1,6 +1,7 @@
import { Observer } from "../core/observer";
import { CompiledTemplate, QWeb } from "../qweb/index";
import { h, patch, VNode } from "../vdom/index";
import { config } from "../config";
import "./directive";
import { Fiber } from "./fiber";
import "./props_validation";
@@ -106,25 +107,14 @@ export class Component<T extends Env, Props extends {}> {
/**
* Creates an instance of Component.
*
* The root component of a component tree needs an environment:
*
* ```javascript
* const root = new RootComponent(env, props);
* ```
*
* Every other component simply needs a reference to its parent:
*
* ```javascript
* const child = new SomeComponent(parent, props);
* ```
*
* Note that most of the time, only the root component needs to be created by
* hand. Other components should be created automatically by the framework (with
* the t-component directive in a template)
*/
constructor(parent: Component<T, any> | T, props?: Props) {
const defaultProps = (<any>this.constructor).defaultProps;
constructor(parent?: Component<T, any>, props?: Props) {
Component.current = this;
const defaultProps = (<any>this.constructor).defaultProps;
if (defaultProps) {
props = this.__applyDefaultProps(props, defaultProps);
}
@@ -136,17 +126,18 @@ export class Component<T extends Env, Props extends {}> {
if (QWeb.dev) {
QWeb.utils.validateProps(this.constructor, this.props);
}
let id: number = nextId++;
const id: number = nextId++;
let depth;
let p: Component<T, any> | null = null;
if (parent instanceof Component) {
p = parent;
if (parent) {
this.env = parent.env;
const __powl__ = parent.__owl__;
__powl__.children[id] = this;
depth = __powl__.depth + 1;
} else {
this.env = parent;
// we are the root component
this.env = config.env as T;
this.env.qweb.on("update", this, () => {
if (this.__owl__.isMounted) {
this.render(true);
@@ -162,6 +153,7 @@ export class Component<T extends Env, Props extends {}> {
});
depth = 0;
}
const qweb = this.env.qweb;
this.__owl__ = {
@@ -171,7 +163,7 @@ export class Component<T extends Env, Props extends {}> {
pvnode: null,
isMounted: false,
isDestroyed: false,
parent: p,
parent: parent || null,
children: {},
cmap: {},
currentFiber: null,
+20 -3
View File
@@ -1,17 +1,20 @@
import { QWeb } from "./qweb/index";
import { Env } from "./component/component";
/**
* This file creates and exports the OWL 'config' object, with keys:
* - 'mode': 'prod' or 'dev',
* - 'env': the environment to use in root components.
*/
interface Config {
env: Env;
mode: string;
}
const _config:Partial<Config> = {};
export const config = {} as Config;
Object.defineProperty(_config, "mode", {
Object.defineProperty(config, "mode", {
get() {
return QWeb.dev ? "dev" : "prod";
},
@@ -28,4 +31,18 @@ Object.defineProperty(_config, "mode", {
},
});
export const config:Config = _config as Config;
let env:Env;
Object.defineProperty(config, "env", {
get() {
if (!env) {
env = {} as Env;
}
if (!env.qweb) {
env.qweb = new QWeb();
}
return env;
},
set(newEnv: Env) {
env = newEnv;
},
});
+8 -6
View File
@@ -1,4 +1,5 @@
import { Component, Env } from "../src/component/component";
import { config } from "../src/config";
import { QWeb } from "../src/qweb/index";
import { useState, useRef } from "../src/hooks";
import {
@@ -29,6 +30,7 @@ let cssEl: HTMLElement;
beforeEach(() => {
fixture = makeTestFixture();
env = makeTestEnv();
config.env = env;
qweb = new QWeb();
});
@@ -108,7 +110,7 @@ describe("animations", () => {
class TestWidget extends Widget {
state = useState({ hide: false });
}
const widget = new TestWidget(env);
const widget = new TestWidget();
// insert widget into the DOM
let def = makeDeferred();
@@ -151,7 +153,7 @@ describe("animations", () => {
state = useState({ hide: false });
span = useRef("span");
}
const widget = new TestWidget(env);
const widget = new TestWidget();
// insert widget into the DOM
let def = makeDeferred();
@@ -180,7 +182,7 @@ describe("animations", () => {
class Parent extends Widget {
static components = { Child: Child };
}
const widget = new Parent(env);
const widget = new Parent();
let def = makeDeferred();
var spanNode;
@@ -220,7 +222,7 @@ describe("animations", () => {
static components = { Child: Child };
state = useState({ display: true });
}
const widget = new Parent(env);
const widget = new Parent();
let def = makeDeferred();
var spanNode;
@@ -283,7 +285,7 @@ describe("animations", () => {
}
}
const widget = new Parent(env);
const widget = new Parent();
await widget.mount(fixture);
let button = widget.el!.querySelector("button");
@@ -341,7 +343,7 @@ describe("animations", () => {
}
}
const widget = new Parent(env);
const widget = new Parent();
await widget.mount(fixture);
let button = widget.el!.querySelector("button");
File diff suppressed because it is too large Load Diff
+39 -37
View File
@@ -1,6 +1,7 @@
import { Component, Env } from "../../src/component/component";
import { makeTestFixture, makeTestEnv, nextTick } from "../helpers";
import { useState } from "../../src/hooks";
import { config } from "../../src/config";
import { QWeb } from "../../src/qweb";
import { xml } from "../../src/tags";
@@ -15,6 +16,7 @@ let dev: boolean = false;
beforeEach(() => {
fixture = makeTestFixture();
env = makeTestEnv();
config.env = env;
dev = QWeb.dev;
QWeb.dev = true;
});
@@ -38,13 +40,13 @@ describe("props validation", () => {
QWeb.dev = true;
expect(() => {
new TestWidget(env);
new TestWidget();
}).toThrow();
QWeb.dev = false;
expect(() => {
new TestWidget(env);
new TestWidget();
}).not.toThrow();
});
@@ -55,7 +57,7 @@ describe("props validation", () => {
}
expect(() => {
new TestWidget(env);
new TestWidget();
}).toThrow("Missing props 'message' (component 'TestWidget')");
});
@@ -76,15 +78,15 @@ describe("props validation", () => {
};
expect(() => {
new TestWidget(env);
new TestWidget();
}).toThrow("Missing props 'p'");
expect(() => {
new TestWidget(env, { p: test.ok });
new TestWidget(undefined, { p: test.ok });
}).not.toThrow();
expect(() => {
new TestWidget(env, { p: test.ko });
new TestWidget(undefined, { p: test.ko });
}).toThrow("Props 'p' of invalid type in component");
}
});
@@ -106,15 +108,15 @@ describe("props validation", () => {
};
expect(() => {
new TestWidget(env);
new TestWidget();
}).toThrow("Missing props 'p'");
expect(() => {
new TestWidget(env, { p: test.ok });
new TestWidget(undefined, { p: test.ok });
}).not.toThrow();
expect(() => {
new TestWidget(env, { p: test.ko });
new TestWidget(undefined, { p: test.ko });
}).toThrow("Props 'p' of invalid type in component");
}
});
@@ -126,12 +128,12 @@ describe("props validation", () => {
};
expect(() => {
new TestWidget(env, { p: "string" });
new TestWidget(env, { p: true });
new TestWidget(undefined, { p: "string" });
new TestWidget(undefined, { p: true });
}).not.toThrow();
expect(() => {
new TestWidget(env, { p: 1 });
new TestWidget(undefined, { p: 1 });
}).toThrow("Props 'p' of invalid type in component");
});
@@ -142,12 +144,12 @@ describe("props validation", () => {
};
expect(() => {
new TestWidget(env, { p: "hey" });
new TestWidget(env, {});
new TestWidget(undefined, { p: "hey" });
new TestWidget(undefined, {});
}).not.toThrow();
expect(() => {
new TestWidget(env, { p: 1 });
new TestWidget(undefined, { p: 1 });
}).toThrow();
});
@@ -158,16 +160,16 @@ describe("props validation", () => {
};
expect(() => {
new TestWidget(env, { p: [] });
new TestWidget(env, { p: ["string"] });
new TestWidget(undefined, { p: [] });
new TestWidget(undefined, { p: ["string"] });
}).not.toThrow();
expect(() => {
new TestWidget(env, { p: [1] });
new TestWidget(undefined, { p: [1] });
}).toThrow();
expect(() => {
new TestWidget(env, { p: ["string", 1] });
new TestWidget(undefined, { p: ["string", 1] });
}).toThrow();
});
@@ -178,13 +180,13 @@ describe("props validation", () => {
};
expect(() => {
new TestWidget(env, { p: [] });
new TestWidget(env, { p: ["string"] });
new TestWidget(env, { p: [false, true, "string"] });
new TestWidget(undefined, { p: [] });
new TestWidget(undefined, { p: ["string"] });
new TestWidget(undefined, { p: [false, true, "string"] });
}).not.toThrow();
expect(() => {
new TestWidget(env, { p: [true, 1] });
new TestWidget(undefined, { p: [true, 1] });
}).toThrow();
});
@@ -197,16 +199,16 @@ describe("props validation", () => {
};
expect(() => {
new TestWidget(env, { p: { id: 1, url: "url" } });
new TestWidget(env, { p: { id: 1, url: "url", extra: true } });
new TestWidget(undefined, { p: { id: 1, url: "url" } });
new TestWidget(undefined, { p: { id: 1, url: "url", extra: true } });
}).not.toThrow();
expect(() => {
new TestWidget(env, { p: { id: "1", url: "url" } });
new TestWidget(undefined, { p: { id: "1", url: "url" } });
}).toThrow();
expect(() => {
new TestWidget(env, { p: { id: 1 } });
new TestWidget(undefined, { p: { id: 1 } });
}).toThrow();
});
@@ -225,12 +227,12 @@ describe("props validation", () => {
};
expect(() => {
new TestWidget(env, { p: { id: 1, url: true } });
new TestWidget(env, { p: { id: 1, url: [12] } });
new TestWidget(undefined, { p: { id: 1, url: true } });
new TestWidget(undefined, { p: { id: 1, url: [12] } });
}).not.toThrow();
expect(() => {
new TestWidget(env, { p: { id: 1, url: [12, true] } });
new TestWidget(undefined, { p: { id: 1, url: [12, true] } });
}).toThrow();
});
@@ -249,7 +251,7 @@ describe("props validation", () => {
class App extends Widget {
static components = { Child };
}
const app = new App(env);
const app = new App();
await app.mount(fixture);
expect(fixture.innerHTML).toBe("<div><div>1</div></div>");
// need to make sure there are 2 call to update props. one at component
@@ -334,7 +336,7 @@ describe("props validation", () => {
static components = { TestWidget };
}
const w = new App(env, {});
const w = new App(undefined, {});
let error;
try {
await w.mount(fixture);
@@ -365,7 +367,7 @@ describe("props validation", () => {
state: any = useState({ p: 1 });
}
const w = new Parent(env);
const w = new Parent();
await w.mount(fixture);
expect(fixture.innerHTML).toBe("<div><div>1</div></div>");
@@ -387,7 +389,7 @@ describe("props validation", () => {
state: any = useState({ p: 1 });
}
const w = new Parent(env);
const w = new Parent();
await w.mount(fixture);
expect(fixture.innerHTML).toBe("<div><div>1</div></div>");
@@ -404,7 +406,7 @@ describe("default props", () => {
static template = xml`<div>hey</div>`;
}
const w = new TestWidget(env, {});
const w = new TestWidget(undefined, {});
expect(w.props.p).toBe(4);
});
@@ -419,7 +421,7 @@ describe("default props", () => {
state: any = useState({ p: 1 });
}
const w = new Parent(env);
const w = new Parent();
await w.mount(fixture);
expect(fixture.innerHTML).toBe("<div><div>1</div></div>");
@@ -440,7 +442,7 @@ describe("default props", () => {
static components = { TestWidget };
}
const w = new App(env, {});
const w = new App(undefined, {});
await w.mount(fixture);
expect(fixture.innerHTML).toBe("<div><span>heyhey</span></div>");
});
+13 -13
View File
@@ -1,6 +1,7 @@
import { makeDeferred, makeTestEnv, makeTestFixture, nextTick } from "./helpers";
import { Component, Env } from "../src/component/component";
import { Component } from "../src/component/component";
import { Context, useContext } from "../src/context";
import { config } from "../src/config";
import { xml } from "../src/tags";
import { useState } from "../src/hooks";
@@ -11,14 +12,13 @@ import { useState } from "../src/hooks";
// We create before each test:
// - fixture: a div, appended to the DOM, intended to be the target of dom
// manipulations. Note that it is removed after each test.
// - env: a WEnv, necessary to create new components
// - a test env, necessary to create components, that is set as env
let fixture: HTMLElement;
let env: Env;
beforeEach(() => {
fixture = makeTestFixture();
env = makeTestEnv();
config.env = makeTestEnv();
});
afterEach(() => {
@@ -37,7 +37,7 @@ describe("Context", () => {
static template = xml`<div><t t-esc="contextObj.value"/></div>`;
contextObj = useContext(testContext);
}
const test = new Test(env);
const test = new Test();
await test.mount(fixture);
expect(fixture.innerHTML).toBe("<div>123</div>");
});
@@ -49,7 +49,7 @@ describe("Context", () => {
static template = xml`<div><t t-esc="contextObj.value"/></div>`;
contextObj = useContext(testContext);
}
const test = new Test(env);
const test = new Test();
await test.mount(fixture);
expect(fixture.innerHTML).toBe("<div>123</div>");
test.contextObj.value = 321;
@@ -68,7 +68,7 @@ describe("Context", () => {
static template = xml`<div><Child /><Child /></div>`;
static components = { Child };
}
const parent = new Parent(env);
const parent = new Parent();
await parent.mount(fixture);
expect(fixture.innerHTML).toBe("<div><span>123</span><span>123</span></div>");
testContext.state.value = 321;
@@ -95,7 +95,7 @@ describe("Context", () => {
static template = xml`<div><Child /><Child /></div>`;
static components = { Child };
}
const parent = new Parent(env);
const parent = new Parent();
await parent.mount(fixture);
expect(fixture.innerHTML).toBe("<div><span>123</span><span>123</span></div>");
testContext.state.value = 321;
@@ -139,7 +139,7 @@ describe("Context", () => {
static components = { Child, Parent };
}
const app = new App(env);
const app = new App();
await app.mount(fixture);
expect(fixture.innerHTML).toBe(
"<div><span><p>123</p></span><div><span><p>123</p></span><span><p>123</p></span></div></div>"
@@ -175,7 +175,7 @@ describe("Context", () => {
static template = xml`<div><Child /></div>`;
static components = { Child };
}
const parent = new Parent(env);
const parent = new Parent();
await parent.mount(fixture);
expect(fixture.innerHTML).toBe("<div><span>12</span></div>");
expect(steps).toEqual(["child"]);
@@ -206,7 +206,7 @@ describe("Context", () => {
return super.__render(fiber);
}
}
const parent = new Parent(env);
const parent = new Parent();
await parent.mount(fixture);
expect(fixture.innerHTML).toBe("<div><span>123</span>321</div>");
expect(steps).toEqual(["parent", "child"]);
@@ -240,7 +240,7 @@ describe("Context", () => {
return super.__render(fiber);
}
}
const parent = new Parent(env);
const parent = new Parent();
await parent.mount(fixture);
expect(fixture.innerHTML).toBe("<div><span>123</span></div>");
expect(steps).toEqual(["parent", "child"]);
@@ -284,7 +284,7 @@ describe("Context", () => {
context = useContext(testContext);
}
const component = new ComponentA(env);
const component = new ComponentA();
await component.mount(fixture);
expect(fixture.innerHTML).toBe("<div><p><span>1a</span></p></div>");
+44 -42
View File
@@ -1,5 +1,6 @@
import { makeTestEnv, makeTestFixture, nextTick } from "./helpers";
import { Component, Env } from "../src/component/component";
import { config } from "../src/config";
import {
useState,
onMounted,
@@ -28,6 +29,7 @@ let env: Env;
beforeEach(() => {
fixture = makeTestFixture();
env = makeTestEnv();
config.env = env;
});
afterEach(() => {
@@ -44,7 +46,7 @@ describe("hooks", () => {
static template = xml`<div><t t-esc="counter.value"/></div>`;
counter = useState({ value: 42 });
}
const counter = new Counter(env);
const counter = new Counter();
await counter.mount(fixture);
expect(fixture.innerHTML).toBe("<div>42</div>");
counter.counter.value = 3;
@@ -64,12 +66,12 @@ describe("hooks", () => {
}
class MyComponent extends Component<any, any> {
static template = xml`<div>hey</div>`;
constructor(env) {
super(env);
constructor() {
super();
useMyHook();
}
}
const component = new MyComponent(env);
const component = new MyComponent();
await component.mount(fixture);
expect(component).not.toHaveProperty("mounted");
expect(component).not.toHaveProperty("willUnmount");
@@ -92,8 +94,8 @@ describe("hooks", () => {
}
class MyComponent extends Component<any, any> {
static template = xml`<div>hey</div>`;
constructor(env) {
super(env);
constructor(parent, props) {
super(parent, props);
useMyHook();
}
}
@@ -103,7 +105,7 @@ describe("hooks", () => {
static components = { MyComponent };
state = useState({ flag: true });
}
const parent = new Parent(env);
const parent = new Parent();
await parent.mount(fixture);
expect(fixture.innerHTML).toBe("<div><div>hey</div></div>");
expect(steps).toEqual(["mounted"]);
@@ -126,8 +128,8 @@ describe("hooks", () => {
}
class MyComponent extends Component<any, any> {
static template = xml`<div>hey</div>`;
constructor(env) {
super(env);
constructor() {
super();
useMyHook();
}
mounted() {
@@ -137,7 +139,7 @@ describe("hooks", () => {
steps.push("comp:willunmount");
}
}
const component = new MyComponent(env);
const component = new MyComponent();
await component.mount(fixture);
expect(fixture.innerHTML).toBe("<div>hey</div>");
component.unmount();
@@ -157,8 +159,8 @@ describe("hooks", () => {
}
class MyComponent extends Component<any, any> {
static template = xml`<div>hey</div>`;
constructor(env) {
super(env);
constructor(parent, props) {
super(parent, props);
useMyHook();
}
mounted() {
@@ -175,7 +177,7 @@ describe("hooks", () => {
state = useState({ flag: true });
}
const parent = new Parent(env);
const parent = new Parent();
await parent.mount(fixture);
expect(fixture.innerHTML).toBe("<div><div>hey</div></div>");
parent.state.flag = false;
@@ -197,13 +199,13 @@ describe("hooks", () => {
}
class MyComponent extends Component<any, any> {
static template = xml`<div>hey</div>`;
constructor(env) {
super(env);
constructor() {
super();
useMyHook(1);
useMyHook(2);
}
}
const component = new MyComponent(env);
const component = new MyComponent();
await component.mount(fixture);
expect(fixture.innerHTML).toBe("<div>hey</div>");
component.unmount();
@@ -226,7 +228,7 @@ describe("hooks", () => {
(this.button.el as HTMLButtonElement).innerHTML = String(this.value);
}
}
const counter = new Counter(env);
const counter = new Counter();
await counter.mount(fixture);
expect(fixture.innerHTML).toBe("<div><button>0</button></div>");
counter.increment();
@@ -247,7 +249,7 @@ describe("hooks", () => {
expect(this.spanRef.el).toBeNull();
}
}
const component = new TestRef(env);
const component = new TestRef();
await component.mount(fixture);
expect(fixture.innerHTML).toBe("<div><span>owl</span></div>");
component.state.flag = false;
@@ -270,13 +272,13 @@ describe("hooks", () => {
static template = xml`<div><t t-if="state.flag">hey</t></div>`;
state = useState({ flag: true });
constructor(env) {
super(env);
constructor() {
super();
useMyHook();
}
}
const component = new MyComponent(env);
const component = new MyComponent();
await component.mount(fixture);
expect(component).not.toHaveProperty("patched");
expect(component).not.toHaveProperty("willPatch");
@@ -304,8 +306,8 @@ describe("hooks", () => {
static template = xml`<div><t t-if="state.flag">hey</t></div>`;
state = useState({ flag: true });
constructor(env) {
super(env);
constructor() {
super();
useMyHook();
}
willPatch() {
@@ -315,7 +317,7 @@ describe("hooks", () => {
steps.push("comp:patched");
}
}
const component = new MyComponent(env);
const component = new MyComponent();
await component.mount(fixture);
expect(fixture.innerHTML).toBe("<div>hey</div>");
component.state.flag = false;
@@ -337,13 +339,13 @@ describe("hooks", () => {
class MyComponent extends Component<any, any> {
static template = xml`<div>hey<t t-esc="state.value"/></div>`;
state = useState({ value: 1 });
constructor(env) {
super(env);
constructor() {
super();
useMyHook(1);
useMyHook(2);
}
}
const component = new MyComponent(env);
const component = new MyComponent();
await component.mount(fixture);
expect(fixture.innerHTML).toBe("<div>hey1</div>");
component.state.value++;
@@ -377,13 +379,13 @@ describe("hooks", () => {
<input t-ref="input2"/>
</div>`;
constructor(env) {
super(env);
constructor() {
super();
useAutofocus("input2");
}
}
const component = new SomeComponent(env);
const component = new SomeComponent();
await component.mount(fixture);
expect(fixture.innerHTML).toBe("<div><input><input></div>");
const input2 = fixture.querySelectorAll("input")[1];
@@ -399,13 +401,13 @@ describe("hooks", () => {
</div>`;
state = useState({ flag: false });
constructor(env) {
super(env);
constructor() {
super();
useAutofocus("input2");
}
}
const component = new SomeComponent(env);
const component = new SomeComponent();
await component.mount(fixture);
expect(fixture.innerHTML).toBe("<div><input></div>");
expect(document.activeElement).toBe(document.body);
@@ -420,12 +422,12 @@ describe("hooks", () => {
test("can use sub env", async () => {
class TestComponent extends Component<any, any> {
static template = xml`<div><t t-esc="env.val"/></div>`;
constructor(env) {
super(env);
constructor() {
super();
useSubEnv({ val: 3 });
}
}
const component = new TestComponent(env);
const component = new TestComponent();
await component.mount(fixture);
expect(fixture.innerHTML).toBe("<div>3</div>");
expect(env).not.toHaveProperty("val");
@@ -435,8 +437,8 @@ describe("hooks", () => {
test("parent and child env", async () => {
class Child extends Component<any, any> {
static template = xml`<div><t t-esc="env.val"/></div>`;
constructor(env) {
super(env);
constructor(parent, props) {
super(parent, props);
useSubEnv({ val: 5 });
}
}
@@ -444,12 +446,12 @@ describe("hooks", () => {
class Parent extends Component<any, any> {
static template = xml`<div><t t-esc="env.val"/><Child/></div>`;
static components = { Child };
constructor(env) {
super(env);
constructor() {
super();
useSubEnv({ val: 3 });
}
}
const component = new Parent(env);
const component = new Parent();
await component.mount(fixture);
expect(fixture.innerHTML).toBe("<div>3<div>5</div></div>");
});
@@ -478,7 +480,7 @@ describe("hooks", () => {
state = useState({ value: 1 });
}
const app = new App(env);
const app = new App();
await app.mount(fixture);
expect(app).not.toHaveProperty("willStart");
expect(app).not.toHaveProperty("willUpdateProps");
+7 -7
View File
@@ -1,8 +1,9 @@
import { AsyncRoot } from "../../src/misc/async_root";
import { config } from "../../src/config";
import { useState } from "../../src/hooks";
import { xml } from "../../src/tags";
import { makeDeferred, makeTestFixture, makeTestEnv, nextTick } from "../helpers";
import { Env, Component } from "../../src/component/component";
import { Component } from "../../src/component/component";
//------------------------------------------------------------------------------
// Setup and helpers
@@ -11,14 +12,13 @@ import { Env, Component } from "../../src/component/component";
// We create before each test:
// - fixture: a div, appended to the DOM, intended to be the target of dom
// manipulations. Note that it is removed after each test.
// - env: a WEnv, necessary to create new components
// - a test env, necessary to create components, that is set as env
let fixture: HTMLElement;
let env: Env;
beforeEach(() => {
fixture = makeTestFixture();
env = makeTestEnv();
config.env = makeTestEnv();
});
afterEach(() => {
@@ -55,7 +55,7 @@ describe("Asyncroot", () => {
}
}
const parent = new Parent(env);
const parent = new Parent();
await parent.mount(fixture);
expect(fixture.querySelector(".children")!.innerHTML).toBe("<span>0</span><span>0</span>");
@@ -103,7 +103,7 @@ describe("Asyncroot", () => {
}
}
const parent = new Parent(env);
const parent = new Parent();
await parent.mount(fixture);
expect(fixture.querySelector(".children")!.innerHTML).toBe("<span>0</span><span>0</span>");
@@ -158,7 +158,7 @@ describe("Asyncroot", () => {
}
}
const parent = new Parent(env);
const parent = new Parent();
await parent.mount(fixture);
expect(fixture.querySelector(".children")!.innerHTML).toBe("<span>0/0</span><span>0/0</span>");
+4 -2
View File
@@ -1,4 +1,5 @@
import { Component } from "../../src/component/component";
import { config } from "../../src/config";
import { Link } from "../../src/router/link";
import { RouterEnv } from "../../src/router/router";
import { makeTestEnv, makeTestFixture, nextTick } from "../helpers";
@@ -12,6 +13,7 @@ describe("Link component", () => {
beforeEach(() => {
fixture = makeTestFixture();
env = <RouterEnv>makeTestEnv();
config.env = env;
});
afterEach(() => {
@@ -38,7 +40,7 @@ describe("Link component", () => {
router = new TestRouter(env, routes, { mode: "history" });
router.navigate({ to: "users" });
const app = new App(env);
const app = new App();
await app.mount(fixture);
expect(fixture.innerHTML).toBe('<div><a href="/about">About</a></div>');
@@ -69,7 +71,7 @@ describe("Link component", () => {
router = new TestRouter(env, routes, { mode: "history" });
router.navigate({ to: "users" });
const app = new App(env);
const app = new App();
await app.mount(fixture);
expect(window.location.pathname).toBe("/users");
+5 -3
View File
@@ -1,4 +1,5 @@
import { Component } from "../../src/component/component";
import { config } from "../../src/config";
import { RouterEnv } from "../../src/router/router";
import { RouteComponent } from "../../src/router/route_component";
import { makeTestEnv, makeTestFixture, nextTick } from "../helpers";
@@ -12,6 +13,7 @@ describe("RouteComponent", () => {
beforeEach(() => {
fixture = makeTestFixture();
env = <RouterEnv>makeTestEnv();
config.env = env;
});
afterEach(() => {
@@ -45,7 +47,7 @@ describe("RouteComponent", () => {
router = new TestRouter(env, routes, { mode: "history" });
await router.navigate({ to: "about" });
const app = new App(env);
const app = new App();
await app.mount(fixture);
expect(fixture.innerHTML).toBe("<div><span>About</span></div>");
@@ -72,7 +74,7 @@ describe("RouteComponent", () => {
const routes = [{ name: "book", path: "/book/{{title}}", component: Book }];
router = new TestRouter(env, routes, { mode: "history" });
await router.navigate({ to: "book", params: { title: "1984" } });
const app = new App(env);
const app = new App();
await app.mount(fixture);
expect(fixture.innerHTML).toBe("<div><span>Book 1984</span></div>");
});
@@ -98,7 +100,7 @@ describe("RouteComponent", () => {
const routes = [{ name: "book", path: "/book/{{title}}/{{val.number}}", component: Book }];
router = new TestRouter(env, routes, { mode: "history" });
await router.navigate({ to: "book", params: { title: "1984", val: "123" } });
const app = new App(env);
const app = new App();
await app.mount(fixture);
expect(fixture.innerHTML).toBe("<div><span>Book 1984|124</span></div>");
});
+26 -22
View File
@@ -1,4 +1,5 @@
import { Component, Env } from "../src/component/component";
import { config } from "../src/config";
import { Store, useStore, useDispatch, useGetters } from "../src/store";
import { useState } from "../src/hooks";
import { xml } from "../src/tags";
@@ -12,6 +13,7 @@ describe("connecting a component to store", () => {
beforeEach(() => {
fixture = makeTestFixture();
env = makeTestEnv();
config.env = env;
});
afterEach(() => {
@@ -37,7 +39,7 @@ describe("connecting a component to store", () => {
}
(<any>env).store = store;
const app = new App(env);
const app = new App();
await app.mount(fixture);
expect(fixture.innerHTML).toBe("<div></div>");
@@ -69,7 +71,7 @@ describe("connecting a component to store", () => {
App.prototype.__render = jest.fn(App.prototype.__render);
(<any>env).store = store;
const app = new App(env);
const app = new App();
await app.mount(fixture);
expect(fixture.innerHTML).toBe("<div><span>1</span><span>2</span></div>");
@@ -101,7 +103,7 @@ describe("connecting a component to store", () => {
App.prototype.__render = jest.fn(App.prototype.__render);
(<any>env).store = store;
const app = new App(env);
const app = new App();
await app.mount(fixture);
expect(fixture.innerHTML).toBe("<div></div>");
@@ -134,7 +136,7 @@ describe("connecting a component to store", () => {
}
(<any>env).store = store;
const app = new App(env);
const app = new App();
await app.mount(fixture);
expect(fixture.innerHTML).toBe("<div>0</div>");
@@ -163,7 +165,7 @@ describe("connecting a component to store", () => {
App.prototype.__render = jest.fn(App.prototype.__render);
(<any>env).store = store;
const app = new App(env);
const app = new App();
await app.mount(fixture);
expect(fixture.innerHTML).toBe("<div>0</div>");
@@ -198,7 +200,7 @@ describe("connecting a component to store", () => {
todos = useStore(state => state.todos, { store });
}
const app = new App(env);
const app = new App();
await app.mount(fixture);
expect(fixture.innerHTML).toBe("<div></div>");
@@ -228,7 +230,7 @@ describe("connecting a component to store", () => {
storeState = useStore(state => state);
dispatch = useDispatch();
}
const app = new App(env);
const app = new App();
await app.mount(fixture);
expect(fixture.innerHTML).toBe("<div><button>Inc</button><span>1</span></div>");
@@ -257,7 +259,7 @@ describe("connecting a component to store", () => {
}
(<any>env).store = store;
const app = new App(env);
const app = new App();
await app.mount(fixture);
expect(fixture.innerHTML).toBe("<div><span>jupiler</span></div>");
@@ -294,7 +296,7 @@ describe("connecting a component to store", () => {
}
(<any>env).store = store;
const app = new TodoList(env);
const app = new TodoList();
await app.mount(fixture);
expect(fixture.innerHTML).toBe("<div><span>jupiler</span></div>");
@@ -347,7 +349,7 @@ describe("connecting a component to store", () => {
}
(<any>env).store = store;
const app = new TodoList(env);
const app = new TodoList();
await app.mount(fixture);
expect(fixture.innerHTML).toBe(
@@ -370,7 +372,7 @@ describe("connecting a component to store", () => {
const state = { beers: { 1: { name: "jupiler" }, 2: { name: "kwak" } } };
const store = new Store({ state });
(<any>env).store = store;
const app = new App(env);
const app = new App();
await app.mount(fixture);
expect(fixture.innerHTML).toBe("<div><span>jupiler</span></div>");
@@ -401,7 +403,7 @@ describe("connecting a component to store", () => {
const store = new Store({ state, actions });
(<any>env).store = store;
const app = new App(env);
const app = new App();
await app.mount(fixture);
expect(fixture.innerHTML).toBe("<div><span>jupiler</span></div>");
@@ -446,7 +448,7 @@ describe("connecting a component to store", () => {
};
const store = new Store({ state, actions });
(<any>env).store = store;
const app = new App(env);
const app = new App();
await app.mount(fixture);
expect(fixture.innerHTML).toBe("<div><div><span>taster:aaron</span></div></div>");
@@ -513,7 +515,7 @@ describe("connecting a component to store", () => {
};
const store = new Store({ state, actions });
(<any>env).store = store;
const app = new App(env);
const app = new App();
await app.mount(fixture);
expect(fixture.innerHTML).toBe("<div><div><span>taster:aaron</span></div></div>");
@@ -588,7 +590,7 @@ describe("connecting a component to store", () => {
const store = new Store({ state, actions });
(<any>env).store = store;
const app = new Parent(env);
const app = new Parent();
await app.mount(fixture);
expect(fixture.innerHTML).toBe("<div><span>a</span></div>");
@@ -639,7 +641,7 @@ describe("connecting a component to store", () => {
const store = new Store({ state, actions });
(<any>env).store = store;
const app = new Parent(env);
const app = new Parent();
await app.mount(fixture);
expect(fixture.innerHTML).toBe("<div><span>abc</span></div>");
@@ -713,7 +715,7 @@ describe("connecting a component to store", () => {
}
(<any>env).store = store;
const app = new TodoApp(env);
const app = new TodoApp();
await app.mount(fixture);
expect(fixture.innerHTML).toBe(
@@ -787,7 +789,7 @@ describe("connecting a component to store", () => {
}
(<any>env).store = store;
const app = new TodoApp(env);
const app = new TodoApp();
await app.mount(fixture);
expect(fixture.innerHTML).toBe(
@@ -833,7 +835,7 @@ describe("connecting a component to store", () => {
const store = new Store({ state, actions });
(<any>env).store = store;
const app = new App(env);
const app = new App();
await app.mount(fixture);
expect(fixture.innerHTML).toBe("<div>a</div>");
@@ -870,7 +872,7 @@ describe("connecting a component to store", () => {
}
const store = new TestStore({ state: { val: 1 } });
(<any>env).store = store;
const parent = new Parent(env);
const parent = new Parent();
await parent.mount(fixture);
expect(steps).toEqual(["on:update"]);
@@ -901,7 +903,7 @@ describe("connecting a component to store", () => {
const store = new Store({ state, actions });
(<any>env).store = store;
const app = new App(env);
const app = new App();
await app.mount(fixture);
expect(fixture.innerHTML).toBe("<div>0</div>");
@@ -920,6 +922,7 @@ describe("various scenarios", () => {
beforeEach(() => {
fixture = makeTestFixture();
env = makeTestEnv();
config.env = env;
});
afterEach(() => {
@@ -978,7 +981,8 @@ describe("various scenarios", () => {
}
(<any>env).store = store;
const message = new Message(env);
const message = new Message();
await message.mount(fixture);
expect(fixture.innerHTML).toMatchSnapshot();
+2 -2
View File
@@ -145,10 +145,10 @@ class App extends owl.Component {
//------------------------------------------------------------------------------
async function start() {
const templates = await owl.utils.loadFile("templates.xml");
const env = {
owl.config.env = {
qweb: new owl.QWeb({ templates })
};
const app = new App(env);
const app = new App();
app.mount(document.body);
}
+11 -2
View File
@@ -90,7 +90,14 @@ function makeCodeIframe(js, css, xml, errorHandler) {
owlScript.addEventListener("load", () => {
const script = doc.createElement("script");
script.type = "text/javascript";
const content = `owl.__info__.mode = 'dev';\nwindow.TEMPLATES = \`${sanitizedXML}\`;\n${js}`;
const content = `
{
owl.__info__.mode = 'dev';
let templates = \`${sanitizedXML}\`;
const qweb = new owl.QWeb({ templates });
owl.config.env = { qweb };
}
${js}`;
script.innerHTML = content;
iframe.contentWindow.addEventListener("error", errorHandler);
iframe.contentWindow.addEventListener("unhandledrejection", errorHandler);
@@ -436,7 +443,9 @@ async function start() {
owl.utils.whenReady()
]);
const qweb = new owl.QWeb({ templates });
const app = new App({ qweb });
debugger;
owl.config.env = { qweb };
const app = new App();
app.mount(document.body);
}
+27 -42
View File
@@ -16,9 +16,7 @@ class App extends Component {
App.components = { Greeter };
// Application setup
// Note that the xml templates are injected into the global TEMPLATES variable.
const qweb = new owl.QWeb({ templates: TEMPLATES});
const app = new App({ qweb });
const app = new App();
app.mount(document.body);
`;
@@ -70,8 +68,7 @@ class App extends Component {
}
App.components = { Counter };
const qweb = new owl.QWeb({ templates: TEMPLATES});
const app = new App({qweb});
const app = new App();
app.mount(document.body);
`;
@@ -228,8 +225,7 @@ class App extends Component {
}
App.components = { DemoComponent };
const qweb = new owl.QWeb({ templates: TEMPLATES});
const app = new App({ qweb });
const app = new App();
app.mount(document.body);
`;
@@ -298,8 +294,7 @@ class App extends owl.Component {
}
// Application setup
const qweb = new owl.QWeb({ templates: TEMPLATES});
const app = new App({ qweb });
const app = new App();
app.mount(document.body);
`;
@@ -349,11 +344,9 @@ const themeContext = new Context({
background: '#000',
foreground: '#fff',
});
const env = {
qweb: new owl.QWeb({ templates: TEMPLATES}),
themeContext: themeContext,
};
const app = new App(env);
// Add the themeContext the environment to make it available to all components
owl.config.env.themeContext = themeContext;
const app = new App();
app.mount(document.body);
`;
@@ -534,26 +527,25 @@ TodoApp.components = { TodoItem };
//------------------------------------------------------------------------------
// App Initialization
//------------------------------------------------------------------------------
function saveState(state) {
const str = JSON.stringify(state);
window.localStorage.setItem(LOCALSTORAGE_KEY, str);
}
function loadState() {
const localState = window.localStorage.getItem(LOCALSTORAGE_KEY);
return localState ? JSON.parse(localState) : initialState;
}
function makeStore() {
function saveState(state) {
const str = JSON.stringify(state);
window.localStorage.setItem(LOCALSTORAGE_KEY, str);
}
function loadState() {
const localState = window.localStorage.getItem(LOCALSTORAGE_KEY);
return localState ? JSON.parse(localState) : initialState;
}
function makeEnv() {
const state = loadState();
const store = new owl.Store({ state, actions });
store.on("update", null, () => saveState(store.state));
const qweb = new owl.QWeb({ templates: TEMPLATES});
return { qweb, store };
return store;
}
const env = makeEnv();
const app = new TodoApp(env);
owl.config.env.store = makeStore();
const app = new TodoApp();
app.mount(document.body);
`;
@@ -1040,12 +1032,9 @@ function setupResponsivePlugin(env) {
//------------------------------------------------------------------------------
// Application Startup
//------------------------------------------------------------------------------
const env = {
qweb: new owl.QWeb({ templates: TEMPLATES}),
};
setupResponsivePlugin(env);
setupResponsivePlugin(owl.config.env);
const app = new App(env);
const app = new App();
app.mount(document.body);
`;
@@ -1187,8 +1176,7 @@ class App extends Component {
App.components = {Card, Counter};
// Application setup
const qweb = new owl.QWeb({ templates: TEMPLATES});
const app = new App({ qweb });
const app = new App();
app.mount(document.body);`;
const SLOTS_XML = `<templates>
@@ -1301,10 +1289,9 @@ class App extends Component {
}, 3000);
}
}
App.components = {SlowComponent, NotificationList};
App.components = {SlowComponent, NotificationList, AsyncRoot};
const qweb = new owl.QWeb({ templates: TEMPLATES});
const app = new App({ qweb });
const app = new App();
app.mount(document.body);
`;
@@ -1373,8 +1360,7 @@ class Form extends Component {
}
// Application setup
const qweb = new owl.QWeb({ templates: TEMPLATES});
const form = new Form({ qweb });
const form = new Form();
form.mount(document.body);
`;
@@ -1540,7 +1526,6 @@ class App extends Component {
}
App.components = { WindowManager };
const qweb = new owl.QWeb({ templates: TEMPLATES});
const windows = [
{
name: "Hello",
@@ -1558,8 +1543,8 @@ const windows = [
}
];
const env = { qweb, windows };
const app = new App(env);
owl.config.env.windows = windows;
const app = new App();
app.mount(document.body);
`;