mirror of
https://github.com/odoo/owl.git
synced 2025-10-06 19:59:41 +07:00
Compare commits
58 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 73f94fba3f | |||
| 7a16449724 | |||
| 718c765e3b | |||
| 150d620b8e | |||
| 307b936d01 | |||
| 6950f8e628 | |||
| 4e3b7c74da | |||
| 98bb2842d8 | |||
| 6e185f987b | |||
| c0f495661d | |||
| 0f7a8289a6 | |||
| 1c3b04f6a8 | |||
| 64db7777dd | |||
| 37313c47a3 | |||
| 3f563de9c0 | |||
| 97564a7612 | |||
| f1abf7f2ea | |||
| 8464a1b04e | |||
| c0a62dfd05 | |||
| 600f1e35d4 | |||
| b3181f119d | |||
| 1e1e05350a | |||
| b242230e20 | |||
| 9fe2da704e | |||
| 21b1661d39 | |||
| ec05b1f5e3 | |||
| 8083678f03 | |||
| 61c2ec5d83 | |||
| e579a993fd | |||
| ea3c6f7bf0 | |||
| 9cfafc30b5 | |||
| ad42c583c6 | |||
| 6faaa6c361 | |||
| bff539cabf | |||
| 88fd1cf483 | |||
| f0b75b6890 | |||
| a1552117f9 | |||
| 17ae1d06c4 | |||
| 27629cedfa | |||
| 3a93370ab6 | |||
| a6bb4d8ee1 | |||
| caf842c482 | |||
| 1b513a1637 | |||
| 52a200878a | |||
| f4994a20d8 | |||
| 7419c1982a | |||
| a8c8f6affe | |||
| dabe7f9bfa | |||
| d08ea63565 | |||
| 7ebe0da962 | |||
| 3e0e8475a6 | |||
| 2e83c739b9 | |||
| 922eb7cd98 | |||
| d12115554c | |||
| 1a6b26c8c6 | |||
| d67d295eaa | |||
| 9cbcf20b33 | |||
| 3a461e1dd1 |
@@ -14,7 +14,7 @@ jobs:
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [10.x, 12.x, 14.x]
|
||||
node-version: [12.x, 14.x, 16.x]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
@@ -24,4 +24,4 @@ jobs:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
- run: npm install
|
||||
- run: npm run test
|
||||
- run: npm run prettier
|
||||
- run: npm run check-formatting
|
||||
|
||||
+4
-1
@@ -28,4 +28,7 @@ node_modules
|
||||
|
||||
release-notes.md
|
||||
|
||||
.rpt2_cache
|
||||
.rpt2_cache
|
||||
|
||||
# useful in some cases
|
||||
/temp
|
||||
@@ -1,5 +1,9 @@
|
||||
<h1 align="center">🦉 <a href="https://odoo.github.io/owl/">OWL Framework</a> 🦉</h1>
|
||||
|
||||
[](https://www.gnu.org/licenses/lgpl-3.0)
|
||||
[](https://badge.fury.io/js/@odoo%2Fowl)
|
||||
[](https://www.npmjs.com/package/@odoo/owl)
|
||||
|
||||
_Class based components with hooks, reactive state and concurrent mode_
|
||||
|
||||
## Project Overview
|
||||
@@ -120,7 +124,7 @@ npm install @odoo/owl
|
||||
|
||||
If you want to use a simple `<script>` tag, the last release can be downloaded here:
|
||||
|
||||
- [owl-1.2.6](https://github.com/odoo/owl/releases/tag/v1.2.6)
|
||||
- [owl-1.4.9](https://github.com/odoo/owl/releases/tag/v1.4.9)
|
||||
|
||||
## License
|
||||
|
||||
|
||||
@@ -70,7 +70,9 @@ just put the following code:
|
||||
Note that we put everything inside an immediately executed function to avoid leaking
|
||||
anything to the global scope.
|
||||
|
||||
Finally, `owl.js` should be the last version downloaded from the Owl repository (you can use `owl.min.js` if you prefer).
|
||||
Finally, `owl.js` should be the last version downloaded from the Owl repository (you can use `owl.min.js` if you prefer). Be aware that you should download the `owl.iife.js` or `owl.iife.min.js`, because these files
|
||||
are built to run directly on the browser (other files such as `owl.cjs.js` are
|
||||
built to be bundled by other tools).
|
||||
|
||||
Now, the project should be ready. Loading the `index.html` file into a browser
|
||||
should show an empty page, with the title `Owl Todo App`, and it should log a
|
||||
|
||||
@@ -4,7 +4,7 @@ OWL, React and Vue have the same main feature: they allow developers to build
|
||||
declarative user interfaces. To do that, all these frameworks uses a virtual dom. However, there are still obviously many differences.
|
||||
|
||||
In this page, we try to highlight some of these differences. Obviously, a lot of
|
||||
effort was done to be fair. However, if you disagree with some of the points
|
||||
effort was put to be fair. However, if you disagree with some of the points
|
||||
discussed, feel free to open an issue/submit a PR to correct this text.
|
||||
|
||||
## Content
|
||||
@@ -47,7 +47,7 @@ components are fast enough for all our usecases, and making it as simple as
|
||||
possible for developers is more valuable (for us).
|
||||
|
||||
Also, functions or class based components are more than just syntax. Functions
|
||||
comes with a mindset of composition and class are about inheritance. Clearly,
|
||||
come with a mindset of composition and class are about inheritance. Clearly,
|
||||
both of these are important mechanisms for reusing code. Also, one does not
|
||||
exclude the other.
|
||||
|
||||
|
||||
@@ -13,6 +13,8 @@
|
||||
- [Setting Variables](#setting-variables)
|
||||
- [Conditionals](#conditionals)
|
||||
- [Dynamic Attributes](#dynamic-attributes)
|
||||
- [Dynamic Class Attribute](#dynamic-class-attribute)
|
||||
- [Dynamic Tag Names](#dynamic-tag-names)
|
||||
- [Loops](#loops)
|
||||
- [Rendering Sub Templates](#rendering-sub-templates)
|
||||
- [Dynamic Sub Templates](#dynamic-sub-templates)
|
||||
@@ -76,6 +78,7 @@ needs. Here is a list of all Owl specific directives:
|
||||
| `t-transition` | [Defining an animation](animations.md#css-transitions) |
|
||||
| `t-slot` | [Rendering a slot](slots.md) |
|
||||
| `t-model` | [Form input bindings](component.md#form-input-bindings) |
|
||||
| `t-tag` | [Rendering nodes with dynamic tag name](#dynamic-tag-names) |
|
||||
|
||||
## Reference
|
||||
|
||||
@@ -324,6 +327,40 @@ values) or a pair `[key, value]`. For example:
|
||||
<div t-att="['a', 'b']"/> <!-- <div a="b"></div> -->
|
||||
```
|
||||
|
||||
### Dynamic class attribute
|
||||
|
||||
For convenience, Owl supports a special case for the `t-att-class` case: one can
|
||||
use an object with keys describing the classes, and values boolean value denoting
|
||||
if the class is or is not present:
|
||||
|
||||
```xml
|
||||
<div t-att-class="{'a': true, 'b': true}"/> <!-- result: <div class="a b"></div> -->
|
||||
|
||||
<div t-att-class="{'a b': true, 'c': true}"/> <!-- result: <div class="a b c"></div> -->
|
||||
```
|
||||
|
||||
Note that it can be combined with normal class attribute:
|
||||
|
||||
```xml
|
||||
<div class="a" t-att-class="{'b': true}"/> <!-- result: <div class="a b"></div> -->
|
||||
```
|
||||
|
||||
### Dynamic tag names
|
||||
|
||||
When writing generic components or templates, the specific concrete tag for an
|
||||
HTML element is not known yet. In those situations, the `t-tag` directive is
|
||||
useful. It simply evaluates dynamically an expression to use as a tag name. The
|
||||
template:
|
||||
|
||||
```xml
|
||||
<t t-tag="tag">
|
||||
<span>content</span>
|
||||
</t>
|
||||
```
|
||||
|
||||
will be rendered as `<div><span>content</span></div>` if the `tag` context key
|
||||
is set to `div`.
|
||||
|
||||
### Loops
|
||||
|
||||
QWeb has an iteration directive `t-foreach` which take an expression returning the
|
||||
|
||||
+10
-10
@@ -62,14 +62,14 @@ The CSS tag is useful to define a css stylesheet in the javascript file:
|
||||
|
||||
```js
|
||||
class MyComponent extends Component {
|
||||
static template = xml`
|
||||
static template = xml`
|
||||
<div class="my-component">some template</div>
|
||||
`;
|
||||
static css`
|
||||
.my-component {
|
||||
color: red;
|
||||
}
|
||||
`;
|
||||
static style = css`
|
||||
.my-component {
|
||||
color: red;
|
||||
}
|
||||
`;
|
||||
}
|
||||
```
|
||||
|
||||
@@ -176,9 +176,9 @@ const uuid = generateUUID();
|
||||
class MyComponent extends Component {
|
||||
static template = xml`<div data-o-${uuid}="">...</div>`;
|
||||
static style = css`
|
||||
[data-o-${uuid}] {
|
||||
color: red;
|
||||
}
|
||||
`;
|
||||
[data-o-${uuid}] {
|
||||
color: red;
|
||||
}
|
||||
`;
|
||||
}
|
||||
```
|
||||
|
||||
+9
-8
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@odoo/owl",
|
||||
"version": "1.2.6",
|
||||
"version": "1.4.9",
|
||||
"description": "Odoo Web Library (OWL)",
|
||||
"main": "dist/owl.cjs.js",
|
||||
"browser": "dist/owl.iife.js",
|
||||
@@ -10,7 +10,7 @@
|
||||
"dist"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=10.15.3"
|
||||
"node": ">=12.18.3"
|
||||
},
|
||||
"scripts": {
|
||||
"build:bundle": "rollup -c",
|
||||
@@ -37,28 +37,29 @@
|
||||
},
|
||||
"homepage": "https://github.com/odoo/owl#readme",
|
||||
"devDependencies": {
|
||||
"@types/jest": "^23.3.14",
|
||||
"@types/jest": "^27.0.1",
|
||||
"@types/node": "^14.11.8",
|
||||
"chalk": "^3.0.0",
|
||||
"cpx": "^1.5.0",
|
||||
"current-git-branch": "^1.1.0",
|
||||
"git-rev-sync": "^1.12.0",
|
||||
"github-api": "^3.3.0",
|
||||
"jest": "^23.6.0",
|
||||
"jest-environment-jsdom": "^24.7.1",
|
||||
"jest": "^27.1.0",
|
||||
"jest-environment-jsdom": "^27.1.0",
|
||||
"live-server": "^1.2.1",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"prettier": "^2.0.4",
|
||||
"rollup": "^1.6.0",
|
||||
"rollup": "^2.56.3",
|
||||
"rollup-plugin-terser": "^7.0.2",
|
||||
"rollup-plugin-typescript2": "^0.27.3",
|
||||
"rollup-plugin-typescript2": "^0.30.0",
|
||||
"sass": "^1.16.1",
|
||||
"source-map-support": "^0.5.10",
|
||||
"ts-jest": "^23.10.5",
|
||||
"ts-jest": "^27.0.5",
|
||||
"typescript": "^3.7.2",
|
||||
"uglify-es": "^3.3.9"
|
||||
},
|
||||
"jest": {
|
||||
"testEnvironment": "jsdom",
|
||||
"roots": [
|
||||
"<rootDir>/src",
|
||||
"<rootDir>/tests"
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
# 🦉 OWL Roadmap 🦉
|
||||
|
||||
- Current version: 1.2.6
|
||||
- Current version: 1.4.9
|
||||
- Status: stable
|
||||
|
||||
This roadmap is only an attempt at predicting Owl's future. Everything may
|
||||
|
||||
@@ -400,6 +400,7 @@ export class Component<Props extends {} = any, T extends Env = Env> {
|
||||
if (currentFiber && !currentFiber.isRendered && !currentFiber.isCompleted) {
|
||||
return scheduler.addFiber(currentFiber.root);
|
||||
}
|
||||
|
||||
// if we aren't mounted at this point, it implies that there is a
|
||||
// currentFiber that is already rendered (isRendered is true), so we are
|
||||
// about to be mounted
|
||||
@@ -505,7 +506,6 @@ export class Component<Props extends {} = any, T extends Env = Env> {
|
||||
const __owl__ = this.__owl__;
|
||||
|
||||
__owl__.status = STATUS.MOUNTED;
|
||||
__owl__.currentFiber = null;
|
||||
this.mounted();
|
||||
if (__owl__.mountedCB) {
|
||||
__owl__.mountedCB();
|
||||
@@ -767,7 +767,7 @@ export async function mount<T extends Type<Component>>(
|
||||
const { env, props, target } = params;
|
||||
let origEnv = C.hasOwnProperty("env") ? (C as any).env : null;
|
||||
if (env) {
|
||||
((C as any) as typeof Component).env = env;
|
||||
(C as any as typeof Component).env = env;
|
||||
}
|
||||
const component: Component = new C(null, props);
|
||||
if (origEnv) {
|
||||
|
||||
@@ -234,7 +234,11 @@ QWeb.addDirective({
|
||||
} else if (!name.startsWith("t-")) {
|
||||
if (name !== "class" && name !== "style") {
|
||||
// this is a prop!
|
||||
props[name] = ctx.formatExpression(value) || "undefined";
|
||||
if (value.includes("=>")) {
|
||||
props[name] = ctx.captureExpression(value);
|
||||
} else {
|
||||
props[name] = ctx.formatExpression(value) || "undefined";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -245,7 +249,18 @@ QWeb.addDirective({
|
||||
.join(",");
|
||||
let componentID = ctx.generateID();
|
||||
|
||||
const templateKey = ctx.generateTemplateKey();
|
||||
let hasDefinedKey = false;
|
||||
let templateKey;
|
||||
if (node.tagName === "t" && !node.hasAttribute("t-key") && value.match(INTERP_REGEXP)) {
|
||||
defineComponentKey();
|
||||
const id = ctx.generateID();
|
||||
// the ___ is to make sure we have no possible conflict with normal
|
||||
// template keys
|
||||
ctx.addLine(`let k${id} = '___' + componentKey${componentID}`);
|
||||
templateKey = `k${id}`;
|
||||
} else {
|
||||
templateKey = ctx.generateTemplateKey();
|
||||
}
|
||||
let ref = node.getAttribute("t-ref");
|
||||
let refExpr = "";
|
||||
let refKey: string = "";
|
||||
@@ -291,7 +306,7 @@ QWeb.addDirective({
|
||||
if (tattClass) {
|
||||
let tattExpr = ctx.formatExpression(tattClass);
|
||||
if (tattExpr[0] !== "{" || tattExpr[tattExpr.length - 1] !== "}") {
|
||||
tattExpr = `utils.toObj(${tattExpr})`;
|
||||
tattExpr = `utils.toClassObj(${tattExpr})`;
|
||||
}
|
||||
if (classAttr) {
|
||||
ctx.addLine(`Object.assign(${classObj}, ${tattExpr})`);
|
||||
@@ -336,7 +351,7 @@ QWeb.addDirective({
|
||||
}
|
||||
if (hasDynamicProps) {
|
||||
const dynamicProp = ctx.formatExpression(node.getAttribute("t-props")!);
|
||||
ctx.addLine(`let props${componentID} = Object.assign({${propStr}}, ${dynamicProp});`);
|
||||
ctx.addLine(`let props${componentID} = Object.assign({}, ${dynamicProp}, {${propStr}});`);
|
||||
} else {
|
||||
ctx.addLine(`let props${componentID} = {${propStr}};`);
|
||||
}
|
||||
@@ -355,7 +370,7 @@ QWeb.addDirective({
|
||||
// SLOTS
|
||||
const hasSlots = node.childNodes.length;
|
||||
|
||||
let scope = hasSlots ? `Object.assign(Object.create(context), scope)` : "undefined";
|
||||
let scope = hasSlots ? `utils.combine(context, scope)` : "undefined";
|
||||
|
||||
ctx.addIf(`w${componentID}`);
|
||||
|
||||
@@ -378,9 +393,15 @@ QWeb.addDirective({
|
||||
ctx.addElse();
|
||||
|
||||
// new component
|
||||
function defineComponentKey() {
|
||||
if (!hasDefinedKey) {
|
||||
const interpValue = ctx.interpolate(value);
|
||||
ctx.addLine(`let componentKey${componentID} = ${interpValue};`);
|
||||
hasDefinedKey = true;
|
||||
}
|
||||
}
|
||||
defineComponentKey();
|
||||
const contextualValue = value.match(INTERP_REGEXP) ? "false" : ctx.formatExpression(value);
|
||||
const interpValue = ctx.interpolate(value);
|
||||
ctx.addLine(`let componentKey${componentID} = ${interpValue};`);
|
||||
ctx.addLine(
|
||||
`let W${componentID} = ${contextualValue} || context.constructor.components[componentKey${componentID}] || QWeb.components[componentKey${componentID}];`
|
||||
);
|
||||
@@ -452,12 +473,17 @@ QWeb.addDirective({
|
||||
}
|
||||
}
|
||||
if (clone.childNodes.length) {
|
||||
let hasContent = false;
|
||||
const t = clone.ownerDocument!.createElement("t");
|
||||
for (let child of Object.values(clone.childNodes)) {
|
||||
hasContent =
|
||||
hasContent || (child instanceof Text ? Boolean(child.textContent.trim().length) : true);
|
||||
t.appendChild(child);
|
||||
}
|
||||
const slotFn = qweb._compile(`slot_default_template`, { elem: t, hasParent: true });
|
||||
QWeb.slots[`${slotId}_default`] = slotFn;
|
||||
if (hasContent) {
|
||||
const slotFn = qweb._compile(`slot_default_template`, { elem: t, hasParent: true });
|
||||
QWeb.slots[`${slotId}_default`] = slotFn;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+35
-16
@@ -198,6 +198,7 @@ export class Fiber {
|
||||
// build patchQueue
|
||||
const patchQueue: Fiber[] = [];
|
||||
const doWork: (Fiber) => Fiber | null = function (f) {
|
||||
f.component.__owl__.currentFiber = null;
|
||||
patchQueue.push(f);
|
||||
return f.child;
|
||||
};
|
||||
@@ -242,8 +243,9 @@ export class Fiber {
|
||||
}
|
||||
component.__patch(target!, fiber.vnode!);
|
||||
} else {
|
||||
if (fiber.shouldPatch) {
|
||||
component.__patch(component.__owl__.vnode!, fiber.vnode!);
|
||||
const vnode = component.__owl__.vnode;
|
||||
if (fiber.shouldPatch && vnode) {
|
||||
component.__patch(vnode, fiber.vnode!);
|
||||
// When updating a Component's props (in directive),
|
||||
// the component has a pvnode AND should be patched.
|
||||
// However, its pvnode.elm may have changed if it is a High Order Component
|
||||
@@ -255,10 +257,6 @@ export class Fiber {
|
||||
component.__owl__.pvnode!.elm = component.__owl__.vnode!.elm;
|
||||
}
|
||||
}
|
||||
const compOwl = component.__owl__;
|
||||
if (fiber === compOwl.currentFiber) {
|
||||
compOwl.currentFiber = null;
|
||||
}
|
||||
}
|
||||
|
||||
// insert into the DOM (mount case)
|
||||
@@ -326,22 +324,43 @@ export class Fiber {
|
||||
|
||||
const qweb = component.env.qweb;
|
||||
let root = component;
|
||||
let canCatch = false;
|
||||
while (component && !(canCatch = !!component.catchError)) {
|
||||
root = component;
|
||||
component = component.__owl__.parent!;
|
||||
}
|
||||
qweb.trigger("error", error);
|
||||
|
||||
if (canCatch) {
|
||||
component.catchError!(error);
|
||||
} else {
|
||||
function handle(error) {
|
||||
let canCatch = false;
|
||||
qweb.trigger("error", error);
|
||||
while (component && !(canCatch = !!component.catchError)) {
|
||||
root = component;
|
||||
component = component.__owl__.parent!;
|
||||
}
|
||||
if (canCatch) {
|
||||
try {
|
||||
component.catchError!(error);
|
||||
} catch (e) {
|
||||
root = component;
|
||||
component = component.__owl__.parent!;
|
||||
return handle(e);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
let isHandled = handle(error);
|
||||
|
||||
if (!isHandled) {
|
||||
// the 3 next lines aim to mark the root fiber as being in error, and
|
||||
// to force it to end, without waiting for its children
|
||||
this.root.counter = 0;
|
||||
this.root.error = error;
|
||||
scheduler.flush();
|
||||
root.destroy();
|
||||
// at this point, the state of the application is corrupted and we could
|
||||
// have a lot of issues or crashes. So we destroy the application in a try
|
||||
// catch and swallow these errors because the fiber is already in error,
|
||||
// and this is the actual issue that needs to be solved, not those followup
|
||||
// errors.
|
||||
try {
|
||||
root.destroy();
|
||||
} catch (e) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+9
-7
@@ -1,4 +1,5 @@
|
||||
import { QWeb } from "./qweb/index";
|
||||
import { TRANSLATABLE_ATTRS } from "./qweb/qweb";
|
||||
|
||||
/**
|
||||
* This file creates and exports the OWL 'config' object, with keys:
|
||||
@@ -9,9 +10,12 @@ import { QWeb } from "./qweb/index";
|
||||
interface Config {
|
||||
mode: string;
|
||||
enableTransitions: boolean;
|
||||
translatableAttributes: string[];
|
||||
}
|
||||
|
||||
export const config = {} as Config;
|
||||
export const config = {
|
||||
translatableAttributes: TRANSLATABLE_ATTRS,
|
||||
} as Config;
|
||||
|
||||
Object.defineProperty(config, "mode", {
|
||||
get() {
|
||||
@@ -20,12 +24,10 @@ Object.defineProperty(config, "mode", {
|
||||
set(mode: string) {
|
||||
QWeb.dev = mode === "dev";
|
||||
if (QWeb.dev) {
|
||||
const url = `https://github.com/odoo/owl/blob/master/doc/reference/config.md#mode`;
|
||||
console.warn(
|
||||
`Owl is running in 'dev' mode. This is not suitable for production use. See ${url} for more information.`
|
||||
);
|
||||
} else {
|
||||
console.log(`Owl is now running in 'prod' mode.`);
|
||||
console.info(`Owl is running in 'dev' mode.
|
||||
|
||||
This is not suitable for production use.
|
||||
See https://github.com/odoo/owl/blob/master/doc/reference/config.md#mode for more information.`);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
@@ -281,7 +281,9 @@ QWeb.addDirective({
|
||||
|
||||
// Step 4: add the appropriate function call to current component
|
||||
// ------------------------------------------------
|
||||
const parentComponent = `utils.getComponent(context)`;
|
||||
const parentComponent = ctx.rootContext.shouldDefineParent
|
||||
? `parent`
|
||||
: `utils.getComponent(context)`;
|
||||
const key = ctx.generateTemplateKey();
|
||||
const parentNode = ctx.parentNode ? `c${ctx.parentNode}` : "result";
|
||||
const extra = `Object.assign({}, extra, {parentNode: ${parentNode}, parent: ${parentComponent}, key: ${key}})`;
|
||||
@@ -324,7 +326,8 @@ QWeb.addDirective({
|
||||
ctx.addLine(`if (!_${arrayID}) { throw new Error('QWeb error: Invalid loop expression')}`);
|
||||
let keysID = ctx.generateID();
|
||||
let valuesID = ctx.generateID();
|
||||
ctx.addLine(`let _${keysID} = _${valuesID} = _${arrayID};`);
|
||||
ctx.addLine(`let _${keysID} = _${arrayID};`);
|
||||
ctx.addLine(`let _${valuesID} = _${arrayID};`);
|
||||
ctx.addIf(`!(_${arrayID} instanceof Array)`);
|
||||
ctx.addLine(`_${keysID} = Object.keys(_${arrayID});`);
|
||||
ctx.addLine(`_${valuesID} = Object.values(_${arrayID});`);
|
||||
|
||||
@@ -163,7 +163,18 @@ export class CompilationContext {
|
||||
const done = new Set();
|
||||
return tokens
|
||||
.map((tok) => {
|
||||
if (tok.varName) {
|
||||
// "this" in captured expressions should be the current component
|
||||
if (tok.value === "this") {
|
||||
if (!done.has("this")) {
|
||||
done.add("this");
|
||||
this.addLine(`const this_${argId} = utils.getComponent(context);`);
|
||||
}
|
||||
tok.value = `this_${argId}`;
|
||||
}
|
||||
// Variables that should be looked up in the scope. isLocal is for arrow
|
||||
// function arguments that should stay untouched (eg "ev => ev" should
|
||||
// not become "const ev_1 = scope['ev']; ev_1 => ev_1")
|
||||
if (tok.varName && !tok.isLocal) {
|
||||
if (!done.has(tok.varName)) {
|
||||
done.add(tok.varName);
|
||||
this.addLine(`const ${tok.varName}_${argId} = ${tok.value};`);
|
||||
|
||||
@@ -25,9 +25,10 @@
|
||||
// Misc types, constants and helpers
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
const RESERVED_WORDS = "true,false,NaN,null,undefined,debugger,console,window,in,instanceof,new,function,return,this,eval,void,Math,RegExp,Array,Object,Date".split(
|
||||
","
|
||||
);
|
||||
const RESERVED_WORDS =
|
||||
"true,false,NaN,null,undefined,debugger,console,window,in,instanceof,new,function,return,this,eval,void,Math,RegExp,Array,Object,Date".split(
|
||||
","
|
||||
);
|
||||
|
||||
const WORD_REPLACEMENT = Object.assign(Object.create(null), {
|
||||
and: "&&",
|
||||
@@ -57,6 +58,7 @@ type TKind =
|
||||
| "RIGHT_PAREN"
|
||||
| "COMMA"
|
||||
| "VALUE"
|
||||
| "TEMPLATE_STRING"
|
||||
| "SYMBOL"
|
||||
| "OPERATOR"
|
||||
| "COLON";
|
||||
@@ -67,6 +69,8 @@ interface Token {
|
||||
originalValue?: string;
|
||||
size?: number;
|
||||
varName?: string;
|
||||
replace?: Function;
|
||||
isLocal?: boolean;
|
||||
}
|
||||
|
||||
const STATIC_TOKEN_MAP: { [key: string]: TKind } = Object.assign(Object.create(null), {
|
||||
@@ -89,7 +93,7 @@ type Tokenizer = (expr: string) => Token | false;
|
||||
let tokenizeString: Tokenizer = function (expr) {
|
||||
let s = expr[0];
|
||||
let start = s;
|
||||
if (s !== "'" && s !== '"') {
|
||||
if (s !== "'" && s !== '"' && s !== "`") {
|
||||
return false;
|
||||
}
|
||||
let i = 1;
|
||||
@@ -111,6 +115,17 @@ let tokenizeString: Tokenizer = function (expr) {
|
||||
throw new Error("Invalid expression");
|
||||
}
|
||||
s += start;
|
||||
if (start === "`") {
|
||||
return {
|
||||
type: "TEMPLATE_STRING",
|
||||
value: s,
|
||||
replace(replacer) {
|
||||
return s.replace(/\$\{(.*?)\}/g, (match, group) => {
|
||||
return "${" + replacer(group) + "}";
|
||||
});
|
||||
},
|
||||
};
|
||||
}
|
||||
return { type: "VALUE", value: s };
|
||||
};
|
||||
|
||||
@@ -210,6 +225,10 @@ export function tokenize(expr: string): Token[] {
|
||||
// Expression "evaluator"
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
const isLeftSeparator = (token) => token && (token.type === "LEFT_BRACE" || token.type === "COMMA");
|
||||
const isRightSeparator = (token) =>
|
||||
token && (token.type === "RIGHT_BRACE" || token.type === "COMMA");
|
||||
|
||||
/**
|
||||
* This is the main function exported by this file. This is the code that will
|
||||
* process an expression (given as a string) and returns another expression with
|
||||
@@ -236,15 +255,42 @@ export function tokenize(expr: string): Token[] {
|
||||
* the list of variables so it does not get replaced by a lookup in the context
|
||||
*/
|
||||
export function compileExprToArray(expr: string, scope: { [key: string]: QWebVar }): Token[] {
|
||||
const localVars = new Set<string>();
|
||||
scope = Object.create(scope);
|
||||
const tokens = tokenize(expr);
|
||||
for (let i = 0; i < tokens.length; i++) {
|
||||
|
||||
let i = 0;
|
||||
let stack = []; // to track last opening [ or {
|
||||
|
||||
while (i < tokens.length) {
|
||||
let token = tokens[i];
|
||||
let prevToken = tokens[i - 1];
|
||||
let nextToken = tokens[i + 1];
|
||||
let groupType = stack[stack.length - 1];
|
||||
|
||||
switch (token.type) {
|
||||
case "LEFT_BRACE":
|
||||
case "LEFT_BRACKET":
|
||||
stack.push(token.type);
|
||||
break;
|
||||
case "RIGHT_BRACE":
|
||||
case "RIGHT_BRACKET":
|
||||
stack.pop();
|
||||
}
|
||||
|
||||
let isVar = token.type === "SYMBOL" && !RESERVED_WORDS.includes(token.value);
|
||||
if (token.type === "SYMBOL" && !RESERVED_WORDS.includes(token.value)) {
|
||||
if (prevToken) {
|
||||
// normalize missing tokens: {a} should be equivalent to {a:a}
|
||||
if (
|
||||
groupType === "LEFT_BRACE" &&
|
||||
isLeftSeparator(prevToken) &&
|
||||
isRightSeparator(nextToken)
|
||||
) {
|
||||
tokens.splice(i + 1, 0, { type: "COLON", value: ":" }, { ...token });
|
||||
nextToken = tokens[i + 1];
|
||||
}
|
||||
|
||||
if (prevToken.type === "OPERATOR" && prevToken.value === ".") {
|
||||
isVar = false;
|
||||
} else if (prevToken.type === "LEFT_BRACE" || prevToken.type === "COMMA") {
|
||||
@@ -254,6 +300,9 @@ export function compileExprToArray(expr: string, scope: { [key: string]: QWebVar
|
||||
}
|
||||
}
|
||||
}
|
||||
if (token.type === "TEMPLATE_STRING") {
|
||||
token.value = token.replace((expr) => compileExpr(expr, scope));
|
||||
}
|
||||
if (nextToken && nextToken.type === "OPERATOR" && nextToken.value === "=>") {
|
||||
if (token.type === "RIGHT_PAREN") {
|
||||
let j = i - 1;
|
||||
@@ -261,11 +310,13 @@ export function compileExprToArray(expr: string, scope: { [key: string]: QWebVar
|
||||
if (tokens[j].type === "SYMBOL" && tokens[j].originalValue) {
|
||||
tokens[j].value = tokens[j].originalValue!;
|
||||
scope[tokens[j].value] = { id: tokens[j].value, expr: tokens[j].value };
|
||||
localVars.add(tokens[j].value);
|
||||
}
|
||||
j--;
|
||||
}
|
||||
} else {
|
||||
scope[token.value] = { id: token.value, expr: token.value };
|
||||
localVars.add(token.value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -278,6 +329,14 @@ export function compileExprToArray(expr: string, scope: { [key: string]: QWebVar
|
||||
token.value = `scope['${token.value}']`;
|
||||
}
|
||||
}
|
||||
i++;
|
||||
}
|
||||
// Mark all variables that have been used locally.
|
||||
// This assumes the expression has only one scope (incorrect but "good enough for now")
|
||||
for (const token of tokens) {
|
||||
if (token.type === "SYMBOL" && localVars.has(token.value)) {
|
||||
token.isLocal = true;
|
||||
}
|
||||
}
|
||||
return tokens;
|
||||
}
|
||||
|
||||
+10
-1
@@ -2,6 +2,7 @@ import { STATUS } from "../component/component";
|
||||
import { VNode } from "../vdom/index";
|
||||
import { INTERP_REGEXP } from "./compilation_context";
|
||||
import { QWeb } from "./qweb";
|
||||
import { browser } from "../browser";
|
||||
|
||||
/**
|
||||
* Owl QWeb Extensions
|
||||
@@ -75,6 +76,7 @@ export function makeHandlerCode(
|
||||
// we need to capture every variable in it
|
||||
putInCache = false;
|
||||
code = ctx.captureExpression(value);
|
||||
code = `const res = (() => { return ${code} })(); if (typeof res === 'function') { res(e) }`;
|
||||
}
|
||||
const modCode = mods.map((mod) => modcodes[mod]).join("");
|
||||
let handler = `function (e) {if (context.__owl__.status === ${STATUS.DESTROYED}){return}${modCode}${code}}`;
|
||||
@@ -197,7 +199,14 @@ function whenTransitionEnd(elm: HTMLElement, cb) {
|
||||
const durations: Array<string> = (styles.transitionDuration || "").split(", ");
|
||||
const timeout: number = getTimeout(delays, durations);
|
||||
if (timeout > 0) {
|
||||
elm.addEventListener("transitionend", cb, { once: true });
|
||||
const transitionEndCB = () => {
|
||||
if (!elm.parentNode) return;
|
||||
cb();
|
||||
browser.clearTimeout(fallbackTimeout);
|
||||
elm.removeEventListener("transitionend", transitionEndCB);
|
||||
};
|
||||
elm.addEventListener("transitionend", transitionEndCB, { once: true });
|
||||
const fallbackTimeout = browser.setTimeout(transitionEndCB, timeout + 1);
|
||||
} else {
|
||||
cb();
|
||||
}
|
||||
|
||||
+87
-19
@@ -66,7 +66,7 @@ interface QWebConfig {
|
||||
// Const/global stuff/helpers
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
const TRANSLATABLE_ATTRS = ["label", "title", "placeholder", "alt"];
|
||||
export const TRANSLATABLE_ATTRS = ["label", "title", "placeholder", "alt"];
|
||||
|
||||
const lineBreakRE = /[\r\n]/;
|
||||
const whitespaceRE = /\s+/g;
|
||||
@@ -80,7 +80,7 @@ const NODE_HOOKS_PARAMS = {
|
||||
};
|
||||
|
||||
interface Utils {
|
||||
toObj(expr: any): Object;
|
||||
toClassObj(expr: any): Object;
|
||||
shallowEqual(p1: Object, p2: Object): boolean;
|
||||
[key: string]: any;
|
||||
}
|
||||
@@ -111,20 +111,53 @@ function vDomToString(vdom: VNode[]): string {
|
||||
|
||||
const UTILS: Utils = {
|
||||
zero: Symbol("zero"),
|
||||
toObj(expr) {
|
||||
toClassObj(expr) {
|
||||
const result = {};
|
||||
if (typeof expr === "string") {
|
||||
// we transform here a list of classes into an object:
|
||||
// 'hey you' becomes {hey: true, you: true}
|
||||
expr = expr.trim();
|
||||
if (!expr) {
|
||||
return {};
|
||||
}
|
||||
let words = expr.split(/\s+/);
|
||||
let result = {};
|
||||
for (let i = 0; i < words.length; i++) {
|
||||
result[words[i]] = true;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
return expr;
|
||||
// this is already an object, but we may need to split keys:
|
||||
// {'a b': true, 'a c': false} should become {a: true, b: true, c: false}
|
||||
for (let key in expr) {
|
||||
const value = expr[key];
|
||||
const words = key.split(/\s+/);
|
||||
for (let word of words) {
|
||||
result[word] = result[word] || value;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
},
|
||||
/**
|
||||
* This method combines the current context with the variables defined in a
|
||||
* scope for use in a slot.
|
||||
*
|
||||
* The implementation is kind of tricky because we want to preserve the
|
||||
* prototype chain structure of the cloned result. So we need to traverse the
|
||||
* prototype chain, cloning each level respectively.
|
||||
*/
|
||||
combine(context, scope) {
|
||||
let clone = context;
|
||||
const scopeStack = [];
|
||||
while (!isComponent(scope)) {
|
||||
scopeStack.push(scope);
|
||||
scope = scope.__proto__;
|
||||
}
|
||||
while (scopeStack.length) {
|
||||
let scope = scopeStack.pop();
|
||||
clone = Object.create(clone);
|
||||
Object.assign(clone, scope);
|
||||
}
|
||||
return clone;
|
||||
},
|
||||
shallowEqual,
|
||||
addNameSpace(vnode) {
|
||||
@@ -203,6 +236,7 @@ export class QWeb extends EventBus {
|
||||
att: 1,
|
||||
attf: 1,
|
||||
translation: 1,
|
||||
tag: 1,
|
||||
};
|
||||
static DIRECTIVES: Directive[] = [];
|
||||
|
||||
@@ -295,6 +329,9 @@ export class QWeb extends EventBus {
|
||||
* template, with the name given by the t-name attribute.
|
||||
*/
|
||||
addTemplates(xmlstr: string | Document) {
|
||||
if (!xmlstr) {
|
||||
return;
|
||||
}
|
||||
const doc = typeof xmlstr === "string" ? parseXML(xmlstr) : xmlstr;
|
||||
const templates = doc.getElementsByTagName("templates")[0];
|
||||
if (!templates) {
|
||||
@@ -435,6 +472,7 @@ export class QWeb extends EventBus {
|
||||
ctx.variables = Object.create(null);
|
||||
ctx.parentNode = ctx.generateID();
|
||||
ctx.allowMultipleRoots = true;
|
||||
ctx.shouldDefineParent = true;
|
||||
ctx.hasParentWidget = true;
|
||||
ctx.shouldDefineResult = false;
|
||||
ctx.addLine(`let c${ctx.parentNode} = extra.parentNode;`);
|
||||
@@ -522,7 +560,11 @@ export class QWeb extends EventBus {
|
||||
}
|
||||
|
||||
if (node.tagName !== "t" && node.hasAttribute("t-call")) {
|
||||
const tCallNode = document.createElement("t");
|
||||
const tCallNode = document.implementation.createDocument(
|
||||
"http://www.w3.org/1999/xhtml",
|
||||
"t",
|
||||
null
|
||||
).documentElement;
|
||||
tCallNode.setAttribute("t-call", node.getAttribute("t-call")!);
|
||||
node.removeAttribute("t-call");
|
||||
node.prepend(tCallNode);
|
||||
@@ -558,7 +600,11 @@ export class QWeb extends EventBus {
|
||||
throw new Error(`Unknown QWeb directive: '${attrName}'`);
|
||||
}
|
||||
if (node.tagName !== "t" && (attrName === "t-esc" || attrName === "t-raw")) {
|
||||
const tNode = document.createElement("t");
|
||||
const tNode = document.implementation.createDocument(
|
||||
"http://www.w3.org/1999/xhtml",
|
||||
"t",
|
||||
null
|
||||
).documentElement;
|
||||
tNode.setAttribute(attrName, node.getAttribute(attrName)!);
|
||||
for (let child of Array.from(node.childNodes)) {
|
||||
tNode.appendChild(child);
|
||||
@@ -614,14 +660,22 @@ export class QWeb extends EventBus {
|
||||
}
|
||||
}
|
||||
|
||||
if (node.nodeName !== "t") {
|
||||
let nodeID = this._compileGenericNode(node, ctx, withHandlers);
|
||||
ctx = ctx.withParent(nodeID);
|
||||
if (node.nodeName !== "t" || node.hasAttribute("t-tag")) {
|
||||
let nodeHooks = {};
|
||||
let addNodeHook = function (hook, handler) {
|
||||
nodeHooks[hook] = nodeHooks[hook] || [];
|
||||
nodeHooks[hook].push(handler);
|
||||
};
|
||||
if (node.tagName === "select" && node.hasAttribute("t-att-value")) {
|
||||
const value = node.getAttribute("t-att-value");
|
||||
let exprId = ctx.generateID();
|
||||
ctx.addLine(`let expr${exprId} = ${ctx.formatExpression(value)};`);
|
||||
let expr = `expr${exprId}`;
|
||||
node.setAttribute("t-att-value", expr);
|
||||
addNodeHook("create", `n.elm.value=${expr};`);
|
||||
}
|
||||
let nodeID = this._compileGenericNode(node, ctx, withHandlers);
|
||||
ctx = ctx.withParent(nodeID);
|
||||
|
||||
for (let { directive, value, fullName } of validDirectives) {
|
||||
if (directive.atNodeCreation) {
|
||||
@@ -703,16 +757,18 @@ export class QWeb extends EventBus {
|
||||
isProp = key === "selected" || key === "disabled";
|
||||
break;
|
||||
case "textarea":
|
||||
isProp = key === "readonly" || key === "disabled";
|
||||
isProp = key === "readonly" || key === "disabled" || key === "value";
|
||||
break;
|
||||
case "select":
|
||||
isProp = key === "disabled" || key === "value";
|
||||
break;
|
||||
case "button":
|
||||
case "select":
|
||||
case "optgroup":
|
||||
isProp = key === "disabled";
|
||||
break;
|
||||
}
|
||||
if (isProp) {
|
||||
props.push(`${key}: _${val}`);
|
||||
props.push(`${key}: ${val}`);
|
||||
}
|
||||
}
|
||||
let classObj = "";
|
||||
@@ -748,7 +804,7 @@ export class QWeb extends EventBus {
|
||||
name = '"' + name + '"';
|
||||
}
|
||||
attrs.push(`${name}: _${attID}`);
|
||||
handleProperties(name, attID);
|
||||
handleProperties(name, `_${attID}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -760,7 +816,7 @@ export class QWeb extends EventBus {
|
||||
|
||||
if (attName === "class") {
|
||||
ctx.rootContext.shouldDefineUtils = true;
|
||||
formattedValue = `utils.toObj(${formattedValue})`;
|
||||
formattedValue = `utils.toClassObj(${formattedValue})`;
|
||||
if (classObj) {
|
||||
ctx.addLine(`Object.assign(${classObj}, ${formattedValue})`);
|
||||
} else {
|
||||
@@ -783,9 +839,14 @@ export class QWeb extends EventBus {
|
||||
const attrIndex = attrs.findIndex((att) => att.startsWith(attName + ":"));
|
||||
attrs.splice(attrIndex, 1);
|
||||
}
|
||||
ctx.addLine(`let _${attID} = ${formattedValue};`);
|
||||
attrs.push(`${attName}: _${attID}`);
|
||||
handleProperties(attName, attID);
|
||||
if (node.nodeName === "select" && attName === "value") {
|
||||
attrs.push(`${attName}: ${v}`);
|
||||
handleProperties(attName, v);
|
||||
} else {
|
||||
ctx.addLine(`let _${attID} = ${formattedValue};`);
|
||||
attrs.push(`${attName}: _${attID}`);
|
||||
handleProperties(attName, "_" + attID);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -841,7 +902,14 @@ export class QWeb extends EventBus {
|
||||
ctx.addLine(`}`);
|
||||
ctx.closeIf();
|
||||
}
|
||||
ctx.addLine(`let vn${nodeID} = h('${node.nodeName}', p${nodeID}, c${nodeID});`);
|
||||
let nodeName = `'${node.nodeName}'`;
|
||||
if ((<Element>node).hasAttribute("t-tag")) {
|
||||
const tagExpr = (<Element>node).getAttribute("t-tag");
|
||||
(<Element>node).removeAttribute("t-tag");
|
||||
nodeName = `tag${ctx.generateID()}`;
|
||||
ctx.addLine(`let ${nodeName} = ${ctx.formatExpression(tagExpr)};`);
|
||||
}
|
||||
ctx.addLine(`let vn${nodeID} = h(${nodeName}, p${nodeID}, c${nodeID});`);
|
||||
if (ctx.parentNode) {
|
||||
ctx.addLine(`c${ctx.parentNode}.push(vn${nodeID});`);
|
||||
} else if (ctx.loopNumber || ctx.hasKey0) {
|
||||
|
||||
@@ -125,7 +125,10 @@ export class Router {
|
||||
const initialParams = this.currentParams;
|
||||
const result = await this.matchAndApplyRules(path);
|
||||
if (result.type === "match") {
|
||||
const finalPath = this.routeToPath(result.route, result.params);
|
||||
let finalPath = this.routeToPath(result.route, result.params);
|
||||
if (path.indexOf("?") > -1) {
|
||||
finalPath += "?" + path.split("?")[1];
|
||||
}
|
||||
const isPopStateEvent = ev && ev instanceof PopStateEvent;
|
||||
if (!isPopStateEvent) {
|
||||
this.setUrlFromPath(finalPath);
|
||||
@@ -239,6 +242,9 @@ export class Router {
|
||||
if (route.path === "*") {
|
||||
return {};
|
||||
}
|
||||
if (path.indexOf("?") > -1) {
|
||||
path = path.split("?")[0];
|
||||
}
|
||||
if (path.startsWith("#")) {
|
||||
path = path.slice(1);
|
||||
}
|
||||
|
||||
+2
-1
@@ -231,7 +231,8 @@ function updateClass(oldVnode: VNode, vnode: VNode): void {
|
||||
elm = vnode.elm as Element;
|
||||
|
||||
for (name in oldClass) {
|
||||
if (name && !klass[name]) {
|
||||
if (name && !klass[name] && !Object.prototype.hasOwnProperty.call(klass, name)) {
|
||||
// was `true` and now not provided
|
||||
elm.classList.remove(name);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,7 +90,7 @@ exports[`class and style attributes with t-component t-att-class is properly add
|
||||
let scope = Object.create(context);
|
||||
let h = this.h;
|
||||
let _7 = {'c':true};
|
||||
Object.assign(_7, utils.toObj({d:scope['state'].d}))
|
||||
Object.assign(_7, utils.toClassObj({d:scope['state'].d}))
|
||||
let c8 = [], p8 = {key:8,class:_7};
|
||||
let vn8 = h('span', p8, c8);
|
||||
return vn8;
|
||||
@@ -112,7 +112,7 @@ exports[`class and style attributes with t-component t-att-class is properly add
|
||||
// Component 'Child'
|
||||
const ref4 = \`child\`;
|
||||
let _5 = {'a':true};
|
||||
Object.assign(_5, utils.toObj(scope['state'].b?'b':''))
|
||||
Object.assign(_5, utils.toClassObj(scope['state'].b?'b':''))
|
||||
let w2 = '__3__' in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap['__3__']] : false;
|
||||
let props2 = {};
|
||||
if (w2 && w2.__owl__.currentFiber && !w2.__owl__.vnode) {
|
||||
@@ -148,7 +148,7 @@ exports[`class and style attributes with t-component t-att-class is properly add
|
||||
let scope = Object.create(context);
|
||||
let h = this.h;
|
||||
let _7 = {'c':true};
|
||||
Object.assign(_7, utils.toObj(scope['state'].d?'d':''))
|
||||
Object.assign(_7, utils.toClassObj(scope['state'].d?'d':''))
|
||||
let c8 = [], p8 = {key:8,class:_7};
|
||||
let vn8 = h('span', p8, c8);
|
||||
return vn8;
|
||||
|
||||
@@ -51,7 +51,8 @@ exports[`basic widget properties reconciliation alg works for t-foreach in t-for
|
||||
let vn1 = h('div', p1, c1);
|
||||
let _2 = scope['state'].s;
|
||||
if (!_2) { throw new Error('QWeb error: Invalid loop expression')}
|
||||
let _3 = _4 = _2;
|
||||
let _3 = _2;
|
||||
let _4 = _2;
|
||||
if (!(_2 instanceof Array)) {
|
||||
_3 = Object.keys(_2);
|
||||
_4 = Object.values(_2);
|
||||
@@ -68,7 +69,8 @@ exports[`basic widget properties reconciliation alg works for t-foreach in t-for
|
||||
let key1 = i1;
|
||||
let _6 = scope['section'].blips;
|
||||
if (!_6) { throw new Error('QWeb error: Invalid loop expression')}
|
||||
let _7 = _8 = _6;
|
||||
let _7 = _6;
|
||||
let _8 = _6;
|
||||
if (!(_6 instanceof Array)) {
|
||||
_7 = Object.keys(_6);
|
||||
_8 = Object.values(_6);
|
||||
@@ -257,6 +259,45 @@ exports[`basic widget properties t-key on a component with t-if, and a sibling c
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`composition can switch between dynamic components without the need for a t-key 1`] = `
|
||||
"function anonymous(context, extra
|
||||
) {
|
||||
// Template name: \\"__template__3\\"
|
||||
let utils = this.constructor.utils;
|
||||
let QWeb = this.constructor;
|
||||
let parent = context;
|
||||
let scope = Object.create(context);
|
||||
let h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
let vn1 = h('div', p1, c1);
|
||||
// Component '{{state.child}}'
|
||||
let componentKey2 = (scope['state'].child);
|
||||
let k3 = '___' + componentKey2
|
||||
let w2 = k3 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[k3]] : false;
|
||||
let props2 = {};
|
||||
if (w2 && w2.__owl__.currentFiber && !w2.__owl__.vnode) {
|
||||
w2.destroy();
|
||||
w2 = false;
|
||||
}
|
||||
if (w2) {
|
||||
w2.__updateProps(props2, extra.fiber, undefined);
|
||||
let pvnode = w2.__owl__.pvnode;
|
||||
c1.push(pvnode);
|
||||
} else {
|
||||
let W2 = false || context.constructor.components[componentKey2] || QWeb.components[componentKey2];
|
||||
if (!W2) {throw new Error('Cannot find the definition of component \\"' + componentKey2 + '\\"')}
|
||||
w2 = new W2(parent, props2);
|
||||
parent.__owl__.cmap[k3] = w2.__owl__.id;
|
||||
let fiber = w2.__prepare(extra.fiber, undefined, () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
|
||||
let pvnode = h('dummy', {key: k3, hook: {remove() {},destroy(vn) {w2.destroy();}}});
|
||||
c1.push(pvnode);
|
||||
w2.__owl__.pvnode = pvnode;
|
||||
}
|
||||
w2.__owl__.parentLastFiberId = extra.fiber.id;
|
||||
return vn1;
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`composition sub components with some state rendered in a loop 1`] = `
|
||||
"function anonymous(context, extra
|
||||
) {
|
||||
@@ -270,7 +311,8 @@ exports[`composition sub components with some state rendered in a loop 1`] = `
|
||||
let vn1 = h('div', p1, c1);
|
||||
let _2 = scope['state'].numbers;
|
||||
if (!_2) { throw new Error('QWeb error: Invalid loop expression')}
|
||||
let _3 = _4 = _2;
|
||||
let _3 = _2;
|
||||
let _4 = _2;
|
||||
if (!(_2 instanceof Array)) {
|
||||
_3 = Object.keys(_2);
|
||||
_4 = Object.values(_2);
|
||||
@@ -330,7 +372,9 @@ exports[`composition t-component with dynamic value 1`] = `
|
||||
let c1 = [], p1 = {key:1};
|
||||
let vn1 = h('div', p1, c1);
|
||||
// Component '{{state.widget}}'
|
||||
let w2 = '__3__' in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap['__3__']] : false;
|
||||
let componentKey2 = (scope['state'].widget);
|
||||
let k3 = '___' + componentKey2
|
||||
let w2 = k3 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[k3]] : false;
|
||||
let props2 = {};
|
||||
if (w2 && w2.__owl__.currentFiber && !w2.__owl__.vnode) {
|
||||
w2.destroy();
|
||||
@@ -341,13 +385,12 @@ exports[`composition t-component with dynamic value 1`] = `
|
||||
let pvnode = w2.__owl__.pvnode;
|
||||
c1.push(pvnode);
|
||||
} else {
|
||||
let componentKey2 = (scope['state'].widget);
|
||||
let W2 = false || context.constructor.components[componentKey2] || QWeb.components[componentKey2];
|
||||
if (!W2) {throw new Error('Cannot find the definition of component \\"' + componentKey2 + '\\"')}
|
||||
w2 = new W2(parent, props2);
|
||||
parent.__owl__.cmap['__3__'] = w2.__owl__.id;
|
||||
parent.__owl__.cmap[k3] = w2.__owl__.id;
|
||||
let fiber = w2.__prepare(extra.fiber, undefined, () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
|
||||
let pvnode = h('dummy', {key: '__3__', hook: {remove() {},destroy(vn) {w2.destroy();}}});
|
||||
let pvnode = h('dummy', {key: k3, hook: {remove() {},destroy(vn) {w2.destroy();}}});
|
||||
c1.push(pvnode);
|
||||
w2.__owl__.pvnode = pvnode;
|
||||
}
|
||||
@@ -368,7 +411,9 @@ exports[`composition t-component with dynamic value 2 1`] = `
|
||||
let c1 = [], p1 = {key:1};
|
||||
let vn1 = h('div', p1, c1);
|
||||
// Component 'Widget{{state.widget}}'
|
||||
let w2 = '__3__' in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap['__3__']] : false;
|
||||
let componentKey2 = \`Widget\${scope['state'].widget}\`;
|
||||
let k3 = '___' + componentKey2
|
||||
let w2 = k3 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[k3]] : false;
|
||||
let props2 = {};
|
||||
if (w2 && w2.__owl__.currentFiber && !w2.__owl__.vnode) {
|
||||
w2.destroy();
|
||||
@@ -379,13 +424,12 @@ exports[`composition t-component with dynamic value 2 1`] = `
|
||||
let pvnode = w2.__owl__.pvnode;
|
||||
c1.push(pvnode);
|
||||
} else {
|
||||
let componentKey2 = \`Widget\${scope['state'].widget}\`;
|
||||
let W2 = false || context.constructor.components[componentKey2] || QWeb.components[componentKey2];
|
||||
if (!W2) {throw new Error('Cannot find the definition of component \\"' + componentKey2 + '\\"')}
|
||||
w2 = new W2(parent, props2);
|
||||
parent.__owl__.cmap['__3__'] = w2.__owl__.id;
|
||||
parent.__owl__.cmap[k3] = w2.__owl__.id;
|
||||
let fiber = w2.__prepare(extra.fiber, undefined, () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
|
||||
let pvnode = h('dummy', {key: '__3__', hook: {remove() {},destroy(vn) {w2.destroy();}}});
|
||||
let pvnode = h('dummy', {key: k3, hook: {remove() {},destroy(vn) {w2.destroy();}}});
|
||||
c1.push(pvnode);
|
||||
w2.__owl__.pvnode = pvnode;
|
||||
}
|
||||
@@ -467,7 +511,7 @@ exports[`dynamic t-props basic use 1`] = `
|
||||
let vn1 = h('div', p1, c1);
|
||||
// Component 'Child'
|
||||
let w2 = '__3__' in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap['__3__']] : false;
|
||||
let props2 = Object.assign({}, scope['some'].obj);
|
||||
let props2 = Object.assign({}, scope['some'].obj, {});
|
||||
if (w2 && w2.__owl__.currentFiber && !w2.__owl__.vnode) {
|
||||
w2.destroy();
|
||||
w2 = false;
|
||||
@@ -518,7 +562,7 @@ exports[`other directives with t-component slot setted value (with t-set) not ac
|
||||
w3 = false;
|
||||
}
|
||||
if (w3) {
|
||||
w3.__updateProps(props3, extra.fiber, Object.assign(Object.create(context), scope));
|
||||
w3.__updateProps(props3, extra.fiber, utils.combine(context, scope));
|
||||
let pvnode = w3.__owl__.pvnode;
|
||||
c1.push(pvnode);
|
||||
} else {
|
||||
@@ -528,7 +572,7 @@ exports[`other directives with t-component slot setted value (with t-set) not ac
|
||||
w3 = new W3(parent, props3);
|
||||
parent.__owl__.cmap['__4__'] = w3.__owl__.id;
|
||||
w3.__owl__.slotId = 1;
|
||||
let fiber = w3.__prepare(extra.fiber, Object.assign(Object.create(context), scope), () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
|
||||
let fiber = w3.__prepare(extra.fiber, utils.combine(context, scope), () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
|
||||
let pvnode = h('dummy', {key: '__4__', hook: {remove() {},destroy(vn) {w3.destroy();}}});
|
||||
c1.push(pvnode);
|
||||
w3.__owl__.pvnode = pvnode;
|
||||
@@ -556,7 +600,8 @@ exports[`other directives with t-component t-on expression captured in t-foreach
|
||||
scope.iter = 0;
|
||||
let _2 = scope['arr'];
|
||||
if (!_2) { throw new Error('QWeb error: Invalid loop expression')}
|
||||
let _3 = _4 = _2;
|
||||
let _3 = _2;
|
||||
let _4 = _2;
|
||||
if (!(_2 instanceof Array)) {
|
||||
_3 = Object.keys(_2);
|
||||
_4 = Object.values(_2);
|
||||
@@ -579,7 +624,7 @@ exports[`other directives with t-component t-on expression captured in t-foreach
|
||||
c6.push(vn7);
|
||||
const otherState_8 = scope['otherState'];
|
||||
const iter_8 = scope.iter;
|
||||
p7.on['click'] = function (e) {if (context.__owl__.status === 5){return}otherState_8.vals.push(iter_8+'_'+iter_8)};
|
||||
p7.on['click'] = function (e) {if (context.__owl__.status === 5){return}const res = (() => { return otherState_8.vals.push(iter_8+'_'+iter_8) })(); if (typeof res === 'function') { res(e) }};
|
||||
c7.push({text: \`expr\`});
|
||||
utils.getScope(scope, 'iter').iter = scope.iter+1;
|
||||
}
|
||||
@@ -598,7 +643,8 @@ exports[`other directives with t-component t-on expression in t-foreach 1`] = `
|
||||
let vn1 = h('div', p1, c1);
|
||||
let _2 = scope['state'].values;
|
||||
if (!_2) { throw new Error('QWeb error: Invalid loop expression')}
|
||||
let _3 = _4 = _2;
|
||||
let _3 = _2;
|
||||
let _4 = _2;
|
||||
if (!(_2 instanceof Array)) {
|
||||
_3 = Object.keys(_2);
|
||||
_4 = Object.values(_2);
|
||||
@@ -630,7 +676,7 @@ exports[`other directives with t-component t-on expression in t-foreach 1`] = `
|
||||
c6.push(vn9);
|
||||
const otherState_10 = scope['otherState'];
|
||||
const val_10 = scope['val'];
|
||||
p9.on['click'] = function (e) {if (context.__owl__.status === 5){return}otherState_10.vals.push(val_10)};
|
||||
p9.on['click'] = function (e) {if (context.__owl__.status === 5){return}const res = (() => { return otherState_10.vals.push(val_10) })(); if (typeof res === 'function') { res(e) }};
|
||||
c9.push({text: \`Expr\`});
|
||||
}
|
||||
scope = _origScope5;
|
||||
@@ -650,7 +696,8 @@ exports[`other directives with t-component t-on expression in t-foreach with t-s
|
||||
scope.bossa = 'nova';
|
||||
let _2 = scope['state'].values;
|
||||
if (!_2) { throw new Error('QWeb error: Invalid loop expression')}
|
||||
let _3 = _4 = _2;
|
||||
let _3 = _2;
|
||||
let _4 = _2;
|
||||
if (!(_2 instanceof Array)) {
|
||||
_3 = Object.keys(_2);
|
||||
_4 = Object.values(_2);
|
||||
@@ -684,7 +731,7 @@ exports[`other directives with t-component t-on expression in t-foreach with t-s
|
||||
const otherState_10 = scope['otherState'];
|
||||
const val_10 = scope['val'];
|
||||
const bossa_10 = scope.bossa;
|
||||
p9.on['click'] = function (e) {if (context.__owl__.status === 5){return}otherState_10.vals.push(val_10+'_'+bossa_10)};
|
||||
p9.on['click'] = function (e) {if (context.__owl__.status === 5){return}const res = (() => { return otherState_10.vals.push(val_10+'_'+bossa_10) })(); if (typeof res === 'function') { res(e) }};
|
||||
c9.push({text: \`Expr\`});
|
||||
}
|
||||
scope = _origScope5;
|
||||
@@ -703,7 +750,8 @@ exports[`other directives with t-component t-on method call in t-foreach 1`] = `
|
||||
let vn1 = h('div', p1, c1);
|
||||
let _2 = scope['state'].values;
|
||||
if (!_2) { throw new Error('QWeb error: Invalid loop expression')}
|
||||
let _3 = _4 = _2;
|
||||
let _3 = _2;
|
||||
let _4 = _2;
|
||||
if (!(_2 instanceof Array)) {
|
||||
_3 = Object.keys(_2);
|
||||
_4 = Object.values(_2);
|
||||
@@ -1011,7 +1059,7 @@ exports[`other directives with t-component t-on with inline statement 1`] = `
|
||||
if (!W3) {throw new Error('Cannot find the definition of component \\"' + componentKey3 + '\\"')}
|
||||
w3 = new W3(parent, props3);
|
||||
parent.__owl__.cmap['__4__'] = w3.__owl__.id;
|
||||
let fiber = w3.__prepare(extra.fiber, undefined, () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; utils.assignHooks(vnode.data, {create(_, vn){vn.elm.addEventListener('ev', function (e) {if (context.__owl__.status === 5){return}state_5.counter++});}});});
|
||||
let fiber = w3.__prepare(extra.fiber, undefined, () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; utils.assignHooks(vnode.data, {create(_, vn){vn.elm.addEventListener('ev', function (e) {if (context.__owl__.status === 5){return}const res = (() => { return state_5.counter++ })(); if (typeof res === 'function') { res(e) }});}});});
|
||||
let pvnode = h('dummy', {key: '__4__', hook: {remove() {},destroy(vn) {w3.destroy();}}});
|
||||
c1.push(pvnode);
|
||||
w3.__owl__.pvnode = pvnode;
|
||||
@@ -1369,7 +1417,8 @@ exports[`other directives with t-component t-set outside modified in t-foreach 1
|
||||
scope.iter = 0;
|
||||
let _2 = scope['state'].values;
|
||||
if (!_2) { throw new Error('QWeb error: Invalid loop expression')}
|
||||
let _3 = _4 = _2;
|
||||
let _3 = _2;
|
||||
let _4 = _2;
|
||||
if (!(_2 instanceof Array)) {
|
||||
_3 = Object.keys(_2);
|
||||
_4 = Object.values(_2);
|
||||
@@ -1405,6 +1454,350 @@ exports[`other directives with t-component t-set outside modified in t-foreach 1
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`props evaluation arrow function prop captures component instance as 'this' 1`] = `
|
||||
"function anonymous(context, extra
|
||||
) {
|
||||
// Template name: \\"Parent\\"
|
||||
let utils = this.constructor.utils;
|
||||
let QWeb = this.constructor;
|
||||
let parent = context;
|
||||
let scope = Object.create(context);
|
||||
let h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
let vn1 = h('div', p1, c1);
|
||||
// Component 'Child'
|
||||
const this_2 = utils.getComponent(context);
|
||||
let w3 = '__4__' in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap['__4__']] : false;
|
||||
let props3 = {callback:value=>this_2.setValue(value),value:scope['state'].val};
|
||||
if (w3 && w3.__owl__.currentFiber && !w3.__owl__.vnode) {
|
||||
w3.destroy();
|
||||
w3 = false;
|
||||
}
|
||||
if (w3) {
|
||||
w3.__updateProps(props3, extra.fiber, undefined);
|
||||
let pvnode = w3.__owl__.pvnode;
|
||||
c1.push(pvnode);
|
||||
} else {
|
||||
let componentKey3 = \`Child\`;
|
||||
let W3 = scope['Child'] || context.constructor.components[componentKey3] || QWeb.components[componentKey3];
|
||||
if (!W3) {throw new Error('Cannot find the definition of component \\"' + componentKey3 + '\\"')}
|
||||
w3 = new W3(parent, props3);
|
||||
parent.__owl__.cmap['__4__'] = w3.__owl__.id;
|
||||
let fiber = w3.__prepare(extra.fiber, undefined, () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
|
||||
let pvnode = h('dummy', {key: '__4__', hook: {remove() {},destroy(vn) {w3.destroy();}}});
|
||||
c1.push(pvnode);
|
||||
w3.__owl__.pvnode = pvnode;
|
||||
}
|
||||
w3.__owl__.parentLastFiberId = extra.fiber.id;
|
||||
return vn1;
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`props evaluation arrow function prop captures component instance as 'this' 2`] = `
|
||||
"function anonymous(context, extra
|
||||
) {
|
||||
// Template name: \\"Child\\"
|
||||
let scope = Object.create(context);
|
||||
let h = this.h;
|
||||
let c5 = [], p5 = {key:5};
|
||||
let vn5 = h('span', p5, c5);
|
||||
let _6 = scope['props'].value;
|
||||
if (_6 != null) {
|
||||
c5.push({text: _6});
|
||||
}
|
||||
return vn5;
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`props evaluation arrow function prop captures context component instance as 'this' inside slot 1`] = `
|
||||
"function anonymous(context, extra
|
||||
) {
|
||||
// Template name: \\"Parent\\"
|
||||
let utils = this.constructor.utils;
|
||||
let QWeb = this.constructor;
|
||||
let parent = context;
|
||||
let scope = Object.create(context);
|
||||
let h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
let vn1 = h('div', p1, c1);
|
||||
// Component 'Wrapper'
|
||||
let w2 = '__3__' in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap['__3__']] : false;
|
||||
let props2 = {};
|
||||
if (w2 && w2.__owl__.currentFiber && !w2.__owl__.vnode) {
|
||||
w2.destroy();
|
||||
w2 = false;
|
||||
}
|
||||
if (w2) {
|
||||
w2.__updateProps(props2, extra.fiber, utils.combine(context, scope));
|
||||
let pvnode = w2.__owl__.pvnode;
|
||||
c1.push(pvnode);
|
||||
} else {
|
||||
let componentKey2 = \`Wrapper\`;
|
||||
let W2 = scope['Wrapper'] || context.constructor.components[componentKey2] || QWeb.components[componentKey2];
|
||||
if (!W2) {throw new Error('Cannot find the definition of component \\"' + componentKey2 + '\\"')}
|
||||
w2 = new W2(parent, props2);
|
||||
parent.__owl__.cmap['__3__'] = w2.__owl__.id;
|
||||
w2.__owl__.slotId = 1;
|
||||
let fiber = w2.__prepare(extra.fiber, utils.combine(context, scope), () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
|
||||
let pvnode = h('dummy', {key: '__3__', hook: {remove() {},destroy(vn) {w2.destroy();}}});
|
||||
c1.push(pvnode);
|
||||
w2.__owl__.pvnode = pvnode;
|
||||
}
|
||||
w2.__owl__.parentLastFiberId = extra.fiber.id;
|
||||
return vn1;
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`props evaluation arrow function prop captures context component instance as 'this' inside slot 2`] = `
|
||||
"function anonymous(context, extra
|
||||
) {
|
||||
// Template name: \\"Wrapper\\"
|
||||
let utils = this.constructor.utils;
|
||||
let result;
|
||||
let h = this.h;
|
||||
const slot8 = this.constructor.slots[context.__owl__.slotId + '_' + 'default'];
|
||||
if (slot8) {
|
||||
let children9= []
|
||||
result = {}
|
||||
slot8.call(this, context.__owl__.scope, Object.assign({}, extra, {parentNode: children9, parent: extra.parent || context}));
|
||||
utils.defineProxy(result, children9[0]);
|
||||
}
|
||||
return result;
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`props evaluation arrow function prop captures context component instance as 'this' inside slot 3`] = `
|
||||
"function anonymous(context, extra
|
||||
) {
|
||||
// Template name: \\"slot_default_template\\"
|
||||
let utils = this.constructor.utils;
|
||||
let QWeb = this.constructor;
|
||||
let parent = extra.parent;
|
||||
let scope = Object.create(context);
|
||||
let h = this.h;
|
||||
let c4 = extra.parentNode;
|
||||
// Component 'Child'
|
||||
const this_5 = utils.getComponent(context);
|
||||
let w6 = '__7__' in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap['__7__']] : false;
|
||||
let props6 = {callback:value=>this_5.setValue(value),value:scope['state'].val};
|
||||
if (w6 && w6.__owl__.currentFiber && !w6.__owl__.vnode) {
|
||||
w6.destroy();
|
||||
w6 = false;
|
||||
}
|
||||
if (w6) {
|
||||
w6.__updateProps(props6, extra.fiber, undefined);
|
||||
let pvnode = w6.__owl__.pvnode;
|
||||
c4.push(pvnode);
|
||||
} else {
|
||||
let componentKey6 = \`Child\`;
|
||||
let W6 = scope['Child'] || context.constructor.components[componentKey6] || QWeb.components[componentKey6];
|
||||
if (!W6) {throw new Error('Cannot find the definition of component \\"' + componentKey6 + '\\"')}
|
||||
w6 = new W6(parent, props6);
|
||||
parent.__owl__.cmap['__7__'] = w6.__owl__.id;
|
||||
let fiber = w6.__prepare(extra.fiber, undefined, () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
|
||||
let pvnode = h('dummy', {key: '__7__', hook: {remove() {},destroy(vn) {w6.destroy();}}});
|
||||
c4.push(pvnode);
|
||||
w6.__owl__.pvnode = pvnode;
|
||||
}
|
||||
w6.__owl__.parentLastFiberId = extra.fiber.id;
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`props evaluation arrow function prop captures context component instance as 'this' inside slot 4`] = `
|
||||
"function anonymous(context, extra
|
||||
) {
|
||||
// Template name: \\"Child\\"
|
||||
let scope = Object.create(context);
|
||||
let h = this.h;
|
||||
let c10 = [], p10 = {key:10};
|
||||
let vn10 = h('span', p10, c10);
|
||||
let _11 = scope['props'].value;
|
||||
if (_11 != null) {
|
||||
c10.push({text: _11});
|
||||
}
|
||||
return vn10;
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`props evaluation arrow function prop captures context component instance as 'this' inside slot default content 1`] = `
|
||||
"function anonymous(context, extra
|
||||
) {
|
||||
// Template name: \\"Parent\\"
|
||||
let utils = this.constructor.utils;
|
||||
let QWeb = this.constructor;
|
||||
let parent = context;
|
||||
let scope = Object.create(context);
|
||||
let h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
let vn1 = h('div', p1, c1);
|
||||
// Component 'Wrapper'
|
||||
let w2 = '__3__' in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap['__3__']] : false;
|
||||
let props2 = {};
|
||||
if (w2 && w2.__owl__.currentFiber && !w2.__owl__.vnode) {
|
||||
w2.destroy();
|
||||
w2 = false;
|
||||
}
|
||||
if (w2) {
|
||||
w2.__updateProps(props2, extra.fiber, undefined);
|
||||
let pvnode = w2.__owl__.pvnode;
|
||||
c1.push(pvnode);
|
||||
} else {
|
||||
let componentKey2 = \`Wrapper\`;
|
||||
let W2 = scope['Wrapper'] || context.constructor.components[componentKey2] || QWeb.components[componentKey2];
|
||||
if (!W2) {throw new Error('Cannot find the definition of component \\"' + componentKey2 + '\\"')}
|
||||
w2 = new W2(parent, props2);
|
||||
parent.__owl__.cmap['__3__'] = w2.__owl__.id;
|
||||
let fiber = w2.__prepare(extra.fiber, undefined, () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
|
||||
let pvnode = h('dummy', {key: '__3__', hook: {remove() {},destroy(vn) {w2.destroy();}}});
|
||||
c1.push(pvnode);
|
||||
w2.__owl__.pvnode = pvnode;
|
||||
}
|
||||
w2.__owl__.parentLastFiberId = extra.fiber.id;
|
||||
return vn1;
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`props evaluation arrow function prop captures context component instance as 'this' inside slot default content 2`] = `
|
||||
"function anonymous(context, extra
|
||||
) {
|
||||
// Template name: \\"Wrapper\\"
|
||||
let utils = this.constructor.utils;
|
||||
let QWeb = this.constructor;
|
||||
let parent = context;
|
||||
let scope = Object.create(context);
|
||||
let result;
|
||||
let h = this.h;
|
||||
const slot4 = this.constructor.slots[context.__owl__.slotId + '_' + 'default'];
|
||||
if (slot4) {
|
||||
let children5= []
|
||||
result = {}
|
||||
slot4.call(this, context.__owl__.scope, Object.assign({}, extra, {parentNode: children5, parent: extra.parent || context}));
|
||||
utils.defineProxy(result, children5[0]);
|
||||
} else {
|
||||
// Component 'Child'
|
||||
const this_6 = utils.getComponent(context);
|
||||
let w7 = '__8__' in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap['__8__']] : false;
|
||||
let vn9 = {};
|
||||
result = vn9;
|
||||
let props7 = {callback:value=>this_6.setValue(value),value:scope['state'].val};
|
||||
if (w7 && w7.__owl__.currentFiber && !w7.__owl__.vnode) {
|
||||
w7.destroy();
|
||||
w7 = false;
|
||||
}
|
||||
if (w7) {
|
||||
w7.__updateProps(props7, extra.fiber, undefined);
|
||||
let pvnode = w7.__owl__.pvnode;
|
||||
utils.defineProxy(vn9, pvnode);
|
||||
} else {
|
||||
let componentKey7 = \`Child\`;
|
||||
let W7 = scope['Child'] || context.constructor.components[componentKey7] || QWeb.components[componentKey7];
|
||||
if (!W7) {throw new Error('Cannot find the definition of component \\"' + componentKey7 + '\\"')}
|
||||
w7 = new W7(parent, props7);
|
||||
parent.__owl__.cmap['__8__'] = w7.__owl__.id;
|
||||
let fiber = w7.__prepare(extra.fiber, undefined, () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
|
||||
let pvnode = h('dummy', {key: '__8__', hook: {remove() {},destroy(vn) {w7.destroy();}}});
|
||||
utils.defineProxy(vn9, pvnode);
|
||||
w7.__owl__.pvnode = pvnode;
|
||||
}
|
||||
w7.__owl__.parentLastFiberId = extra.fiber.id;
|
||||
}
|
||||
return result;
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`props evaluation arrow function prop captures context component instance as 'this' inside slot default content 3`] = `
|
||||
"function anonymous(context, extra
|
||||
) {
|
||||
// Template name: \\"Child\\"
|
||||
let scope = Object.create(context);
|
||||
let h = this.h;
|
||||
let c10 = [], p10 = {key:10};
|
||||
let vn10 = h('span', p10, c10);
|
||||
let _11 = scope['props'].value;
|
||||
if (_11 != null) {
|
||||
c10.push({text: _11});
|
||||
}
|
||||
return vn10;
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`props evaluation arrow function prop captures loop variables 1`] = `
|
||||
"function anonymous(context, extra
|
||||
) {
|
||||
// Template name: \\"Parent\\"
|
||||
let utils = this.constructor.utils;
|
||||
let QWeb = this.constructor;
|
||||
let parent = context;
|
||||
let scope = Object.create(context);
|
||||
let h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
let vn1 = h('div', p1, c1);
|
||||
let _2 = [0,1];
|
||||
if (!_2) { throw new Error('QWeb error: Invalid loop expression')}
|
||||
let _3 = _2;
|
||||
let _4 = _2;
|
||||
if (!(_2 instanceof Array)) {
|
||||
_3 = Object.keys(_2);
|
||||
_4 = Object.values(_2);
|
||||
}
|
||||
let _length3 = _3.length;
|
||||
let _origScope5 = scope;
|
||||
scope = Object.create(scope);
|
||||
for (let i1 = 0; i1 < _length3; i1++) {
|
||||
scope.loopVar_first = i1 === 0
|
||||
scope.loopVar_last = i1 === _length3 - 1
|
||||
scope.loopVar_index = i1
|
||||
scope.loopVar = _3[i1]
|
||||
scope.loopVar_value = _4[i1]
|
||||
let key1 = scope['loopVar'];
|
||||
// Component 'Child'
|
||||
const this_6 = utils.getComponent(context);
|
||||
const loopVar_6 = scope['loopVar'];
|
||||
let k8 = \`__8__\${key1}__\`;
|
||||
let w7 = k8 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[k8]] : false;
|
||||
let props7 = {callback:()=>this_6.setValue(loopVar_6),value:scope['state'].val};
|
||||
if (w7 && w7.__owl__.currentFiber && !w7.__owl__.vnode) {
|
||||
w7.destroy();
|
||||
w7 = false;
|
||||
}
|
||||
if (w7) {
|
||||
w7.__updateProps(props7, extra.fiber, undefined);
|
||||
let pvnode = w7.__owl__.pvnode;
|
||||
c1.push(pvnode);
|
||||
} else {
|
||||
let componentKey7 = \`Child\`;
|
||||
let W7 = scope['Child'] || context.constructor.components[componentKey7] || QWeb.components[componentKey7];
|
||||
if (!W7) {throw new Error('Cannot find the definition of component \\"' + componentKey7 + '\\"')}
|
||||
w7 = new W7(parent, props7);
|
||||
parent.__owl__.cmap[k8] = w7.__owl__.id;
|
||||
let fiber = w7.__prepare(extra.fiber, undefined, () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
|
||||
let pvnode = h('dummy', {key: k8, hook: {remove() {},destroy(vn) {w7.destroy();}}});
|
||||
c1.push(pvnode);
|
||||
w7.__owl__.pvnode = pvnode;
|
||||
}
|
||||
w7.__owl__.parentLastFiberId = extra.fiber.id;
|
||||
}
|
||||
scope = _origScope5;
|
||||
return vn1;
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`props evaluation arrow function prop captures loop variables 2`] = `
|
||||
"function anonymous(context, extra
|
||||
) {
|
||||
// Template name: \\"Child\\"
|
||||
let scope = Object.create(context);
|
||||
let h = this.h;
|
||||
let c9 = [], p9 = {key:9};
|
||||
let vn9 = h('span', p9, c9);
|
||||
let _10 = scope['props'].value;
|
||||
if (_10 != null) {
|
||||
c9.push({text: _10});
|
||||
}
|
||||
return vn9;
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`props evaluation t-set with a body expression can be used as textual prop 1`] = `
|
||||
"function anonymous(context, extra
|
||||
) {
|
||||
@@ -1519,7 +1912,8 @@ exports[`random stuff/miscellaneous t-on with handler bound to dynamic argument
|
||||
let vn1 = h('div', p1, c1);
|
||||
let _2 = scope['items'];
|
||||
if (!_2) { throw new Error('QWeb error: Invalid loop expression')}
|
||||
let _3 = _4 = _2;
|
||||
let _3 = _2;
|
||||
let _4 = _2;
|
||||
if (!(_2 instanceof Array)) {
|
||||
_3 = Object.keys(_2);
|
||||
_4 = Object.values(_2);
|
||||
@@ -1573,6 +1967,7 @@ exports[`t-call handlers are properly bound through a t-call 1`] = `
|
||||
) {
|
||||
// Template name: \\"sub\\"
|
||||
let utils = this.constructor.utils;
|
||||
let parent = extra.parent;
|
||||
let h = this.h;
|
||||
let c2 = extra.parentNode;
|
||||
let key0 = extra.key || \\"\\";
|
||||
@@ -1591,6 +1986,7 @@ exports[`t-call handlers with arguments are properly bound through a t-call 1`]
|
||||
) {
|
||||
// Template name: \\"sub\\"
|
||||
let utils = this.constructor.utils;
|
||||
let parent = extra.parent;
|
||||
let scope = Object.create(context);
|
||||
let h = this.h;
|
||||
let c2 = extra.parentNode;
|
||||
@@ -1795,7 +2191,8 @@ exports[`t-model directive in a t-foreach 1`] = `
|
||||
let vn1 = h('div', p1, c1);
|
||||
let _2 = scope['state'];
|
||||
if (!_2) { throw new Error('QWeb error: Invalid loop expression')}
|
||||
let _3 = _4 = _2;
|
||||
let _3 = _2;
|
||||
let _4 = _2;
|
||||
if (!(_2 instanceof Array)) {
|
||||
_3 = Object.keys(_2);
|
||||
_4 = Object.values(_2);
|
||||
@@ -1835,7 +2232,8 @@ exports[`t-model directive in a t-foreach, part 2 1`] = `
|
||||
let vn1 = h('div', p1, c1);
|
||||
let _2 = scope['state'];
|
||||
if (!_2) { throw new Error('QWeb error: Invalid loop expression')}
|
||||
let _3 = _4 = _2;
|
||||
let _3 = _2;
|
||||
let _4 = _2;
|
||||
if (!(_2 instanceof Array)) {
|
||||
_3 = Object.keys(_2);
|
||||
_4 = Object.values(_2);
|
||||
|
||||
@@ -19,7 +19,7 @@ exports[`t-slot directive can define and call slots 1`] = `
|
||||
w2 = false;
|
||||
}
|
||||
if (w2) {
|
||||
w2.__updateProps(props2, extra.fiber, Object.assign(Object.create(context), scope));
|
||||
w2.__updateProps(props2, extra.fiber, utils.combine(context, scope));
|
||||
let pvnode = w2.__owl__.pvnode;
|
||||
c1.push(pvnode);
|
||||
} else {
|
||||
@@ -29,7 +29,7 @@ exports[`t-slot directive can define and call slots 1`] = `
|
||||
w2 = new W2(parent, props2);
|
||||
parent.__owl__.cmap['__3__'] = w2.__owl__.id;
|
||||
w2.__owl__.slotId = 1;
|
||||
let fiber = w2.__prepare(extra.fiber, Object.assign(Object.create(context), scope), () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
|
||||
let fiber = w2.__prepare(extra.fiber, utils.combine(context, scope), () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
|
||||
let pvnode = h('dummy', {key: '__3__', hook: {remove() {},destroy(vn) {w2.destroy();}}});
|
||||
c1.push(pvnode);
|
||||
w2.__owl__.pvnode = pvnode;
|
||||
@@ -44,23 +44,23 @@ exports[`t-slot directive can define and call slots 2`] = `
|
||||
) {
|
||||
// Template name: \\"Dialog\\"
|
||||
let h = this.h;
|
||||
let c8 = [], p8 = {key:8};
|
||||
let vn8 = h('div', p8, c8);
|
||||
let c9 = [], p9 = {key:9};
|
||||
let vn9 = h('div', p9, c9);
|
||||
let c10 = [], p10 = {key:10};
|
||||
let vn10 = h('div', p10, c10);
|
||||
c9.push(vn10);
|
||||
const slot11 = this.constructor.slots[context.__owl__.slotId + '_' + 'header'];
|
||||
if (slot11) {
|
||||
slot11.call(this, context.__owl__.scope, Object.assign({}, extra, {parentNode: c10, parent: extra.parent || context}));
|
||||
c8.push(vn9);
|
||||
const slot10 = this.constructor.slots[context.__owl__.slotId + '_' + 'header'];
|
||||
if (slot10) {
|
||||
slot10.call(this, context.__owl__.scope, Object.assign({}, extra, {parentNode: c9, parent: extra.parent || context}));
|
||||
}
|
||||
let c12 = [], p12 = {key:12};
|
||||
let vn12 = h('div', p12, c12);
|
||||
c9.push(vn12);
|
||||
const slot13 = this.constructor.slots[context.__owl__.slotId + '_' + 'footer'];
|
||||
if (slot13) {
|
||||
slot13.call(this, context.__owl__.scope, Object.assign({}, extra, {parentNode: c12, parent: extra.parent || context}));
|
||||
let c11 = [], p11 = {key:11};
|
||||
let vn11 = h('div', p11, c11);
|
||||
c8.push(vn11);
|
||||
const slot12 = this.constructor.slots[context.__owl__.slotId + '_' + 'footer'];
|
||||
if (slot12) {
|
||||
slot12.call(this, context.__owl__.scope, Object.assign({}, extra, {parentNode: c11, parent: extra.parent || context}));
|
||||
}
|
||||
return vn9;
|
||||
return vn8;
|
||||
}"
|
||||
`;
|
||||
|
||||
@@ -68,6 +68,7 @@ exports[`t-slot directive can define and call slots 3`] = `
|
||||
"function anonymous(context, extra
|
||||
) {
|
||||
// Template name: \\"slot_header_template\\"
|
||||
let parent = extra.parent;
|
||||
let h = this.h;
|
||||
let c4 = extra.parentNode;
|
||||
let c5 = [], p5 = {key:5};
|
||||
@@ -81,6 +82,7 @@ exports[`t-slot directive can define and call slots 4`] = `
|
||||
"function anonymous(context, extra
|
||||
) {
|
||||
// Template name: \\"slot_footer_template\\"
|
||||
let parent = extra.parent;
|
||||
let h = this.h;
|
||||
let c6 = extra.parentNode;
|
||||
let c7 = [], p7 = {key:7};
|
||||
@@ -109,7 +111,7 @@ exports[`t-slot directive can define and call slots using old t-set keyword 1`]
|
||||
w2 = false;
|
||||
}
|
||||
if (w2) {
|
||||
w2.__updateProps(props2, extra.fiber, Object.assign(Object.create(context), scope));
|
||||
w2.__updateProps(props2, extra.fiber, utils.combine(context, scope));
|
||||
let pvnode = w2.__owl__.pvnode;
|
||||
c1.push(pvnode);
|
||||
} else {
|
||||
@@ -119,7 +121,7 @@ exports[`t-slot directive can define and call slots using old t-set keyword 1`]
|
||||
w2 = new W2(parent, props2);
|
||||
parent.__owl__.cmap['__3__'] = w2.__owl__.id;
|
||||
w2.__owl__.slotId = 1;
|
||||
let fiber = w2.__prepare(extra.fiber, Object.assign(Object.create(context), scope), () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
|
||||
let fiber = w2.__prepare(extra.fiber, utils.combine(context, scope), () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
|
||||
let pvnode = h('dummy', {key: '__3__', hook: {remove() {},destroy(vn) {w2.destroy();}}});
|
||||
c1.push(pvnode);
|
||||
w2.__owl__.pvnode = pvnode;
|
||||
@@ -134,23 +136,23 @@ exports[`t-slot directive can define and call slots using old t-set keyword 2`]
|
||||
) {
|
||||
// Template name: \\"__template__1\\"
|
||||
let h = this.h;
|
||||
let c8 = [], p8 = {key:8};
|
||||
let vn8 = h('div', p8, c8);
|
||||
let c9 = [], p9 = {key:9};
|
||||
let vn9 = h('div', p9, c9);
|
||||
let c10 = [], p10 = {key:10};
|
||||
let vn10 = h('div', p10, c10);
|
||||
c9.push(vn10);
|
||||
const slot11 = this.constructor.slots[context.__owl__.slotId + '_' + 'header'];
|
||||
if (slot11) {
|
||||
slot11.call(this, context.__owl__.scope, Object.assign({}, extra, {parentNode: c10, parent: extra.parent || context}));
|
||||
c8.push(vn9);
|
||||
const slot10 = this.constructor.slots[context.__owl__.slotId + '_' + 'header'];
|
||||
if (slot10) {
|
||||
slot10.call(this, context.__owl__.scope, Object.assign({}, extra, {parentNode: c9, parent: extra.parent || context}));
|
||||
}
|
||||
let c12 = [], p12 = {key:12};
|
||||
let vn12 = h('div', p12, c12);
|
||||
c9.push(vn12);
|
||||
const slot13 = this.constructor.slots[context.__owl__.slotId + '_' + 'footer'];
|
||||
if (slot13) {
|
||||
slot13.call(this, context.__owl__.scope, Object.assign({}, extra, {parentNode: c12, parent: extra.parent || context}));
|
||||
let c11 = [], p11 = {key:11};
|
||||
let vn11 = h('div', p11, c11);
|
||||
c8.push(vn11);
|
||||
const slot12 = this.constructor.slots[context.__owl__.slotId + '_' + 'footer'];
|
||||
if (slot12) {
|
||||
slot12.call(this, context.__owl__.scope, Object.assign({}, extra, {parentNode: c11, parent: extra.parent || context}));
|
||||
}
|
||||
return vn9;
|
||||
return vn8;
|
||||
}"
|
||||
`;
|
||||
|
||||
@@ -158,6 +160,7 @@ exports[`t-slot directive can define and call slots using old t-set keyword 3`]
|
||||
"function anonymous(context, extra
|
||||
) {
|
||||
// Template name: \\"slot_header_template\\"
|
||||
let parent = extra.parent;
|
||||
let h = this.h;
|
||||
let c4 = extra.parentNode;
|
||||
let c5 = [], p5 = {key:5};
|
||||
@@ -171,6 +174,7 @@ exports[`t-slot directive can define and call slots using old t-set keyword 4`]
|
||||
"function anonymous(context, extra
|
||||
) {
|
||||
// Template name: \\"slot_footer_template\\"
|
||||
let parent = extra.parent;
|
||||
let h = this.h;
|
||||
let c6 = extra.parentNode;
|
||||
let c7 = [], p7 = {key:7};
|
||||
@@ -184,6 +188,7 @@ exports[`t-slot directive content is the default slot 1`] = `
|
||||
"function anonymous(context, extra
|
||||
) {
|
||||
// Template name: \\"slot_default_template\\"
|
||||
let parent = extra.parent;
|
||||
let h = this.h;
|
||||
let c4 = extra.parentNode;
|
||||
let c5 = [], p5 = {key:5};
|
||||
@@ -210,10 +215,50 @@ exports[`t-slot directive dafault slots can define a default content 1`] = `
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`t-slot directive default slot next to named slot, with default content 1`] = `
|
||||
"function anonymous(context, extra
|
||||
) {
|
||||
// Template name: \\"__template__2\\"
|
||||
let utils = this.constructor.utils;
|
||||
let QWeb = this.constructor;
|
||||
let parent = context;
|
||||
let scope = Object.create(context);
|
||||
let h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
let vn1 = h('div', p1, c1);
|
||||
// Component 'Dialog'
|
||||
let w2 = '__3__' in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap['__3__']] : false;
|
||||
let props2 = {};
|
||||
if (w2 && w2.__owl__.currentFiber && !w2.__owl__.vnode) {
|
||||
w2.destroy();
|
||||
w2 = false;
|
||||
}
|
||||
if (w2) {
|
||||
w2.__updateProps(props2, extra.fiber, utils.combine(context, scope));
|
||||
let pvnode = w2.__owl__.pvnode;
|
||||
c1.push(pvnode);
|
||||
} else {
|
||||
let componentKey2 = \`Dialog\`;
|
||||
let W2 = scope['Dialog'] || context.constructor.components[componentKey2] || QWeb.components[componentKey2];
|
||||
if (!W2) {throw new Error('Cannot find the definition of component \\"' + componentKey2 + '\\"')}
|
||||
w2 = new W2(parent, props2);
|
||||
parent.__owl__.cmap['__3__'] = w2.__owl__.id;
|
||||
w2.__owl__.slotId = 1;
|
||||
let fiber = w2.__prepare(extra.fiber, utils.combine(context, scope), () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
|
||||
let pvnode = h('dummy', {key: '__3__', hook: {remove() {},destroy(vn) {w2.destroy();}}});
|
||||
c1.push(pvnode);
|
||||
w2.__owl__.pvnode = pvnode;
|
||||
}
|
||||
w2.__owl__.parentLastFiberId = extra.fiber.id;
|
||||
return vn1;
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`t-slot directive default slot work with text nodes 1`] = `
|
||||
"function anonymous(context, extra
|
||||
) {
|
||||
// Template name: \\"slot_default_template\\"
|
||||
let parent = extra.parent;
|
||||
let h = this.h;
|
||||
let c4 = extra.parentNode;
|
||||
c4.push({text: \`sts rocks\`});
|
||||
@@ -227,15 +272,15 @@ exports[`t-slot directive dynamic t-slot call 1`] = `
|
||||
let utils = this.constructor.utils;
|
||||
let scope = Object.create(context);
|
||||
let h = this.h;
|
||||
let c10 = [], p10 = {key:10,on:{}};
|
||||
let vn10 = h('button', p10, c10);
|
||||
extra.handlers['click__11__'] = extra.handlers['click__11__'] || function (e) {if (context.__owl__.status === 5){return}utils.getComponent(context)['toggle'](e);};
|
||||
p10.on['click'] = extra.handlers['click__11__'];
|
||||
const slot12 = this.constructor.slots[context.__owl__.slotId + '_' + (scope['current'].slot)];
|
||||
if (slot12) {
|
||||
slot12.call(this, context.__owl__.scope, Object.assign({}, extra, {parentNode: c10, parent: extra.parent || context}));
|
||||
let c9 = [], p9 = {key:9,on:{}};
|
||||
let vn9 = h('button', p9, c9);
|
||||
extra.handlers['click__10__'] = extra.handlers['click__10__'] || function (e) {if (context.__owl__.status === 5){return}utils.getComponent(context)['toggle'](e);};
|
||||
p9.on['click'] = extra.handlers['click__10__'];
|
||||
const slot11 = this.constructor.slots[context.__owl__.slotId + '_' + (scope['current'].slot)];
|
||||
if (slot11) {
|
||||
slot11.call(this, context.__owl__.scope, Object.assign({}, extra, {parentNode: c9, parent: extra.parent || context}));
|
||||
}
|
||||
return vn10;
|
||||
return vn9;
|
||||
}"
|
||||
`;
|
||||
|
||||
@@ -243,6 +288,7 @@ exports[`t-slot directive multiple roots are allowed in a default slot 1`] = `
|
||||
"function anonymous(context, extra
|
||||
) {
|
||||
// Template name: \\"slot_default_template\\"
|
||||
let parent = extra.parent;
|
||||
let h = this.h;
|
||||
let c4 = extra.parentNode;
|
||||
let c5 = [], p5 = {key:5};
|
||||
@@ -260,6 +306,7 @@ exports[`t-slot directive multiple roots are allowed in a named slot 1`] = `
|
||||
"function anonymous(context, extra
|
||||
) {
|
||||
// Template name: \\"slot_content_template\\"
|
||||
let parent = extra.parent;
|
||||
let h = this.h;
|
||||
let c4 = extra.parentNode;
|
||||
let c5 = [], p5 = {key:5};
|
||||
@@ -295,6 +342,7 @@ exports[`t-slot directive refs are properly bound in slots 1`] = `
|
||||
) {
|
||||
// Template name: \\"slot_footer_template\\"
|
||||
let utils = this.constructor.utils;
|
||||
let parent = extra.parent;
|
||||
context.__owl__.refs = context.__owl__.refs || {};
|
||||
let h = this.h;
|
||||
let c8 = extra.parentNode;
|
||||
@@ -321,6 +369,7 @@ exports[`t-slot directive slots are rendered with proper context 1`] = `
|
||||
) {
|
||||
// Template name: \\"slot_footer_template\\"
|
||||
let utils = this.constructor.utils;
|
||||
let parent = extra.parent;
|
||||
let h = this.h;
|
||||
let c8 = extra.parentNode;
|
||||
let c9 = [], p9 = {key:9,on:{}};
|
||||
@@ -365,7 +414,8 @@ exports[`t-slot directive slots are rendered with proper context, part 2 2`] = `
|
||||
c1.push(vn2);
|
||||
let _3 = scope['state'].users;
|
||||
if (!_3) { throw new Error('QWeb error: Invalid loop expression')}
|
||||
let _4 = _5 = _3;
|
||||
let _4 = _3;
|
||||
let _5 = _3;
|
||||
if (!(_3 instanceof Array)) {
|
||||
_4 = Object.keys(_3);
|
||||
_5 = Object.values(_3);
|
||||
@@ -392,7 +442,7 @@ exports[`t-slot directive slots are rendered with proper context, part 2 2`] = `
|
||||
w8 = false;
|
||||
}
|
||||
if (w8) {
|
||||
w8.__updateProps(props8, extra.fiber, Object.assign(Object.create(context), scope));
|
||||
w8.__updateProps(props8, extra.fiber, utils.combine(context, scope));
|
||||
let pvnode = w8.__owl__.pvnode;
|
||||
c7.push(pvnode);
|
||||
} else {
|
||||
@@ -402,7 +452,7 @@ exports[`t-slot directive slots are rendered with proper context, part 2 2`] = `
|
||||
w8 = new W8(parent, props8);
|
||||
parent.__owl__.cmap[k9] = w8.__owl__.id;
|
||||
w8.__owl__.slotId = 1;
|
||||
let fiber = w8.__prepare(extra.fiber, Object.assign(Object.create(context), scope), () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
|
||||
let fiber = w8.__prepare(extra.fiber, utils.combine(context, scope), () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
|
||||
let pvnode = h('dummy', {key: k9, hook: {remove() {},destroy(vn) {w8.destroy();}}});
|
||||
c7.push(pvnode);
|
||||
w8.__owl__.pvnode = pvnode;
|
||||
@@ -418,6 +468,7 @@ exports[`t-slot directive slots are rendered with proper context, part 2 3`] = `
|
||||
"function anonymous(context, extra
|
||||
) {
|
||||
// Template name: \\"slot_default_template\\"
|
||||
let parent = extra.parent;
|
||||
let scope = Object.create(context);
|
||||
let h = this.h;
|
||||
let c10 = extra.parentNode;
|
||||
@@ -462,7 +513,8 @@ exports[`t-slot directive slots are rendered with proper context, part 3 2`] = `
|
||||
c1.push(vn2);
|
||||
let _3 = scope['state'].users;
|
||||
if (!_3) { throw new Error('QWeb error: Invalid loop expression')}
|
||||
let _4 = _5 = _3;
|
||||
let _4 = _3;
|
||||
let _5 = _3;
|
||||
if (!(_3 instanceof Array)) {
|
||||
_4 = Object.keys(_3);
|
||||
_5 = Object.values(_3);
|
||||
@@ -490,7 +542,7 @@ exports[`t-slot directive slots are rendered with proper context, part 3 2`] = `
|
||||
w8 = false;
|
||||
}
|
||||
if (w8) {
|
||||
w8.__updateProps(props8, extra.fiber, Object.assign(Object.create(context), scope));
|
||||
w8.__updateProps(props8, extra.fiber, utils.combine(context, scope));
|
||||
let pvnode = w8.__owl__.pvnode;
|
||||
c7.push(pvnode);
|
||||
} else {
|
||||
@@ -500,7 +552,7 @@ exports[`t-slot directive slots are rendered with proper context, part 3 2`] = `
|
||||
w8 = new W8(parent, props8);
|
||||
parent.__owl__.cmap[k9] = w8.__owl__.id;
|
||||
w8.__owl__.slotId = 1;
|
||||
let fiber = w8.__prepare(extra.fiber, Object.assign(Object.create(context), scope), () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
|
||||
let fiber = w8.__prepare(extra.fiber, utils.combine(context, scope), () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
|
||||
let pvnode = h('dummy', {key: k9, hook: {remove() {},destroy(vn) {w8.destroy();}}});
|
||||
c7.push(pvnode);
|
||||
w8.__owl__.pvnode = pvnode;
|
||||
@@ -516,6 +568,7 @@ exports[`t-slot directive slots are rendered with proper context, part 3 3`] = `
|
||||
"function anonymous(context, extra
|
||||
) {
|
||||
// Template name: \\"slot_default_template\\"
|
||||
let parent = extra.parent;
|
||||
let scope = Object.create(context);
|
||||
let h = this.h;
|
||||
let c10 = extra.parentNode;
|
||||
@@ -546,7 +599,7 @@ exports[`t-slot directive slots are rendered with proper context, part 4 1`] = `
|
||||
w2 = false;
|
||||
}
|
||||
if (w2) {
|
||||
w2.__updateProps(props2, extra.fiber, Object.assign(Object.create(context), scope));
|
||||
w2.__updateProps(props2, extra.fiber, utils.combine(context, scope));
|
||||
let pvnode = w2.__owl__.pvnode;
|
||||
c1.push(pvnode);
|
||||
} else {
|
||||
@@ -556,7 +609,7 @@ exports[`t-slot directive slots are rendered with proper context, part 4 1`] = `
|
||||
w2 = new W2(parent, props2);
|
||||
parent.__owl__.cmap['__3__'] = w2.__owl__.id;
|
||||
w2.__owl__.slotId = 1;
|
||||
let fiber = w2.__prepare(extra.fiber, Object.assign(Object.create(context), scope), () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
|
||||
let fiber = w2.__prepare(extra.fiber, utils.combine(context, scope), () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
|
||||
let pvnode = h('dummy', {key: '__3__', hook: {remove() {},destroy(vn) {w2.destroy();}}});
|
||||
c1.push(pvnode);
|
||||
w2.__owl__.pvnode = pvnode;
|
||||
@@ -570,6 +623,7 @@ exports[`t-slot directive slots are rendered with proper context, part 4 2`] = `
|
||||
"function anonymous(context, extra
|
||||
) {
|
||||
// Template name: \\"slot_default_template\\"
|
||||
let parent = extra.parent;
|
||||
let scope = Object.create(context);
|
||||
let h = this.h;
|
||||
let c4 = extra.parentNode;
|
||||
@@ -580,6 +634,96 @@ exports[`t-slot directive slots are rendered with proper context, part 4 2`] = `
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`t-slot directive slots in t-foreach in t-foreach 1`] = `
|
||||
"function anonymous(context, extra
|
||||
) {
|
||||
// Template name: \\"__template__2\\"
|
||||
let utils = this.constructor.utils;
|
||||
let QWeb = this.constructor;
|
||||
let parent = context;
|
||||
let scope = Object.create(context);
|
||||
let h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
let vn1 = h('div', p1, c1);
|
||||
let _2 = scope['tree'];
|
||||
if (!_2) { throw new Error('QWeb error: Invalid loop expression')}
|
||||
let _3 = _2;
|
||||
let _4 = _2;
|
||||
if (!(_2 instanceof Array)) {
|
||||
_3 = Object.keys(_2);
|
||||
_4 = Object.values(_2);
|
||||
}
|
||||
let _length3 = _3.length;
|
||||
let _origScope5 = scope;
|
||||
scope = Object.create(scope);
|
||||
for (let i1 = 0; i1 < _length3; i1++) {
|
||||
scope.node1_first = i1 === 0
|
||||
scope.node1_last = i1 === _length3 - 1
|
||||
scope.node1_index = i1
|
||||
scope.node1 = _3[i1]
|
||||
scope.node1_value = _4[i1]
|
||||
let key1 = scope['node1'].key;
|
||||
let c6 = [], p6 = {key:\`\${key1}_6\`};
|
||||
let vn6 = h('div', p6, c6);
|
||||
c1.push(vn6);
|
||||
let _7 = scope['node1'].value;
|
||||
if (_7 != null) {
|
||||
c6.push({text: _7});
|
||||
}
|
||||
let c8 = [], p8 = {key:\`\${key1}_8\`};
|
||||
let vn8 = h('ul', p8, c8);
|
||||
c1.push(vn8);
|
||||
let _9 = scope['node1'].nodes;
|
||||
if (!_9) { throw new Error('QWeb error: Invalid loop expression')}
|
||||
let _10 = _9;
|
||||
let _11 = _9;
|
||||
if (!(_9 instanceof Array)) {
|
||||
_10 = Object.keys(_9);
|
||||
_11 = Object.values(_9);
|
||||
}
|
||||
let _length10 = _10.length;
|
||||
let _origScope12 = scope;
|
||||
scope = Object.create(scope);
|
||||
for (let i2 = 0; i2 < _length10; i2++) {
|
||||
scope.node2_first = i2 === 0
|
||||
scope.node2_last = i2 === _length10 - 1
|
||||
scope.node2_index = i2
|
||||
scope.node2 = _10[i2]
|
||||
scope.node2_value = _11[i2]
|
||||
let key2 = scope['node2'].key;
|
||||
// Component 'Child'
|
||||
let k14 = \`__14__\${key1}__\${key2}__\`;
|
||||
let w13 = k14 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[k14]] : false;
|
||||
let props13 = {};
|
||||
if (w13 && w13.__owl__.currentFiber && !w13.__owl__.vnode) {
|
||||
w13.destroy();
|
||||
w13 = false;
|
||||
}
|
||||
if (w13) {
|
||||
w13.__updateProps(props13, extra.fiber, utils.combine(context, scope));
|
||||
let pvnode = w13.__owl__.pvnode;
|
||||
c8.push(pvnode);
|
||||
} else {
|
||||
let componentKey13 = \`Child\`;
|
||||
let W13 = scope['Child'] || context.constructor.components[componentKey13] || QWeb.components[componentKey13];
|
||||
if (!W13) {throw new Error('Cannot find the definition of component \\"' + componentKey13 + '\\"')}
|
||||
w13 = new W13(parent, props13);
|
||||
parent.__owl__.cmap[k14] = w13.__owl__.id;
|
||||
w13.__owl__.slotId = 1;
|
||||
let fiber = w13.__prepare(extra.fiber, utils.combine(context, scope), () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
|
||||
let pvnode = h('dummy', {key: k14, hook: {remove() {},destroy(vn) {w13.destroy();}}});
|
||||
c8.push(pvnode);
|
||||
w13.__owl__.pvnode = pvnode;
|
||||
}
|
||||
w13.__owl__.parentLastFiberId = extra.fiber.id;
|
||||
}
|
||||
scope = _origScope12;
|
||||
}
|
||||
scope = _origScope5;
|
||||
return vn1;
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`t-slot directive t-set t-value in a slot 1`] = `
|
||||
"function anonymous(context, extra
|
||||
) {
|
||||
|
||||
@@ -1388,6 +1388,62 @@ describe("async rendering", () => {
|
||||
expect(fixture.innerHTML).toBe("<span>4</span>");
|
||||
});
|
||||
|
||||
test("calling render in destroy", async () => {
|
||||
let a: any = null;
|
||||
let c: any = null;
|
||||
|
||||
class C extends Component {
|
||||
static template = xml`
|
||||
<div>
|
||||
<t t-esc="props.fromA"/>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
let flag = false;
|
||||
class B extends Component {
|
||||
static template = xml`<C fromA="props.fromA"/>`;
|
||||
static components = { C };
|
||||
|
||||
setup() {
|
||||
c = this;
|
||||
}
|
||||
|
||||
mounted() {
|
||||
if (flag) {
|
||||
this.render();
|
||||
} else {
|
||||
flag = true;
|
||||
}
|
||||
}
|
||||
willUnmount() {
|
||||
c.render();
|
||||
}
|
||||
}
|
||||
|
||||
class A extends Component {
|
||||
static template = xml`<B t-key="key" fromA="state"/>`;
|
||||
static components = { B };
|
||||
state = "a";
|
||||
key = 1;
|
||||
|
||||
setup() {
|
||||
a = this;
|
||||
}
|
||||
}
|
||||
|
||||
const parent = new A();
|
||||
await parent.mount(fixture);
|
||||
expect(fixture.innerHTML).toBe("<div>a</div>");
|
||||
|
||||
a.state = "A";
|
||||
a.key = 2;
|
||||
await a.render();
|
||||
// this nextTick is critical, otherwise jest may silently swallow errors
|
||||
await nextTick();
|
||||
|
||||
expect(fixture.innerHTML).toBe("<div>A</div>");
|
||||
});
|
||||
|
||||
test("change state and call manually render: no unnecessary rendering", async () => {
|
||||
class Widget extends Component {
|
||||
static template = xml`<div><t t-esc="state.val"/></div>`;
|
||||
|
||||
@@ -265,7 +265,9 @@ describe("class and style attributes with t-component", () => {
|
||||
error = e;
|
||||
}
|
||||
expect(error).toBeDefined();
|
||||
expect(error.message).toBe("Cannot read property 'crash' of undefined");
|
||||
const regexp =
|
||||
/Cannot read properties of undefined \(reading 'crash'\)|Cannot read property 'crash' of undefined/g;
|
||||
expect(error.message).toMatch(regexp);
|
||||
expect(fixture.innerHTML).toBe("");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -149,6 +149,24 @@ describe("basic widget properties", () => {
|
||||
expect(fixture.innerHTML).toBe("<div>1<button>Inc</button></div>");
|
||||
});
|
||||
|
||||
test("support for callable expression in event handler", async () => {
|
||||
class Counter extends Component {
|
||||
static template = xml`
|
||||
<div><t t-esc="state.value"/><input type="text" t-on-input="obj.onInput"/></div>`;
|
||||
state = useState({ value: "" });
|
||||
obj = { onInput: (ev) => (this.state.value = ev.target.value) };
|
||||
}
|
||||
|
||||
const counter = await mount(Counter, { target: fixture });
|
||||
await nextTick();
|
||||
expect(fixture.innerHTML).toBe(`<div><input type="text"></div>`);
|
||||
const input = (<HTMLElement>counter.el).getElementsByTagName("input")[0];
|
||||
input.value = "test";
|
||||
input.dispatchEvent(new Event("input"));
|
||||
await nextTick();
|
||||
expect(fixture.innerHTML).toBe(`<div>test<input type="text"></div>`);
|
||||
});
|
||||
|
||||
test("can handle empty props", async () => {
|
||||
class Child extends Component {
|
||||
static template = xml`<span><t t-esc="props.val"/></span>`;
|
||||
@@ -1228,6 +1246,30 @@ describe("composition", () => {
|
||||
expect(fixture.innerHTML).toBe("<div>child b</div>");
|
||||
});
|
||||
|
||||
test("can switch between dynamic components without the need for a t-key", async () => {
|
||||
class A extends Component {
|
||||
static template = xml`<span>child a</span>`;
|
||||
}
|
||||
class B extends Component {
|
||||
static template = xml`<span>child b</span>`;
|
||||
}
|
||||
class App extends Component {
|
||||
static template = xml`
|
||||
<div>
|
||||
<t t-component="{{state.child}}"/>
|
||||
</div>`;
|
||||
static components = { A, B };
|
||||
|
||||
state = useState({ child: "A" });
|
||||
}
|
||||
const app = await mount(App, { target: fixture });
|
||||
expect(fixture.innerHTML).toBe("<div><span>child a</span></div>");
|
||||
app.state.child = "B";
|
||||
await nextTick();
|
||||
expect(fixture.innerHTML).toBe("<div><span>child b</span></div>");
|
||||
expect(QWeb.TEMPLATES[App.template].fn.toString()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test("don't fallback to global registry if widget defined locally", async () => {
|
||||
QWeb.registerComponent("WidgetB", WidgetB); // should not use this widget
|
||||
env.qweb.addTemplate("ParentWidget", `<div><t t-component="WidgetB"/></div>`);
|
||||
@@ -1839,6 +1881,176 @@ describe("props evaluation ", () => {
|
||||
await widget.mount(fixture);
|
||||
expect(fixture.innerHTML).toBe("<div><span><p>4343</p><p>43</p></span></div>");
|
||||
});
|
||||
|
||||
test("arrow function prop captures component instance as 'this'", async () => {
|
||||
expect.assertions(5);
|
||||
let child, parent;
|
||||
class Child extends Component {
|
||||
setup() {
|
||||
child = this;
|
||||
}
|
||||
}
|
||||
env.qweb.addTemplate("Child", `<span><t t-esc="props.value"/></span>`);
|
||||
|
||||
class Parent extends Component {
|
||||
static components = { Child };
|
||||
state = useState({ val: 42 });
|
||||
setup() {
|
||||
parent = this;
|
||||
}
|
||||
setValue(value) {
|
||||
expect(this).toBe(parent);
|
||||
this.state.val = value;
|
||||
}
|
||||
}
|
||||
env.qweb.addTemplate(
|
||||
"Parent",
|
||||
`<div>
|
||||
<Child callback="value => this.setValue(value)" value="state.val"/>
|
||||
</div>`
|
||||
);
|
||||
|
||||
const widget = new Parent();
|
||||
await widget.mount(fixture);
|
||||
expect(fixture.innerHTML).toBe("<div><span>42</span></div>");
|
||||
child.props.callback(123);
|
||||
await nextTick();
|
||||
expect(fixture.innerHTML).toBe("<div><span>123</span></div>");
|
||||
expect(env.qweb.templates.Parent.fn.toString()).toMatchSnapshot();
|
||||
expect(env.qweb.templates.Child.fn.toString()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test("arrow function prop captures context component instance as 'this' inside slot", async () => {
|
||||
expect.assertions(7);
|
||||
let child, parent;
|
||||
class Child extends Component {
|
||||
setup() {
|
||||
child = this;
|
||||
}
|
||||
}
|
||||
env.qweb.addTemplate("Child", `<span><t t-esc="props.value"/></span>`);
|
||||
|
||||
class Wrapper extends Component {}
|
||||
env.qweb.addTemplate("Wrapper", `<t t-slot="default"/>`);
|
||||
|
||||
class Parent extends Component {
|
||||
static components = { Child, Wrapper };
|
||||
state = useState({ val: 42 });
|
||||
setup() {
|
||||
parent = this;
|
||||
}
|
||||
setValue(value) {
|
||||
expect(this).toBe(parent);
|
||||
this.state.val = value;
|
||||
}
|
||||
}
|
||||
env.qweb.addTemplate(
|
||||
"Parent",
|
||||
`<div>
|
||||
<Wrapper>
|
||||
<Child callback="value => this.setValue(value)" value="state.val"/>
|
||||
</Wrapper>
|
||||
</div>`
|
||||
);
|
||||
|
||||
const widget = new Parent();
|
||||
await widget.mount(fixture);
|
||||
expect(fixture.innerHTML).toBe("<div><span>42</span></div>");
|
||||
child.props.callback(123);
|
||||
await nextTick();
|
||||
expect(fixture.innerHTML).toBe("<div><span>123</span></div>");
|
||||
expect(env.qweb.templates.Parent.fn.toString()).toMatchSnapshot();
|
||||
expect(env.qweb.templates.Wrapper.fn.toString()).toMatchSnapshot();
|
||||
expect(QWeb.slots["1_default"].toString()).toMatchSnapshot();
|
||||
expect(env.qweb.templates.Child.fn.toString()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test("arrow function prop captures context component instance as 'this' inside slot default content", async () => {
|
||||
expect.assertions(6);
|
||||
let child, wrapper;
|
||||
class Child extends Component {
|
||||
setup() {
|
||||
child = this;
|
||||
}
|
||||
}
|
||||
env.qweb.addTemplate("Child", `<span><t t-esc="props.value"/></span>`);
|
||||
|
||||
class Wrapper extends Component {
|
||||
static components = { Child };
|
||||
state = useState({ val: 42 });
|
||||
setup() {
|
||||
wrapper = this;
|
||||
}
|
||||
setValue(value) {
|
||||
expect(this).toBe(wrapper);
|
||||
this.state.val = value;
|
||||
}
|
||||
}
|
||||
env.qweb.addTemplate(
|
||||
"Wrapper",
|
||||
`<t t-slot="default">
|
||||
<Child callback="value => this.setValue(value)" value="state.val"/>
|
||||
</t>`
|
||||
);
|
||||
|
||||
class Parent extends Component {
|
||||
static components = { Wrapper };
|
||||
}
|
||||
env.qweb.addTemplate(
|
||||
"Parent",
|
||||
`<div>
|
||||
<Wrapper/>
|
||||
</div>`
|
||||
);
|
||||
|
||||
const widget = new Parent();
|
||||
await widget.mount(fixture);
|
||||
expect(fixture.innerHTML).toBe("<div><span>42</span></div>");
|
||||
child.props.callback(123);
|
||||
await nextTick();
|
||||
expect(fixture.innerHTML).toBe("<div><span>123</span></div>");
|
||||
expect(env.qweb.templates.Parent.fn.toString()).toMatchSnapshot();
|
||||
expect(env.qweb.templates.Wrapper.fn.toString()).toMatchSnapshot();
|
||||
expect(env.qweb.templates.Child.fn.toString()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test("arrow function prop captures loop variables", async () => {
|
||||
let children = [];
|
||||
class Child extends Component {
|
||||
setup() {
|
||||
children.push(this);
|
||||
}
|
||||
}
|
||||
env.qweb.addTemplate("Child", `<span><t t-esc="props.value"/></span>`);
|
||||
|
||||
class Parent extends Component {
|
||||
static components = { Child };
|
||||
state = useState({ val: 42 });
|
||||
setValue(value) {
|
||||
this.state.val = value;
|
||||
}
|
||||
}
|
||||
env.qweb.addTemplate(
|
||||
"Parent",
|
||||
`<div>
|
||||
<t t-foreach="[0, 1]" t-as="loopVar" t-key="loopVar">
|
||||
<Child callback="() => this.setValue(loopVar)" value="state.val"/>
|
||||
</t>
|
||||
</div>`
|
||||
);
|
||||
|
||||
const widget = new Parent();
|
||||
await widget.mount(fixture);
|
||||
expect(fixture.innerHTML).toBe("<div><span>42</span><span>42</span></div>");
|
||||
children[0].props.callback();
|
||||
await nextTick();
|
||||
expect(fixture.innerHTML).toBe("<div><span>0</span><span>0</span></div>");
|
||||
children[1].props.callback();
|
||||
await nextTick();
|
||||
expect(fixture.innerHTML).toBe("<div><span>1</span><span>1</span></div>");
|
||||
expect(env.qweb.templates.Parent.fn.toString()).toMatchSnapshot();
|
||||
expect(env.qweb.templates.Child.fn.toString()).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
describe("other directives with t-component", () => {
|
||||
@@ -4022,6 +4234,30 @@ describe("dynamic t-props", () => {
|
||||
expect(fixture.innerHTML).toBe("<div><span>3</span></div>");
|
||||
expect(env.qweb.templates[Parent.template].fn.toString()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test("t-props with props", async () => {
|
||||
expect.assertions(1);
|
||||
|
||||
class Child extends Component {
|
||||
static template = xml`<div />`;
|
||||
setup() {
|
||||
expect(this.props).toEqual({ a: 1, b: 2, c: "c" });
|
||||
}
|
||||
}
|
||||
class Parent extends Component {
|
||||
static template = xml`
|
||||
<div>
|
||||
<Child t-props="props" a="1" b="2" />
|
||||
</div>
|
||||
`;
|
||||
static components = { Child };
|
||||
|
||||
props = { a: "a", c: "c" };
|
||||
}
|
||||
|
||||
const widget = new Parent();
|
||||
await widget.mount(fixture);
|
||||
});
|
||||
});
|
||||
|
||||
describe("support svg components", () => {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Component, Env, STATUS } from "../../src/component/component";
|
||||
import { Component, Env, mount, STATUS } from "../../src/component/component";
|
||||
import { useState } from "../../src/hooks";
|
||||
import { xml } from "../../src/tags";
|
||||
import { makeTestEnv, makeTestFixture, nextTick } from "../helpers";
|
||||
@@ -95,7 +95,9 @@ describe("component error handling (catchError)", () => {
|
||||
try {
|
||||
await super.render();
|
||||
} catch (e) {
|
||||
expect(e.message).toBe("Cannot read property 'this' of undefined");
|
||||
expect(e.message).toMatch(
|
||||
/Cannot read properties of undefined \(reading 'this'\)|Cannot read property 'this' of undefined/
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -359,7 +361,9 @@ describe("component error handling (catchError)", () => {
|
||||
error = e;
|
||||
}
|
||||
expect(error).toBeDefined();
|
||||
expect(error.message).toBe("Cannot read property 'crash' of undefined");
|
||||
const regexp =
|
||||
/Cannot read properties of undefined \(reading 'crash'\)|Cannot read property 'crash' of undefined/g;
|
||||
expect(error.message).toMatch(regexp);
|
||||
|
||||
expect(console.error).toBeCalledTimes(0);
|
||||
console.error = consoleError;
|
||||
@@ -469,7 +473,9 @@ describe("component error handling (catchError)", () => {
|
||||
error = e;
|
||||
}
|
||||
expect(error).toBeDefined();
|
||||
expect(error.message).toBe("Cannot read property 'crash' of undefined");
|
||||
const regexp =
|
||||
/Cannot read properties of undefined \(reading 'crash'\)|Cannot read property 'crash' of undefined/g;
|
||||
expect(error.message).toMatch(regexp);
|
||||
|
||||
expect(console.error).toBeCalledTimes(0);
|
||||
console.error = consoleError;
|
||||
@@ -495,7 +501,9 @@ describe("component error handling (catchError)", () => {
|
||||
error = e;
|
||||
}
|
||||
expect(error).toBeDefined();
|
||||
expect(error.message).toBe("Cannot read property 'crash' of undefined");
|
||||
const regexp =
|
||||
/Cannot read properties of undefined \(reading 'crash'\)|Cannot read property 'crash' of undefined/g;
|
||||
expect(error.message).toMatch(regexp);
|
||||
|
||||
expect(console.error).toBeCalledTimes(0);
|
||||
console.error = consoleError;
|
||||
@@ -518,6 +526,95 @@ describe("component error handling (catchError)", () => {
|
||||
error = e;
|
||||
}
|
||||
expect(error).toBeDefined();
|
||||
expect(error.message).toBe("Cannot read property 'y' of undefined");
|
||||
const regexp =
|
||||
/Cannot read properties of undefined \(reading 'y'\)|Cannot read property 'y' of undefined/g;
|
||||
expect(error.message).toMatch(regexp);
|
||||
});
|
||||
|
||||
test("simple catchError", async () => {
|
||||
class Boom extends Component {
|
||||
static template = xml`<div t-esc="a.b.c"/>`;
|
||||
}
|
||||
|
||||
class Parent extends Component {
|
||||
static template = xml`
|
||||
<div>
|
||||
<t t-if="error">Error</t>
|
||||
<t t-else="">
|
||||
<Boom />
|
||||
</t>
|
||||
</div>`;
|
||||
static components = { Boom };
|
||||
|
||||
error = false;
|
||||
|
||||
catchError(error) {
|
||||
this.error = error;
|
||||
this.render();
|
||||
}
|
||||
}
|
||||
|
||||
await mount(Parent, { target: fixture });
|
||||
expect(fixture.innerHTML).toBe("<div>Error</div>");
|
||||
});
|
||||
|
||||
test("catchError in catchError", async () => {
|
||||
class Boom extends Component {
|
||||
static template = xml`<div t-esc="a.b.c"/>`;
|
||||
}
|
||||
|
||||
class Child extends Component {
|
||||
static template = xml`
|
||||
<div>
|
||||
<Boom />
|
||||
</div>`;
|
||||
static components = { Boom };
|
||||
|
||||
catchError(error) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
class Parent extends Component {
|
||||
static template = xml`
|
||||
<div>
|
||||
<t t-if="error">Error</t>
|
||||
<t t-else="">
|
||||
<Child />
|
||||
</t>
|
||||
</div>`;
|
||||
static components = { Child };
|
||||
|
||||
error = false;
|
||||
|
||||
catchError(error) {
|
||||
this.error = error;
|
||||
this.render();
|
||||
}
|
||||
}
|
||||
|
||||
await mount(Parent, { target: fixture });
|
||||
expect(fixture.innerHTML).toBe("<div>Error</div>");
|
||||
});
|
||||
|
||||
test("errors in mounted and in willUnmount", async () => {
|
||||
expect.assertions(1);
|
||||
class Example extends Component {
|
||||
static template = xml`<div/>`;
|
||||
val;
|
||||
mounted() {
|
||||
throw new Error("Error in mounted");
|
||||
this.val = { foo: "bar" };
|
||||
}
|
||||
willUnmount() {
|
||||
console.log(this.val.foo);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
await mount(Example, { target: fixture });
|
||||
} catch (e) {
|
||||
expect(e.message).toBe("Error in mounted");
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -325,6 +325,70 @@ describe("t-slot directive", () => {
|
||||
expect(QWeb.slots["1_default"].toString()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test("slots in t-foreach in t-foreach", async () => {
|
||||
class Child extends Component {
|
||||
static template = xml`
|
||||
<div><t t-slot="default" /></div>
|
||||
`;
|
||||
}
|
||||
|
||||
class App extends Component {
|
||||
static template = xml`
|
||||
<div>
|
||||
<t t-foreach="tree" t-as="node1" t-key="node1.key">
|
||||
<div t-esc="node1.value" />
|
||||
<ul>
|
||||
<t t-foreach="node1.nodes" t-as="node2" t-key="node2.key">
|
||||
<Child>
|
||||
<li t-esc="node1.value" />
|
||||
</Child>
|
||||
</t>
|
||||
</ul>
|
||||
</t>
|
||||
</div>`;
|
||||
|
||||
static components = { Child };
|
||||
|
||||
tree = [
|
||||
{
|
||||
key: "a",
|
||||
value: "A",
|
||||
nodes: [
|
||||
{
|
||||
key: "1",
|
||||
value: "A-1",
|
||||
},
|
||||
{
|
||||
key: "2",
|
||||
value: "A-2",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
key: "b",
|
||||
value: "B",
|
||||
nodes: [
|
||||
{
|
||||
key: "1",
|
||||
value: "B-1",
|
||||
},
|
||||
{
|
||||
key: "2",
|
||||
value: "B-2",
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
await mount(App, { target: fixture });
|
||||
|
||||
expect(fixture.innerHTML).toBe(
|
||||
"<div><div>A</div><ul><div><li>A</li></div><div><li>A</li></div></ul><div>B</div><ul><div><li>B</li></div><div><li>B</li></div></ul></div>"
|
||||
);
|
||||
expect(env.qweb.templates[App.template].fn.toString()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test("refs are properly bound in slots", async () => {
|
||||
class Dialog extends Component {
|
||||
static template = xml`<span><t t-slot="footer"/></span>`;
|
||||
@@ -403,6 +467,48 @@ describe("t-slot directive", () => {
|
||||
expect(QWeb.slots["1_default"].toString()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test("default slot next to named slot, with default content", async () => {
|
||||
class Dialog extends Component {
|
||||
// We're using 2 slots here: a "default" one and a "footer",
|
||||
// both having default children nodes.
|
||||
static template = xml`
|
||||
<div class="Dialog">
|
||||
<div class="content">
|
||||
<t t-slot="default">
|
||||
Default content
|
||||
</t>
|
||||
</div>
|
||||
<div class="footer">
|
||||
<t t-slot="footer">
|
||||
Default footer
|
||||
</t>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
class App extends Component {
|
||||
// Here we're trying to assign the "footer" slot with some content
|
||||
static components = { Dialog };
|
||||
static template = xml`
|
||||
<div>
|
||||
<Dialog>
|
||||
<t t-set-slot="footer">
|
||||
Overridden footer
|
||||
</t>
|
||||
</Dialog>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
await mount(App, { target: fixture });
|
||||
|
||||
expect(fixture.innerHTML).toBe(
|
||||
'<div><div class="Dialog"><div class="content"> Default content </div><div class="footer"> Overridden footer </div></div></div>'
|
||||
);
|
||||
expect(QWeb.TEMPLATES[App.template].fn.toString()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test("multiple roots are allowed in a named slot", async () => {
|
||||
env.qweb.addTemplates(`
|
||||
<templates>
|
||||
@@ -1118,4 +1224,150 @@ describe("t-slot directive", () => {
|
||||
|
||||
expect(env.qweb.templates[Toggler.template].fn.toString()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test("t-slot within dynamic t-call", async () => {
|
||||
let child;
|
||||
class Child extends Component {
|
||||
static template = xml`<div class="child"/>`;
|
||||
constructor(...args) {
|
||||
super(...args);
|
||||
child = this;
|
||||
}
|
||||
}
|
||||
|
||||
class Slotted extends Component {
|
||||
static template = xml`<div class="slotted"><t t-slot="default" /></div>`;
|
||||
}
|
||||
|
||||
class UsingTcallInSlotted extends Component {
|
||||
tcallTemplate = xml`<div class="slot"><Child/></div>`;
|
||||
static template = xml`
|
||||
<div>
|
||||
<Slotted>
|
||||
<t t-call="{{ tcallTemplate }}"/>
|
||||
</Slotted>
|
||||
</div>`;
|
||||
static components = { Slotted, Child };
|
||||
}
|
||||
|
||||
await mount(UsingTcallInSlotted, { target: fixture });
|
||||
|
||||
expect(child.__owl__.parent).toBeInstanceOf(Slotted);
|
||||
expect(fixture.innerHTML).toBe(
|
||||
`<div><div class="slotted"><div class="slot"><div class="child"></div></div></div></div>`
|
||||
);
|
||||
});
|
||||
|
||||
test("t-slot scope context", async () => {
|
||||
expect.assertions(1);
|
||||
|
||||
class Wrapper extends Component {
|
||||
static template = xml`<t t-slot="default"/>`;
|
||||
}
|
||||
|
||||
let dialog;
|
||||
|
||||
class Dialog extends Component {
|
||||
static template = xml`
|
||||
<Wrapper>
|
||||
<div t-on-click="onClick">
|
||||
<t t-slot="default" />
|
||||
</div>
|
||||
</Wrapper>
|
||||
`;
|
||||
|
||||
static components = { Wrapper };
|
||||
|
||||
setup() {
|
||||
dialog = this;
|
||||
}
|
||||
|
||||
onClick(ev) {
|
||||
// we do not use expect(this).toBe(dialog) here because if it fails, it
|
||||
// may blow up jest because it then tries to compute a diff, which is
|
||||
// infinite if there is a cycle
|
||||
expect(this === dialog).toBe(true);
|
||||
}
|
||||
}
|
||||
|
||||
class Parent extends Component {
|
||||
static template = xml`
|
||||
<Dialog>
|
||||
<button>The Button</button>
|
||||
</Dialog>`;
|
||||
static components = { Dialog };
|
||||
}
|
||||
|
||||
await mount(Parent, { target: fixture });
|
||||
|
||||
document.querySelector("button").click();
|
||||
await nextTick();
|
||||
});
|
||||
|
||||
test("t-slot in recursive templates", async () => {
|
||||
QWeb.registerTemplate(
|
||||
"_test_recursive_template",
|
||||
`
|
||||
<Wrapper>
|
||||
<t t-esc="name" />
|
||||
<t t-foreach="items" t-as="item">
|
||||
|
||||
<t t-if="!item.children.length">
|
||||
<t t-esc="item.name" />
|
||||
</t>
|
||||
|
||||
<t t-else="" t-call="_test_recursive_template">
|
||||
<t t-set="name" t-value="item.name" />
|
||||
<t t-set="items" t-value="item.children" />
|
||||
</t>
|
||||
|
||||
</t>
|
||||
</Wrapper>`
|
||||
);
|
||||
|
||||
class Wrapper extends Component {
|
||||
static template = xml`
|
||||
<wrapper>
|
||||
<t t-slot="default"/>
|
||||
</wrapper>`;
|
||||
}
|
||||
|
||||
class Parent extends Component {
|
||||
static template = "_test_recursive_template";
|
||||
static components = { Wrapper };
|
||||
name = "foo";
|
||||
items = [
|
||||
{
|
||||
name: "foo-0",
|
||||
children: [
|
||||
{ name: "foo-00", children: [] },
|
||||
{
|
||||
name: "foo-01",
|
||||
children: [
|
||||
{ name: "foo-010", children: [] },
|
||||
{ name: "foo-011", children: [] },
|
||||
{
|
||||
name: "foo-012",
|
||||
children: [
|
||||
{ name: "foo-0120", children: [] },
|
||||
{ name: "foo-0121", children: [] },
|
||||
{ name: "foo-0122", children: [] },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{ name: "foo-02", children: [] },
|
||||
],
|
||||
},
|
||||
{ name: "foo-1", children: [] },
|
||||
{ name: "foo-2", children: [] },
|
||||
];
|
||||
}
|
||||
|
||||
await mount(Parent, { target: fixture });
|
||||
|
||||
expect(fixture.innerHTML).toBe(
|
||||
"<wrapper>foo<wrapper>foo-0foo-00<wrapper>foo-01foo-010foo-011<wrapper>foo-012foo-0120foo-0121foo-0122</wrapper></wrapper>foo-02</wrapper>foo-1foo-2</wrapper>"
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -75,8 +75,6 @@ describe("hooks", () => {
|
||||
}
|
||||
const component = new MyComponent();
|
||||
await component.mount(fixture);
|
||||
expect(component).not.toHaveProperty("mounted");
|
||||
expect(component).not.toHaveProperty("willUnmount");
|
||||
expect(fixture.innerHTML).toBe("<div>hey</div>");
|
||||
expect(steps).toEqual(["mounted"]);
|
||||
component.unmount();
|
||||
@@ -383,8 +381,6 @@ describe("hooks", () => {
|
||||
|
||||
const component = new MyComponent();
|
||||
await component.mount(fixture);
|
||||
expect(component).not.toHaveProperty("patched");
|
||||
expect(component).not.toHaveProperty("willPatch");
|
||||
expect(steps).toEqual([]);
|
||||
|
||||
expect(fixture.innerHTML).toBe("<div>hey</div>");
|
||||
@@ -630,8 +626,6 @@ describe("hooks", () => {
|
||||
|
||||
const app = new App();
|
||||
await app.mount(fixture);
|
||||
expect(app).not.toHaveProperty("willStart");
|
||||
expect(app).not.toHaveProperty("willUpdateProps");
|
||||
expect(fixture.innerHTML).toBe("<div><span>1</span></div>");
|
||||
|
||||
// NOTE: 'on2ndStart' appears first in the list even though
|
||||
|
||||
@@ -546,7 +546,9 @@ describe("Portal: Basic use and DOM placement", () => {
|
||||
error = e;
|
||||
}
|
||||
expect(error).toBeDefined();
|
||||
expect(error.message).toBe("Cannot read property 'crash' of undefined");
|
||||
const regexp =
|
||||
/Cannot read properties of undefined \(reading 'crash'\)|Cannot read property 'crash' of undefined/g;
|
||||
expect(error.message).toMatch(regexp);
|
||||
});
|
||||
|
||||
test("portal manual unmount", async () => {
|
||||
|
||||
@@ -7,7 +7,7 @@ exports[`attributes class and t-att-class should combine together 1`] = `
|
||||
let utils = this.constructor.utils;
|
||||
let scope = Object.create(context);
|
||||
let h = this.h;
|
||||
let _1 = utils.toObj(scope['value']);
|
||||
let _1 = utils.toClassObj(scope['value']);
|
||||
Object.assign(_1, {'hello':true})
|
||||
let c3 = [], p3 = {key:3,class:_1};
|
||||
let vn3 = h('div', p3, c3);
|
||||
@@ -74,7 +74,7 @@ exports[`attributes dynamic class attribute 1`] = `
|
||||
let utils = this.constructor.utils;
|
||||
let scope = Object.create(context);
|
||||
let h = this.h;
|
||||
let _1 = utils.toObj(scope['c']);
|
||||
let _1 = utils.toClassObj(scope['c']);
|
||||
let c2 = [], p2 = {key:2,class:_1};
|
||||
let vn2 = h('div', p2, c2);
|
||||
return vn2;
|
||||
@@ -88,7 +88,7 @@ exports[`attributes dynamic empty class attribute 1`] = `
|
||||
let utils = this.constructor.utils;
|
||||
let scope = Object.create(context);
|
||||
let h = this.h;
|
||||
let _1 = utils.toObj(scope['c']);
|
||||
let _1 = utils.toClassObj(scope['c']);
|
||||
let c2 = [], p2 = {key:2,class:_1};
|
||||
let vn2 = h('div', p2, c2);
|
||||
return vn2;
|
||||
@@ -195,7 +195,7 @@ exports[`attributes from object variables set previously 1`] = `
|
||||
let c1 = [], p1 = {key:1};
|
||||
let vn1 = h('div', p1, c1);
|
||||
scope.o = {a:'b'};
|
||||
let _2 = utils.toObj(scope.o.a);
|
||||
let _2 = utils.toClassObj(scope.o.a);
|
||||
let c3 = [], p3 = {key:3,class:_2};
|
||||
let vn3 = h('span', p3, c3);
|
||||
c1.push(vn3);
|
||||
@@ -213,7 +213,7 @@ exports[`attributes from variables set previously 1`] = `
|
||||
let c1 = [], p1 = {key:1};
|
||||
let vn1 = h('div', p1, c1);
|
||||
scope.abc = 'def';
|
||||
let _2 = utils.toObj(scope.abc);
|
||||
let _2 = utils.toClassObj(scope.abc);
|
||||
let c3 = [], p3 = {key:3,class:_2};
|
||||
let vn3 = h('span', p3, c3);
|
||||
c1.push(vn3);
|
||||
@@ -288,7 +288,7 @@ exports[`attributes t-att-class and class should combine together 1`] = `
|
||||
let scope = Object.create(context);
|
||||
let h = this.h;
|
||||
let _2 = {'hello':true};
|
||||
Object.assign(_2, utils.toObj(scope['value']))
|
||||
Object.assign(_2, utils.toClassObj(scope['value']))
|
||||
let c3 = [], p3 = {key:3,class:_2};
|
||||
let vn3 = h('div', p3, c3);
|
||||
return vn3;
|
||||
@@ -303,7 +303,7 @@ exports[`attributes t-att-class with object 1`] = `
|
||||
let scope = Object.create(context);
|
||||
let h = this.h;
|
||||
let _2 = {'static':true};
|
||||
Object.assign(_2, utils.toObj({a:scope['b'],c:scope['d'],e:scope['f']}))
|
||||
Object.assign(_2, utils.toClassObj({a:scope['b'],c:scope['d'],e:scope['f']}))
|
||||
let c3 = [], p3 = {key:3,class:_2};
|
||||
let vn3 = h('div', p3, c3);
|
||||
return vn3;
|
||||
@@ -429,7 +429,8 @@ exports[`foreach does not pollute the rendering context 1`] = `
|
||||
let vn1 = h('div', p1, c1);
|
||||
let _2 = [1];
|
||||
if (!_2) { throw new Error('QWeb error: Invalid loop expression')}
|
||||
let _3 = _4 = _2;
|
||||
let _3 = _2;
|
||||
let _4 = _2;
|
||||
if (!(_2 instanceof Array)) {
|
||||
_3 = Object.keys(_2);
|
||||
_4 = Object.values(_2);
|
||||
@@ -464,7 +465,8 @@ exports[`foreach iterate on items (on a element node) 1`] = `
|
||||
let vn1 = h('div', p1, c1);
|
||||
let _2 = [1,2];
|
||||
if (!_2) { throw new Error('QWeb error: Invalid loop expression')}
|
||||
let _3 = _4 = _2;
|
||||
let _3 = _2;
|
||||
let _4 = _2;
|
||||
if (!(_2 instanceof Array)) {
|
||||
_3 = Object.keys(_2);
|
||||
_4 = Object.values(_2);
|
||||
@@ -502,7 +504,8 @@ exports[`foreach iterate on items 1`] = `
|
||||
let vn1 = h('div', p1, c1);
|
||||
let _2 = [3,2,1];
|
||||
if (!_2) { throw new Error('QWeb error: Invalid loop expression')}
|
||||
let _3 = _4 = _2;
|
||||
let _3 = _2;
|
||||
let _4 = _2;
|
||||
if (!(_2 instanceof Array)) {
|
||||
_3 = Object.keys(_2);
|
||||
_4 = Object.values(_2);
|
||||
@@ -549,7 +552,8 @@ exports[`foreach iterate, dict param 1`] = `
|
||||
let vn1 = h('div', p1, c1);
|
||||
let _2 = scope['value'];
|
||||
if (!_2) { throw new Error('QWeb error: Invalid loop expression')}
|
||||
let _3 = _4 = _2;
|
||||
let _3 = _2;
|
||||
let _4 = _2;
|
||||
if (!(_2 instanceof Array)) {
|
||||
_3 = Object.keys(_2);
|
||||
_4 = Object.values(_2);
|
||||
@@ -596,7 +600,8 @@ exports[`foreach iterate, position 1`] = `
|
||||
let vn1 = h('div', p1, c1);
|
||||
let _2 = Array(5);
|
||||
if (!_2) { throw new Error('QWeb error: Invalid loop expression')}
|
||||
let _3 = _4 = _2;
|
||||
let _3 = _2;
|
||||
let _4 = _2;
|
||||
if (!(_2 instanceof Array)) {
|
||||
_3 = Object.keys(_2);
|
||||
_4 = Object.values(_2);
|
||||
@@ -641,7 +646,8 @@ exports[`foreach t-call with body in t-foreach in t-foreach 1`] = `
|
||||
let vn1 = h('div', p1, c1);
|
||||
let _2 = scope['numbers'];
|
||||
if (!_2) { throw new Error('QWeb error: Invalid loop expression')}
|
||||
let _3 = _4 = _2;
|
||||
let _3 = _2;
|
||||
let _4 = _2;
|
||||
if (!(_2 instanceof Array)) {
|
||||
_3 = Object.keys(_2);
|
||||
_4 = Object.values(_2);
|
||||
@@ -658,7 +664,8 @@ exports[`foreach t-call with body in t-foreach in t-foreach 1`] = `
|
||||
let key1 = i1;
|
||||
let _6 = scope['letters'];
|
||||
if (!_6) { throw new Error('QWeb error: Invalid loop expression')}
|
||||
let _7 = _8 = _6;
|
||||
let _7 = _6;
|
||||
let _8 = _6;
|
||||
if (!(_6 instanceof Array)) {
|
||||
_7 = Object.keys(_6);
|
||||
_8 = Object.values(_6);
|
||||
@@ -729,7 +736,8 @@ exports[`foreach t-call without body in t-foreach in t-foreach 1`] = `
|
||||
let vn1 = h('div', p1, c1);
|
||||
let _2 = scope['numbers'];
|
||||
if (!_2) { throw new Error('QWeb error: Invalid loop expression')}
|
||||
let _3 = _4 = _2;
|
||||
let _3 = _2;
|
||||
let _4 = _2;
|
||||
if (!(_2 instanceof Array)) {
|
||||
_3 = Object.keys(_2);
|
||||
_4 = Object.values(_2);
|
||||
@@ -746,7 +754,8 @@ exports[`foreach t-call without body in t-foreach in t-foreach 1`] = `
|
||||
let key1 = i1;
|
||||
let _6 = scope['letters'];
|
||||
if (!_6) { throw new Error('QWeb error: Invalid loop expression')}
|
||||
let _7 = _8 = _6;
|
||||
let _7 = _6;
|
||||
let _8 = _6;
|
||||
if (!(_6 instanceof Array)) {
|
||||
_7 = Object.keys(_6);
|
||||
_8 = Object.values(_6);
|
||||
@@ -811,7 +820,8 @@ exports[`foreach t-foreach in t-forach 1`] = `
|
||||
let vn1 = h('div', p1, c1);
|
||||
let _2 = scope['numbers'];
|
||||
if (!_2) { throw new Error('QWeb error: Invalid loop expression')}
|
||||
let _3 = _4 = _2;
|
||||
let _3 = _2;
|
||||
let _4 = _2;
|
||||
if (!(_2 instanceof Array)) {
|
||||
_3 = Object.keys(_2);
|
||||
_4 = Object.values(_2);
|
||||
@@ -828,7 +838,8 @@ exports[`foreach t-foreach in t-forach 1`] = `
|
||||
let key1 = i1;
|
||||
let _6 = scope['letters'];
|
||||
if (!_6) { throw new Error('QWeb error: Invalid loop expression')}
|
||||
let _7 = _8 = _6;
|
||||
let _7 = _6;
|
||||
let _8 = _6;
|
||||
if (!(_6 instanceof Array)) {
|
||||
_7 = Object.keys(_6);
|
||||
_8 = Object.values(_6);
|
||||
@@ -871,7 +882,8 @@ exports[`foreach warn if no key in some case 1`] = `
|
||||
let vn1 = h('div', p1, c1);
|
||||
let _2 = [1,2];
|
||||
if (!_2) { throw new Error('QWeb error: Invalid loop expression')}
|
||||
let _3 = _4 = _2;
|
||||
let _3 = _2;
|
||||
let _4 = _2;
|
||||
if (!(_2 instanceof Array)) {
|
||||
_3 = Object.keys(_2);
|
||||
_4 = Object.values(_2);
|
||||
@@ -940,7 +952,8 @@ exports[`misc global 1`] = `
|
||||
let vn1 = h('div', p1, c1);
|
||||
let _2 = [4,5,6];
|
||||
if (!_2) { throw new Error('QWeb error: Invalid loop expression')}
|
||||
let _3 = _4 = _2;
|
||||
let _3 = _2;
|
||||
let _4 = _2;
|
||||
if (!(_2 instanceof Array)) {
|
||||
_3 = Object.keys(_2);
|
||||
_4 = Object.values(_2);
|
||||
@@ -1073,6 +1086,39 @@ exports[`special cases for some specific html attributes/properties input type=
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`special cases for some specific html attributes/properties select with t-att-value 1`] = `
|
||||
"function anonymous(context, extra
|
||||
) {
|
||||
// Template name: \\"test\\"
|
||||
let scope = Object.create(context);
|
||||
let h = this.h;
|
||||
let expr1 = scope['value'];
|
||||
let c3 = [], p3 = {key:3,attrs:{value: expr1},props:{value: expr1}};
|
||||
let vn3 = h('select', p3, c3);
|
||||
p3.hook = {
|
||||
create: (_, n) => {
|
||||
n.elm.value=expr1;
|
||||
},
|
||||
};
|
||||
let _4 = 'potato';
|
||||
let c5 = [], p5 = {key:5,attrs:{value: _4}};
|
||||
let vn5 = h('option', p5, c5);
|
||||
c3.push(vn5);
|
||||
c5.push({text: \`Potato\`});
|
||||
let _6 = 'tomato';
|
||||
let c7 = [], p7 = {key:7,attrs:{value: _6}};
|
||||
let vn7 = h('option', p7, c7);
|
||||
c3.push(vn7);
|
||||
c7.push({text: \`Tomato\`});
|
||||
let _8 = 'onion';
|
||||
let c9 = [], p9 = {key:9,attrs:{value: _8}};
|
||||
let vn9 = h('option', p9, c9);
|
||||
c3.push(vn9);
|
||||
c9.push({text: \`Onion\`});
|
||||
return vn3;
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`special cases for some specific html attributes/properties various boolean html attributes 1`] = `
|
||||
"function anonymous(context, extra
|
||||
) {
|
||||
@@ -1203,6 +1249,55 @@ exports[`static templates empty div 1`] = `
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`static templates inline template string in t-esc 1`] = `
|
||||
"function anonymous(context, extra
|
||||
) {
|
||||
// Template name: \\"test\\"
|
||||
let scope = Object.create(context);
|
||||
let result;
|
||||
let h = this.h;
|
||||
let _1 = \`text\`;
|
||||
if (_1 != null) {
|
||||
let vn2 = {text: _1};
|
||||
result = vn2
|
||||
}
|
||||
return result;
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`static templates inline template string with content in t-esc 1`] = `
|
||||
"function anonymous(context, extra
|
||||
) {
|
||||
// Template name: \\"test\\"
|
||||
let scope = Object.create(context);
|
||||
let result;
|
||||
let h = this.h;
|
||||
scope.v = 1;
|
||||
let _1 = \`text\${scope.v}\`;
|
||||
if (_1 != null) {
|
||||
let vn2 = {text: _1};
|
||||
result = vn2
|
||||
}
|
||||
return result;
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`static templates inline template string with variable in context 1`] = `
|
||||
"function anonymous(context, extra
|
||||
) {
|
||||
// Template name: \\"test\\"
|
||||
let scope = Object.create(context);
|
||||
let result;
|
||||
let h = this.h;
|
||||
let _1 = \`text \${scope['v']}\`;
|
||||
if (_1 != null) {
|
||||
let vn2 = {text: _1};
|
||||
result = vn2
|
||||
}
|
||||
return result;
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`static templates properly handle comments 1`] = `
|
||||
"function anonymous(context, extra
|
||||
) {
|
||||
@@ -1514,6 +1609,7 @@ exports[`t-call (template calling recursive template, part 1 2`] = `
|
||||
) {
|
||||
// Template name: \\"recursive\\"
|
||||
let utils = this.constructor.utils;
|
||||
let parent = extra.parent;
|
||||
let scope = Object.create(context);
|
||||
let h = this.h;
|
||||
let c3 = extra.parentNode;
|
||||
@@ -1530,7 +1626,7 @@ exports[`t-call (template calling recursive template, part 1 2`] = `
|
||||
scope = Object.create(scope);
|
||||
scope.__access_mode__ = 'ro';
|
||||
let k7 = \`__7__\${key0}__\`;
|
||||
this.constructor.subTemplates['1'].call(this, scope, Object.assign({}, extra, {parentNode: c4, parent: utils.getComponent(context), key: k7}));
|
||||
this.constructor.subTemplates['1'].call(this, scope, Object.assign({}, extra, {parentNode: c4, parent: parent, key: k7}));
|
||||
scope = _origScope6;
|
||||
}
|
||||
}"
|
||||
@@ -1566,6 +1662,7 @@ exports[`t-call (template calling recursive template, part 2 2`] = `
|
||||
) {
|
||||
// Template name: \\"nodeTemplate\\"
|
||||
let utils = this.constructor.utils;
|
||||
let parent = extra.parent;
|
||||
let scope = Object.create(context);
|
||||
let h = this.h;
|
||||
let c2 = extra.parentNode;
|
||||
@@ -1582,7 +1679,8 @@ exports[`t-call (template calling recursive template, part 2 2`] = `
|
||||
}
|
||||
let _6 = scope['node'].children||[];
|
||||
if (!_6) { throw new Error('QWeb error: Invalid loop expression')}
|
||||
let _7 = _8 = _6;
|
||||
let _7 = _6;
|
||||
let _8 = _6;
|
||||
if (!(_6 instanceof Array)) {
|
||||
_7 = Object.keys(_6);
|
||||
_8 = Object.values(_6);
|
||||
@@ -1607,7 +1705,7 @@ exports[`t-call (template calling recursive template, part 2 2`] = `
|
||||
scope[utils.zero] = c__0;
|
||||
}
|
||||
let k11 = \`__11__\${key0}__\${key1}__\`;
|
||||
this.constructor.subTemplates['1'].call(this, scope, Object.assign({}, extra, {parentNode: c3, parent: utils.getComponent(context), key: k11}));
|
||||
this.constructor.subTemplates['1'].call(this, scope, Object.assign({}, extra, {parentNode: c3, parent: parent, key: k11}));
|
||||
}
|
||||
scope = _origScope10;
|
||||
}
|
||||
@@ -1645,6 +1743,7 @@ exports[`t-call (template calling recursive template, part 3 2`] = `
|
||||
) {
|
||||
// Template name: \\"nodeTemplate\\"
|
||||
let utils = this.constructor.utils;
|
||||
let parent = extra.parent;
|
||||
let scope = Object.create(context);
|
||||
let h = this.h;
|
||||
let c2 = extra.parentNode;
|
||||
@@ -1661,7 +1760,8 @@ exports[`t-call (template calling recursive template, part 3 2`] = `
|
||||
}
|
||||
let _6 = scope['node'].children||[];
|
||||
if (!_6) { throw new Error('QWeb error: Invalid loop expression')}
|
||||
let _7 = _8 = _6;
|
||||
let _7 = _6;
|
||||
let _8 = _6;
|
||||
if (!(_6 instanceof Array)) {
|
||||
_7 = Object.keys(_6);
|
||||
_8 = Object.values(_6);
|
||||
@@ -1686,7 +1786,7 @@ exports[`t-call (template calling recursive template, part 3 2`] = `
|
||||
scope[utils.zero] = c__0;
|
||||
}
|
||||
let k11 = \`__11__\${key0}__\${key1}__\`;
|
||||
this.constructor.subTemplates['1'].call(this, scope, Object.assign({}, extra, {parentNode: c3, parent: utils.getComponent(context), key: k11}));
|
||||
this.constructor.subTemplates['1'].call(this, scope, Object.assign({}, extra, {parentNode: c3, parent: parent, key: k11}));
|
||||
}
|
||||
scope = _origScope10;
|
||||
}
|
||||
@@ -1725,6 +1825,7 @@ exports[`t-call (template calling recursive template, part 4: with t-set recursi
|
||||
) {
|
||||
// Template name: \\"nodeTemplate\\"
|
||||
let utils = this.constructor.utils;
|
||||
let parent = extra.parent;
|
||||
let scope = Object.create(context);
|
||||
let h = this.h;
|
||||
let c2 = extra.parentNode;
|
||||
@@ -1746,7 +1847,8 @@ exports[`t-call (template calling recursive template, part 4: with t-set recursi
|
||||
}
|
||||
let _6 = scope['node'].children||[];
|
||||
if (!_6) { throw new Error('QWeb error: Invalid loop expression')}
|
||||
let _7 = _8 = _6;
|
||||
let _7 = _6;
|
||||
let _8 = _6;
|
||||
if (!(_6 instanceof Array)) {
|
||||
_7 = Object.keys(_6);
|
||||
_8 = Object.values(_6);
|
||||
@@ -1771,7 +1873,7 @@ exports[`t-call (template calling recursive template, part 4: with t-set recursi
|
||||
scope[utils.zero] = c__0;
|
||||
}
|
||||
let k11 = \`__11__\${key0}__\${key1}__\`;
|
||||
this.constructor.subTemplates['1'].call(this, scope, Object.assign({}, extra, {parentNode: c3, parent: utils.getComponent(context), key: k11}));
|
||||
this.constructor.subTemplates['1'].call(this, scope, Object.assign({}, extra, {parentNode: c3, parent: parent, key: k11}));
|
||||
}
|
||||
scope = _origScope10;
|
||||
}
|
||||
@@ -1912,7 +2014,8 @@ exports[`t-call (template calling t-call with t-set inside and outside 1`] = `
|
||||
let vn1 = h('div', p1, c1);
|
||||
let _2 = scope['list'];
|
||||
if (!_2) { throw new Error('QWeb error: Invalid loop expression')}
|
||||
let _3 = _4 = _2;
|
||||
let _3 = _2;
|
||||
let _4 = _2;
|
||||
if (!(_2 instanceof Array)) {
|
||||
_3 = Object.keys(_2);
|
||||
_4 = Object.values(_2);
|
||||
@@ -2689,7 +2792,8 @@ exports[`t-key t-key directive in a list 1`] = `
|
||||
let vn1 = h('ul', p1, c1);
|
||||
let _2 = scope['beers'];
|
||||
if (!_2) { throw new Error('QWeb error: Invalid loop expression')}
|
||||
let _3 = _4 = _2;
|
||||
let _3 = _2;
|
||||
let _4 = _2;
|
||||
if (!(_2 instanceof Array)) {
|
||||
_3 = Object.keys(_2);
|
||||
_4 = Object.values(_2);
|
||||
@@ -2793,7 +2897,8 @@ exports[`t-on can bind handlers with loop variable as argument 1`] = `
|
||||
let vn1 = h('ul', p1, c1);
|
||||
let _2 = ['someval'];
|
||||
if (!_2) { throw new Error('QWeb error: Invalid loop expression')}
|
||||
let _3 = _4 = _2;
|
||||
let _3 = _2;
|
||||
let _4 = _2;
|
||||
if (!(_2 instanceof Array)) {
|
||||
_3 = Object.keys(_2);
|
||||
_4 = Object.values(_2);
|
||||
@@ -2946,7 +3051,7 @@ exports[`t-on t-on with empty handler (only modifiers) 1`] = `
|
||||
let c2 = [], p2 = {key:2,on:{}};
|
||||
let vn2 = h('button', p2, c2);
|
||||
c1.push(vn2);
|
||||
p2.on['click'] = function (e) {if (context.__owl__.status === 5){return}e.preventDefault();};
|
||||
p2.on['click'] = function (e) {if (context.__owl__.status === 5){return}e.preventDefault();const res = (() => { return })(); if (typeof res === 'function') { res(e) }};
|
||||
c2.push({text: \`Button\`});
|
||||
return vn1;
|
||||
}"
|
||||
@@ -2961,7 +3066,7 @@ exports[`t-on t-on with inline statement (function call) 1`] = `
|
||||
let c1 = [], p1 = {key:1,on:{}};
|
||||
let vn1 = h('button', p1, c1);
|
||||
const state_2 = scope['state'];
|
||||
p1.on['click'] = function (e) {if (context.__owl__.status === 5){return}state_2.incrementCounter(2)};
|
||||
p1.on['click'] = function (e) {if (context.__owl__.status === 5){return}const res = (() => { return state_2.incrementCounter(2) })(); if (typeof res === 'function') { res(e) }};
|
||||
c1.push({text: \`Click\`});
|
||||
return vn1;
|
||||
}"
|
||||
@@ -2976,7 +3081,7 @@ exports[`t-on t-on with inline statement 1`] = `
|
||||
let c1 = [], p1 = {key:1,on:{}};
|
||||
let vn1 = h('button', p1, c1);
|
||||
const state_2 = scope['state'];
|
||||
p1.on['click'] = function (e) {if (context.__owl__.status === 5){return}state_2.counter++};
|
||||
p1.on['click'] = function (e) {if (context.__owl__.status === 5){return}const res = (() => { return state_2.counter++ })(); if (typeof res === 'function') { res(e) }};
|
||||
c1.push({text: \`Click\`});
|
||||
return vn1;
|
||||
}"
|
||||
@@ -2991,7 +3096,7 @@ exports[`t-on t-on with inline statement, part 2 1`] = `
|
||||
let c1 = [], p1 = {key:1,on:{}};
|
||||
let vn1 = h('button', p1, c1);
|
||||
const state_2 = scope['state'];
|
||||
p1.on['click'] = function (e) {if (context.__owl__.status === 5){return}state_2.flag=!state_2.flag};
|
||||
p1.on['click'] = function (e) {if (context.__owl__.status === 5){return}const res = (() => { return state_2.flag=!state_2.flag })(); if (typeof res === 'function') { res(e) }};
|
||||
c1.push({text: \`Toggle\`});
|
||||
return vn1;
|
||||
}"
|
||||
@@ -3007,7 +3112,7 @@ exports[`t-on t-on with inline statement, part 3 1`] = `
|
||||
let vn1 = h('button', p1, c1);
|
||||
const state_2 = scope['state'];
|
||||
const someFunction_2 = scope['someFunction'];
|
||||
p1.on['click'] = function (e) {if (context.__owl__.status === 5){return}state_2.n=someFunction_2(3)};
|
||||
p1.on['click'] = function (e) {if (context.__owl__.status === 5){return}const res = (() => { return state_2.n=someFunction_2(3) })(); if (typeof res === 'function') { res(e) }};
|
||||
c1.push({text: \`Toggle\`});
|
||||
return vn1;
|
||||
}"
|
||||
@@ -3075,7 +3180,8 @@ exports[`t-on t-on with prevent modifier in t-foreach 1`] = `
|
||||
let vn1 = h('div', p1, c1);
|
||||
let _2 = scope['projects'];
|
||||
if (!_2) { throw new Error('QWeb error: Invalid loop expression')}
|
||||
let _3 = _4 = _2;
|
||||
let _3 = _2;
|
||||
let _4 = _2;
|
||||
if (!(_2 instanceof Array)) {
|
||||
_3 = Object.keys(_2);
|
||||
_4 = Object.values(_2);
|
||||
@@ -3346,7 +3452,8 @@ exports[`t-ref refs in a loop 1`] = `
|
||||
let vn1 = h('div', p1, c1);
|
||||
let _2 = scope['items'];
|
||||
if (!_2) { throw new Error('QWeb error: Invalid loop expression')}
|
||||
let _3 = _4 = _2;
|
||||
let _3 = _2;
|
||||
let _4 = _2;
|
||||
if (!(_2 instanceof Array)) {
|
||||
_3 = Object.keys(_2);
|
||||
_4 = Object.values(_2);
|
||||
@@ -3590,7 +3697,8 @@ exports[`t-set t-set should reuse variable if possible 1`] = `
|
||||
scope.v = 1;
|
||||
let _2 = scope['list'];
|
||||
if (!_2) { throw new Error('QWeb error: Invalid loop expression')}
|
||||
let _3 = _4 = _2;
|
||||
let _3 = _2;
|
||||
let _4 = _2;
|
||||
if (!(_2 instanceof Array)) {
|
||||
_3 = Object.keys(_2);
|
||||
_4 = Object.values(_2);
|
||||
@@ -3857,6 +3965,20 @@ exports[`t-set value priority 1`] = `
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`translation support can add additional attributes to the list of translatable attributes 1`] = `
|
||||
"function anonymous(context, extra
|
||||
) {
|
||||
// Template name: \\"test\\"
|
||||
let h = this.h;
|
||||
let _1 = 'word';
|
||||
let _2 = 'mot';
|
||||
let c3 = [], p3 = {key:3,attrs:{tomato: _1,potato: _2}};
|
||||
let vn3 = h('div', p3, c3);
|
||||
c3.push({text: \`text\`});
|
||||
return vn3;
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`translation support can translate node content 1`] = `
|
||||
"function anonymous(context, extra
|
||||
) {
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`memory t-foreach does not leak stuff in global scope 1`] = `
|
||||
"function anonymous(context, extra
|
||||
) {
|
||||
// Template name: \\"test\\"
|
||||
let scope = Object.create(context);
|
||||
let h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
let vn1 = h('p', p1, c1);
|
||||
let _2 = [3,2,1];
|
||||
if (!_2) { throw new Error('QWeb error: Invalid loop expression')}
|
||||
let _3 = _2;
|
||||
let _4 = _2;
|
||||
if (!(_2 instanceof Array)) {
|
||||
_3 = Object.keys(_2);
|
||||
_4 = Object.values(_2);
|
||||
}
|
||||
let _length3 = _3.length;
|
||||
let _origScope5 = scope;
|
||||
scope = Object.create(scope);
|
||||
for (let i1 = 0; i1 < _length3; i1++) {
|
||||
scope.item_first = i1 === 0
|
||||
scope.item_last = i1 === _length3 - 1
|
||||
scope.item_index = i1
|
||||
scope.item = _3[i1]
|
||||
scope.item_value = _4[i1]
|
||||
let key1 = i1;
|
||||
let _6 = scope['item'];
|
||||
if (_6 != null) {
|
||||
c1.push({text: _6});
|
||||
}
|
||||
}
|
||||
scope = _origScope5;
|
||||
return vn1;
|
||||
}"
|
||||
`;
|
||||
@@ -0,0 +1,57 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`qweb t-att t-att-class with multiple classes 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.toClassObj({'a b c':scope['value']});
|
||||
let c2 = [], p2 = {key:2,class:_1};
|
||||
let vn2 = h('div', p2, c2);
|
||||
return vn2;
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`qweb t-att t-att-class with multiple classes 2`] = `
|
||||
"function anonymous(context, extra
|
||||
) {
|
||||
// Template name: \\"test\\"
|
||||
let utils = this.constructor.utils;
|
||||
let scope = Object.create(context);
|
||||
let h = this.h;
|
||||
let _3 = utils.toClassObj({['a b c']:scope['value']});
|
||||
let c4 = [], p4 = {key:4,class:_3};
|
||||
let vn4 = h('div', p4, c4);
|
||||
return vn4;
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`qweb t-att t-att-class with multiple classes, some of which are duplicate 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.toClassObj({'a b c':scope['value'],'a b d':!scope['value']});
|
||||
let c2 = [], p2 = {key:2,class:_1};
|
||||
let vn2 = h('div', p2, c2);
|
||||
return vn2;
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`qweb t-att t-att-class with multiple classes, some of which are duplicate 2`] = `
|
||||
"function anonymous(context, extra
|
||||
) {
|
||||
// Template name: \\"test\\"
|
||||
let utils = this.constructor.utils;
|
||||
let scope = Object.create(context);
|
||||
let h = this.h;
|
||||
let _3 = utils.toClassObj({'a b c':scope['value'],'a b d':!scope['value']});
|
||||
let c4 = [], p4 = {key:4,class:_3};
|
||||
let vn4 = h('div', p4, c4);
|
||||
return vn4;
|
||||
}"
|
||||
`;
|
||||
@@ -0,0 +1,71 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`qweb t-tag simple usecases 1`] = `
|
||||
"function anonymous(context, extra
|
||||
) {
|
||||
// Template name: \\"test\\"
|
||||
let scope = Object.create(context);
|
||||
let result;
|
||||
let h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
let tag2 = 'div';
|
||||
let vn1 = h(tag2, p1, c1);
|
||||
result = vn1;
|
||||
return result;
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`qweb t-tag simple usecases 2`] = `
|
||||
"function anonymous(context, extra
|
||||
) {
|
||||
// Template name: \\"test\\"
|
||||
let scope = Object.create(context);
|
||||
let result;
|
||||
let h = this.h;
|
||||
let c3 = [], p3 = {key:3};
|
||||
let tag4 = scope['tag'];
|
||||
let vn3 = h(tag4, p3, c3);
|
||||
result = vn3;
|
||||
c3.push({text: \`text\`});
|
||||
return result;
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`qweb t-tag with multiple attributes 1`] = `
|
||||
"function anonymous(context, extra
|
||||
) {
|
||||
// Template name: \\"test\\"
|
||||
let scope = Object.create(context);
|
||||
let result;
|
||||
let h = this.h;
|
||||
let _2 = {'blueberry':true};
|
||||
let _3 = 'raspberry';
|
||||
let c4 = [], p4 = {key:4,attrs:{taste: _3},class:_2};
|
||||
let tag5 = scope['tag'];
|
||||
let vn4 = h(tag5, p4, c4);
|
||||
result = vn4;
|
||||
c4.push({text: \`gooseberry\`});
|
||||
return result;
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`qweb t-tag with multiple child nodes 1`] = `
|
||||
"function anonymous(context, extra
|
||||
) {
|
||||
// Template name: \\"test\\"
|
||||
let scope = Object.create(context);
|
||||
let result;
|
||||
let h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
let tag2 = scope['tag'];
|
||||
let vn1 = h(tag2, p1, c1);
|
||||
result = vn1;
|
||||
c1.push({text: \` pear \`});
|
||||
let c3 = [], p3 = {key:3};
|
||||
let vn3 = h('span', p3, c3);
|
||||
c1.push(vn3);
|
||||
c3.push({text: \`apple\`});
|
||||
c1.push({text: \` strawberry \`});
|
||||
return result;
|
||||
}"
|
||||
`;
|
||||
+73
-4
@@ -1,4 +1,5 @@
|
||||
import { QWeb } from "../../src/qweb/index";
|
||||
import { config } from "../../src/index";
|
||||
import { nextTick, normalize, renderToDOM, renderToString, trim } from "../helpers";
|
||||
import { patch } from "../../src/vdom";
|
||||
|
||||
@@ -13,6 +14,7 @@ let qweb: QWeb;
|
||||
|
||||
beforeEach(() => {
|
||||
QWeb.TEMPLATES = {};
|
||||
QWeb.nextId = 1;
|
||||
qweb = new QWeb();
|
||||
});
|
||||
|
||||
@@ -31,6 +33,21 @@ describe("static templates", () => {
|
||||
expect(renderToString(qweb, "test", { text: "hello vdom" })).toBe("hello vdom");
|
||||
});
|
||||
|
||||
test("inline template string in t-esc", () => {
|
||||
qweb.addTemplate("test", '<t><t t-esc="`text`"/></t>');
|
||||
expect(renderToString(qweb, "test")).toBe("text");
|
||||
});
|
||||
|
||||
test("inline template string with content in t-esc", () => {
|
||||
qweb.addTemplate("test", '<t><t t-set="v" t-value="1"/><t t-esc="`text${v}`"/></t>');
|
||||
expect(renderToString(qweb, "test")).toBe("text1");
|
||||
});
|
||||
|
||||
test("inline template string with variable in context", () => {
|
||||
qweb.addTemplate("test", '<t><t t-esc="`text ${v}`"/></t>');
|
||||
expect(renderToString(qweb, "test", { v: "from context" })).toBe("text from context");
|
||||
});
|
||||
|
||||
test("simple string, with some dynamic value", () => {
|
||||
qweb.addTemplate("test", '<t>hello <t t-esc="text"/></t>');
|
||||
expect(renderToString(qweb, "test", { text: "vdom" })).toBe("hello vdom");
|
||||
@@ -1847,8 +1864,7 @@ describe("t-ref", () => {
|
||||
|
||||
describe("loading templates", () => {
|
||||
test("can initialize qweb with a string", () => {
|
||||
const templates = `
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
const templates = `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<templates id="template" xml:space="preserve">
|
||||
<div t-name="hey">jupiler</div>
|
||||
</templates>`;
|
||||
@@ -1857,8 +1873,7 @@ describe("loading templates", () => {
|
||||
});
|
||||
|
||||
test("can load a few templates from a xml string", () => {
|
||||
const data = `
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
const data = `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<templates id="template" xml:space="preserve">
|
||||
|
||||
<t t-name="items"><li>ok</li><li>foo</li></t>
|
||||
@@ -1931,6 +1946,49 @@ describe("special cases for some specific html attributes/properties", () => {
|
||||
let elm = vnode2.elm as HTMLInputElement;
|
||||
expect(elm.indeterminate).toBe(true);
|
||||
});
|
||||
|
||||
test("textarea with t-att-value", () => {
|
||||
// render input with initial value
|
||||
qweb.addTemplate("test", `<textarea t-att-value="v"/>`);
|
||||
const vnode1 = qweb.render("test", { v: "zucchini" });
|
||||
const vnode2 = patch(document.createElement("textarea"), vnode1);
|
||||
let elm = vnode2.elm as HTMLInputElement;
|
||||
expect(elm.value).toBe("zucchini");
|
||||
|
||||
// change value manually in textarea, to simulate user textarea
|
||||
elm.value = "tomato";
|
||||
expect(elm.value).toBe("tomato");
|
||||
|
||||
// rerender with a different value, and patch actual dom, to check that
|
||||
// textarea value was properly reset by owl
|
||||
const vnode3 = qweb.render("test", { v: "potato" });
|
||||
patch(vnode2, vnode3);
|
||||
expect(elm.value).toBe("potato");
|
||||
});
|
||||
|
||||
test("select with t-att-value", () => {
|
||||
const template = `
|
||||
<select t-att-value="value">
|
||||
<option value="potato">Potato</option>
|
||||
<option value="tomato">Tomato</option>
|
||||
<option value="onion">Onion</option>
|
||||
</select>`;
|
||||
qweb.addTemplate("test", template);
|
||||
const vnode1 = qweb.render("test", { value: "tomato" });
|
||||
const vnode2 = patch(document.createElement("select"), vnode1);
|
||||
let elm = vnode2.elm as HTMLSelectElement;
|
||||
expect(elm.value).toBe("tomato");
|
||||
|
||||
elm.value = "potato";
|
||||
expect(elm.value).toBe("potato");
|
||||
|
||||
// rerender with a different value, and patch actual dom, to check that
|
||||
// select value was properly reset by owl
|
||||
const vnode3 = qweb.render("test", { value: "onion" });
|
||||
patch(vnode2, vnode3);
|
||||
expect(elm.value).toBe("onion");
|
||||
expect(qweb.templates.test.fn.toString()).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
describe("whitespace handling", () => {
|
||||
@@ -2155,6 +2213,17 @@ describe("translation support", () => {
|
||||
);
|
||||
});
|
||||
|
||||
test("can add additional attributes to the list of translatable attributes", () => {
|
||||
const translations = {
|
||||
word: "mot",
|
||||
};
|
||||
const translateFn = (expr) => translations[expr] || expr;
|
||||
const qweb = new QWeb({ translateFn });
|
||||
config.translatableAttributes.push("potato");
|
||||
qweb.addTemplate("test", `<div tomato="word" potato="word">text</div>`);
|
||||
expect(renderToString(qweb, "test")).toBe('<div tomato="word" potato="mot">text</div>');
|
||||
});
|
||||
|
||||
test("translation is done on the trimmed text, with extra spaces readded after", () => {
|
||||
const translations = {
|
||||
word: "mot",
|
||||
|
||||
@@ -200,4 +200,27 @@ describe("expression evaluation", () => {
|
||||
test("works with builtin properties", () => {
|
||||
expect(compileExpr("state.constructor.name", {})).toBe("scope['state'].constructor.name");
|
||||
});
|
||||
|
||||
test("works with shortcut object key description", () => {
|
||||
expect(compileExpr("{a}", {})).toBe("{a:scope['a']}");
|
||||
expect(compileExpr("{a,b}", {})).toBe("{a:scope['a'],b:scope['b']}");
|
||||
expect(compileExpr("{a,b:3,c}", {})).toBe("{a:scope['a'],b:3,c:scope['c']}");
|
||||
});
|
||||
|
||||
test("works with short object description and lists ", () => {
|
||||
expect(compileExpr("[a, b]", {})).toBe("[scope['a'],scope['b']]");
|
||||
expect(compileExpr("[a, b, c]", {})).toBe("[scope['a'],scope['b'],scope['c']]");
|
||||
expect(compileExpr("[a, {b, c},d]", {})).toBe(
|
||||
"[scope['a'],{b:scope['b'],c:scope['c']},scope['d']]"
|
||||
);
|
||||
expect(compileExpr("{a:[b, {c, d: e}]}", {})).toBe(
|
||||
"{a:[scope['b'],{c:scope['c'],d:scope['e']}]}"
|
||||
);
|
||||
});
|
||||
|
||||
test("template strings", () => {
|
||||
expect(compileExpr("`hey`", {})).toBe("`hey`");
|
||||
expect(compileExpr("`hey ${you}`", {})).toBe("`hey ${scope['you']}`");
|
||||
expect(compileExpr("`hey ${1 + 2}`", {})).toBe("`hey ${1+2}`");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
import { QWeb } from "../../src/qweb/index";
|
||||
import { renderToString } from "../helpers";
|
||||
|
||||
describe("memory", () => {
|
||||
test("t-foreach does not leak stuff in global scope", () => {
|
||||
let qweb = new QWeb();
|
||||
const initialNumberOfGlobals = Object.keys(window).length;
|
||||
qweb.addTemplate("test", `<p><t t-foreach="[3, 2, 1]" t-as="item"><t t-esc="item"/></t></p>`);
|
||||
const result = renderToString(qweb, "test");
|
||||
const expected = `<p>321</p>`;
|
||||
expect(result).toBe(expected);
|
||||
expect(Object.keys(window).length).toBe(initialNumberOfGlobals);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,36 @@
|
||||
import { QWeb } from "../../src/qweb/index";
|
||||
import { renderToString } from "../helpers";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Setup and helpers
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
function render(template, context = {}) {
|
||||
const qweb = new QWeb();
|
||||
qweb.addTemplate("test", template);
|
||||
return renderToString(qweb, "test", context);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Tests
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
describe("qweb t-att", () => {
|
||||
test("t-att-class with multiple classes", () => {
|
||||
expect(render(`<div t-att-class="{'a b c': value}" />`, { value: true })).toBe(
|
||||
'<div class="a b c"></div>'
|
||||
);
|
||||
expect(render(`<div t-att-class="{['a b c']: value}" />`, { value: true })).toBe(
|
||||
'<div class="a b c"></div>'
|
||||
);
|
||||
});
|
||||
|
||||
test("t-att-class with multiple classes, some of which are duplicate", () => {
|
||||
expect(render(`<div t-att-class="{'a b c': value, 'a b d': !value}" />`, { value: true })).toBe(
|
||||
'<div class="a b c"></div>'
|
||||
);
|
||||
expect(
|
||||
render(`<div t-att-class="{'a b c': value, 'a b d': !value}" />`, { value: false })
|
||||
).toBe('<div class="a b d"></div>');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,42 @@
|
||||
import { QWeb } from "../../src/qweb/index";
|
||||
import { renderToString } from "../helpers";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Setup and helpers
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
function render(template, context = {}) {
|
||||
const qweb = new QWeb();
|
||||
qweb.addTemplate("test", template);
|
||||
return renderToString(qweb, "test", context);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Tests
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
describe("qweb t-tag", () => {
|
||||
test("simple usecases", () => {
|
||||
expect(render(`<t t-tag="'div'"></t>`)).toBe("<div></div>");
|
||||
expect(render(`<t t-tag="tag">text</t>`, { tag: "span" })).toBe("<span>text</span>");
|
||||
});
|
||||
|
||||
test("with multiple child nodes", () => {
|
||||
const template = `
|
||||
<t t-tag="tag">
|
||||
pear
|
||||
<span>apple</span>
|
||||
strawberry
|
||||
</t>`;
|
||||
expect(render(template, { tag: "div" })).toBe(
|
||||
"<div> pear <span>apple</span> strawberry </div>"
|
||||
);
|
||||
});
|
||||
|
||||
test("with multiple attributes", () => {
|
||||
const template = `
|
||||
<t t-tag="tag" class="blueberry" taste="raspberry">gooseberry</t>`;
|
||||
const expected = `<div taste=\"raspberry\" class=\"blueberry\">gooseberry</div>`;
|
||||
expect(render(template, { tag: "div" })).toBe(expected);
|
||||
});
|
||||
});
|
||||
@@ -7,7 +7,7 @@ exports[`Link component can render simple cases 1`] = `
|
||||
let utils = this.constructor.utils;
|
||||
let scope = Object.create(context);
|
||||
let h = this.h;
|
||||
let _5 = utils.toObj({'router-link-active':scope['isActive']});
|
||||
let _5 = utils.toClassObj({'router-link-active':scope['isActive']});
|
||||
let _6 = scope['href'];
|
||||
let c7 = [], p7 = {key:7,attrs:{href: _6},class:_5,on:{}};
|
||||
let vn7 = h('a', p7, c7);
|
||||
|
||||
@@ -18,7 +18,7 @@ exports[`RouteComponent can render simple cases 1`] = `
|
||||
let w4 = k5 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[k5]] : false;
|
||||
let vn6 = {};
|
||||
result = vn6;
|
||||
let props4 = Object.assign({}, scope['env'].router.currentParams);
|
||||
let props4 = Object.assign({}, scope['env'].router.currentParams, {});
|
||||
if (w4 && w4.__owl__.currentFiber && !w4.__owl__.vnode) {
|
||||
w4.destroy();
|
||||
w4 = false;
|
||||
|
||||
@@ -80,7 +80,7 @@ describe("Link component", () => {
|
||||
await app.mount(fixture);
|
||||
|
||||
expect(window.location.pathname).toBe("/users");
|
||||
var evt = new MouseEvent("click", {
|
||||
var evt = new MouseEvent("contextmenu", {
|
||||
button: 1,
|
||||
});
|
||||
|
||||
|
||||
@@ -60,6 +60,13 @@ describe("router miscellaneous", () => {
|
||||
await router.navigate({ to: "users", params: { id: 3 } });
|
||||
expect(window.location.href).toBe("http://localhost/test.html#/users/3");
|
||||
});
|
||||
|
||||
test("navigate using path and query string should preserve query string", async () => {
|
||||
router = new TestRouter(env, [{ name: "users", path: "/users/{{id}}" }]);
|
||||
await router.navigate({ path: "/users/3?test=1" });
|
||||
expect(window.location.pathname).toBe("/users/3");
|
||||
expect(window.location.search).toBe("?test=1");
|
||||
});
|
||||
});
|
||||
|
||||
describe("routeToPath", () => {
|
||||
@@ -117,6 +124,11 @@ describe("getRouteParams", () => {
|
||||
expect(getRouteParams({ path: "*" }, "somepath")).toEqual({});
|
||||
});
|
||||
|
||||
test("properly match routes with query params", () => {
|
||||
expect(getRouteParams({ path: "/home" }, "/home?test=1")).toEqual({});
|
||||
expect(getRouteParams({ path: "/home" }, "/home?test1=1&test2=2")).toEqual({});
|
||||
});
|
||||
|
||||
test("properly match simple routes, mode hash", () => {
|
||||
// simple route
|
||||
expect(getRouteParams({ path: "/home" }, "#/home")).toEqual({});
|
||||
|
||||
@@ -72,7 +72,7 @@ if __name__ == "__main__":
|
||||
* Make an iframe, with all the js, css and xml properly injected.
|
||||
*/
|
||||
function makeCodeIframe(js, css, xml) {
|
||||
const sanitizedXML = xml.replace(/<!--[\s\S]*?-->/g, "");
|
||||
const sanitizedXML = xml.replace(/<!--[\s\S]*?-->/g, "").replace(/`/g, '\\\`');
|
||||
|
||||
|
||||
// create iframe
|
||||
|
||||
+2
-1
@@ -79,7 +79,8 @@ async function startRelease() {
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
log(`Step 4/${STEPS}: creating git commit...`);
|
||||
const gitResult = await execCommand(`git commit -am "[REL] v${next}\n\n${content}"`);
|
||||
const escapedContent = content.replace(/\"/g, '\\\"').replace(/\`/g, '\\\`');
|
||||
const gitResult = await execCommand(`git commit -am "[REL] v${next}\n\n${escapedContent}"`);
|
||||
if (gitResult !== 0) {
|
||||
logError("Git commit failed. Aborting.");
|
||||
return;
|
||||
|
||||
+2
-2
@@ -13,8 +13,8 @@
|
||||
// "compileOnSave": false, // Signals to the IDE to generate all files for a given tsconfig.json upon saving.
|
||||
"compilerOptions": {
|
||||
// Main options
|
||||
"target": "esnext", // Specify ECMAScript target version: 'es3' (default), 'es5', 'es2015', 'es2016', 'es2017','es2018' or 'esnext'.
|
||||
"module": "esnext", // Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'.
|
||||
"target": "es2017", // Specify ECMAScript target version: 'es3' (default), 'es5', 'es2015', 'es2016', 'es2017','es2018' or 'esnext'.
|
||||
"module": "es6", // Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'.
|
||||
// "lib": ["esnext", "dom"], // Specify library files to be included in the compilation.
|
||||
// "allowJs": false, // Allow javascript files to be compiled.
|
||||
// "checkJs": false, // Report errors in .js files.
|
||||
|
||||
Reference in New Issue
Block a user