Compare commits

..

9 Commits

Author SHA1 Message Date
Géry Debongnie c5a2f52afb [REL] v1.0.9
# v1.0.9

- qweb fix: add support for svg namespace in t-raw
- qweb fix: properly handle subtemplates in shared templates
2020-06-02 10:38:32 +02:00
Michael Mattiello (mcm) 9fd8315c53 [FIX] qweb: support of svg in t-raw
Before this commit, t-raw a string containing `<svg>` did not display
the svg element. This was because of missing namespace in vnode's data.

Now, `vdom.addNS` is called to add the svg namespace when necessary.
2020-05-25 14:02:34 +02:00
Géry Debongnie 85318b3ae6 [FIX] qweb: properly handle subtemplates in shared templates
With QWeb, we can register globally templates (using the xml tag or
the registerTemplate function). However, these templates, once
compiled, can generate sub template compiled functions. Before this
commit, these sub functions were local to a specific instance.

This means that creating a new QWeb instance and rendering a global
parent template would crash, since it was unable to find the actual sub
function.

This commit fixes the issue: the sub functions are now shared
statically, but with a unique ID, so we do not have issues with sub
functions having a same name in different QWeb instance.

closes #701
2020-05-25 11:43:24 +02:00
Géry Debongnie 4b961cbffe [REL] v1.0.8
# v1.0.8

- qweb fix: do not override t-att-class with class attribute in some cases
- component fix: refuse to mount a destroyed component
- hooks type improvement: useRef is now generic, and can capture the type of a component
- qweb fix: t-esc inside t-call using outside t-set
2020-05-18 09:07:31 +02:00
Aaron Bohy 2af8cccd65 [FIX] qweb: t-esc inside t-call using outside t-set
Before this commit, the generated code was incorrect, and crashed,
when there was a t-esc="abc" in a subtemplate, with t-set="abc"
done outside the subtemplate, with syntax <t t-set="abc">value</t>.
2020-05-15 13:47:57 +02:00
Géry Debongnie b8d3618afa [IMP] hooks: improve useRef typings
With this commit, we can get the type for the target component for a
ref.

By default, if the generic type is not given, the ref will simply use
the base Component type.
2020-04-30 16:23:36 +02:00
Géry Debongnie 559fadb62a [FIX] component: properly handle mounting a destroyed component
part of #685
2020-04-22 09:29:39 +02:00
Géry Debongnie c36333dbbc [CLEANUP] update prettier to v2.0.4 2020-04-21 16:23:31 +02:00
Géry Debongnie b5c3422b4d [FIX] qweb: do not override t-att-class with class
There was some code in qweb to make sure that we support setting class
and t-att-class on the same html element:

<div class="some class" t-att-class="{b: true}">...</div>

But the code did not work in the other direction:

<div t-att-class="{b: true}" class="some class">...</div>

With this commit, we just add the missing if statement

closes #664
2020-04-21 16:23:31 +02:00
66 changed files with 863 additions and 775 deletions
+2 -2
View File
@@ -121,8 +121,8 @@ npm install @odoo/owl
If you want to use a simple `<script>` tag, the last release can be downloaded here:
- [owl-1.0.7.js](https://github.com/odoo/owl/releases/download/v1.0.7/owl.js)
- [owl-1.0.7.min.js](https://github.com/odoo/owl/releases/download/v1.0.7/owl.min.js)
- [owl-1.0.9.js](https://github.com/odoo/owl/releases/download/v1.0.9/owl.js)
- [owl-1.0.9.min.js](https://github.com/odoo/owl/releases/download/v1.0.9/owl.min.js)
## License
+1 -1
View File
@@ -111,7 +111,7 @@ class Parent extends Component {
static components = { OrderLine };
orders = useState([
{ id: 1, name: "Coffee", quantity: 0 },
{ id: 2, name: "Tea", quantity: 0 }
{ id: 2, name: "Tea", quantity: 0 },
]);
addToOrder(event) {
+11 -11
View File
@@ -281,7 +281,7 @@ import { Component } from "@odoo/owl";
import "regenerator-runtime/runtime";
export async function nextTick() {
return new Promise(function(resolve) {
return new Promise(function (resolve) {
setTimeout(() => Component.scheduler.requestAnimationFrame(() => resolve()));
});
}
@@ -361,26 +361,26 @@ const HtmlWebpackPlugin = require("html-webpack-plugin");
const host = process.env.HOST || "localhost";
module.exports = function(env, argv) {
module.exports = function (env, argv) {
const mode = argv.mode || "development";
return {
mode: mode,
entry: "./src/main.js",
output: {
filename: "main.js",
path: path.resolve(__dirname, "dist")
path: path.resolve(__dirname, "dist"),
},
module: {
rules: [
{
test: /\.jsx?$/,
loader: "babel-loader",
exclude: /node_modules/
}
]
exclude: /node_modules/,
},
],
},
resolve: {
extensions: [".js", ".jsx"]
extensions: [".js", ".jsx"],
},
devServer: {
contentBase: path.resolve(__dirname, "public/index.html"),
@@ -388,14 +388,14 @@ module.exports = function(env, argv) {
hot: true,
host,
port: 3000,
publicPath: "/"
publicPath: "/",
},
plugins: [
new HtmlWebpackPlugin({
inject: true,
template: path.resolve(__dirname, "public/index.html")
})
]
template: path.resolve(__dirname, "public/index.html"),
}),
],
};
};
```
+19 -19
View File
@@ -62,7 +62,7 @@ our application. `app.js` is where we will write all our code. For now, let's
just put the following code:
```js
(function() {
(function () {
console.log("hello owl", owl.__info__.version);
})();
```
@@ -170,13 +170,13 @@ class App extends Component {
{
id: 1,
title: "buy milk",
isCompleted: true
isCompleted: true,
},
{
id: 2,
title: "clean house",
isCompleted: false
}
isCompleted: false,
},
];
}
```
@@ -562,23 +562,23 @@ const actions = {
const task = {
id: state.nextId++,
title: title,
isCompleted: false
isCompleted: false,
};
state.tasks.push(task);
}
},
toggleTask({ state }, id) {
const task = state.tasks.find(t => t.id === id);
const task = state.tasks.find((t) => t.id === id);
task.isCompleted = !task.isCompleted;
},
deleteTask({ state }, id) {
const index = state.tasks.findIndex(t => t.id === id);
const index = state.tasks.findIndex((t) => t.id === id);
state.tasks.splice(index, 1);
}
},
};
const initialState = {
nextId: 1,
tasks: []
tasks: [],
};
// -------------------------------------------------------------------------
@@ -616,7 +616,7 @@ class App extends Component {
static components = { Task };
inputRef = useRef("add-input");
tasks = useStore(state => state.tasks);
tasks = useStore((state) => state.tasks);
dispatch = useDispatch();
mounted() {
@@ -811,7 +811,7 @@ For reference, here is the final code:
```
```js
(function() {
(function () {
const { Component, Store } = owl;
const { xml } = owl.tags;
const { whenReady } = owl.utils;
@@ -827,24 +827,24 @@ For reference, here is the final code:
const task = {
id: state.nextId++,
title: title,
isCompleted: false
isCompleted: false,
};
state.tasks.push(task);
}
},
toggleTask({ state }, id) {
const task = state.tasks.find(t => t.id === id);
const task = state.tasks.find((t) => t.id === id);
task.isCompleted = !task.isCompleted;
},
deleteTask({ state }, id) {
const index = state.tasks.findIndex(t => t.id === id);
const index = state.tasks.findIndex((t) => t.id === id);
state.tasks.splice(index, 1);
}
},
};
const initialState = {
nextId: 1,
tasks: []
tasks: [],
};
// -------------------------------------------------------------------------
@@ -897,7 +897,7 @@ For reference, here is the final code:
static components = { Task };
inputRef = useRef("add-input");
tasks = useStore(state => state.tasks);
tasks = useStore((state) => state.tasks);
filter = useState({ value: "all" });
dispatch = useDispatch();
@@ -916,9 +916,9 @@ For reference, here is the final code:
get displayedTasks() {
switch (this.filter.value) {
case "active":
return this.tasks.filter(t => !t.isCompleted);
return this.tasks.filter((t) => !t.isCompleted);
case "completed":
return this.tasks.filter(t => t.isCompleted);
return this.tasks.filter((t) => t.isCompleted);
case "all":
return this.tasks;
}
+3 -3
View File
@@ -257,11 +257,11 @@ to the store like in redux, with the `useStore` hook (see the [store documentati
const actions = {
increment({ state }, val) {
state.counter.value += val;
}
},
};
const state = {
counter: { value: 0 }
counter: { value: 0 },
};
const store = new owl.Store({ state, actions });
@@ -270,7 +270,7 @@ class Counter extends Component {
<button t-name="Counter" t-on-click="dispatch('increment')">
Click Me! [<t t-esc="counter.value"/>]
</button>`;
counter = useStore(state => state.counter);
counter = useStore((state) => state.counter);
dispatch = useDispatch();
}
+4 -4
View File
@@ -204,7 +204,7 @@ to be called in the constructor.
class Counter extends owl.Component {
static props = {
initialValue: Number,
optional: true
optional: true,
};
}
```
@@ -217,7 +217,7 @@ to be called in the constructor.
```js
class Counter extends owl.Component {
static defaultProps = {
initialValue: 0
initialValue: 0,
};
}
```
@@ -804,8 +804,8 @@ class RootNode extends Component {
children: [
{ label: "b" },
{ label: "c", children: [{ label: "d" }, { label: "e" }] },
{ label: "f", children: [{ label: "g" }] }
]
{ label: "f", children: [{ label: "g" }] },
],
};
}
```
+2 -2
View File
@@ -113,10 +113,10 @@ async function myEnv() {
qweb: qweb,
services: {
localStorage: localStorage,
rpc: rpc
rpc: rpc,
},
debug: false,
inMobileMode: true
inMobileMode: true,
};
}
+1 -1
View File
@@ -74,7 +74,7 @@ broadcasted to the application by an event on the `qweb` instance. It may be
useful, for example, to log the error somewhere.
```js
env.qweb.on("error", null, function(error) {
env.qweb.on("error", null, function (error) {
// do something
// react to the error
});
+1 -1
View File
@@ -7,7 +7,7 @@ triggering events, and callbacks.
```js
const bus = new owl.core.EventBus();
bus.on("some-event", null, function(...args) {
bus.on("some-event", null, function (...args) {
console.log(...args);
});
+1 -1
View File
@@ -259,7 +259,7 @@ function useLoader() {
}
onWillStart(() => updateRecord(component.props.id));
onWillUpdateProps(nextProps => updateRecord(nextProps.id));
onWillUpdateProps((nextProps) => updateRecord(nextProps.id));
return record;
}
+2 -2
View File
@@ -114,9 +114,9 @@ For example:
const translations = {
hello: "bonjour",
yes: "oui",
no: "non"
no: "non",
};
const translateFn = str => translations[str] || str;
const translateFn = (str) => translations[str] || str;
const qweb = new QWeb({ translateFn });
```
+14 -14
View File
@@ -40,14 +40,14 @@ const actions = {
state.todos.push({
id: state.nextId++,
message,
isCompleted: false
isCompleted: false,
});
}
},
};
const state = {
todos: [],
nextId: 1
nextId: 1,
};
const store = new owl.Store({ state, actions });
@@ -90,7 +90,7 @@ const config = {
state,
actions,
getters,
env
env,
};
const store = new Store(config);
```
@@ -110,7 +110,7 @@ const actions = {
} catch (e) {
state.loginState = "error";
}
}
},
};
```
@@ -144,7 +144,7 @@ const actions = {
state.recordId = recordId;
const data = await doSomeRPC("/read/", recordId);
state.recordData = data;
}
},
};
```
@@ -159,7 +159,7 @@ const actions = {
const data = await doSomeRPC("/read/", recordId);
state.recordId = recordId;
state.recordData = data;
}
},
};
```
@@ -187,14 +187,14 @@ transform the data contained in the store.
```js
const getters = {
getPost({ state }, id) {
const post = state.posts.find(p => p.id === id);
const author = state.authors.find(a => a.id === post.id);
const post = state.posts.find((p) => p.id === id);
const author = state.authors.find((a) => a.id === post.id);
return {
id,
author,
content: post.content
content: post.content,
};
}
},
};
// somewhere else
@@ -224,11 +224,11 @@ Assume we have this store:
const actions = {
increment({ state }, val) {
state.counter.value += val;
}
},
};
const state = {
counter: { value: 0 }
counter: { value: 0 },
};
const store = new owl.Store({ state, actions });
```
@@ -245,7 +245,7 @@ A counter component can then select this value and dispatch an action like this:
```js
class Counter extends Component {
counter = useStore(state => state.counter);
counter = useStore((state) => state.counter);
dispatch = useDispatch();
}
+2 -2
View File
@@ -19,7 +19,7 @@ not ready yet, resolved directly otherwise). If called with a callback as
argument, it executes it as soon as the DOM ready (or directly).
```js
Promise.all([loadFile("templates.xml"), owl.utils.whenReady()]).then(function([templates]) {
Promise.all([loadFile("templates.xml"), owl.utils.whenReady()]).then(function ([templates]) {
const qweb = new owl.QWeb({ templates });
const app = new App({ qweb });
app.mount(document.body);
@@ -29,7 +29,7 @@ Promise.all([loadFile("templates.xml"), owl.utils.whenReady()]).then(function([t
or alternatively:
```js
owl.utils.whenReady(function() {
owl.utils.whenReady(function () {
const qweb = new owl.QWeb();
const app = new App({ qweb });
app.mount(document.body);
+4 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@odoo/owl",
"version": "1.0.7",
"version": "1.0.9",
"description": "Odoo Web Library (OWL)",
"main": "dist/owl.js",
"types": "dist/types/index.d.ts",
@@ -49,7 +49,7 @@
"jest-environment-jsdom": "^24.7.1",
"live-server": "^1.2.1",
"npm-run-all": "^4.1.5",
"prettier": "^1.19.1",
"prettier": "^2.0.4",
"rollup": "^1.6.0",
"rollup-plugin-typescript2": "^0.20.1",
"sass": "^1.16.1",
@@ -78,6 +78,7 @@
]
},
"prettier": {
"printWidth": 100
"printWidth": 100,
"endOfLine": "auto"
}
}
+1 -1
View File
@@ -1,6 +1,6 @@
# 🦉 OWL Roadmap 🦉
- Current version: 1.0.7
- Current version: 1.0.9
- Status: stable
This roadmap is only an attempt at predicting Owl's future. Everything may
+1 -1
View File
@@ -19,5 +19,5 @@ export const browser: Browser = {
random: Math.random,
Date: window.Date,
fetch: (window.fetch || (() => {})).bind(window),
localStorage: window.localStorage
localStorage: window.localStorage,
};
+7 -4
View File
@@ -203,7 +203,7 @@ export class Component<Props extends {} = any, T extends Env = Env> {
renderFn: qweb.render.bind(qweb, template),
classObj: null,
refs: null,
scope: null
scope: null,
};
if (constr.style) {
this.__applyStyles(constr);
@@ -316,6 +316,9 @@ export class Component<Props extends {} = any, T extends Env = Env> {
return Promise.resolve();
}
}
if (__owl__.isDestroyed) {
throw new Error("Cannot mount a destroyed component");
}
if (__owl__.currentFiber) {
const currentFiber = __owl__.currentFiber;
if (currentFiber.target === target && currentFiber.position === position) {
@@ -514,7 +517,7 @@ export class Component<Props extends {} = any, T extends Env = Env> {
const ev = new OwlEvent(component, eventType, {
bubbles: true,
cancelable: true,
detail: payload
detail: payload,
});
const triggerHook = this.env[portalSymbol as any];
if (triggerHook) {
@@ -549,7 +552,7 @@ export class Component<Props extends {} = any, T extends Env = Env> {
}
await Promise.all([
this.willUpdateProps(nextProps),
__owl__.willUpdatePropsCB && __owl__.willUpdatePropsCB(nextProps)
__owl__.willUpdatePropsCB && __owl__.willUpdatePropsCB(nextProps),
]);
if (fiber.isCompleted) {
return;
@@ -646,7 +649,7 @@ export class Component<Props extends {} = any, T extends Env = Env> {
try {
let vnode = __owl__.renderFn!(this, {
handlers: __owl__.boundHandlers,
fiber: fiber
fiber: fiber,
});
// we iterate over the children to detect those that no longer belong to the
// current rendering: those ones, if not mounted yet, can (and have to) be
+6 -6
View File
@@ -7,7 +7,7 @@ import { makeHandlerCode, MODS_CODE } from "../qweb/extensions";
//------------------------------------------------------------------------------
const T_COMPONENT_MODS_CODE = Object.assign({}, MODS_CODE, {
self: "if (e.target !== vn.elm) {return}"
self: "if (e.target !== vn.elm) {return}",
});
QWeb.utils.defineProxy = function defineProxy(target, source) {
@@ -18,7 +18,7 @@ QWeb.utils.defineProxy = function defineProxy(target, source) {
},
set(val) {
source[k] = val;
}
},
});
}
};
@@ -238,7 +238,7 @@ QWeb.addDirective({
// computing the props string representing the props object
let propStr = Object.keys(props)
.map(k => k + ":" + props[k])
.map((k) => k + ":" + props[k])
.join(",");
let componentID = ctx.generateID();
@@ -280,7 +280,7 @@ QWeb.addDirective({
let classDef = classAttr
.trim()
.split(/\s+/)
.map(a => `'${a}':true`)
.map((a) => `'${a}':true`)
.join(",");
classObj = `_${ctx.generateID()}`;
ctx.addLine(`let ${classObj} = {${classDef}};`);
@@ -298,7 +298,7 @@ QWeb.addDirective({
}
}
let eventsCode = events
.map(function([name, value]) {
.map(function ([name, value]) {
const capture = name.match(/\.capture/);
name = capture ? name.replace(/\.capture/, "") : name;
const { event, handler } = makeHandlerCode(
@@ -469,5 +469,5 @@ QWeb.addDirective({
ctx.addLine(`w${componentID}.__owl__.parentLastFiberId = extra.fiber.id;`);
return true;
}
},
});
+2 -2
View File
@@ -193,7 +193,7 @@ export class Fiber {
// build patchQueue
const patchQueue: Fiber[] = [];
const doWork: (Fiber) => Fiber | null = function(f) {
const doWork: (Fiber) => Fiber | null = function (f) {
patchQueue.push(f);
return f.child;
};
@@ -280,7 +280,7 @@ export class Fiber {
* Cancel a fiber and all its children.
*/
cancel() {
this._walk(f => {
this._walk((f) => {
if (!f.isRendered) {
f.root.counter--;
}
+1 -1
View File
@@ -11,7 +11,7 @@ import { QWeb } from "../qweb/index";
* This is why it is only done in 'dev' mode.
*/
QWeb.utils.validateProps = function(Widget, props: Object) {
QWeb.utils.validateProps = function (Widget, props: Object) {
const propsDef = (<any>Widget).props;
if (propsDef instanceof Array) {
// list of strings (prop names)
+3 -3
View File
@@ -50,7 +50,7 @@ export class Scheduler {
return reject(fiber.error);
}
resolve();
}
},
});
if (!this.isRunning) {
this.start();
@@ -60,7 +60,7 @@ export class Scheduler {
rejectFiber(fiber: Fiber, reason: string) {
fiber = fiber.root;
const index = this.tasks.findIndex(t => t.fiber === fiber);
const index = this.tasks.findIndex((t) => t.fiber === fiber);
if (index >= 0) {
const [task] = this.tasks.splice(index, 1);
fiber.cancel();
@@ -76,7 +76,7 @@ export class Scheduler {
flush() {
let tasks = this.tasks;
this.tasks = [];
tasks = tasks.filter(task => {
tasks = tasks.filter((task) => {
if (task.fiber.isCompleted) {
task.callback();
return false;
+1 -1
View File
@@ -8,7 +8,7 @@
export const STYLESHEETS: { [id: string]: HTMLStyleElement } = {};
export function processSheet(str: string): string {
const tokens = str.split(/(\{|\}|;)/).map(s => s.trim());
const tokens = str.split(/(\{|\}|;)/).map((s) => s.trim());
const selectorStack: string[][] = [];
const parts: string[] = [];
let rules: string[] = [];
+1 -1
View File
@@ -26,5 +26,5 @@ Object.defineProperty(config, "mode", {
} else {
console.log(`Owl is now running in 'prod' mode.`);
}
}
},
});
+6 -6
View File
@@ -79,9 +79,9 @@ export class Context extends EventBus {
async __notifyComponents() {
const rev = ++this.rev;
const subscriptions = this.subscriptions.update;
const groups = partitionBy(subscriptions, s => (s.owner ? s.owner.__owl__.depth : -1));
const groups = partitionBy(subscriptions, (s) => (s.owner ? s.owner.__owl__.depth : -1));
for (let group of groups) {
const proms = group.map(sub => sub.callback.call(sub.owner, rev));
const proms = group.map((sub) => sub.callback.call(sub.owner, rev));
// at this point, each component in the current group has registered a
// top level fiber in the scheduler. It could happen that rendering these
// components is done (if they have no children). This is why we manually
@@ -116,7 +116,7 @@ export function useContextWithCB(ctx: Context, component: Component, method): an
__owl__.observer.notifyCB = component.render.bind(component);
}
const currentCB = __owl__.observer.notifyCB;
__owl__.observer.notifyCB = function() {
__owl__.observer.notifyCB = function () {
if (ctx.rev > mapping[id]) {
// in this case, the context has been updated since we were rendering
// last, and we do not need to render here with the observer. A
@@ -128,18 +128,18 @@ export function useContextWithCB(ctx: Context, component: Component, method): an
mapping[id] = 0;
const renderFn = __owl__.renderFn;
__owl__.renderFn = function(comp, params) {
__owl__.renderFn = function (comp, params) {
mapping[id] = ctx.rev;
return renderFn(comp, params);
};
ctx.on("update", component, async contextRev => {
ctx.on("update", component, async (contextRev) => {
if (mapping[id] < contextRev) {
mapping[id] = contextRev;
await method();
}
});
const __destroy = component.__destroy;
component.__destroy = parent => {
component.__destroy = (parent) => {
ctx.off("update", component);
delete mapping[id];
__destroy.call(component, parent);
+2 -2
View File
@@ -44,7 +44,7 @@ export class EventBus {
}
this.subscriptions[eventType].push({
owner,
callback
callback,
});
}
@@ -54,7 +54,7 @@ export class EventBus {
off(eventType: string, owner: any) {
const subs = this.subscriptions[eventType];
if (subs) {
this.subscriptions[eventType] = subs.filter(s => s.owner !== owner);
this.subscriptions[eventType] = subs.filter((s) => s.owner !== owner);
}
}
+2 -2
View File
@@ -67,14 +67,14 @@ export class Observer {
self.notifyCB();
}
return true;
}
},
});
const metadata = {
value,
proxy,
rev: this.rev,
parent
parent,
};
this.weakMap.set(value, metadata);
+12 -12
View File
@@ -36,11 +36,11 @@ export function useState<T>(state: T): T {
// -----------------------------------------------------------------------------
function makeLifecycleHook(method: string, reverse: boolean = false) {
if (reverse) {
return function(cb) {
return function (cb) {
const component: Component = Component.current!;
if (component.__owl__[method]) {
const current = component.__owl__[method];
component.__owl__[method] = function() {
component.__owl__[method] = function () {
current.call(component);
cb.call(component);
};
@@ -49,11 +49,11 @@ function makeLifecycleHook(method: string, reverse: boolean = false) {
}
};
} else {
return function(cb) {
return function (cb) {
const component: Component = Component.current!;
if (component.__owl__[method]) {
const current = component.__owl__[method];
component.__owl__[method] = function() {
component.__owl__[method] = function () {
cb.call(component);
current.call(component);
};
@@ -65,11 +65,11 @@ function makeLifecycleHook(method: string, reverse: boolean = false) {
}
function makeAsyncHook(method: string) {
return function(cb) {
return function (cb) {
const component: Component = Component.current!;
if (component.__owl__[method]) {
const current = component.__owl__[method];
component.__owl__[method] = function(...args) {
component.__owl__[method] = function (...args) {
return Promise.all([current.call(component, ...args), cb.call(component, ...args)]);
};
} else {
@@ -94,12 +94,12 @@ export const onWillUpdateProps = makeAsyncHook("willUpdatePropsCB");
* The purpose of this hook is to allow components to get a reference to a sub
* html node or component.
*/
interface Ref {
interface Ref<C extends Component = Component> {
el: HTMLElement | null;
comp: Component | null;
comp: C | null;
}
export function useRef(name: string): Ref {
export function useRef<C extends Component = Component>(name: string): Ref<C> {
const __owl__ = Component.current!.__owl__;
return {
get el(): HTMLElement | null {
@@ -111,10 +111,10 @@ export function useRef(name: string): Ref {
}
return null;
},
get comp(): Component | null {
get comp(): C | null {
const val = __owl__.refs && __owl__.refs[name];
return val instanceof Component ? val : null;
}
return val instanceof Component ? (val as C) : null;
},
};
}
+1 -1
View File
@@ -35,6 +35,6 @@ export const hooks = Object.assign({}, _hooks, {
useContext: _context.useContext,
useDispatch: _store.useDispatch,
useGetters: _store.useGetters,
useStore: _store.useStore
useStore: _store.useStore,
});
export const __info__ = {};
+4 -4
View File
@@ -26,8 +26,8 @@ export class Portal extends Component<Props> {
static template = xml`<portal><t t-slot="default"/></portal>`;
static props = {
target: {
type: String
}
type: String,
},
};
// boolean to indicate whether or not we must listen to 'dom-appended' event
@@ -55,12 +55,12 @@ export class Portal extends Component<Props> {
// put a callback in the env that is propagated to children s.t. portal can
// register an handler to those events just before children will trigger them
useSubEnv({
[portalSymbol]: ev => {
[portalSymbol]: (ev) => {
if (!this._handledEvents.has(ev.type)) {
this.portal!.elm!.addEventListener(ev.type, this._handlerTunnel);
this._handledEvents.add(ev.type);
}
}
},
});
}
/**
+22 -15
View File
@@ -49,6 +49,7 @@ function compileValueNode(value: any, node: Element, qweb: QWeb, ctx: Compilatio
if (ctx.escaping) {
let protectID;
if (value.hasBody) {
ctx.rootContext.shouldDefineUtils = true;
protectID = ctx.startProtectScope();
ctx.addLine(
`${exprID} = ${exprID} instanceof utils.VDomArray ? utils.vDomToString(${exprID}) : ${exprID};`
@@ -96,7 +97,7 @@ QWeb.addDirective({
let value = ctx.getValue(node.getAttribute("t-esc")!);
compileValueNode(value, node, qweb, ctx.subContext("escaping", true));
return true;
}
},
});
QWeb.addDirective({
@@ -106,7 +107,7 @@ QWeb.addDirective({
let value = ctx.getValue(node.getAttribute("t-raw")!);
compileValueNode(value, node, qweb, ctx);
return true;
}
},
});
//------------------------------------------------------------------------------
@@ -163,7 +164,7 @@ QWeb.addDirective({
}
}
return true;
}
},
});
//------------------------------------------------------------------------------
@@ -179,7 +180,7 @@ QWeb.addDirective({
},
finalize({ ctx }) {
ctx.closeIf();
}
},
});
QWeb.addDirective({
@@ -195,7 +196,7 @@ QWeb.addDirective({
},
finalize({ ctx }) {
ctx.closeIf();
}
},
});
QWeb.addDirective({
@@ -208,7 +209,7 @@ QWeb.addDirective({
},
finalize({ ctx }) {
ctx.closeIf();
}
},
});
//------------------------------------------------------------------------------
@@ -233,10 +234,12 @@ QWeb.addDirective({
// Step 2: compile target template in sub templates
// ------------------------------------------------
if (!qweb.subTemplates[subTemplate]) {
qweb.subTemplates[subTemplate] = true;
let subId = qweb.subTemplates[subTemplate];
if (!subId) {
subId = QWeb.nextId++;
qweb.subTemplates[subTemplate] = subId;
const subTemplateFn = qweb._compile(subTemplate, nodeTemplate.elem, ctx, true);
qweb.subTemplates[subTemplate] = subTemplateFn;
QWeb.subTemplates[subId] = subTemplateFn;
}
// Step 3: compile t-call body if necessary
@@ -274,12 +277,16 @@ QWeb.addDirective({
const parentNode = ctx.parentNode ? `c${ctx.parentNode}` : "result";
const extra = `Object.assign({}, extra, {parentNode: ${parentNode}, parent: ${parentComponent}, key: ${key}})`;
if (ctx.parentNode) {
ctx.addLine(`this.subTemplates['${subTemplate}'].call(this, ${callingScope}, ${extra});`);
ctx.addLine(
`this.constructor.subTemplates['${subId}'].call(this, ${callingScope}, ${extra});`
);
} else {
// this is a t-call with no parentnode, we need to extract the result
ctx.rootContext.shouldDefineResult = true;
ctx.addLine(`result = []`);
ctx.addLine(`this.subTemplates['${subTemplate}'].call(this, ${callingScope}, ${extra});`);
ctx.addLine(
`this.constructor.subTemplates['${subId}'].call(this, ${callingScope}, ${extra});`
);
ctx.addLine(`result = result[0]`);
}
@@ -292,7 +299,7 @@ QWeb.addDirective({
}
return true;
}
},
});
//------------------------------------------------------------------------------
@@ -353,7 +360,7 @@ QWeb.addDirective({
ctx.addLine("}");
ctx.stopProtectScope(varsID);
return true;
}
},
});
//------------------------------------------------------------------------------
@@ -364,7 +371,7 @@ QWeb.addDirective({
priority: 1,
atNodeEncounter({ ctx }) {
ctx.addLine("debugger;");
}
},
});
//------------------------------------------------------------------------------
@@ -376,5 +383,5 @@ QWeb.addDirective({
atNodeEncounter({ ctx, value }) {
const expr = ctx.formatExpression(value);
ctx.addLine(`console.log(${expr})`);
}
},
});
+2 -2
View File
@@ -162,7 +162,7 @@ export class CompilationContext {
const tokens = compileExprToArray(expr, this.variables);
const done = new Set();
return tokens
.map(tok => {
.map((tok) => {
if (tok.varName) {
if (!done.has(tok.varName)) {
done.add(tok.varName);
@@ -189,7 +189,7 @@ export class CompilationContext {
return `(${this.formatExpression(s.slice(2, -2))})`;
}
let r = s.replace(/\{\{.*?\}\}/g, s => "${" + this.formatExpression(s.slice(2, -2)) + "}");
let r = s.replace(/\{\{.*?\}\}/g, (s) => "${" + this.formatExpression(s.slice(2, -2)) + "}");
return "`" + r + "`";
}
startProtectScope(codeBlock?: boolean): number {
+9 -9
View File
@@ -35,7 +35,7 @@ const WORD_REPLACEMENT = {
gt: ">",
gte: ">=",
lt: "<",
lte: "<="
lte: "<=",
};
export interface QWebVar {
@@ -77,7 +77,7 @@ const STATIC_TOKEN_MAP: { [key: string]: TKind } = {
":": "COLON",
",": "COMMA",
"(": "LEFT_PAREN",
")": "RIGHT_PAREN"
")": "RIGHT_PAREN",
};
// note that the space after typeof is relevant. It makes sure that the formatted
@@ -86,7 +86,7 @@ const OPERATORS = "...,.,===,==,+,!==,!=,!,||,&&,>=,>,<=,<,?,-,*,/,%,typeof ,=>,
type Tokenizer = (expr: string) => Token | false;
let tokenizeString: Tokenizer = function(expr) {
let tokenizeString: Tokenizer = function (expr) {
let s = expr[0];
let start = s;
if (s !== "'" && s !== '"') {
@@ -114,7 +114,7 @@ let tokenizeString: Tokenizer = function(expr) {
return { type: "VALUE", value: s };
};
let tokenizeNumber: Tokenizer = function(expr) {
let tokenizeNumber: Tokenizer = function (expr) {
let s = expr[0];
if (s && s.match(/[0-9]/)) {
let i = 1;
@@ -128,7 +128,7 @@ let tokenizeNumber: Tokenizer = function(expr) {
}
};
let tokenizeSymbol: Tokenizer = function(expr) {
let tokenizeSymbol: Tokenizer = function (expr) {
let s = expr[0];
if (s && s.match(/[a-zA-Z_\$]/)) {
let i = 1;
@@ -145,7 +145,7 @@ let tokenizeSymbol: Tokenizer = function(expr) {
}
};
const tokenizeStatic: Tokenizer = function(expr) {
const tokenizeStatic: Tokenizer = function (expr) {
const char = expr[0];
if (char && char in STATIC_TOKEN_MAP) {
return { type: STATIC_TOKEN_MAP[char], value: char };
@@ -153,7 +153,7 @@ const tokenizeStatic: Tokenizer = function(expr) {
return false;
};
const tokenizeOperator: Tokenizer = function(expr) {
const tokenizeOperator: Tokenizer = function (expr) {
for (let op of OPERATORS) {
if (expr.startsWith(op)) {
return { type: "OPERATOR", value: op };
@@ -167,7 +167,7 @@ const TOKENIZERS = [
tokenizeNumber,
tokenizeOperator,
tokenizeSymbol,
tokenizeStatic
tokenizeStatic,
];
/**
@@ -284,6 +284,6 @@ export function compileExprToArray(expr: string, scope: { [key: string]: QWebVar
export function compileExpr(expr: string, scope: { [key: string]: QWebVar }): string {
return compileExprToArray(expr, scope)
.map(t => t.value)
.map((t) => t.value)
.join("");
}
+14 -14
View File
@@ -23,7 +23,7 @@ import { QWeb } from "./qweb";
export const MODS_CODE = {
prevent: "e.preventDefault();",
self: "if (e.target !== this.elm) {return}",
stop: "e.stopPropagation();"
stop: "e.stopPropagation();",
};
interface HandlerInfo {
@@ -50,7 +50,7 @@ export function makeHandlerCode(
let code: string;
// check if it is a method with no args, a method with args or an expression
let args: string = "";
const name: string = value.replace(/\(.*\)/, function(_args) {
const name: string = value.replace(/\(.*\)/, function (_args) {
args = _args.slice(1, -1);
return "";
});
@@ -74,7 +74,7 @@ export function makeHandlerCode(
putInCache = false;
code = ctx.captureExpression(value);
}
const modCode = mods.map(mod => modcodes[mod]).join("");
const modCode = mods.map((mod) => modcodes[mod]).join("");
let handler = `function (e) {if (!context.__owl__.isMounted){return}${modCode}${code}}`;
if (putInCache) {
const key = ctx.generateTemplateKey(event);
@@ -90,7 +90,7 @@ QWeb.addDirective({
atNodeCreation({ ctx, fullName, value, nodeID }) {
const { event, handler } = makeHandlerCode(ctx, fullName, value, true);
ctx.addLine(`p${nodeID}.on['${event}'] = ${handler};`);
}
},
});
//------------------------------------------------------------------------------
@@ -105,17 +105,17 @@ QWeb.addDirective({
ctx.addLine(`const ${refKey} = ${ctx.interpolate(value)};`);
addNodeHook("create", `context.__owl__.refs[${refKey}] = n.elm;`);
addNodeHook("destroy", `delete context.__owl__.refs[${refKey}];`);
}
},
});
//------------------------------------------------------------------------------
// t-transition
//------------------------------------------------------------------------------
QWeb.utils.nextFrame = function(cb: () => void) {
QWeb.utils.nextFrame = function (cb: () => void) {
requestAnimationFrame(() => requestAnimationFrame(cb));
};
QWeb.utils.transitionInsert = function(vn: VNode, name: string) {
QWeb.utils.transitionInsert = function (vn: VNode, name: string) {
const elm = <HTMLElement>vn.elm;
// remove potential duplicated vnode that is currently being removed, to
// prevent from having twice the same node in the DOM during an animation
@@ -139,7 +139,7 @@ QWeb.utils.transitionInsert = function(vn: VNode, name: string) {
});
};
QWeb.utils.transitionRemove = function(vn: VNode, name: string, rm: () => void) {
QWeb.utils.transitionRemove = function (vn: VNode, name: string, rm: () => void) {
const elm = <HTMLElement>vn.elm;
elm.setAttribute("data-owl-key", vn.key!);
@@ -209,12 +209,12 @@ QWeb.addDirective({
let name = value;
const hooks = {
insert: `utils.transitionInsert(vn, '${name}');`,
remove: `utils.transitionRemove(vn, '${name}', rm);`
remove: `utils.transitionRemove(vn, '${name}', rm);`,
};
for (let hookName in hooks) {
addNodeHook(hookName, hooks[hookName]);
}
}
},
});
//------------------------------------------------------------------------------
@@ -251,13 +251,13 @@ QWeb.addDirective({
}
ctx.closeIf();
return true;
}
},
});
//------------------------------------------------------------------------------
// t-model
//------------------------------------------------------------------------------
QWeb.utils.toNumber = function(val: string): number | string {
QWeb.utils.toNumber = function (val: string): number | string {
const n = parseFloat(val);
return isNaN(n) ? val : n;
};
@@ -305,7 +305,7 @@ QWeb.addDirective({
}
ctx.addLine(`extra.handlers[${key}] = extra.handlers[${key}] || (${handler});`);
ctx.addLine(`p${nodeID}.on['${event}'] = extra.handlers[${key}];`);
}
},
});
//------------------------------------------------------------------------------
@@ -329,5 +329,5 @@ QWeb.addDirective({
if (ctx.loopNumber === 0) {
ctx.rootContext.hasKey0 = ctx.keyStack.pop() as boolean;
}
}
},
});
+28 -22
View File
@@ -77,7 +77,7 @@ const NODE_HOOKS_PARAMS = {
create: "(_, n)",
insert: "vn",
remove: "(vn, rm)",
destroy: "()"
destroy: "()",
};
interface Utils {
@@ -112,9 +112,9 @@ const UTILS: Utils = {
addNS(vnode.data, vnode.children, vnode.sel);
},
VDomArray: class VDomArray extends Array {},
vDomToString: function(vdom: VNode[]): string {
vDomToString: function (vdom: VNode[]): string {
return vdom
.map(vnode => {
.map((vnode) => {
if (vnode.sel) {
const node = document.createElement(vnode.sel);
const result = patch(node, vnode);
@@ -145,7 +145,7 @@ const UTILS: Utils = {
obj = newObj;
}
return obj;
}
},
};
function parseXML(xml: string): Document {
@@ -195,7 +195,7 @@ export class QWeb extends EventBus {
name: 1,
att: 1,
attf: 1,
translation: 1
translation: 1,
};
static DIRECTIVES: Directive[] = [];
@@ -212,10 +212,12 @@ export class QWeb extends EventBus {
static slots = {};
static nextSlotId = 1;
// recursiveTemplates contains sub templates called with t-call, but which
// ends up in recursive situations. This is very similar to the slot situation,
// as in we need to propagate the scope.
subTemplates = {};
// subTemplates are stored in two objects: a (local) mapping from a name to an
// id, and a (global) mapping from an id to the compiled function. This is
// necessary to ensure that global templates can be called with more than one
// QWeb instance.
subTemplates: {[key: string]: number} = {};
static subTemplates: {[id: number]: Function} = {};
isUpdating: boolean = false;
translateFn?: QWebConfig["translateFn"];
@@ -239,7 +241,7 @@ export class QWeb extends EventBus {
QWeb.DIRECTIVE_NAMES[directive.name] = 1;
QWeb.DIRECTIVES.sort((d1, d2) => d1.priority - d2.priority);
if (directive.extraNames) {
directive.extraNames.forEach(n => (QWeb.DIRECTIVE_NAMES[n] = 1));
directive.extraNames.forEach((n) => (QWeb.DIRECTIVE_NAMES[n] = 1));
}
}
@@ -303,11 +305,11 @@ export class QWeb extends EventBus {
this._processTemplate(elem);
const template = {
elem,
fn: function(this: QWeb, context, extra) {
fn: function (this: QWeb, context, extra) {
const compiledFunction = this._compile(name, elem);
template.fn = compiledFunction;
return compiledFunction.call(this, context, extra);
}
},
};
this.templates[name] = template;
}
@@ -317,10 +319,10 @@ export class QWeb extends EventBus {
for (let i = 0, ilen = tbranch.length; i < ilen; i++) {
let node = tbranch[i];
let prevElem = node.previousElementSibling!;
let pattr = function(name) {
let pattr = function (name) {
return prevElem.getAttribute(name);
};
let nattr = function(name) {
let nattr = function (name) {
return +!!node.getAttribute(name);
};
if (prevElem && (pattr("t-if") || pattr("t-elif"))) {
@@ -330,7 +332,7 @@ export class QWeb extends EventBus {
);
}
if (
["t-if", "t-elif", "t-else"].map(nattr).reduce(function(a, b) {
["t-if", "t-elif", "t-else"].map(nattr).reduce(function (a, b) {
return a + b;
}) > 1
) {
@@ -582,7 +584,7 @@ export class QWeb extends EventBus {
qweb: this,
ctx,
fullName,
value
value,
});
if (isDone) {
for (let { directive, value, fullName } of finalizers) {
@@ -597,7 +599,7 @@ export class QWeb extends EventBus {
let nodeID = this._compileGenericNode(node, ctx, withHandlers);
ctx = ctx.withParent(nodeID);
let nodeHooks = {};
let addNodeHook = function(hook, handler) {
let addNodeHook = function (hook, handler) {
nodeHooks[hook] = nodeHooks[hook] || [];
nodeHooks[hook].push(handler);
};
@@ -611,7 +613,7 @@ export class QWeb extends EventBus {
fullName,
value,
nodeID,
addNodeHook
addNodeHook,
});
}
}
@@ -702,10 +704,14 @@ export class QWeb extends EventBus {
if ((value = value.trim())) {
let classDef = value
.split(/\s+/)
.map(a => `'${escapeQuotes(a)}':true`)
.map((a) => `'${escapeQuotes(a)}':true`)
.join(",");
classObj = `_${ctx.generateID()}`;
ctx.addLine(`let ${classObj} = {${classDef}};`);
if (classObj) {
ctx.addLine(`Object.assign(${classObj}, {${classDef}})`);
} else {
classObj = `_${ctx.generateID()}`;
ctx.addLine(`let ${classObj} = {${classDef}};`);
}
}
} else {
ctx.addLine(`let _${attID} = '${escapeQuotes(value)}';`);
@@ -746,7 +752,7 @@ export class QWeb extends EventBus {
const attValueID = ctx.generateID();
ctx.addLine(`let _${attValueID} = ${formattedValue};`);
formattedValue = `'${attValue}' + (_${attValueID} ? ' ' + _${attValueID} : '')`;
const attrIndex = attrs.findIndex(att => att.startsWith(attName + ":"));
const attrIndex = attrs.findIndex((att) => att.startsWith(attName + ":"));
attrs.splice(attrIndex, 1);
}
ctx.addLine(`let _${attID} = ${formattedValue};`);
+3 -3
View File
@@ -97,7 +97,7 @@ export class Router {
//--------------------------------------------------------------------------
async start() {
(this as any)._listener = ev => this._navigate(this.currentPath(), ev);
(this as any)._listener = (ev) => this._navigate(this.currentPath(), ev);
window.addEventListener("popstate", (this as any)._listener);
if (this.mode === "hash") {
window.addEventListener("hashchange", (this as any)._listener);
@@ -198,7 +198,7 @@ export class Router {
return {
type: "match",
route: route,
params: params
params: params,
};
}
}
@@ -223,7 +223,7 @@ export class Router {
const result = await route.beforeRouteEnter({
env: this.env,
from: this.currentRoute,
to: route
to: route,
});
if (result === false) {
return { type: "cancelled" };
+7 -7
View File
@@ -53,7 +53,7 @@ export class Store extends Context {
if (config.getters) {
const firstArg = {
state: this.state,
getters: this.getters
getters: this.getters,
};
for (let g in config.getters) {
this.getters[g] = config.getters[g].bind(this, firstArg);
@@ -70,7 +70,7 @@ export class Store extends Context {
dispatch: this.dispatch.bind(this),
env: this.env,
state: this.state,
getters: this.getters
getters: this.getters,
},
...payload
);
@@ -113,11 +113,11 @@ export function useStore(selector, options: SelectorOptions = {}): any {
}
return false;
}
store.updateFunctions[componentId].push(function(): boolean {
store.updateFunctions[componentId].push(function (): boolean {
return selectCompareUpdate(store!.state, component.props);
});
useContextWithCB(store, component, function(): Promise<void> | void {
useContextWithCB(store, component, function (): Promise<void> | void {
let shouldRender = false;
for (let fn of store.updateFunctions[componentId]) {
shouldRender = fn() || shouldRender;
@@ -126,12 +126,12 @@ export function useStore(selector, options: SelectorOptions = {}): any {
return component.render();
}
});
onWillUpdateProps(props => {
onWillUpdateProps((props) => {
selectCompareUpdate(store.state, props);
});
const __destroy = component.__destroy;
component.__destroy = parent => {
component.__destroy = (parent) => {
delete store.updateFunctions[componentId];
__destroy.call(component, parent);
};
@@ -148,7 +148,7 @@ export function useStore(selector, options: SelectorOptions = {}): any {
},
has(target, k) {
return k in result;
}
},
});
}
+6 -6
View File
@@ -13,13 +13,13 @@
import { browser } from "./browser";
export function whenReady(fn?: any) {
return new Promise(function(resolve) {
return new Promise(function (resolve) {
if (document.readyState !== "loading") {
resolve();
} else {
document.addEventListener("DOMContentLoaded", resolve, false);
}
}).then(fn || function() {});
}).then(fn || function () {});
}
const loadedScripts: { [key: string]: Promise<void> } = {};
@@ -28,14 +28,14 @@ export function loadJS(url: string): Promise<void> {
if (url in loadedScripts) {
return loadedScripts[url];
}
const promise: Promise<void> = new Promise(function(resolve, reject) {
const promise: Promise<void> = new Promise(function (resolve, reject) {
const script = document.createElement("script");
script.type = "text/javascript";
script.src = url;
script.onload = function() {
script.onload = function () {
resolve();
};
script.onerror = function() {
script.onerror = function () {
reject(`Error loading file '${url}'`);
};
const head = document.head || document.getElementsByTagName("head")[0];
@@ -75,7 +75,7 @@ export function escape(str: string | number | undefined): string {
*/
export function debounce(func: Function, wait: number, immediate?: boolean): Function {
let timeout;
return function(this: any) {
return function (this: any) {
const context = this;
const args = arguments;
function later() {
+6 -2
View File
@@ -1,4 +1,4 @@
import { VNode, h } from "./vdom";
import { VNode, h, addNS } from "./vdom";
const parser = new DOMParser();
@@ -23,5 +23,9 @@ function htmlToVNode(node: ChildNode): VNode {
for (let c of node.childNodes) {
children.push(htmlToVNode(c));
}
return h((node as Element).tagName, { attrs }, children);
const vnode = h((node as Element).tagName, { attrs }, children);
if (vnode.sel === "svg") {
addNS(vnode.data, (vnode as any).children, vnode.sel);
}
return vnode;
}
+3 -3
View File
@@ -33,7 +33,7 @@ function updateProps(oldVnode: VNode, vnode: VNode): void {
export const propsModule = {
create: updateProps,
update: updateProps
update: updateProps,
} as Module;
//------------------------------------------------------------------------------
@@ -151,7 +151,7 @@ function updateEventListeners(oldVnode: VNode, vnode?: VNode): void {
export const eventListenersModule = {
create: updateEventListeners,
update: updateEventListeners,
destroy: updateEventListeners
destroy: updateEventListeners,
} as Module;
//------------------------------------------------------------------------------
@@ -210,7 +210,7 @@ function updateAttrs(oldVnode: VNode, vnode: VNode): void {
export const attrsModule = {
create: updateAttrs,
update: updateAttrs
update: updateAttrs,
} as Module;
//------------------------------------------------------------------------------
+1 -1
View File
@@ -520,7 +520,7 @@ const htmlDomApi = {
parentNode,
nextSibling,
tagName,
setTextContent
setTextContent,
} as DOMAPI;
//------------------------------------------------------------------------------
+13 -13
View File
@@ -9,7 +9,7 @@ import {
patchNextFrame,
renderToDOM,
unpatchNextFrame,
nextTick
nextTick,
} from "./helpers";
//------------------------------------------------------------------------------
@@ -71,7 +71,7 @@ describe("animations", () => {
qweb.addTemplate("test", `<span t-transition="chimay">blue</span>`);
let def = makeDeferred();
patchNextFrame(cb => {
patchNextFrame((cb) => {
expect(node.className).toBe("chimay-enter chimay-enter-active");
cb();
expect(node.className).toBe("chimay-enter-active chimay-enter-to");
@@ -91,7 +91,7 @@ describe("animations", () => {
qweb.addTemplate("test", `<span t-transition="chimay">blue</span>`);
let def = makeDeferred();
patchNextFrame(cb => {
patchNextFrame((cb) => {
expect(node.className).toBe("chimay-enter chimay-enter-active");
cb();
expect(node.className).toBe("chimay-enter-active chimay-enter-to");
@@ -112,7 +112,7 @@ describe("animations", () => {
qweb.addTemplate("test", `<span t-transition="jupiler">blue</span>`);
let def = makeDeferred();
patchNextFrame(cb => {
patchNextFrame((cb) => {
expect(node.className).toBe("jupiler-enter jupiler-enter-active");
cb();
expect(node.className).toBe("");
@@ -139,7 +139,7 @@ describe("animations", () => {
// insert widget into the DOM
let def = makeDeferred();
var spanNode;
patchNextFrame(cb => {
patchNextFrame((cb) => {
expect(spanNode.className).toBe("chimay-enter chimay-enter-active");
cb();
expect(spanNode.className).toBe("chimay-enter-active chimay-enter-to");
@@ -155,7 +155,7 @@ describe("animations", () => {
// remove span from the DOM
def = makeDeferred();
widget.state.hide = true;
patchNextFrame(cb => {
patchNextFrame((cb) => {
expect(spanNode.className).toBe("chimay-leave chimay-leave-active");
cb();
expect(spanNode.className).toBe("chimay-leave-active chimay-leave-to");
@@ -182,7 +182,7 @@ describe("animations", () => {
// insert widget into the DOM
let def = makeDeferred();
var spanNode;
patchNextFrame(cb => {
patchNextFrame((cb) => {
expect(spanNode.className).toBe("chimay-enter chimay-enter-active");
cb();
expect(spanNode.className).toBe("chimay-enter-active chimay-enter-to");
@@ -210,7 +210,7 @@ describe("animations", () => {
let def = makeDeferred();
var spanNode;
patchNextFrame(cb => {
patchNextFrame((cb) => {
expect(fixture.innerHTML).toBe(
'<div><span class="chimay-enter chimay-enter-active">blue</span></div>'
);
@@ -250,7 +250,7 @@ describe("animations", () => {
let def = makeDeferred();
var spanNode;
patchNextFrame(cb => {
patchNextFrame((cb) => {
expect(fixture.innerHTML).toBe(
'<div><span class="chimay-enter chimay-enter-active">blue</span></div>'
);
@@ -275,7 +275,7 @@ describe("animations", () => {
// remove span from the DOM
def = makeDeferred();
widget.state.display = false;
patchNextFrame(cb => {
patchNextFrame((cb) => {
expect(fixture.innerHTML).toBe(
'<div><span class="chimay-leave chimay-leave-active" data-owl-key="__3__">blue</span></div>'
);
@@ -315,7 +315,7 @@ describe("animations", () => {
let def = makeDeferred();
let phase = "enter";
patchNextFrame(cb => {
patchNextFrame((cb) => {
let spans = fixture.querySelectorAll("span");
expect(spans.length).toBe(1);
expect(spans[0].className).toBe(`chimay-${phase} chimay-${phase}-active`);
@@ -366,7 +366,7 @@ describe("animations", () => {
let def = makeDeferred();
let phase = "enter";
patchNextFrame(cb => {
patchNextFrame((cb) => {
let spans = fixture.querySelectorAll("span");
expect(spans.length).toBe(1);
expect(spans[0].className).toBe(`chimay-${phase} chimay-${phase}-active`);
@@ -413,7 +413,7 @@ describe("animations", () => {
state = useState({ flag: false });
}
patchNextFrame(cb => cb());
patchNextFrame((cb) => cb());
const widget = new Parent();
await widget.mount(fixture);
@@ -1253,7 +1253,7 @@ exports[`other directives with t-component t-set can't alter from within callee
if (scope.iter != null) {
c2.push({text: scope.iter});
}
this.subTemplates['ChildWidget'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__4__'}));
this.constructor.subTemplates['2'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__4__'}));
let c5 = [], p5 = {key:5};
let vn5 = h('p', p5, c5);
c1.push(vn5);
@@ -1289,7 +1289,7 @@ exports[`other directives with t-component t-set can't alter in t-call body 1`]
utils.getScope(scope, 'iter').iter = 'inCall';
scope[utils.zero] = c__0;
}
this.subTemplates['ChildWidget'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__5__'}));
this.constructor.subTemplates['2'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__5__'}));
scope = _origScope4;
}
let c6 = [], p6 = {key:6};
@@ -1410,7 +1410,7 @@ exports[`random stuff/miscellaneous can inject values in tagged templates 1`] =
let h = this.h;
let c1 = [], p1 = {key:1};
let vn1 = h('div', p1, c1);
this.subTemplates['__template__1'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__4__'}));
this.constructor.subTemplates['3'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__4__'}));
return vn1;
}"
`;
+2 -2
View File
@@ -27,7 +27,7 @@ afterEach(() => {
function children(w: Component): Component[] {
const childrenMap = w.__owl__.children;
return Object.keys(childrenMap).map(id => childrenMap[id]);
return Object.keys(childrenMap).map((id) => childrenMap[id]);
}
describe("async rendering", () => {
@@ -404,7 +404,7 @@ describe("async rendering", () => {
}
class ChildB extends Component {
willStart(): any {
return new Promise(function() {});
return new Promise(function () {});
}
}
class Parent extends Component {
+41 -32
View File
@@ -1,16 +1,16 @@
import { Component, Env } from "../../src/component/component";
import { EventBus } from "../../src/core/event_bus";
import { useRef, useState } from "../../src/hooks";
import { QWeb } from "../../src/qweb/qweb";
import { xml } from "../../src/tags";
import { useState, useRef } from "../../src/hooks";
import { EventBus } from "../../src/core/event_bus";
import {
editInput,
makeDeferred,
makeTestFixture,
makeTestEnv,
makeTestFixture,
nextMicroTick,
nextTick,
normalize,
editInput
} from "../helpers";
//------------------------------------------------------------------------------
@@ -39,7 +39,7 @@ afterEach(() => {
function children(w: Component): Component[] {
const childrenMap = w.__owl__.children;
return Object.keys(childrenMap).map(id => childrenMap[id]);
return Object.keys(childrenMap).map((id) => childrenMap[id]);
}
// Test components
@@ -133,7 +133,7 @@ describe("basic widget properties", () => {
static template = xml`
<div><t t-esc="state.counter"/><button t-on-click="state.counter++">Inc</button></div>`;
state = useState({
counter: 0
counter: 0,
});
}
@@ -167,7 +167,7 @@ describe("basic widget properties", () => {
static template = xml`
<div><t t-esc="state.counter"/><button t-on-click="state.counter++">Inc</button></div>`;
state = useState({
counter: 0
counter: 0,
});
}
@@ -537,7 +537,7 @@ describe("lifecycle hooks", () => {
"child:mounted",
"parent:willUnmount",
"child:willUnmount",
"childchild:willUnmount"
"childchild:willUnmount",
]);
});
@@ -589,7 +589,7 @@ describe("lifecycle hooks", () => {
"childchild:willPatch",
"childchild:patched",
"child:patched",
"parent:patched"
"parent:patched",
]);
});
@@ -782,7 +782,7 @@ describe("lifecycle hooks", () => {
"c mounted",
"p mounted",
"p willunmount",
"c willunmount"
"c willunmount",
]);
});
@@ -960,7 +960,7 @@ describe("lifecycle hooks", () => {
"parent:willPatch",
"child:willPatch",
"child:patched",
"parent:patched"
"parent:patched",
]);
});
});
@@ -1134,7 +1134,7 @@ describe("composition", () => {
class App extends Component {
static template = xml`<t t-component="myComponent" t-key="state.child"/>`;
state = useState({
child: "a"
child: "a",
});
get myComponent() {
return this.state.child === "a" ? A : B;
@@ -1158,7 +1158,7 @@ describe("composition", () => {
class App extends Component {
static template = xml`<t t-component="myComponent" t-key="state.child"/>`;
state = useState({
child: "a"
child: "a",
});
get myComponent() {
return this.state.child === "a" ? A : B;
@@ -1228,7 +1228,7 @@ describe("composition", () => {
static template = xml`
<div><t t-esc="state.counter"/><button t-on-click="state.counter++">Inc</button></div>`;
state = useState({
counter: 0
counter: 0,
});
}
@@ -1327,7 +1327,7 @@ describe("composition", () => {
static template = xml`
<div><t t-esc="state.counter"/><button t-on-click="state.counter++">Inc</button></div>`;
state = useState({
counter: 0
counter: 0,
});
}
@@ -1350,7 +1350,7 @@ describe("composition", () => {
static template = xml`
<div><t t-esc="state.counter"/><button t-on-click="state.counter++">Inc</button></div>`;
state = useState({
counter: 0
counter: 0,
});
}
@@ -1389,7 +1389,7 @@ describe("composition", () => {
);
class Parent extends Component {
state = useState({
numbers: [1, 2, 3]
numbers: [1, 2, 3],
});
static components = { ChildWidget };
}
@@ -1429,7 +1429,7 @@ describe("composition", () => {
class Parent extends Component {
static template = "parent";
state = useState({
numbers: [1, 2, 3]
numbers: [1, 2, 3],
});
static components = { ChildWidget };
}
@@ -1513,8 +1513,8 @@ describe("composition", () => {
blips: [
{ a: "a", id: 1 },
{ b: "b", id: 2 },
{ c: "c", id: 4 }
]
{ c: "c", id: 4 },
],
});
}
const parent = new Parent();
@@ -1621,8 +1621,8 @@ describe("composition", () => {
records: [
{ id: 1, val: 1 },
{ id: 2, val: 2 },
{ id: 3, val: 3 }
]
{ id: 3, val: 3 },
],
});
static components = { ChildWidget };
}
@@ -2003,7 +2003,7 @@ describe("other directives with t-component", () => {
}
const widget = new ParentWidget();
await widget.mount(fixture);
(<HTMLElement>fixture).addEventListener("ev", function(e) {
(<HTMLElement>fixture).addEventListener("ev", function (e) {
steps.push(e.defaultPrevented);
});
@@ -2038,7 +2038,7 @@ describe("other directives with t-component", () => {
}
const widget = new ParentWidget();
await widget.mount(fixture);
(<HTMLElement>fixture).addEventListener("ev", function(e) {
(<HTMLElement>fixture).addEventListener("ev", function (e) {
steps.push(e.defaultPrevented);
});
@@ -2976,7 +2976,7 @@ describe("random stuff/miscellaneous", () => {
"D:mounted",
"C:mounted",
"B:mounted",
"A:mounted"
"A:mounted",
]);
// update
@@ -2999,7 +2999,7 @@ describe("random stuff/miscellaneous", () => {
"E:destroy",
"F:mounted",
"D:patched",
"C:patched"
"C:patched",
]);
});
@@ -3410,7 +3410,7 @@ describe("t-model directive", () => {
state = useState([
{ f: false, id: 1 },
{ f: false, id: 2 },
{ f: false, id: 3 }
{ f: false, id: 3 },
]);
}
const comp = new SomeComponent();
@@ -3455,7 +3455,7 @@ describe("environment and plugins", () => {
let bus = new EventBus();
// definition of a plugin
const somePlugin = env => {
const somePlugin = (env) => {
env.someFlag = true;
bus.on("some-event", null, () => {
env.someFlag = !env.someFlag;
@@ -3825,7 +3825,9 @@ describe("t-call", () => {
expect(fixture.innerHTML).toBe("<div><p>lucas</p></div>");
fixture.querySelector("p")!.click();
expect(env.qweb.subTemplates["sub"].toString()).toMatchSnapshot();
const subId = env.qweb.subTemplates["sub"];
const subFn = QWeb.subTemplates[subId] as any;
expect(subFn.toString()).toMatchSnapshot();
});
test("parent is set within t-call", async () => {
@@ -3847,7 +3849,10 @@ describe("t-call", () => {
expect(fixture.innerHTML).toBe("<div><span>lucas</span></div>");
expect(child.__owl__.parent).toBe(parent);
expect(env.qweb.subTemplates["sub"].toString()).toMatchSnapshot();
const subId = env.qweb.subTemplates["sub"];
const subFn = QWeb.subTemplates[subId] as any;
expect(subFn.toString()).toMatchSnapshot();
});
test("t-call in t-foreach and children component", async () => {
@@ -3889,7 +3894,9 @@ describe("t-call", () => {
expect(fixture.innerHTML).toBe("<span>lucas</span>");
expect(child.__owl__.parent).toBe(parent);
expect(env.qweb.subTemplates["sub"].toString()).toMatchSnapshot();
const subId = env.qweb.subTemplates["sub"];
const subFn = QWeb.subTemplates[subId] as any;
expect(subFn.toString()).toMatchSnapshot();
});
test("handlers with arguments are properly bound through a t-call", async () => {
@@ -3905,7 +3912,9 @@ describe("t-call", () => {
}
const parent = new Parent();
await parent.mount(fixture);
expect(env.qweb.subTemplates["sub"].toString()).toMatchSnapshot();
const subId = env.qweb.subTemplates["sub"];
const subFn = QWeb.subTemplates[subId] as any;
expect(subFn.toString()).toMatchSnapshot();
fixture.querySelector("p")!.click();
});
+15 -15
View File
@@ -92,7 +92,7 @@ describe("props validation", () => {
{ type: String, ok: "1", ko: 1 },
{ type: Object, ok: {}, ko: "1" },
{ type: Date, ok: new Date(), ko: "1" },
{ type: Function, ok: () => {}, ko: "1" }
{ type: Function, ok: () => {}, ko: "1" },
];
let props;
@@ -149,7 +149,7 @@ describe("props validation", () => {
{ type: String, ok: "1", ko: 1 },
{ type: Object, ok: {}, ko: "1" },
{ type: Date, ok: new Date(), ko: "1" },
{ type: Function, ok: () => {}, ko: "1" }
{ type: Function, ok: () => {}, ko: "1" },
];
let props;
@@ -396,7 +396,7 @@ describe("props validation", () => {
class TestWidget extends Component {
static template = xml`<div>hey</div>`;
static props = {
p: { type: Object, shape: { id: Number, url: String } }
p: { type: Object, shape: { id: Number, url: String } },
};
}
class Parent extends Component {
@@ -458,9 +458,9 @@ describe("props validation", () => {
type: Object,
shape: {
id: Number,
url: [Boolean, { type: Array, element: Number }]
}
}
url: [Boolean, { type: Array, element: Number }],
},
},
};
}
class Parent extends Component {
@@ -510,10 +510,10 @@ describe("props validation", () => {
element: {
type: Object,
shape: {
num: { type: Number, optional: true }
}
}
}
num: { type: Number, optional: true },
},
},
},
};
}
let error;
@@ -539,8 +539,8 @@ describe("props validation", () => {
class TestComponent extends Component {
static props = {
size: {
validate: e => ["small", "medium", "large"].includes(e)
}
validate: (e) => ["small", "medium", "large"].includes(e),
},
};
}
let error;
@@ -561,13 +561,13 @@ describe("props validation", () => {
});
test("can validate with a custom validator, and a type", () => {
const validator = jest.fn(n => 0 <= n && n <= 10);
const validator = jest.fn((n) => 0 <= n && n <= 10);
class TestComponent extends Component {
static props = {
n: {
type: Number,
validate: validator
}
validate: validator,
},
};
}
let error;
+5 -5
View File
@@ -28,7 +28,7 @@ afterEach(() => {
function children(w: Component): Component[] {
const childrenMap = w.__owl__.children;
return Object.keys(childrenMap).map(id => childrenMap[id]);
return Object.keys(childrenMap).map((id) => childrenMap[id]);
}
//------------------------------------------------------------------------------
@@ -224,8 +224,8 @@ describe("t-slot directive", () => {
state = useState({
users: [
{ id: 1, name: "Aaron" },
{ id: 2, name: "David" }
]
{ id: 2, name: "David" },
],
});
static components = { Link };
}
@@ -268,8 +268,8 @@ describe("t-slot directive", () => {
state = useState({
users: [
{ id: 1, name: "Aaron" },
{ id: 2, name: "David" }
]
{ id: 2, name: "David" },
],
});
static components = { Link };
}
+20 -1
View File
@@ -2,6 +2,7 @@ import { Component, Env } from "../../src/component/component";
import { useState } from "../../src/hooks";
import { xml } from "../../src/tags";
import { makeDeferred, makeTestEnv, makeTestFixture, nextTick, nextMicroTick } from "../helpers";
import { scheduler } from "../../src/component/scheduler";
//------------------------------------------------------------------------------
// Setup and helpers
@@ -335,7 +336,7 @@ describe("unmounting and remounting", () => {
class Child extends Component {
static template = xml`<span t-esc="state.val"/>`;
state = useState({
val: "C1"
val: "C1",
});
constructor(parent, props) {
super(parent, props);
@@ -490,4 +491,22 @@ describe("unmounting and remounting", () => {
expect(steps).toEqual(["1 resolved", "2 resolved"]);
expect(fixture.innerHTML).toBe("<div>Hey</div>");
});
test("mounting a destroyed widget", async () => {
class MyWidget extends Component {
static template = xml`<div>Hey</div>`;
}
const w = new MyWidget();
w.destroy(); // because, why not
let error;
try {
await w.mount(fixture);
} catch (e) {
error = e;
}
expect(scheduler.tasks.length).toBe(0);
expect(error).toBeDefined();
expect(error.message).toBe("Cannot mount a destroyed component");
});
});
+1 -1
View File
@@ -14,7 +14,7 @@ describe("event bus behaviour", () => {
expect.assertions(1);
const bus = new EventBus();
const owner = {};
bus.on("event", owner, function(this: any) {
bus.on("event", owner, function (this: any) {
expect(this).toBe(owner);
});
bus.trigger("event");
+2 -2
View File
@@ -22,7 +22,7 @@ describe("observer", () => {
expect(observer.rev).toBe(2);
expect(obj2).toEqual({
a: 2
a: 2,
});
});
@@ -47,7 +47,7 @@ describe("observer", () => {
expect(observer.rev).toBe(5);
expect(obj).toEqual({
a: null,
b: undefined
b: undefined,
});
});
+3 -3
View File
@@ -9,7 +9,7 @@ import "../src/component/directive";
import { browser } from "../src/browser";
// modifies scheduler to make it faster to test components
scheduler.requestAnimationFrame = function(callback: FrameRequestCallback) {
scheduler.requestAnimationFrame = function (callback: FrameRequestCallback) {
setTimeout(callback, 1);
return 1;
};
@@ -43,7 +43,7 @@ export function nextMicroTick(): Promise<void> {
}
export async function nextTick(): Promise<void> {
return new Promise(function(resolve) {
return new Promise(function (resolve) {
setTimeout(() => scheduler.requestAnimationFrame(() => resolve()));
});
}
@@ -77,7 +77,7 @@ export function makeDeferred(): Deferred {
export function makeTestEnv(): Env {
return {
qweb: new QWeb(),
browser: browser
browser: browser,
};
}
+7 -7
View File
@@ -10,7 +10,7 @@ import {
onWillStart,
onWillUpdateProps,
useSubEnv,
useExternalListener
useExternalListener,
} from "../src/hooks";
import { xml } from "../src/tags";
@@ -214,7 +214,7 @@ describe("hooks", () => {
"hook:mounted1",
"hook:mounted2",
"hook:willunmount2",
"hook:willunmount1"
"hook:willunmount1",
]);
});
@@ -267,7 +267,7 @@ describe("hooks", () => {
class WidgetC extends Component {
static template = xml`<div class="outer-div">Hello<WidgetB t-ref="mywidgetb" /></div>`;
static components = { WidgetB };
ref = useRef("mywidgetb");
ref = useRef<WidgetB>("mywidgetb");
}
const widget = new WidgetC();
@@ -560,7 +560,7 @@ describe("hooks", () => {
test("can use onWillStart, onWillUpdateProps", async () => {
const steps: string[] = [];
async function slow(): Promise<string> {
return new Promise(resolve => {
return new Promise((resolve) => {
setTimeout(() => {
resolve("slow");
}, 0);
@@ -571,7 +571,7 @@ describe("hooks", () => {
steps.push(await slow());
steps.push("onWillStart");
});
onWillUpdateProps(async nextProps => {
onWillUpdateProps(async (nextProps) => {
expect(nextProps).toEqual({ value: 2 });
steps.push(await slow());
steps.push("onWillUpdateProps");
@@ -581,7 +581,7 @@ describe("hooks", () => {
onWillStart(() => {
steps.push("on2ndStart");
});
onWillUpdateProps(nextProps => {
onWillUpdateProps((nextProps) => {
expect(nextProps).toEqual({ value: 2 });
steps.push("on2ndUpdate");
});
@@ -623,7 +623,7 @@ describe("hooks", () => {
"onWillStart",
"on2ndUpdate",
"slow",
"onWillUpdateProps"
"onWillUpdateProps",
]);
});
+4 -4
View File
@@ -486,7 +486,7 @@ describe("Portal: Basic use and DOM placement", () => {
"parent:mounted",
"parent:willPatch",
"child:mounted",
"parent:patched"
"parent:patched",
]);
parent.state.val = 2;
@@ -499,7 +499,7 @@ describe("Portal: Basic use and DOM placement", () => {
"parent:willPatch",
"child:willPatch",
"child:patched",
"parent:patched"
"parent:patched",
]);
parent.state.hasChild = false;
@@ -515,7 +515,7 @@ describe("Portal: Basic use and DOM placement", () => {
"parent:patched",
"parent:willPatch",
"child:willUnmount",
"parent:patched"
"parent:patched",
]);
});
@@ -741,7 +741,7 @@ describe("Portal: Events handling", () => {
steps.push(ev.type as string);
}
}
const bodyListener = ev => {
const bodyListener = (ev) => {
steps.push(`body: ${ev.type}`);
};
document.body.addEventListener("click", bodyListener);
+94 -90
View File
@@ -1,5 +1,20 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`attributes class and t-att-class should combine together 1`] = `
"function anonymous(context, extra
) {
// Template name: \\"test\\"
let utils = this.constructor.utils;
let scope = Object.create(context);
let h = this.h;
let _1 = utils.toObj(scope['value']);
Object.assign(_1, {'hello':true})
let c3 = [], p3 = {key:3,class:_1};
let vn3 = h('div', p3, c3);
return vn3;
}"
`;
exports[`attributes class and t-attf-class with ternary operation 1`] = `
"function anonymous(context, extra
) {
@@ -734,7 +749,7 @@ exports[`loading templates can load a few templates from a xml string 1`] = `
let h = this.h;
let c1 = [], p1 = {key:1};
let vn1 = h('ul', p1, c1);
this.subTemplates['items'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__4__'}));
this.constructor.subTemplates['1'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__4__'}));
return vn1;
}"
`;
@@ -788,23 +803,23 @@ exports[`misc global 1`] = `
scope[utils.zero] = c__0;
}
let k14 = \`__14__\${key1}__\`;
this.subTemplates['_callee-uses-foo'].call(this, scope, Object.assign({}, extra, {parentNode: c__0, parent: utils.getComponent(context), key: k14}));
this.constructor.subTemplates['2'].call(this, scope, Object.assign({}, extra, {parentNode: c__0, parent: utils.getComponent(context), key: k14}));
scope = _origScope13;
}
let k15 = \`__15__\${key1}__\`;
this.subTemplates['_callee-uses-foo'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c__0, parent: utils.getComponent(context), key: k15}));
this.constructor.subTemplates['2'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c__0, parent: utils.getComponent(context), key: k15}));
utils.getScope(scope, 'foo').foo = 'bbb';
let k16 = \`__16__\${key1}__\`;
this.subTemplates['_callee-uses-foo'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c__0, parent: utils.getComponent(context), key: k16}));
this.constructor.subTemplates['2'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c__0, parent: utils.getComponent(context), key: k16}));
scope[utils.zero] = c__0;
}
let k17 = \`__17__\${key1}__\`;
this.subTemplates['_callee-asc'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: k17}));
this.constructor.subTemplates['1'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: k17}));
scope = _origScope10;
}
}
scope = _origScope5;
this.subTemplates['_callee-asc-toto'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__20__'}));
this.constructor.subTemplates['3'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__20__'}));
return vn1;
}"
`;
@@ -1093,25 +1108,11 @@ exports[`t-call (template calling basic caller 1`] = `
let h = this.h;
let c1 = [], p1 = {key:1};
let vn1 = h('div', p1, c1);
this.subTemplates['_basic-callee'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__3__'}));
this.constructor.subTemplates['1'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__3__'}));
return vn1;
}"
`;
exports[`t-call (template calling basic caller 2`] = `
"function anonymous(context, extra
) {
// Template name: \\"_basic-callee\\"
let h = this.h;
let c1 = extra.parentNode;
let key0 = extra.key || \\"\\";
let c2 = [], p2 = {key:\`\${key0}_2\`};
let vn2 = h('span', p2, c2);
c1.push(vn2);
c2.push({text: \`ok\`});
}"
`;
exports[`t-call (template calling basic caller, no parent node 1`] = `
"function anonymous(context, extra
) {
@@ -1121,26 +1122,12 @@ exports[`t-call (template calling basic caller, no parent node 1`] = `
let result;
let h = this.h;
result = []
this.subTemplates['_basic-callee'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: result, parent: utils.getComponent(context), key: '__3__'}));
this.constructor.subTemplates['1'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: result, parent: utils.getComponent(context), key: '__3__'}));
result = result[0]
return result;
}"
`;
exports[`t-call (template calling basic caller, no parent node 2`] = `
"function anonymous(context, extra
) {
// Template name: \\"_basic-callee\\"
let h = this.h;
let c1 = extra.parentNode;
let key0 = extra.key || \\"\\";
let c2 = [], p2 = {key:\`\${key0}_2\`};
let vn2 = h('div', p2, c2);
c1.push(vn2);
c2.push({text: \`ok\`});
}"
`;
exports[`t-call (template calling call with several sub nodes on same line 1`] = `
"function anonymous(context, extra
) {
@@ -1167,28 +1154,14 @@ exports[`t-call (template calling call with several sub nodes on same line 1`] =
c5.push({text: \`yay\`});
scope[utils.zero] = c__0;
}
this.subTemplates['SubTemplate'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__6__'}));
this.constructor.subTemplates['1'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__6__'}));
scope = _origScope3;
}
return vn1;
}"
`;
exports[`t-call (template calling call with several sub nodes on same line 2`] = `
"function anonymous(context, extra
) {
// Template name: \\"SubTemplate\\"
let utils = this.constructor.utils;
let scope = Object.create(context);
let h = this.h;
let c1 = extra.parentNode;
let key0 = extra.key || \\"\\";
let c2 = [], p2 = {key:\`\${key0}_2\`};
let vn2 = h('div', p2, c2);
c1.push(vn2);
c2.push(...scope[utils.zero]);
}"
`;
exports[`t-call (template calling call with several sub nodes on same line 2`] = `"1"`;
exports[`t-call (template calling cascading t-call t-raw='0' 1`] = `
"function anonymous(context, extra
@@ -1216,7 +1189,7 @@ exports[`t-call (template calling cascading t-call t-raw='0' 1`] = `
c14.push({text: \`yay\`});
scope[utils.zero] = c__0;
}
this.subTemplates['SubTemplate'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__15__'}));
this.constructor.subTemplates['1'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__15__'}));
scope = _origScope12;
}
return vn1;
@@ -1233,7 +1206,7 @@ exports[`t-call (template calling inherit context 1`] = `
let c1 = [], p1 = {key:1};
let vn1 = h('div', p1, c1);
scope.foo = 1;
this.subTemplates['_callee-uses-foo'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__2__'}));
this.constructor.subTemplates['1'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__2__'}));
return vn1;
}"
`;
@@ -1252,7 +1225,7 @@ exports[`t-call (template calling recursive template, part 1 1`] = `
c1.push(vn2);
c2.push({text: \`hey\`});
if (false) {
this.subTemplates['recursive'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__6__'}));
this.constructor.subTemplates['1'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__6__'}));
}
return vn1;
}"
@@ -1276,7 +1249,7 @@ exports[`t-call (template calling recursive template, part 1 2`] = `
c4.push({text: \`hey\`});
if (false) {
let k5 = \`__5__\${key0}__\`;
this.subTemplates['recursive'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c3, parent: utils.getComponent(context), key: k5}));
this.constructor.subTemplates['1'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c3, parent: utils.getComponent(context), key: k5}));
}
}"
`;
@@ -1299,7 +1272,7 @@ exports[`t-call (template calling recursive template, part 2 1`] = `
utils.getScope(scope, 'node').node = scope['root'];
scope[utils.zero] = c__0;
}
this.subTemplates['nodeTemplate'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__12__'}));
this.constructor.subTemplates['1'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__12__'}));
scope = _origScope11;
}
return vn1;
@@ -1352,7 +1325,7 @@ exports[`t-call (template calling recursive template, part 2 2`] = `
scope[utils.zero] = c__0;
}
let k10 = \`__10__\${key0}__\${key1}__\`;
this.subTemplates['nodeTemplate'].call(this, scope, Object.assign({}, extra, {parentNode: c2, parent: utils.getComponent(context), key: k10}));
this.constructor.subTemplates['1'].call(this, scope, Object.assign({}, extra, {parentNode: c2, parent: utils.getComponent(context), key: k10}));
scope = _origScope9;
}
}
@@ -1378,7 +1351,7 @@ exports[`t-call (template calling recursive template, part 3 1`] = `
utils.getScope(scope, 'node').node = scope['root'];
scope[utils.zero] = c__0;
}
this.subTemplates['nodeTemplate'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__12__'}));
this.constructor.subTemplates['1'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__12__'}));
scope = _origScope11;
}
return vn1;
@@ -1431,7 +1404,7 @@ exports[`t-call (template calling recursive template, part 3 2`] = `
scope[utils.zero] = c__0;
}
let k10 = \`__10__\${key0}__\${key1}__\`;
this.subTemplates['nodeTemplate'].call(this, scope, Object.assign({}, extra, {parentNode: c2, parent: utils.getComponent(context), key: k10}));
this.constructor.subTemplates['1'].call(this, scope, Object.assign({}, extra, {parentNode: c2, parent: utils.getComponent(context), key: k10}));
scope = _origScope9;
}
}
@@ -1458,7 +1431,7 @@ exports[`t-call (template calling recursive template, part 4: with t-set recursi
utils.getScope(scope, 'node').node = scope['root'];
scope[utils.zero] = c__0;
}
this.subTemplates['nodeTemplate'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__12__'}));
this.constructor.subTemplates['1'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__12__'}));
scope = _origScope11;
}
return vn1;
@@ -1516,7 +1489,7 @@ exports[`t-call (template calling recursive template, part 4: with t-set recursi
scope[utils.zero] = c__0;
}
let k10 = \`__10__\${key0}__\${key1}__\`;
this.subTemplates['nodeTemplate'].call(this, scope, Object.assign({}, extra, {parentNode: c2, parent: utils.getComponent(context), key: k10}));
this.constructor.subTemplates['1'].call(this, scope, Object.assign({}, extra, {parentNode: c2, parent: utils.getComponent(context), key: k10}));
scope = _origScope9;
}
}
@@ -1542,7 +1515,7 @@ exports[`t-call (template calling scoped parameters 1`] = `
utils.getScope(scope, 'foo').foo = 42;
scope[utils.zero] = c__0;
}
this.subTemplates['_basic-callee'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__3__'}));
this.constructor.subTemplates['1'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__3__'}));
scope = _origScope2;
}
if (scope.foo != null) {
@@ -1562,26 +1535,12 @@ exports[`t-call (template calling t-call with t-if 1`] = `
let c1 = [], p1 = {key:1};
let vn1 = h('div', p1, c1);
if (scope['flag']) {
this.subTemplates['sub'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__3__'}));
this.constructor.subTemplates['1'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__3__'}));
}
return vn1;
}"
`;
exports[`t-call (template calling t-call with t-if 2`] = `
"function anonymous(context, extra
) {
// Template name: \\"sub\\"
let h = this.h;
let c1 = extra.parentNode;
let key0 = extra.key || \\"\\";
let c2 = [], p2 = {key:\`\${key0}_2\`};
let vn2 = h('span', p2, c2);
c1.push(vn2);
c2.push({text: \`ok\`});
}"
`;
exports[`t-call (template calling t-call with t-set inside and outside 1`] = `
"function anonymous(context, extra
) {
@@ -1619,7 +1578,7 @@ exports[`t-call (template calling t-call with t-set inside and outside 1`] = `
scope[utils.zero] = c__0;
}
let k9 = \`__9__\${key1}__\`;
this.subTemplates['sub'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: k9}));
this.constructor.subTemplates['1'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: k9}));
scope = _origScope8;
}
}
@@ -1638,7 +1597,7 @@ exports[`t-call (template calling t-call with t-set inside and outside. 2 1`] =
let c1 = [], p1 = {key:1};
let vn1 = h('p', p1, c1);
scope.w = 'fromwrapper';
this.subTemplates['main'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__11__'}));
this.constructor.subTemplates['1'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__11__'}));
return vn1;
}"
`;
@@ -1654,7 +1613,7 @@ exports[`t-call (template calling t-call, conditional and t-set in t-call body 1
let vn1 = h('div', p1, c1);
scope.v1 = 'elif';
if (scope.v1==='if') {
this.subTemplates['callee1'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__3__'}));
this.constructor.subTemplates['1'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__3__'}));
}
else if (scope.v1==='elif') {
{
@@ -1666,7 +1625,7 @@ exports[`t-call (template calling t-call, conditional and t-set in t-call body 1
utils.getScope(scope, 'v').v = 'success';
scope[utils.zero] = c__0;
}
this.subTemplates['callee2'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__7__'}));
this.constructor.subTemplates['2'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__7__'}));
scope = _origScope6;
}
}
@@ -1683,7 +1642,35 @@ exports[`t-call (template calling t-call, global templates 1`] = `
let h = this.h;
let c1 = [], p1 = {key:1};
let vn1 = h('div', p1, c1);
this.subTemplates['john'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__3__'}));
this.constructor.subTemplates['1'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__3__'}));
return vn1;
}"
`;
exports[`t-call (template calling two different QWeb instances, and shared templates 1`] = `
"function anonymous(context, extra
) {
// Template name: \\"main\\"
let utils = this.constructor.utils;
let scope = Object.create(context);
let h = this.h;
let c1 = [], p1 = {key:1};
let vn1 = h('div', p1, c1);
this.constructor.subTemplates['1'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__3__'}));
return vn1;
}"
`;
exports[`t-call (template calling two different QWeb instances, and shared templates 2`] = `
"function anonymous(context, extra
) {
// Template name: \\"main\\"
let utils = this.constructor.utils;
let scope = Object.create(context);
let h = this.h;
let c1 = [], p1 = {key:1};
let vn1 = h('div', p1, c1);
this.constructor.subTemplates['1'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__3__'}));
return vn1;
}"
`;
@@ -1706,7 +1693,7 @@ exports[`t-call (template calling with unused body 1`] = `
scope[utils.zero] = c__0;
}
result = []
this.subTemplates['_basic-callee'].call(this, scope, Object.assign({}, extra, {parentNode: result, parent: utils.getComponent(context), key: '__4__'}));
this.constructor.subTemplates['1'].call(this, scope, Object.assign({}, extra, {parentNode: result, parent: utils.getComponent(context), key: '__4__'}));
result = result[0]
scope = _origScope3;
}
@@ -1732,7 +1719,7 @@ exports[`t-call (template calling with unused setbody 1`] = `
scope[utils.zero] = c__0;
}
result = []
this.subTemplates['_basic-callee'].call(this, scope, Object.assign({}, extra, {parentNode: result, parent: utils.getComponent(context), key: '__4__'}));
this.constructor.subTemplates['1'].call(this, scope, Object.assign({}, extra, {parentNode: result, parent: utils.getComponent(context), key: '__4__'}));
result = result[0]
scope = _origScope3;
}
@@ -1758,7 +1745,7 @@ exports[`t-call (template calling with used body 1`] = `
scope[utils.zero] = c__0;
}
result = []
this.subTemplates['_callee-printsbody'].call(this, scope, Object.assign({}, extra, {parentNode: result, parent: utils.getComponent(context), key: '__4__'}));
this.constructor.subTemplates['1'].call(this, scope, Object.assign({}, extra, {parentNode: result, parent: utils.getComponent(context), key: '__4__'}));
result = result[0]
scope = _origScope3;
}
@@ -1784,7 +1771,7 @@ exports[`t-call (template calling with used set body 1`] = `
utils.getScope(scope, 'foo').foo = 'ok';
scope[utils.zero] = c__0;
}
this.subTemplates['_callee-uses-foo'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__4__'}));
this.constructor.subTemplates['1'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__4__'}));
scope = _origScope3;
}
return vn1;
@@ -1922,6 +1909,23 @@ exports[`t-esc literal 1`] = `
}"
`;
exports[`t-esc t-esc inside t-call, with t-set outside 1`] = `
"function anonymous(context, extra
) {
// Template name: \\"main\\"
let utils = this.constructor.utils;
let scope = Object.create(context);
let h = this.h;
let c1 = [], p1 = {key:1};
let vn1 = h('div', p1, c1);
let c2 = new utils.VDomArray();
c2.push({text: \`Hi\`});
scope.v = c2
this.constructor.subTemplates['1'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__5__'}));
return vn1;
}"
`;
exports[`t-esc t-esc is escaped 1`] = `
"function anonymous(context, extra
) {
@@ -1986,7 +1990,7 @@ exports[`t-esc t-esc=0 is escaped 1`] = `
c4.push({text: \`escaped\`});
scope[utils.zero] = c__0;
}
this.subTemplates['test'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__5__'}));
this.constructor.subTemplates['1'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__5__'}));
scope = _origScope3;
}
return vn1;
@@ -2782,7 +2786,7 @@ exports[`t-on t-on with t-call 1`] = `
let h = this.h;
let c1 = [], p1 = {key:1};
let vn1 = h('div', p1, c1);
this.subTemplates['sub'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__4__'}));
this.constructor.subTemplates['1'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__4__'}));
return vn1;
}"
`;
@@ -2796,7 +2800,7 @@ exports[`t-on t-on, with arguments and t-call 1`] = `
let h = this.h;
let c1 = [], p1 = {key:1};
let vn1 = h('div', p1, c1);
this.subTemplates['sub'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__4__'}));
this.constructor.subTemplates['1'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__4__'}));
return vn1;
}"
`;
+79 -51
View File
@@ -195,7 +195,7 @@ describe("t-esc", () => {
v2: undefined,
v3: null,
v4: 0,
v5: ""
v5: "",
};
expect(renderToString(qweb, "test", vals)).toBe(
"<div><p>false</p><p></p><p></p><p>0</p><p></p></div>"
@@ -207,6 +207,13 @@ describe("t-esc", () => {
const result = renderToString(qweb, "test", { state: { list: [1, 2] } });
expect(result).toBe("<span>1,2</span>");
});
test("t-esc inside t-call, with t-set outside", () => {
qweb.addTemplate("main", `<div><t t-set="v">Hi</t><t t-call="sub"/></div>`);
qweb.addTemplate("sub", `<span t-esc="v"/>`);
const result = renderToString(qweb, "main");
expect(result).toBe("<div><span>Hi</span></div>");
});
});
describe("t-raw", () => {
@@ -486,7 +493,7 @@ describe("t-if", () => {
cond3: false,
cond4: false,
m: 5,
n: 2
n: 2,
};
expect(normalize(renderToString(qweb, "test", context))).toBe("<div>andormgtnlt</div>");
});
@@ -625,7 +632,7 @@ describe("attributes", () => {
test("object", () => {
qweb.addTemplate("test", `<div t-att="value"/>`);
const result = renderToString(qweb, "test", {
value: { a: 1, b: 2, c: 3 }
value: { a: 1, b: 2, c: 3 },
});
expect(result).toBe(`<div a="1" b="2" c="3"></div>`);
});
@@ -684,7 +691,7 @@ describe("attributes", () => {
const result = renderToString(qweb, "test", {
value1: 0,
value2: 1,
value3: 2
value3: 2,
});
expect(result).toBe(`<div foo="a 0 is 1 of 2 ]"></div>`);
});
@@ -703,7 +710,7 @@ describe("attributes", () => {
const result = renderToString(qweb, "test", {
bar: 0,
baz: 1,
qux: { qux: "<>" }
qux: { qux: "<>" },
});
const expected = '<div foo="<foo" bar="0" baz="<1>" qux="<>"></div>';
expect(result).toBe(expected);
@@ -715,6 +722,12 @@ describe("attributes", () => {
expect(result).toBe(`<div class="hello world"></div>`);
});
test("class and t-att-class should combine together", () => {
qweb.addTemplate("test", `<div t-att-class="value" class="hello" />`);
const result = renderToString(qweb, "test", { value: "world" });
expect(result).toBe(`<div class="world hello"></div>`);
});
test("class and t-attf-class with ternary operation", () => {
qweb.addTemplate("test", `<div class="hello" t-attf-class="{{value ? 'world' : ''}}"/>`);
const result = renderToString(qweb, "test", { value: true });
@@ -734,7 +747,7 @@ describe("t-call (template calling", () => {
qweb.addTemplate("caller", '<div><t t-call="_basic-callee"/></div>');
const expected = "<div><span>ok</span></div>";
expect(renderToString(qweb, "caller")).toBe(expected);
expect(qweb.subTemplates["_basic-callee"].toString()).toMatchSnapshot();
expect(qweb.subTemplates["_basic-callee"]).toBeTruthy();
});
test("basic caller, no parent node", () => {
@@ -742,7 +755,7 @@ describe("t-call (template calling", () => {
qweb.addTemplate("caller", '<t t-call="_basic-callee"/>');
const expected = "<div>ok</div>";
expect(renderToString(qweb, "caller")).toBe(expected);
expect(qweb.subTemplates["_basic-callee"].toString()).toMatchSnapshot();
expect(qweb.subTemplates["_basic-callee"]).toBeTruthy();
});
test("t-call with t-if", () => {
@@ -750,7 +763,7 @@ describe("t-call (template calling", () => {
qweb.addTemplate("caller", '<div><t t-if="flag" t-call="sub"/></div>');
const expected = "<div><span>ok</span></div>";
expect(renderToString(qweb, "caller", { flag: true })).toBe(expected);
expect(qweb.subTemplates["sub"].toString()).toMatchSnapshot();
expect(qweb.subTemplates["sub"]).toBeTruthy();
});
test("t-call not allowed on a non t node", () => {
@@ -885,7 +898,8 @@ describe("t-call (template calling", () => {
`);
const expected = "<div><span>hey</span></div>";
expect(renderToString(qweb, "recursive")).toBe(expected);
const recursiveFn = Object.values(qweb.subTemplates)[0] as any;
const subId = qweb.subTemplates["recursive"];
const recursiveFn = QWeb.subTemplates[subId] as any;
expect(recursiveFn.toString()).toMatchSnapshot();
});
@@ -913,7 +927,8 @@ describe("t-call (template calling", () => {
expect(renderToString(qweb, "Parent", { root }, { fiber: { vars: {}, scope: {} } })).toBe(
expected
);
const recursiveFn = Object.values(qweb.subTemplates)[0] as any;
const subId = qweb.subTemplates["nodeTemplate"];
const recursiveFn = QWeb.subTemplates[subId] as any;
expect(recursiveFn.toString()).toMatchSnapshot();
});
@@ -940,7 +955,8 @@ describe("t-call (template calling", () => {
const expected =
"<div><div><p>a</p><div><p>b</p><div><p>d</p></div></div><div><p>c</p></div></div></div>";
expect(renderToString(qweb, "Parent", { root }, { fiber: {} })).toBe(expected);
const recursiveFn = Object.values(qweb.subTemplates)[0] as any;
const subId = qweb.subTemplates["nodeTemplate"];
const recursiveFn = QWeb.subTemplates[subId] as any;
expect(recursiveFn.toString()).toMatchSnapshot();
});
@@ -967,12 +983,13 @@ describe("t-call (template calling", () => {
`);
const root = {
val: "a",
children: [{ val: "b", children: [{ val: "c", children: [{ val: "d" }] }] }]
children: [{ val: "b", children: [{ val: "c", children: [{ val: "d" }] }] }],
};
const expected =
"<div><div><p>a 2</p><div><p>b 3</p><div><p>c 4</p><div><p>d 5</p></div></div></div></div></div>";
expect(renderToString(qweb, "Parent", { root })).toBe(expected);
const recursiveFn = Object.values(qweb.subTemplates)[0] as any;
const subId = qweb.subTemplates["nodeTemplate"];
const recursiveFn = QWeb.subTemplates[subId] as any;
expect(recursiveFn.toString()).toMatchSnapshot();
});
@@ -1044,6 +1061,17 @@ describe("t-call (template calling", () => {
const context = { list: [{ val: 1 }, { val: 2 }, { val: 3 }] };
expect(trim(renderToString(qweb, "wrapper", context))).toBe(expected);
});
test("two different QWeb instances, and shared templates", () => {
QWeb.registerTemplate("sub", `<span>ok</span>`);
QWeb.registerTemplate("main", `<div><t t-call="sub"/></div>`);
const qweb1 = new QWeb();
const qweb2 = new QWeb();
expect(renderToString(qweb1, "main")).toBe("<div><span>ok</span></div>");
expect(renderToString(qweb2, "main")).toBe("<div><span>ok</span></div>");
});
});
describe("foreach", () => {
@@ -1229,7 +1257,7 @@ describe("t-on", () => {
{
add() {
a = 3;
}
},
},
{ handlers: [] }
);
@@ -1252,7 +1280,7 @@ describe("t-on", () => {
},
handleDblClick() {
steps.push("dblclick");
}
},
},
{ handlers: [] }
);
@@ -1272,7 +1300,7 @@ describe("t-on", () => {
{
add(n) {
a = a + n;
}
},
},
{ handlers: [] }
);
@@ -1289,7 +1317,7 @@ describe("t-on", () => {
{
add({ val }) {
a = a + val;
}
},
},
{ handlers: [] }
);
@@ -1306,7 +1334,7 @@ describe("t-on", () => {
{
doSomething(arg) {
expect(arg).toEqual({});
}
},
},
{ handlers: [] }
);
@@ -1322,7 +1350,7 @@ describe("t-on", () => {
{
doSomething(arg) {
expect(arg).toEqual({});
}
},
},
{ handlers: [] }
);
@@ -1344,7 +1372,7 @@ describe("t-on", () => {
{
activate(action) {
expect(action).toBe("someval");
}
},
},
{ handlers: [] }
);
@@ -1357,7 +1385,7 @@ describe("t-on", () => {
let owner = {
add() {
expect(this).toBe(owner);
}
},
};
const node = renderToDOM(qweb, "test", owner, { handlers: [] });
(<HTMLElement>node).click();
@@ -1367,8 +1395,8 @@ describe("t-on", () => {
qweb.addTemplate("test", `<button t-on-click="state.counter++">Click</button>`);
let owner = {
state: {
counter: 0
}
counter: 0,
},
};
const node = renderToDOM(qweb, "test", owner, { handlers: [] });
expect(owner.state.counter).toBe(0);
@@ -1381,10 +1409,10 @@ describe("t-on", () => {
let owner = {
state: {
counter: 0,
incrementCounter: inc => {
incrementCounter: (inc) => {
owner.state.counter += inc;
}
}
},
},
};
const node = renderToDOM(qweb, "test", owner, { handlers: [] });
expect(owner.state.counter).toBe(0);
@@ -1396,8 +1424,8 @@ describe("t-on", () => {
qweb.addTemplate("test", `<button t-on-click="state.flag = !state.flag">Toggle</button>`);
let owner = {
state: {
flag: true
}
flag: true,
},
};
const node = renderToDOM(qweb, "test", owner, { handlers: [] });
expect(owner.state.flag).toBe(true);
@@ -1414,8 +1442,8 @@ describe("t-on", () => {
return n + 1;
},
state: {
n: 11
}
n: 11,
},
};
const node = renderToDOM(qweb, "test", owner, { handlers: [] });
expect(owner.state.n).toBe(11);
@@ -1431,7 +1459,7 @@ describe("t-on", () => {
let owner = {
update() {
expect(this).toBe(owner);
}
},
};
const node = renderToDOM(qweb, "main", owner, { handlers: [] });
@@ -1448,7 +1476,7 @@ describe("t-on", () => {
expect(this).toBe(owner);
expect(val).toBe(444);
},
value: 444
value: 444,
};
const node = renderToDOM(qweb, "main", owner, { handlers: [] });
@@ -1477,7 +1505,7 @@ describe("t-on", () => {
onClickPreventedAndStopped(e) {
expect(e.defaultPrevented).toBe(true);
expect(e.cancelBubble).toBe(true);
}
},
};
const node = renderToDOM(qweb, "test", owner, { handlers: [] });
@@ -1503,7 +1531,7 @@ describe("t-on", () => {
},
onClickSelf(e) {
steps.push("onClickSelf");
}
},
};
const node = renderToDOM(qweb, "test", owner, { handlers: [] });
@@ -1527,10 +1555,10 @@ describe("t-on", () => {
);
let steps: boolean[] = [];
let owner = {
onClick() {}
onClick() {},
};
const node = renderToDOM(qweb, "test", owner, { handlers: [] });
(<HTMLElement>node).addEventListener("click", function(e) {
(<HTMLElement>node).addEventListener("click", function (e) {
steps.push(e.defaultPrevented);
});
@@ -1552,10 +1580,10 @@ describe("t-on", () => {
);
let steps: boolean[] = [];
let owner = {
onClick() {}
onClick() {},
};
const node = renderToDOM(qweb, "test", owner, { handlers: [] });
(<HTMLElement>node).addEventListener("click", function(e) {
(<HTMLElement>node).addEventListener("click", function (e) {
steps.push(e.defaultPrevented);
});
@@ -1583,13 +1611,13 @@ describe("t-on", () => {
const owner = {
projects: [
{ id: 1, name: "Project 1" },
{ id: 2, name: "Project 2" }
{ id: 2, name: "Project 2" },
],
onEdit(projectId, ev) {
expect(ev.defaultPrevented).toBe(true);
steps.push(projectId);
}
},
};
const node = <HTMLElement>renderToDOM(qweb, "test", owner, { handlers: [] });
@@ -1614,7 +1642,7 @@ describe("t-on", () => {
);
const node = renderToDOM(qweb, "test", {}, { handlers: [] });
node.addEventListener("click", e => {
node.addEventListener("click", (e) => {
expect(e.defaultPrevented).toBe(true);
});
@@ -1630,7 +1658,7 @@ describe("t-on", () => {
text: "Click here",
onClick() {
steps.push("onClick");
}
},
};
const node = <HTMLElement>renderToDOM(qweb, "test", owner, { handlers: [] });
@@ -1649,7 +1677,7 @@ describe("t-on", () => {
html: "Click <b>here</b>",
onClick() {
steps.push("onClick");
}
},
};
const node = <HTMLElement>renderToDOM(qweb, "test", owner, { handlers: [] });
@@ -1676,7 +1704,7 @@ describe("t-on", () => {
},
doSomething() {
steps.push("normal");
}
},
};
const node = renderToDOM(qweb, "test", owner, { handlers: [] });
@@ -1840,7 +1868,7 @@ describe("t-key", () => {
);
expect(
renderToString(qweb, "test", {
beers: [{ id: 12, name: "Chimay Rouge" }]
beers: [{ id: 12, name: "Chimay Rouge" }],
})
).toMatchSnapshot();
});
@@ -1953,9 +1981,9 @@ describe("properly support svg", () => {
describe("translation support", () => {
test("can translate node content", () => {
const translations = {
word: "mot"
word: "mot",
};
const translateFn = expr => translations[expr] || expr;
const translateFn = (expr) => translations[expr] || expr;
const qweb = new QWeb({ translateFn });
qweb.addTemplate("test", "<div>word</div>");
expect(renderToString(qweb, "test")).toBe("<div>mot</div>");
@@ -1963,9 +1991,9 @@ describe("translation support", () => {
test("does not translate node content if disabled", () => {
const translations = {
word: "mot"
word: "mot",
};
const translateFn = expr => translations[expr] || expr;
const translateFn = (expr) => translations[expr] || expr;
const qweb = new QWeb({ translateFn });
qweb.addTemplate(
"test",
@@ -1980,9 +2008,9 @@ describe("translation support", () => {
test("some attributes are translated", () => {
const translations = {
word: "mot"
word: "mot",
};
const translateFn = expr => translations[expr] || expr;
const translateFn = (expr) => translations[expr] || expr;
const qweb = new QWeb({ translateFn });
qweb.addTemplate(
"test",
+10 -10
View File
@@ -6,12 +6,12 @@ describe("tokenizer", () => {
expect(tokenize("{}")).toEqual([
{ type: "LEFT_BRACE", value: "{" },
{ type: "RIGHT_BRACE", value: "}" }
{ type: "RIGHT_BRACE", value: "}" },
]);
expect(tokenize("{ }}")).toEqual([
{ type: "LEFT_BRACE", value: "{" },
{ type: "RIGHT_BRACE", value: "}" },
{ type: "RIGHT_BRACE", value: "}" }
{ type: "RIGHT_BRACE", value: "}" },
]);
expect(tokenize("a")).toEqual([{ type: "SYMBOL", value: "a" }]);
expect(tokenize("true")).toEqual([{ type: "SYMBOL", value: "true" }]);
@@ -25,15 +25,15 @@ describe("tokenizer", () => {
{ type: "SYMBOL", value: "a" },
{ type: "COLON", value: ":" },
{ type: "VALUE", value: "2" },
{ type: "RIGHT_BRACE", value: "}" }
{ type: "RIGHT_BRACE", value: "}" },
]);
expect(tokenize("a,")).toEqual([
{ type: "SYMBOL", value: "a" },
{ type: "COMMA", value: "," }
{ type: "COMMA", value: "," },
]);
expect(tokenize("][")).toEqual([
{ type: "RIGHT_BRACKET", value: "]" },
{ type: "LEFT_BRACKET", value: "[" }
{ type: "LEFT_BRACKET", value: "[" },
]);
});
@@ -44,23 +44,23 @@ describe("tokenizer", () => {
{ type: "OPERATOR", value: "<" },
{ type: "OPERATOR", value: ">" },
{ type: "OPERATOR", value: "!==" },
{ type: "OPERATOR", value: "!=" }
{ type: "OPERATOR", value: "!=" },
]);
expect(tokenize("typeof a")).toEqual([
{ type: "OPERATOR", value: "typeof " },
{ type: "SYMBOL", value: "a" }
{ type: "SYMBOL", value: "a" },
]);
expect(tokenize("a...1")).toEqual([
{ type: "SYMBOL", value: "a" },
{ type: "OPERATOR", value: "..." },
{ type: "VALUE", value: "1" }
{ type: "VALUE", value: "1" },
]);
expect(tokenize("a in b")).toEqual([
{ type: "SYMBOL", value: "a" },
{ type: "OPERATOR", value: "in " },
{ type: "SYMBOL", value: "b" }
{ type: "SYMBOL", value: "b" },
]);
});
@@ -76,7 +76,7 @@ describe("tokenizer", () => {
expect(tokenize('"hello ged"')).toEqual([{ type: "VALUE", value: '"hello ged"' }]);
expect(tokenize('"hello ged"}')).toEqual([
{ type: "VALUE", value: '"hello ged"' },
{ type: "RIGHT_BRACE", value: "}" }
{ type: "RIGHT_BRACE", value: "}" },
]);
expect(tokenize('"hello \\"ged\\""')).toEqual([{ type: "VALUE", value: '"hello \\"ged\\""' }]);
});
+3 -3
View File
@@ -37,7 +37,7 @@ describe("Link component", () => {
const routes = [
{ name: "about", path: "/about" },
{ name: "users", path: "/users" }
{ name: "users", path: "/users" },
];
router = new TestRouter(env, routes, { mode: "history" });
@@ -71,7 +71,7 @@ describe("Link component", () => {
const routes = [
{ name: "about", path: "/about" },
{ name: "users", path: "/users" }
{ name: "users", path: "/users" },
];
router = new TestRouter(env, routes, { mode: "history" });
@@ -81,7 +81,7 @@ describe("Link component", () => {
expect(window.location.pathname).toBe("/users");
var evt = new MouseEvent("click", {
button: 1
button: 1,
});
fixture.querySelector("a")!.dispatchEvent(evt);
+1 -1
View File
@@ -41,7 +41,7 @@ describe("RouteComponent", () => {
const routes = [
{ name: "about", path: "/about", component: About },
{ name: "users", path: "/users", component: Users }
{ name: "users", path: "/users", component: Users },
];
router = new TestRouter(env, routes, { mode: "history" });
+10 -10
View File
@@ -21,7 +21,7 @@ describe("router miscellaneous", () => {
test("validate routes shape", () => {
expect(() => {
router = new TestRouter(env, [
{ name: "someroute", path: "/some/path", redirect: { abc: "hey" } as Destination }
{ name: "someroute", path: "/some/path", redirect: { abc: "hey" } as Destination },
]);
}).toThrow(`Invalid destination: {"abc":"hey"}`);
});
@@ -133,7 +133,7 @@ describe("getRouteParams", () => {
test("match some parameterized routes", () => {
router = new TestRouter(env, []);
expect(router["getRouteParams"]({ path: "/invoices/{{id}}" } as Route, "/invoices/3")).toEqual({
id: "3"
id: "3",
});
});
@@ -141,7 +141,7 @@ describe("getRouteParams", () => {
router = new TestRouter(env, [], { mode: "hash" });
expect(router["getRouteParams"]({ path: "/invoices/{{id}}" } as Route, "#/invoices/3")).toEqual(
{
id: "3"
id: "3",
}
);
});
@@ -151,7 +151,7 @@ describe("getRouteParams", () => {
expect(
router["getRouteParams"]({ path: "/invoices/{{id.number}}" } as Route, "/invoices/3")
).toEqual({
id: 3
id: 3,
});
});
@@ -160,7 +160,7 @@ describe("getRouteParams", () => {
expect(
router["getRouteParams"]({ path: "/invoices/{{id.number}}" } as Route, "#/invoices/3")
).toEqual({
id: 3
id: 3,
});
});
});
@@ -169,7 +169,7 @@ describe("redirect", () => {
test("can redirect to other route", async () => {
router = new TestRouter(env, [
{ name: "routea", path: "/some/path" },
{ name: "routeb", path: "/some/other/path", redirect: { to: "routea" } }
{ name: "routeb", path: "/some/other/path", redirect: { to: "routea" } },
]);
await router.start();
@@ -182,7 +182,7 @@ describe("redirect", () => {
test("can redirect to other path", async () => {
router = new TestRouter(env, [
{ name: "routea", path: "/some/path" },
{ name: "routeb", path: "/some/other/path", redirect: { path: "/some/path" } }
{ name: "routeb", path: "/some/other/path", redirect: { path: "/some/path" } },
]);
router.navigate = jest.fn(router.navigate);
@@ -214,7 +214,7 @@ describe("beforeRouteEnter", () => {
const guard = jest.fn(() => false);
router = new TestRouter(env, [
{ name: "routea", path: "/some/patha" },
{ name: "routeb", path: "/some/pathb", beforeRouteEnter: guard }
{ name: "routeb", path: "/some/pathb", beforeRouteEnter: guard },
]);
await router.start();
@@ -235,7 +235,7 @@ describe("beforeRouteEnter", () => {
router = new TestRouter(env, [
{ name: "routea", path: "/some/patha" },
{ name: "routeb", path: "/some/pathb", beforeRouteEnter: guard },
{ name: "routec", path: "/some/pathc" }
{ name: "routec", path: "/some/pathc" },
]);
await router.start();
@@ -255,7 +255,7 @@ describe("beforeRouteEnter", () => {
});
router = new TestRouter(env, [
{ name: "landing", path: "/", beforeRouteEnter: guard },
{ name: "otherroute", path: "/some/other/route" }
{ name: "otherroute", path: "/some/other/route" },
]);
expect(window.location.pathname).toBe("/");
+28 -28
View File
@@ -8,7 +8,7 @@ describe("basic use", () => {
const actions = {
inc({ state }, delta) {
state.n += delta;
}
},
};
const store = new Store({ state, actions });
@@ -25,7 +25,7 @@ describe("basic use", () => {
state.n1 += delta1;
state.n2 += delta2;
state.n3 += delta3;
}
},
};
const store = new Store({ state, actions });
@@ -47,7 +47,7 @@ describe("basic use", () => {
},
inc100({ dispatch }) {
dispatch("inc", 100);
}
},
};
const store = new Store({ state, actions });
@@ -61,7 +61,7 @@ describe("basic use", () => {
const actions = {
inc({ state }) {
return ++state.n;
}
},
};
const store = new Store({ state, actions });
@@ -87,7 +87,7 @@ describe("basic use", () => {
async setTo10({ dispatch }) {
await Promise.resolve();
dispatch("setN", 10);
}
},
};
const store = new Store({ state, actions });
@@ -115,7 +115,7 @@ describe("basic use", () => {
await Promise.resolve();
dispatch("setN", 10);
return 5;
}
},
};
const store = new Store({ state, actions });
@@ -132,7 +132,7 @@ describe("basic use", () => {
const actions = {
someaction({ env }) {
expect(env).toBe(someEnv);
}
},
};
const store = new Store({ state: {}, actions, env: someEnv });
@@ -145,15 +145,15 @@ describe("basic use", () => {
1: {
id: 1,
name: "bertinchamps",
tasterID: 1
}
tasterID: 1,
},
},
tasters: {
1: {
id: 1,
name: "aaron"
}
}
name: "aaron",
},
},
};
const getters: { [key: string]: Getter } = {
beerTasterName({ state }, beerID) {
@@ -161,7 +161,7 @@ describe("basic use", () => {
},
bestBeerName({ state }) {
return state.beers[1].name;
}
},
};
const store = new Store({ state, actions: {}, getters });
expect(store.getters).toBeDefined();
@@ -176,15 +176,15 @@ describe("basic use", () => {
1: {
id: 1,
name: "bertinchamps",
tasterID: 1
}
tasterID: 1,
},
},
tasters: {
1: {
id: 1,
name: "aaron"
}
}
name: "aaron",
},
},
};
const getters = {
beerTasterName({ state }, beerID) {
@@ -192,14 +192,14 @@ describe("basic use", () => {
},
bestBeerName({ state }) {
return state.beers[1].name;
}
},
};
const actions = {
action({ getters }) {
expect(getters).toBeDefined();
expect(getters.bestBeerName()).toBe("bertinchamps");
expect(getters.beerTasterName(1)).toBe("aaron");
}
},
};
const store = new Store({ state, actions, getters });
store.dispatch("action");
@@ -215,7 +215,7 @@ describe("basic use", () => {
},
c({}, i) {
return `c${i}`;
}
},
};
const store = new Store({ getters, state: {}, actions: {} });
@@ -226,7 +226,7 @@ describe("basic use", () => {
describe("advanced state properties", () => {
test("state in the store is reference equal after mutation", async () => {
const actions = {
donothing() {}
donothing() {},
};
const store = new Store({ state: {}, actions });
const state = store.state;
@@ -242,7 +242,7 @@ describe("advanced state properties", () => {
expect(state.a.length).toBe(3);
const l = state.a.push(53);
expect(l).toBe(4);
}
},
};
const store = new Store({ state, actions });
store.dispatch("m");
@@ -253,11 +253,11 @@ describe("advanced state properties", () => {
const actions = {
dosomething({ state }) {
Object.assign(state.westmalle, { a: 3, b: 4 });
}
},
};
const store = new Store({
state: { westmalle: { a: 1, b: 2 } },
actions
actions,
});
store.dispatch("dosomething");
expect(store.state.westmalle).toEqual({ a: 3, b: 4 });
@@ -267,7 +267,7 @@ describe("advanced state properties", () => {
const actions = {
inc({ state }) {
state.counter++;
}
},
};
const state = { counter: 0 };
const store = new Store({ state, actions });
@@ -284,7 +284,7 @@ describe("updates triggered by the store", () => {
const actions = {
inc({ state }, delta) {
state.n += delta;
}
},
};
const store = new Store({ state, actions });
store.on("update", null, () => updateCounter++);
@@ -308,7 +308,7 @@ describe("updates triggered by the store", () => {
noop2({ state }) {
const val = state.n;
state.n = val;
}
},
};
const store = new Store({ state, actions });
store.on("update", null, () => updateCounter++);
+90 -90
View File
@@ -25,7 +25,7 @@ describe("connecting a component to store", () => {
const actions = {
addTodo({ state }, msg) {
state.todos.push({ msg, id: nextId++ });
}
},
};
const store = new Store({ state, actions });
@@ -34,7 +34,7 @@ describe("connecting a component to store", () => {
<div>
<span t-foreach="todos" t-key="todo.id" t-as="todo"><t t-esc="todo.msg"/></span>
</div>`;
todos = useStore(state => state.todos);
todos = useStore((state) => state.todos);
}
(<any>env).store = store;
@@ -56,7 +56,7 @@ describe("connecting a component to store", () => {
},
setNotNull({ state }) {
state.nullValue = "ok";
}
},
};
const store = new Store({ state, actions });
@@ -70,15 +70,15 @@ describe("connecting a component to store", () => {
nullValue: string;
constructor() {
super();
this.isBoolean = useStore(state => state.isBoolean, {
onUpdate: isBoolean => {
this.isBoolean = useStore((state) => state.isBoolean, {
onUpdate: (isBoolean) => {
this.isBoolean = isBoolean;
}
},
});
this.nullValue = useStore(state => state.nullValue, {
onUpdate: nullValue => {
this.nullValue = useStore((state) => state.nullValue, {
onUpdate: (nullValue) => {
this.nullValue = nullValue;
}
},
});
}
}
@@ -105,15 +105,15 @@ describe("connecting a component to store", () => {
class App extends Component {
static template = xml`<div t-esc="mapAdd"/>`;
storeProps = {
array: useStore(state => {
array: useStore((state) => {
if (state.useSmallArray) {
return state.smallerArray;
}
return state.biggerArray;
})
}),
};
get mapAdd() {
return this.storeProps.array.map(a => {
return this.storeProps.array.map((a) => {
return a + 1;
});
}
@@ -133,7 +133,7 @@ describe("connecting a component to store", () => {
test("throw error if no store is found", async () => {
class App extends Component {
static template = xml`<div></div>`;
todos = useStore(state => state.todos);
todos = useStore((state) => state.todos);
}
let error;
@@ -153,7 +153,7 @@ describe("connecting a component to store", () => {
class App extends Component {
static template = xml`<div/>`;
storeState = useStore(state => state.a);
storeState = useStore((state) => state.a);
}
(<any>env).store = store;
@@ -170,7 +170,7 @@ describe("connecting a component to store", () => {
doSomething({ state }) {
state.a = 2;
state.b = 3;
}
},
};
const store = new Store({ state, actions });
@@ -180,8 +180,8 @@ describe("connecting a component to store", () => {
<span t-esc="a.value"/>
<span t-esc="b.value"/>
</div>`;
a = useStore(state => ({ value: state.a }));
b = useStore(state => ({ value: state.b }));
a = useStore((state) => ({ value: state.a }));
b = useStore((state) => ({ value: state.b }));
}
App.prototype.__render = jest.fn(App.prototype.__render);
@@ -204,7 +204,7 @@ describe("connecting a component to store", () => {
const actions = {
addTodo({ state }, msg) {
state.todos.push({ msg, id: nextId++ });
}
},
};
const store = new Store({ state, actions });
@@ -213,7 +213,7 @@ describe("connecting a component to store", () => {
<div>
<span t-foreach="todos" t-key="todo.id" t-as="todo"><t t-esc="todo.msg"/></span>
</div>`;
todos = useStore(state => state.todos);
todos = useStore((state) => state.todos);
}
App.prototype.__render = jest.fn(App.prototype.__render);
@@ -241,13 +241,13 @@ describe("connecting a component to store", () => {
const actions = {
addTodo({ state }, msg) {
state.todos.push({ msg, id: nextId++ });
}
},
};
const store = new Store({ state, actions });
class App extends Component {
static template = xml`<div><t t-esc="nbrTodos.value"/></div>`;
nbrTodos = useStore(state => ({ value: state.todos.length }));
nbrTodos = useStore((state) => ({ value: state.todos.length }));
}
(<any>env).store = store;
@@ -270,12 +270,12 @@ describe("connecting a component to store", () => {
},
incrementA({ state }) {
state.a++;
}
},
};
const store = new Store({ state, actions });
class App extends Component {
static template = xml`<div><t t-esc="nbrTodos.value"/></div>`;
nbrTodos = useStore(state => ({ value: state.todos.length }), { isEqual: shallowEqual });
nbrTodos = useStore((state) => ({ value: state.todos.length }), { isEqual: shallowEqual });
}
App.prototype.__render = jest.fn(App.prototype.__render);
@@ -303,7 +303,7 @@ describe("connecting a component to store", () => {
const actions = {
addTodo({ state }, msg) {
state.todos.push({ msg, id: nextId++ });
}
},
};
const store = new Store({ state, actions });
@@ -312,7 +312,7 @@ describe("connecting a component to store", () => {
<div>
<span t-foreach="todos" t-key="todo.id" t-as="todo"><t t-esc="todo.msg"/></span>
</div>`;
todos = useStore(state => state.todos, { store });
todos = useStore((state) => state.todos, { store });
}
const app = new App();
@@ -331,8 +331,8 @@ describe("connecting a component to store", () => {
actions: {
inc({ state }) {
state.value++;
}
}
},
},
});
(<any>env).store = store;
@@ -342,7 +342,7 @@ describe("connecting a component to store", () => {
<button t-on-click="dispatch('inc')">Inc</button>
<span><t t-esc="storeState.value"/></span>
</div>`;
storeState = useStore(state => state);
storeState = useStore((state) => state);
dispatch = useDispatch();
}
const app = new App();
@@ -360,15 +360,15 @@ describe("connecting a component to store", () => {
const state = {
todos: [
{ id: 1, text: "jupiler" },
{ id: 2, text: "chimay" }
]
{ id: 2, text: "chimay" },
],
};
const store = new Store({ state, actions: {} });
class TodoItem extends Component {
static template = xml`<span><t t-esc="todo.text"/></span>`;
todo = useStore((state, props) => {
return state.todos.find(t => t.id === props.todoId);
return state.todos.find((t) => t.id === props.todoId);
});
}
@@ -395,14 +395,14 @@ describe("connecting a component to store", () => {
const actions = {
addTodo({ state }, text) {
state.todos.push({ text, id: nextId++ });
}
},
};
const store = new Store({ state, actions });
class TodoItem extends Component {
static template = xml`<span><t t-esc="todo.text"/></span>`;
todo = useStore((state, props) => {
return state.todos.find(t => t.id === props.id);
return state.todos.find((t) => t.id === props.id);
});
}
@@ -412,7 +412,7 @@ describe("connecting a component to store", () => {
<TodoItem t-foreach="todos" t-as="todo" id="todo.id" t-key="todo.id"/>
</div>`;
static components = { TodoItem };
todos = useStore(state => state.todos);
todos = useStore((state) => state.todos);
}
(<any>env).store = store;
@@ -431,16 +431,16 @@ describe("connecting a component to store", () => {
importantID: 1,
todos: [
{ id: 1, text: "jupiler" },
{ id: 2, text: "bertinchamps" }
]
{ id: 2, text: "bertinchamps" },
],
};
const getters = {
importantTodoText({ state }) {
return state.todos.find(todo => todo.id === state.importantID).text;
return state.todos.find((todo) => todo.id === state.importantID).text;
},
text({ state }, id) {
return state.todos.find(todo => todo.id === id).text;
}
return state.todos.find((todo) => todo.id === id).text;
},
};
const store = new Store({ state, getters });
@@ -452,10 +452,10 @@ describe("connecting a component to store", () => {
</div>`;
getters = useGetters();
storeProps = useStore((state, props) => {
const todo = state.todos.find(t => t.id === props.id);
const todo = state.todos.find((t) => t.id === props.id);
return {
activeTodoText: this.getters.text(todo.id),
importantTodoText: this.getters.importantTodoText()
importantTodoText: this.getters.importantTodoText(),
};
});
}
@@ -468,7 +468,7 @@ describe("connecting a component to store", () => {
<TodoItem id="todo.id" t-key="todo.id"/>
</t>
</div>`;
todos = useStore(state => state.todos);
todos = useStore((state) => state.todos);
}
(<any>env).store = store;
@@ -513,13 +513,13 @@ describe("connecting a component to store", () => {
</div>`;
// we have here a new object
data = useStore(state => ({ beers: state.beers, otherKey: 1 }));
data = useStore((state) => ({ beers: state.beers, otherKey: 1 }));
}
const actions = {
addBeer({ state }, name) {
state.beers.push({ name });
}
},
};
const state = { beers: [{ name: "jupiler" }] };
@@ -542,9 +542,9 @@ describe("connecting a component to store", () => {
class Beer extends Component {
static template = xml`<span><t t-esc="beer.name"/></span>`;
beer = useStore((state, props) => state.beers[props.id], {
onUpdate: result => {
onUpdate: (result) => {
++counter;
}
},
});
}
@@ -558,7 +558,7 @@ describe("connecting a component to store", () => {
const actions = {
renameBeer({ state }, { id, name }) {
state.beers[id].name = name;
}
},
};
const store = new Store({ state, actions });
(<any>env).store = store;
@@ -609,7 +609,7 @@ describe("connecting a component to store", () => {
data = useStore((state, props) => ({
selected: state.beers[props.id],
consumed: state.beers[state.consumedID] || null,
taster: state.taster
taster: state.taster,
}));
}
@@ -622,14 +622,14 @@ describe("connecting a component to store", () => {
const actions = {
consume({ state }, beerId) {
state.consumedID = beerId;
}
},
};
const state = {
beers: {
1: { name: "jupiler" }
1: { name: "jupiler" },
},
consumedID: null,
taster: "aaron"
taster: "aaron",
};
const store = new Store({ state, actions });
(<any>env).store = store;
@@ -665,11 +665,11 @@ describe("connecting a component to store", () => {
<span t-if="info.selected">selected:<t t-esc="info.selected.name"/></span>
<span t-if="info.consumed">consumed:<t t-esc="info.consumed.name"/></span>
</div>`;
info = useStore(function(state, props) {
info = useStore(function (state, props) {
return {
selected: state.beers[props.id],
consumed: state.beers[state.consumedID] || null,
taster: state.taster
taster: state.taster,
};
});
}
@@ -689,14 +689,14 @@ describe("connecting a component to store", () => {
},
renameBeer({ state }, { beerId, name }) {
state.beers[beerId].name = name;
}
},
};
const state = {
beers: {
1: { name: "jupiler" }
1: { name: "jupiler" },
},
consumedID: null,
taster: "aaron"
taster: "aaron",
};
const store = new Store({ state, actions });
(<any>env).store = store;
@@ -747,11 +747,11 @@ describe("connecting a component to store", () => {
const actions = {
setValue({ state }, val) {
state.x.val = val;
}
},
};
class Parent extends Component {
static template = xml`<div><t t-esc="x.val"/></div>`;
x = useStore(state => {
x = useStore((state) => {
return Object.assign({}, state.x);
});
dispatch = useDispatch();
@@ -782,11 +782,11 @@ describe("connecting a component to store", () => {
static template = xml`<div><Child key="state.current"/></div>`;
static components = { Child };
state = useStore(state => {
state = useStore((state) => {
steps.push("parent");
return {
current: state.current,
isvisible: state.isvisible
isvisible: state.isvisible,
};
});
}
@@ -795,7 +795,7 @@ describe("connecting a component to store", () => {
const actions = {
setCurrent({ state }, c) {
state.current = c;
}
},
};
const store = new Store({ state, actions });
@@ -831,7 +831,7 @@ describe("connecting a component to store", () => {
<Child t-if="state.flag" someId="state.someId"/>
</div>`;
static components = { Child };
state = useStore(s => {
state = useStore((s) => {
steps.push("parent");
return { flag: s.flag, someId: s.someId };
});
@@ -846,7 +846,7 @@ describe("connecting a component to store", () => {
const actions = {
setFlagToFalse({ state }) {
state.flag = false;
}
},
};
const store = new Store({ state, actions });
@@ -874,15 +874,15 @@ describe("connecting a component to store", () => {
const actions = {
editTodo({ state }) {
state.todos[1].title = "abc";
}
},
};
const todos = { 1: { id: 1, title: "kikoou" } };
const state = {
todos
todos,
};
const store = new Store({
state,
actions
actions,
});
class TodoItem extends Component<{}, EnvWithStore> {
@@ -894,7 +894,7 @@ describe("connecting a component to store", () => {
state = useStore((state, props) => {
steps.push("item:usestore");
return {
todo: state.todos[props.id]
todo: state.todos[props.id],
};
});
@@ -914,7 +914,7 @@ describe("connecting a component to store", () => {
</t>
</div>`;
static components = { TodoItem };
state = useStore(state => {
state = useStore((state) => {
steps.push("app:usestore");
return { todos: state.todos };
});
@@ -947,15 +947,15 @@ describe("connecting a component to store", () => {
const actions = {
removeTodo({ state }) {
delete state.todos[1];
}
},
};
const todos = { 1: { id: 1, title: "kikoou" } };
const state = {
todos
todos,
};
const store = new Store({
state,
actions
actions,
});
class TodoItem extends Component<{}, EnvWithStore> {
@@ -967,7 +967,7 @@ describe("connecting a component to store", () => {
state = useStore((state, props) => {
steps.push("item:usestore");
return {
todo: state.todos[props.id]
todo: state.todos[props.id],
};
});
@@ -988,7 +988,7 @@ describe("connecting a component to store", () => {
</t>
</div>`;
static components = { TodoItem };
state = useStore(state => {
state = useStore((state) => {
steps.push("app:usestore");
return { todos: state.todos };
});
@@ -1016,7 +1016,7 @@ describe("connecting a component to store", () => {
"item:usestore",
"item:render",
"app:usestore",
"app:render"
"app:render",
]);
expect(fixture.innerHTML).toBe('<div class="todoapp"></div>');
});
@@ -1026,7 +1026,7 @@ describe("connecting a component to store", () => {
class App extends Component {
static template = xml`<div><t t-esc="store.msg"/></div>`;
store = useStore(s => ({ msg: s.msg }));
store = useStore((s) => ({ msg: s.msg }));
willPatch() {
steps.push("willpatch");
@@ -1040,7 +1040,7 @@ describe("connecting a component to store", () => {
const actions = {
setMsg({ state }, c) {
state.msg = c;
}
},
};
const store = new Store({ state, actions });
@@ -1061,7 +1061,7 @@ describe("connecting a component to store", () => {
class Child extends Component {
static template = xml`<div><t t-esc="store.val"/></div>`;
store = useStore(s => s);
store = useStore((s) => s);
}
class Parent extends Component {
@@ -1099,7 +1099,7 @@ describe("connecting a component to store", () => {
class Child extends Component {
static template = xml`<div><t t-esc="store.val"/></div>`;
store = useStore(s => {
store = useStore((s) => {
steps.push("child selector");
return s;
});
@@ -1107,7 +1107,7 @@ describe("connecting a component to store", () => {
class Parent extends Component {
static template = xml`<div><Child t-if="store.child" /></div>`;
static components = { Child };
store = useStore(s => {
store = useStore((s) => {
steps.push("parent selector");
return s;
});
@@ -1118,7 +1118,7 @@ describe("connecting a component to store", () => {
const actions = {
toggleChild({ state }) {
state.child = !state.child;
}
},
};
const store = new Store({ state, actions });
(<any>env).store = store;
@@ -1137,18 +1137,18 @@ describe("connecting a component to store", () => {
test("dispatch an action", async () => {
class App extends Component {
static template = xml`<div><t t-esc="store.counter"/></div>`;
store = useStore(state => state);
store = useStore((state) => state);
dispatch = useDispatch();
}
const state = {
counter: 0
counter: 0,
};
const actions = {
inc({ state }) {
return ++state.counter;
}
},
};
const store = new Store({ state, actions });
@@ -1185,21 +1185,21 @@ describe("various scenarios", () => {
await Promise.resolve();
delete state.attachments[100];
state.messages[10].attachmentIds = [];
}
},
};
const state = {
attachments: {
100: {
id: 100,
name: "text.txt"
}
name: "text.txt",
},
},
messages: {
10: {
attachmentIds: [100],
id: 10
}
}
id: 10,
},
},
};
const store = new Store({ actions, state });
@@ -1220,7 +1220,7 @@ describe("various scenarios", () => {
<Attachment t-foreach="store.attachmentIds" t-key="attachmentId" t-as="attachmentId" id="attachmentId"/>
</div>`;
store = useStore(state => ({ attachmentIds: state.messages[10].attachmentIds }));
store = useStore((state) => ({ attachmentIds: state.messages[10].attachmentIds }));
static components = { Attachment };
state = { isAttachmentDeleted: false };
dispatch = useDispatch();
+2 -2
View File
@@ -20,7 +20,7 @@ debugOwl(owl, {});
test("can log full lifecycle", async () => {
const steps: string[] = [];
const log = console.log;
console.log = arg => steps.push(arg);
console.log = (arg) => steps.push(arg);
class Child extends Component {
static template = xml`<div>child</div>`;
@@ -51,7 +51,7 @@ test("can log full lifecycle", async () => {
"[OWL_DEBUG] Child<id=2> rendering template",
"[OWL_DEBUG] Parent<id=1> willPatch",
"[OWL_DEBUG] Child<id=2> mounted",
"[OWL_DEBUG] Parent<id=1> patched"
"[OWL_DEBUG] Parent<id=1> patched",
]);
console.log = log;
});
+2 -2
View File
@@ -19,7 +19,7 @@ debugOwl(owl, { logScheduler: true });
test("can log scheduler start and stop", async () => {
const steps: string[] = [];
const log = console.log;
console.log = arg => steps.push(arg);
console.log = (arg) => steps.push(arg);
class Child extends Component {
static template = xml`<div>child</div>`;
@@ -44,7 +44,7 @@ test("can log scheduler start and stop", async () => {
"[OWL_DEBUG] Child<id=2> rendering template",
"[OWL_DEBUG] Child<id=2> mounted",
"[OWL_DEBUG] Parent<id=1> mounted",
"[OWL_DEBUG] scheduler: stop running tasks queue"
"[OWL_DEBUG] scheduler: stop running tasks queue",
]);
console.log = log;
});
+2 -2
View File
@@ -19,7 +19,7 @@ debugOwl(owl, { logScheduler: true });
test("log a specific message for render method calls if component is not mounted", async () => {
const steps: string[] = [];
const log = console.log;
console.log = arg => steps.push(arg);
console.log = (arg) => steps.push(arg);
class Parent extends Component {
static template = xml`<div><t t-esc="state.value"/></div>`;
@@ -40,7 +40,7 @@ test("log a specific message for render method calls if component is not mounted
"[OWL_DEBUG] Parent<id=1> mounted",
"[OWL_DEBUG] scheduler: stop running tasks queue",
"[OWL_DEBUG] Parent<id=1> willUnmount",
"[OWL_DEBUG] Parent<id=1> render (warning: component is not mounted, this render has no effect)"
"[OWL_DEBUG] Parent<id=1> render (warning: component is not mounted, this render has no effect)",
]);
console.log = log;
});
+2 -2
View File
@@ -19,7 +19,7 @@ debugOwl(owl, { logScheduler: true });
test("log a sub component with non stringifiable props", async () => {
const steps: string[] = [];
const log = console.log;
console.log = arg => steps.push(arg);
console.log = (arg) => steps.push(arg);
class Child extends Component {
static template = xml`<span><t t-esc="props.obj.val"/></span>`;
@@ -51,7 +51,7 @@ test("log a sub component with non stringifiable props", async () => {
"[OWL_DEBUG] Parent<id=1> mounted",
"[OWL_DEBUG] scheduler: stop running tasks queue",
"[OWL_DEBUG] Parent<id=1> willUnmount",
"[OWL_DEBUG] Child<id=2> willUnmount"
"[OWL_DEBUG] Child<id=2> willUnmount",
]);
console.log = log;
});
+7 -7
View File
@@ -60,14 +60,14 @@ function getFiles(path: string[] = []): FileData[] {
if (path.length === 0) {
const baseFiles: FileData[] = [
{ name: "README.md", path: [], links: [], sections: [], fullName: "README.md" },
{ name: "roadmap.md", path: [], links: [], sections: [], fullName: "roadmap.md" }
{ name: "roadmap.md", path: [], links: [], sections: [], fullName: "roadmap.md" },
];
const rest = getFiles(["doc"]);
const result = baseFiles.concat(rest);
result.forEach(addMardownData);
return result;
}
const files = fs.readdirSync(path.join("/"), { withFileTypes: true }).map(f => {
const files = fs.readdirSync(path.join("/"), { withFileTypes: true }).map((f) => {
if (f.isDirectory()) {
return getFiles(path.concat(f.name));
}
@@ -78,8 +78,8 @@ function getFiles(path: string[] = []): FileData[] {
path,
links: [],
sections: [],
fullName
}
fullName,
},
];
});
return Array.prototype.concat(...files);
@@ -127,7 +127,7 @@ export function isLinkValid(link: MarkDownLink, current: FileData, files: FileDa
}
// Step 4: check if there is a matching file
let target: FileData | undefined = files.find(f => f.fullName === linkFullName);
let target: FileData | undefined = files.find((f) => f.fullName === linkFullName);
if (!target) {
return false;
}
@@ -135,7 +135,7 @@ export function isLinkValid(link: MarkDownLink, current: FileData, files: FileDa
// Step 5: if necessary, check if there is a corresponding link inside the target
// link name
if (hash) {
if (!target.sections.find(s => s.slug === hash)) {
if (!target.sections.find((s) => s.slug === hash)) {
return false;
}
}
@@ -153,7 +153,7 @@ function slugify(str) {
.toLowerCase()
.replace(/\//g, "") // remove /
.replace(/\s+/g, "-") // Replace spaces with -
.replace(p, c => b.charAt(a.indexOf(c))) // Replace special characters
.replace(p, (c) => b.charAt(a.indexOf(c))) // Replace special characters
.replace(/&/g, "-and-") // Replace & with ‘and’
.replace(/[^\w\-]+/g, "") // Remove all non-word characters
.replace(/\-\-+/g, "-") // Replace multiple - with single -
+199 -192
View File
File diff suppressed because it is too large Load Diff