mirror of
https://github.com/odoo/owl.git
synced 2025-10-06 19:59:41 +07:00
Compare commits
23 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3d0f046d9a | |||
| 4c75bae0e8 | |||
| 54f5819ef9 | |||
| 0c9c5b877b | |||
| b7b0e38fca | |||
| 74bd119bf5 | |||
| 4a0d04e4ae | |||
| 49931104a1 | |||
| 21d1306ec2 | |||
| 21737d33fa | |||
| 23012f3e7c | |||
| 55889febd5 | |||
| 0af46bc123 | |||
| b63cd4c9d9 | |||
| 953778dc50 | |||
| 4f61d9f1e0 | |||
| bc2c7edff4 | |||
| c8d9c0b50e | |||
| 15b25fd838 | |||
| 1db0f5ac9b | |||
| 6465665550 | |||
| 9216c5c24b | |||
| d9bf4284c6 |
@@ -102,10 +102,16 @@ Submit a PR!
|
||||
|
||||
## Installing/Building
|
||||
|
||||
Owl can be installed with the following command:
|
||||
|
||||
```
|
||||
npm install @odoo/owl
|
||||
```
|
||||
|
||||
If you want to use a simple `<script>` tag, the last release can be downloaded here:
|
||||
|
||||
- [owl-1.0.0-beta2.js](https://github.com/odoo/owl/releases/download/v1.0.0-beta2/owl.js)
|
||||
- [owl-1.0.0-beta2.min.js](https://github.com/odoo/owl/releases/download/v1.0.0-beta2/owl.min.js)
|
||||
- [owl-1.0.0.js](https://github.com/odoo/owl/releases/download/v1.0.0/owl.js)
|
||||
- [owl-1.0.0.min.js](https://github.com/odoo/owl/releases/download/v1.0.0/owl.min.js)
|
||||
|
||||
Some npm scripts are available:
|
||||
|
||||
|
||||
+9
-9
@@ -56,15 +56,15 @@ useState Link
|
||||
config RouteComponent
|
||||
mode Router
|
||||
core tags
|
||||
EventBus xml
|
||||
Observer utils
|
||||
hooks debounce
|
||||
onWillStart escape
|
||||
onMounted loadJS
|
||||
onWillUpdateProps loadFile
|
||||
onWillPatch shallowEqual
|
||||
onPatched whenReady
|
||||
onWillUnmount
|
||||
EventBus css
|
||||
Observer xml
|
||||
hooks utils
|
||||
onWillStart debounce
|
||||
onMounted escape
|
||||
onWillUpdateProps loadJS
|
||||
onWillPatch loadFile
|
||||
onPatched shallowEqual
|
||||
onWillUnmount whenReady
|
||||
useContext
|
||||
useState
|
||||
useRef
|
||||
|
||||
@@ -225,6 +225,10 @@ to be called in the constructor.
|
||||
}
|
||||
```
|
||||
|
||||
- **`style`** (string, optional): it should be the return value of the [`css` tag](tags.md#css-tag),
|
||||
which is used to inject stylesheet whenever the component is visible on the
|
||||
screen.
|
||||
|
||||
There is another static property defined on the `Component` class: `current`.
|
||||
This property is set to the currently being defined component (in the constructor).
|
||||
This is the way [hooks](hooks.md) are able to get a reference to the target
|
||||
@@ -421,7 +425,7 @@ likely via a change in its state/props or environment).
|
||||
|
||||
This method is not called on the initial render. It is useful to interact
|
||||
with the DOM (for example, through an external library) whenever the
|
||||
component was patched. Note that this hook will not be called if the compoent is
|
||||
component was patched. Note that this hook will not be called if the component is
|
||||
not in the DOM.
|
||||
|
||||
Updating the component state in this hook is possible, but not encouraged.
|
||||
|
||||
@@ -23,7 +23,7 @@ a rendering that is no longer relevant, restart it, reuse it in some cases.
|
||||
But even though using concurrency is quite simple (and is the default behaviour),
|
||||
asynchrony is difficult, because it introduces an additional dimension that
|
||||
vastly increase the complexity of an application. This section will explain
|
||||
how Owl manages this complexity, how concuurent rendering works in a general way.
|
||||
how Owl manages this complexity, how concurrent rendering works in a general way.
|
||||
|
||||
## Rendering Components
|
||||
|
||||
@@ -120,7 +120,7 @@ Here is what Owl will do:
|
||||
1. hook `willUpdateProps` is called on `D` (async)
|
||||
2. template `D` is rerendered
|
||||
- component `F` is created:
|
||||
1. hook `willStart` is called on `E` (async)
|
||||
1. hook `willStart` is called on `F` (async)
|
||||
2. template `F` is rendered
|
||||
|
||||
3. `willPatch` hooks are called recursively on components `C`, `D` (not on `F`,
|
||||
|
||||
@@ -40,8 +40,8 @@ all templates.
|
||||
Whenever a root component `App` is mounted, Owl will setup a valid environment by
|
||||
following the next steps:
|
||||
|
||||
- take the `env` object defined on `App.env` (if no `env` was explicitely setup,
|
||||
this will be return the empty `env` object defined on `Component`)
|
||||
- take the `env` object defined on `App.env` (if no `env` was explicitly setup,
|
||||
this will return the empty `env` object defined on `Component`)
|
||||
- if `env.qweb` is not set, then Owl will create a `QWeb` instance.
|
||||
|
||||
The correct way to customize an environment is to simply set it up on the root
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
- [`useContext`](#usecontext)
|
||||
- [`useRef`](#useref)
|
||||
- [`useSubEnv`](#usesubenv)
|
||||
- [`useExternalListener`](#useexternallistener)
|
||||
- [`useStore`](#usestore)
|
||||
- [`useDispatch`](#usedispatch)
|
||||
- [`useGetters`](#usegetters)
|
||||
@@ -354,6 +355,17 @@ will be added to the parent environment. Note that it will extend, not replace
|
||||
the parent environment. And of course, the parent environment will not be
|
||||
affected.
|
||||
|
||||
### `useExternalListener`
|
||||
|
||||
The `useExternalListener` hook helps solve a very common problem: adding and removing
|
||||
a listener on some target whenever a component is mounted/unmounted. For example,
|
||||
a dropdown menu (or its parent) may need to listen to a `click` event on `window`
|
||||
to be closed:
|
||||
|
||||
```js
|
||||
useExternalListener(window, "click", this.closeMenu);
|
||||
```
|
||||
|
||||
### `useStore`
|
||||
|
||||
The `useStore` hook is the entry point for a component to connect to the store.
|
||||
|
||||
+134
-4
@@ -4,16 +4,19 @@
|
||||
|
||||
- [Overview](#overview)
|
||||
- [`xml` tag](#xml-tag)
|
||||
- [`css` tag](#css-tag)
|
||||
|
||||
## Overview
|
||||
|
||||
Tags are very small helpers to make it easy to write inline templates. There is
|
||||
only one currently available tag: `xml`, but we plan to add other tags later,
|
||||
such as a `css` tag, which will be used to write [single file components](../tooling.md#single-file-component).
|
||||
Tags are very small helpers intended to make it easy to write inline templates
|
||||
or styles. There are currently two tags: `css` and `xml`. With these functions,
|
||||
it is possible to write [single file components](../tooling.md#single-file-component).
|
||||
|
||||
## XML tag
|
||||
|
||||
Without tags, creating a standalone component would look like this:
|
||||
The `xml` tag is certainly the most useful tag. It is used to define an inline
|
||||
QWeb template for a component. Without tags, creating a standalone component
|
||||
would look like this:
|
||||
|
||||
```js
|
||||
import { Component } from 'owl'
|
||||
@@ -52,3 +55,130 @@ class MyComponent extends Component {
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
## CSS tag
|
||||
|
||||
The CSS tag is useful to define a css stylesheet in the javascript file:
|
||||
|
||||
```js
|
||||
class MyComponent extends Component {
|
||||
static template = xml`
|
||||
<div class="my-component">some template</div>
|
||||
`;
|
||||
static css`
|
||||
.my-component {
|
||||
color: red;
|
||||
}
|
||||
`;
|
||||
}
|
||||
```
|
||||
|
||||
The `css` tag registers internally the css information. Then, whenever the first
|
||||
instance of the component is created, will add a `<style>` tag to the document
|
||||
`<head>`.
|
||||
|
||||
Note that to make it more useful, like other css preprocessors, the `css` tag
|
||||
accepts a small extension of the css specification: css scopes can be nested,
|
||||
and the rules will then be expanded by the `css` helper:
|
||||
|
||||
```scss
|
||||
.my-component {
|
||||
display: block;
|
||||
.sub-component h {
|
||||
color: red;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
will be formatted as:
|
||||
|
||||
```css
|
||||
.my-component {
|
||||
display: block;
|
||||
}
|
||||
.my-component .sub-component h {
|
||||
color: red;
|
||||
}
|
||||
```
|
||||
|
||||
This extension brings another useful feature: the `&` selector which refers to
|
||||
the parent selector. For example, we want our component to be red when hovered.
|
||||
We would like to write something like:
|
||||
|
||||
```scss
|
||||
.my-component {
|
||||
display: block;
|
||||
:hover {
|
||||
color: red;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
but it will be formatted as:
|
||||
|
||||
```css
|
||||
.my-component {
|
||||
display: block;
|
||||
}
|
||||
.my-component :hover {
|
||||
color: red;
|
||||
}
|
||||
```
|
||||
|
||||
The `&` selector can be used to solve this problem:
|
||||
|
||||
```scss
|
||||
.my-component {
|
||||
display: block;
|
||||
&:hover {
|
||||
color: red;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
will be formatted as:
|
||||
|
||||
```css
|
||||
.my-component {
|
||||
display: block;
|
||||
}
|
||||
.my-component:hover {
|
||||
color: red;
|
||||
}
|
||||
```
|
||||
|
||||
Now, there is no additional processing done by the `css` tag. However, since it
|
||||
is done in javascript at runtime, we actually have more power. For example:
|
||||
|
||||
1. sharing values between javascript and css:
|
||||
|
||||
```js
|
||||
import { theme } from "./theme";
|
||||
|
||||
class MyComponent extends Component {
|
||||
static template = xml`<div class="my-component">...</div>`;
|
||||
static style = css`
|
||||
.my-component {
|
||||
color: ${theme.MAIN_COLOR};
|
||||
background-color: ${theme.SECONDARY_color};
|
||||
}
|
||||
`;
|
||||
}
|
||||
```
|
||||
|
||||
2. scoping rules to the current component:
|
||||
|
||||
```js
|
||||
import { generateUUID } from "./utils";
|
||||
|
||||
const uuid = generateUUID();
|
||||
|
||||
class MyComponent extends Component {
|
||||
static template = xml`<div data-o-${uuid}="">...</div>`;
|
||||
static style = css`
|
||||
[data-o-${uuid}] {
|
||||
color: red;
|
||||
}
|
||||
`;
|
||||
}
|
||||
```
|
||||
|
||||
+21
-10
@@ -43,32 +43,43 @@ useful to compare various performance metrics on some tasks.
|
||||
It is very useful to group code by feature instead of by type of file. It makes
|
||||
it easier to scale application to larger size.
|
||||
|
||||
To do so, Owl currently has a small helper that makes it easy to define a
|
||||
template inside a javascript (or typescript) file: the [`xml`](reference/tags.md#xml-tag)
|
||||
helper. With this, a template is automatically registered to [QWeb](reference/qweb_engine.md).
|
||||
To do so, Owl has two small helpers that make it easy to define a
|
||||
template or a stylesheet inside a javascript (or typescript) file: the
|
||||
[`xml`](reference/tags.md#xml-tag) and [`css`](reference/tags.md#css-tag)
|
||||
helper.
|
||||
|
||||
This means that the template and the javascript code can be defined in the same
|
||||
file. It is not currently possible to add css to the same file, but Owl may
|
||||
get a `css` tag helper later.
|
||||
This means that the template, the style and the javascript code can be defined in
|
||||
the same file. For example:
|
||||
|
||||
```js
|
||||
const { Component } = owl;
|
||||
const { xml } = owl.tags;
|
||||
const { xml, css } = owl.tags;
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// TEMPLATE
|
||||
// -----------------------------------------------------------------------------
|
||||
const TEMPLATE = xml/* xml */ `
|
||||
<div class="main two-columns">
|
||||
<div class="main">
|
||||
<Sidebar/>
|
||||
<Content />
|
||||
</div>`;
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// STYLE
|
||||
// -----------------------------------------------------------------------------
|
||||
const STYLE = css/* css */ `
|
||||
.main {
|
||||
display: grid;
|
||||
grid-template-columns: 200px auto;
|
||||
}
|
||||
`;
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// CODE
|
||||
// -----------------------------------------------------------------------------
|
||||
class MyComponent extends Component {
|
||||
class Main extends Component {
|
||||
static template = TEMPLATE;
|
||||
static style = STYLE;
|
||||
static components = { Sidebar, Content };
|
||||
|
||||
// rest of component...
|
||||
@@ -91,7 +102,7 @@ logging useful information is extremely valuable. There is a [javascript file](.
|
||||
Once it is executed, it will log a lot of information on each component main hooks. The following code is a minified version to make it easier to copy/paste:
|
||||
|
||||
```
|
||||
function debugOwl(t,n){let e,o="[OWL_DEBUG]";function s(t){let n=JSON.stringify(t||{});return n.length>200&&(n=n.slice(0,200)+"..."),n}if(Object.defineProperty(t.Component,"current",{get:()=>e,set(i){e=i;const r=i.constructor.name;if(n.componentBlackList&&n.componentBlackList.test(r))return;if(n.componentWhiteList&&!n.componentWhiteList.test(r))return;let l;Object.defineProperty(e,"__owl__",{get:()=>l,set(e){!function(e,i,r){let l=`${i}<id=${r}>`,c=t=>console.log(`${o} ${l} ${t}`),u=t=>(!n.methodBlackList||!n.methodBlackList.includes(t))&&!(n.methodWhiteList&&!n.methodWhiteList.includes(t));u("constructor")&&c(`constructor, props=${s(e.props)}`);u("willStart")&&t.hooks.onWillStart(()=>{c("willStart")});u("mounted")&&t.hooks.onMounted(()=>{c("mounted")});u("willUpdateProps")&&t.hooks.onWillUpdateProps(t=>{c(`willUpdateProps, nextprops=${s(t)}`)});u("willPatch")&&t.hooks.onWillPatch(()=>{c("willPatch")});u("patched")&&t.hooks.onPatched(()=>{c("patched")});u("willUnmount")&&t.hooks.onWillUnmount(()=>{c("willUnmount")});const d=e.__render.bind(e);e.__render=function(...t){c("rendering template"),d(...t)};const h=e.render.bind(e);e.render=function(...t){const n=e.__owl__;let o="render";return n.isMounted||n.currentFiber||(o+=" (warning: component is not mounted, this render has no effect)"),c(o),h(...t)};const p=e.mount.bind(e);e.mount=function(...t){return c("mount"),p(...t)}}(i,r,(l=e).id)}})}}),n.logScheduler){let n=t.Component.scheduler.start,e=t.Component.scheduler.stop;t.Component.scheduler.start=function(){this.isRunning||console.log(`${o} scheduler: start running tasks queue`),n.call(this)},t.Component.scheduler.stop=function(){this.isRunning&&console.log(`${o} scheduler: stop running tasks queue`),e.call(this)}}if(n.logStore){let n=t.Store.prototype.dispatch;t.Store.prototype.dispatch=function(t,...e){return console.log(`${o} store: action '${t}' dispatched. Payload: '${s(e)}'`),n.call(this,t,...e)}}}
|
||||
function debugOwl(t,e){let n,o="[OWL_DEBUG]";function r(t){let e;try{e=JSON.stringify(t||{})}catch(t){e="<JSON error>"}return e.length>200&&(e=e.slice(0,200)+"..."),e}if(Object.defineProperty(t.Component,"current",{get:()=>n,set(s){n=s;const i=s.constructor.name;if(e.componentBlackList&&e.componentBlackList.test(i))return;if(e.componentWhiteList&&!e.componentWhiteList.test(i))return;let l;Object.defineProperty(n,"__owl__",{get:()=>l,set(n){!function(n,s,i){let l=`${s}<id=${i}>`,c=t=>console.log(`${o} ${l} ${t}`),u=t=>(!e.methodBlackList||!e.methodBlackList.includes(t))&&!(e.methodWhiteList&&!e.methodWhiteList.includes(t));u("constructor")&&c(`constructor, props=${r(n.props)}`);u("willStart")&&t.hooks.onWillStart(()=>{c("willStart")});u("mounted")&&t.hooks.onMounted(()=>{c("mounted")});u("willUpdateProps")&&t.hooks.onWillUpdateProps(t=>{c(`willUpdateProps, nextprops=${r(t)}`)});u("willPatch")&&t.hooks.onWillPatch(()=>{c("willPatch")});u("patched")&&t.hooks.onPatched(()=>{c("patched")});u("willUnmount")&&t.hooks.onWillUnmount(()=>{c("willUnmount")});const d=n.__render.bind(n);n.__render=function(...t){c("rendering template"),d(...t)};const h=n.render.bind(n);n.render=function(...t){const e=n.__owl__;let o="render";return e.isMounted||e.currentFiber||(o+=" (warning: component is not mounted, this render has no effect)"),c(o),h(...t)};const p=n.mount.bind(n);n.mount=function(...t){return c("mount"),p(...t)}}(s,i,(l=n).id)}})}}),e.logScheduler){let e=t.Component.scheduler.start,n=t.Component.scheduler.stop;t.Component.scheduler.start=function(){this.isRunning||console.log(`${o} scheduler: start running tasks queue`),e.call(this)},t.Component.scheduler.stop=function(){this.isRunning&&console.log(`${o} scheduler: stop running tasks queue`),n.call(this)}}if(e.logStore){let e=t.Store.prototype.dispatch;t.Store.prototype.dispatch=function(t,...n){return console.log(`${o} store: action '${t}' dispatched. Payload: '${r(n)}'`),e.call(this,t,...n)}}}
|
||||
debugOwl(owl, {
|
||||
// componentBlackList: /App/, // regexp
|
||||
// componentWhiteList: /SomeComponent/, // regexp
|
||||
|
||||
+6
-3
@@ -1,8 +1,11 @@
|
||||
{
|
||||
"name": "owl-framework",
|
||||
"version": "1.0.0-beta2",
|
||||
"name": "@odoo/owl",
|
||||
"version": "1.0.0",
|
||||
"description": "Odoo Web Library (OWL)",
|
||||
"main": "src/index.ts",
|
||||
"main": "dist/owl.js",
|
||||
"types": "dist/types/index.d.ts",
|
||||
"prepublish": "npm run buildcommonjs",
|
||||
"files": ["dist/types/", "dist/owl.js"],
|
||||
"engines": {
|
||||
"node": ">=10.15.3"
|
||||
},
|
||||
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
# 🦉 OWL Roadmap 🦉
|
||||
|
||||
- Current version: 1.0.0-beta2
|
||||
- Status: mostly stable
|
||||
- Current version: 1.0.0
|
||||
- Status: stable
|
||||
|
||||
This roadmap is only an attempt at predicting Owl's future. Everything may
|
||||
change!
|
||||
|
||||
@@ -6,6 +6,7 @@ import "./directive";
|
||||
import { Fiber } from "./fiber";
|
||||
import "./props_validation";
|
||||
import { Scheduler, scheduler } from "./scheduler";
|
||||
import { activateSheet } from "./styles";
|
||||
|
||||
/**
|
||||
* Owl Component System
|
||||
@@ -199,6 +200,9 @@ export class Component<T extends Env, Props extends {}> {
|
||||
refs: null,
|
||||
scope: null
|
||||
};
|
||||
if (constr.style) {
|
||||
this.__applyStyles(constr);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -580,6 +584,20 @@ export class Component<T extends Env, Props extends {}> {
|
||||
return fiber;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply the stylesheets defined by the component. Note that we need to make
|
||||
* sure all inherited stylesheets are applied as well. We then delete the
|
||||
* `style` key from the constructor to make sure we do not apply it again.
|
||||
*/
|
||||
private __applyStyles(constr) {
|
||||
while (constr && constr.style) {
|
||||
if (constr.hasOwnProperty("style")) {
|
||||
activateSheet(constr.style, constr.name);
|
||||
delete constr.style;
|
||||
}
|
||||
constr = constr.__proto__;
|
||||
}
|
||||
}
|
||||
__getTemplate(qweb: QWeb): string {
|
||||
let p = (<any>this).constructor;
|
||||
if (!p.hasOwnProperty("_template")) {
|
||||
|
||||
@@ -211,7 +211,7 @@ QWeb.addDirective({
|
||||
} else if (!name.startsWith("t-")) {
|
||||
if (name !== "class" && name !== "style") {
|
||||
// this is a prop!
|
||||
props[name] = ctx.formatExpression(value);
|
||||
props[name] = ctx.formatExpression(value) || "undefined";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
/**
|
||||
* Owl Style System
|
||||
*
|
||||
* This files contains the Owl code related to processing (extended) css strings
|
||||
* and creating/adding <style> tags to the document head.
|
||||
*/
|
||||
|
||||
export const STYLESHEETS: { [id: string]: HTMLStyleElement } = {};
|
||||
|
||||
export function processSheet(str: string): string {
|
||||
const tokens = str.split(/(\{|\}|;)/).map(s => s.trim());
|
||||
const selectorStack: string[][] = [];
|
||||
const parts: string[] = [];
|
||||
let rules: string[] = [];
|
||||
function generateSelector(stackIndex: number, parentSelector?: string) {
|
||||
const parts: string[] = [];
|
||||
for (const selector of selectorStack[stackIndex]) {
|
||||
let part = (parentSelector && parentSelector + " " + selector) || selector;
|
||||
if (part.includes("&")) {
|
||||
part = selector.replace(/&/g, parentSelector || "");
|
||||
}
|
||||
if (stackIndex < selectorStack.length - 1) {
|
||||
part = generateSelector(stackIndex + 1, part);
|
||||
}
|
||||
parts.push(part);
|
||||
}
|
||||
return parts.join(", ");
|
||||
}
|
||||
function generateRules() {
|
||||
if (rules.length) {
|
||||
parts.push(generateSelector(0) + " {");
|
||||
parts.push(...rules);
|
||||
parts.push("}");
|
||||
rules = [];
|
||||
}
|
||||
}
|
||||
while (tokens.length) {
|
||||
let token = tokens.shift()!;
|
||||
if (token === "}") {
|
||||
generateRules();
|
||||
selectorStack.pop();
|
||||
} else {
|
||||
if (tokens[0] === "{") {
|
||||
generateRules();
|
||||
selectorStack.push(token.split(/\s*,\s*/));
|
||||
tokens.shift();
|
||||
}
|
||||
if (tokens[0] === ";") {
|
||||
rules.push(" " + token + ";");
|
||||
}
|
||||
}
|
||||
}
|
||||
return parts.join("\n");
|
||||
}
|
||||
export function registerSheet(id: string, css: string) {
|
||||
const sheet = document.createElement("style");
|
||||
sheet.innerHTML = processSheet(css);
|
||||
STYLESHEETS[id] = sheet;
|
||||
}
|
||||
|
||||
export function activateSheet(id, name) {
|
||||
const sheet = STYLESHEETS[id];
|
||||
if (!sheet) {
|
||||
throw new Error(
|
||||
`Invalid css stylesheet for component '${name}'. Did you forget to use the 'css' tag helper?`
|
||||
);
|
||||
}
|
||||
sheet.setAttribute("component", name);
|
||||
document.head.appendChild(sheet);
|
||||
}
|
||||
@@ -131,3 +131,27 @@ export function useSubEnv(nextEnv) {
|
||||
const component = Component.current!;
|
||||
component.env = Object.assign(Object.create(component.env), nextEnv);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// useExternalListener
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* When a component needs to listen to DOM Events on element(s) that are not
|
||||
* part of his hierarchy, we can use the `useExternalListener` hook.
|
||||
* It will correctly add and remove the event listener, whenever the
|
||||
* component is mounted and unmounted.
|
||||
*
|
||||
* Example:
|
||||
* a menu needs to listen to the click on window to be closed automatically
|
||||
*
|
||||
* Usage:
|
||||
* in the constructor of the OWL component that needs to be notified,
|
||||
* `useExternalListener(window, 'click', this._doSomething);`
|
||||
* */
|
||||
export function useExternalListener(target: HTMLElement, eventName: string, handler, eventParams?) {
|
||||
const boundHandler = handler.bind(Component.current);
|
||||
|
||||
onMounted(() => target.addEventListener(eventName, boundHandler, eventParams));
|
||||
onWillUnmount(() => target.removeEventListener(eventName, boundHandler, eventParams));
|
||||
}
|
||||
|
||||
+22
-10
@@ -1,6 +1,7 @@
|
||||
import { CompilationContext } from "./compilation_context";
|
||||
import { QWeb } from "./qweb";
|
||||
import { htmlToVDOM } from "../vdom/html_to_vdom";
|
||||
import { QWebVar } from "./expression_parser";
|
||||
|
||||
/**
|
||||
* Owl QWeb Directives
|
||||
@@ -119,7 +120,7 @@ QWeb.addDirective({
|
||||
ctx.rootContext.shouldDefineScope = true;
|
||||
const variable = node.getAttribute("t-set")!;
|
||||
let value = node.getAttribute("t-value")!;
|
||||
ctx.variables[variable] = ctx.variables[variable] || {};
|
||||
ctx.variables[variable] = ctx.variables[variable] || ({} as QWebVar);
|
||||
let qwebvar = ctx.variables[variable];
|
||||
const hasBody = node.hasChildNodes();
|
||||
|
||||
@@ -127,7 +128,12 @@ QWeb.addDirective({
|
||||
qwebvar.expr = `scope.${variable}`;
|
||||
if (value) {
|
||||
const formattedValue = ctx.formatExpression(value);
|
||||
ctx.addLine(`${qwebvar.expr} = ${formattedValue};`);
|
||||
let scopeExpr = `scope`;
|
||||
if (ctx.protectedScopeNumber) {
|
||||
ctx.rootContext.shouldDefineUtils = true;
|
||||
scopeExpr = `utils.getScope(scope, '${variable}')`;
|
||||
}
|
||||
ctx.addLine(`${scopeExpr}.${variable} = ${formattedValue};`);
|
||||
qwebvar.value = formattedValue;
|
||||
}
|
||||
|
||||
@@ -229,7 +235,7 @@ QWeb.addDirective({
|
||||
// ------------------------------------------------
|
||||
if (!qweb.subTemplates[subTemplate]) {
|
||||
qweb.subTemplates[subTemplate] = true;
|
||||
const subTemplateFn = qweb._compile(subTemplate, nodeTemplate.elem, ctx);
|
||||
const subTemplateFn = qweb._compile(subTemplate, nodeTemplate.elem, ctx, true);
|
||||
qweb.subTemplates[subTemplate] = subTemplateFn;
|
||||
}
|
||||
|
||||
@@ -264,17 +270,16 @@ QWeb.addDirective({
|
||||
// ------------------------------------------------
|
||||
const callingScope = hasBody ? "scope" : "Object.assign(Object.create(context), scope)";
|
||||
const parentComponent = `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}})`;
|
||||
if (ctx.parentNode) {
|
||||
ctx.addLine(
|
||||
`this.subTemplates['${subTemplate}'].call(this, ${callingScope}, Object.assign({}, extra, {parentNode: c${ctx.parentNode}, parent: ${parentComponent}}));`
|
||||
);
|
||||
ctx.addLine(`this.subTemplates['${subTemplate}'].call(this, ${callingScope}, ${extra});`);
|
||||
} else {
|
||||
// this is a t-call with no parentnode, we need to extract the result
|
||||
ctx.rootContext.shouldDefineResult = true;
|
||||
ctx.addLine(`result = []`);
|
||||
ctx.addLine(
|
||||
`this.subTemplates['${subTemplate}'].call(this, ${callingScope}, Object.assign({}, extra, {parentNode: result, parent: ${parentComponent}}));`
|
||||
);
|
||||
ctx.addLine(`this.subTemplates['${subTemplate}'].call(this, ${callingScope}, ${extra});`);
|
||||
ctx.addLine(`result = result[0]`);
|
||||
}
|
||||
|
||||
@@ -313,7 +318,7 @@ QWeb.addDirective({
|
||||
ctx.addLine(`_${valuesID} = Object.values(_${arrayID});`);
|
||||
ctx.closeIf();
|
||||
ctx.addLine(`let _length${keysID} = _${keysID}.length;`);
|
||||
let varsID = ctx.startProtectScope();
|
||||
let varsID = ctx.startProtectScope(true);
|
||||
const loopVar = `i${ctx.loopNumber}`;
|
||||
ctx.addLine(`for (let ${loopVar} = 0; ${loopVar} < _length${keysID}; ${loopVar}++) {`);
|
||||
ctx.indent();
|
||||
@@ -334,6 +339,13 @@ QWeb.addDirective({
|
||||
`Directive t-foreach should always be used with a t-key! (in template: '${ctx.templateName}')`
|
||||
);
|
||||
}
|
||||
if (nodeCopy.hasAttribute("t-key")) {
|
||||
const expr = ctx.formatExpression(nodeCopy.getAttribute("t-key")!);
|
||||
ctx.addLine(`let key${ctx.loopNumber} = ${expr};`);
|
||||
nodeCopy.removeAttribute("t-key");
|
||||
} else {
|
||||
ctx.addLine(`let key${ctx.loopNumber} = i${ctx.loopNumber};`);
|
||||
}
|
||||
|
||||
nodeCopy.removeAttribute("t-foreach");
|
||||
qweb._compileNode(nodeCopy, ctx);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { compileExpr, QWebVar } from "./expression_parser";
|
||||
import { compileExpr, compileExprToArray, QWebVar } from "./expression_parser";
|
||||
|
||||
export const INTERP_REGEXP = /\{\{.*?\}\}/g;
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -17,6 +17,7 @@ export class CompilationContext {
|
||||
rootContext: CompilationContext;
|
||||
shouldDefineParent: boolean = false;
|
||||
shouldDefineScope: boolean = false;
|
||||
protectedScopeNumber: number = 0;
|
||||
shouldDefineQWeb: boolean = false;
|
||||
shouldDefineUtils: boolean = false;
|
||||
shouldDefineRefs: boolean = false;
|
||||
@@ -26,7 +27,8 @@ export class CompilationContext {
|
||||
templateName: string;
|
||||
allowMultipleRoots: boolean = false;
|
||||
hasParentWidget: boolean = false;
|
||||
currentKey: string = "";
|
||||
hasKey0: boolean = false;
|
||||
keyStack: boolean[] = [];
|
||||
|
||||
constructor(name?: string) {
|
||||
this.rootContext = this;
|
||||
@@ -48,20 +50,15 @@ export class CompilationContext {
|
||||
*/
|
||||
generateTemplateKey(prefix: string = ""): string {
|
||||
const id = this.generateID();
|
||||
if (this.loopNumber === 0 && !this.currentKey) {
|
||||
if (this.loopNumber === 0 && !this.hasKey0) {
|
||||
return `'${prefix}__${id}__'`;
|
||||
}
|
||||
let locationExpr = `\`${prefix}__${id}__`;
|
||||
for (let i = 0; i < this.loopNumber - 1; i++) {
|
||||
locationExpr += `\${i${i + 1}}__`;
|
||||
}
|
||||
if (this.currentKey) {
|
||||
const k = this.currentKey;
|
||||
this.addLine(`let k${id} = ${locationExpr}\` + ${k};`);
|
||||
} else {
|
||||
locationExpr += this.loopNumber ? `\${i${this.loopNumber}}__\`` : "`";
|
||||
this.addLine(`let k${id} = ${locationExpr};`);
|
||||
let key = `\`${prefix}__${id}__`;
|
||||
let start = this.hasKey0 ? 0 : 1;
|
||||
for (let i = start; i < this.loopNumber + 1; i++) {
|
||||
key += `\${key${i}}__`;
|
||||
}
|
||||
this.addLine(`let k${id} = ${key}\`;`);
|
||||
return `k${id}`;
|
||||
}
|
||||
|
||||
@@ -116,11 +113,11 @@ export class CompilationContext {
|
||||
}
|
||||
|
||||
indent() {
|
||||
this.indentLevel++;
|
||||
this.rootContext.indentLevel++;
|
||||
}
|
||||
|
||||
dedent() {
|
||||
this.indentLevel--;
|
||||
this.rootContext.indentLevel--;
|
||||
}
|
||||
|
||||
addLine(line: string): number {
|
||||
@@ -159,6 +156,24 @@ export class CompilationContext {
|
||||
this.rootContext.shouldDefineScope = true;
|
||||
return compileExpr(expr, this.variables);
|
||||
}
|
||||
captureExpression(expr: string): string {
|
||||
this.rootContext.shouldDefineScope = true;
|
||||
const argId = this.generateID();
|
||||
const tokens = compileExprToArray(expr, this.variables);
|
||||
const done = new Set();
|
||||
return tokens
|
||||
.map(tok => {
|
||||
if (tok.varName) {
|
||||
if (!done.has(tok.varName)) {
|
||||
done.add(tok.varName);
|
||||
this.addLine(`const ${tok.varName}_${argId} = ${tok.value};`);
|
||||
}
|
||||
tok.value = `${tok.varName}_${argId}`;
|
||||
}
|
||||
return tok.value;
|
||||
})
|
||||
.join("");
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform string interpolation on the given string. Note that if the whole
|
||||
@@ -177,14 +192,19 @@ export class CompilationContext {
|
||||
let r = s.replace(/\{\{.*?\}\}/g, s => "${" + this.formatExpression(s.slice(2, -2)) + "}");
|
||||
return "`" + r + "`";
|
||||
}
|
||||
startProtectScope(): number {
|
||||
startProtectScope(codeBlock?: boolean): number {
|
||||
const protectID = this.generateID();
|
||||
this.rootContext.protectedScopeNumber++;
|
||||
this.rootContext.shouldDefineScope = true;
|
||||
const scopeExpr = codeBlock
|
||||
? `Object.create(scope);`
|
||||
: `Object.assign(Object.create(context), scope);`;
|
||||
this.addLine(`let _origScope${protectID} = scope;`);
|
||||
this.addLine(`scope = Object.assign(Object.create(context), scope);`);
|
||||
this.addLine(`scope = ${scopeExpr}`);
|
||||
return protectID;
|
||||
}
|
||||
stopProtectScope(protectID: number) {
|
||||
this.rootContext.protectedScopeNumber--;
|
||||
this.addLine(`scope = _origScope${protectID};`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,6 +66,7 @@ interface Token {
|
||||
value: string;
|
||||
originalValue?: string;
|
||||
size?: number;
|
||||
varName?: string;
|
||||
}
|
||||
|
||||
const STATIC_TOKEN_MAP: { [key: string]: TKind } = {
|
||||
@@ -81,7 +82,7 @@ const STATIC_TOKEN_MAP: { [key: string]: TKind } = {
|
||||
|
||||
// note that the space after typeof is relevant. It makes sure that the formatted
|
||||
// expression has a space after typeof
|
||||
const OPERATORS = "...,.,===,==,+,!==,!=,!,||,&&,>=,>,<=,<,?,-,*,/,%,typeof ,=>,=,;".split(",");
|
||||
const OPERATORS = "...,.,===,==,+,!==,!=,!,||,&&,>=,>,<=,<,?,-,*,/,%,typeof ,=>,=,;,in ".split(",");
|
||||
|
||||
type Tokenizer = (expr: string) => Token | false;
|
||||
|
||||
@@ -234,7 +235,7 @@ export function tokenize(expr: string): Token[] {
|
||||
* the arrow operator, then we add the current (or some previous tokens) token to
|
||||
* the list of variables so it does not get replaced by a lookup in the context
|
||||
*/
|
||||
export function compileExpr(expr: string, scope: { [key: string]: QWebVar }): string {
|
||||
export function compileExprToArray(expr: string, scope: { [key: string]: QWebVar }): Token[] {
|
||||
scope = Object.create(scope);
|
||||
const tokens = tokenize(expr);
|
||||
for (let i = 0; i < tokens.length; i++) {
|
||||
@@ -269,6 +270,7 @@ export function compileExpr(expr: string, scope: { [key: string]: QWebVar }): st
|
||||
}
|
||||
|
||||
if (isVar) {
|
||||
token.varName = token.value;
|
||||
if (token.value in scope && "id" in scope[token.value]) {
|
||||
token.value = scope[token.value].expr!;
|
||||
} else {
|
||||
@@ -277,5 +279,11 @@ export function compileExpr(expr: string, scope: { [key: string]: QWebVar }): st
|
||||
}
|
||||
}
|
||||
}
|
||||
return tokens.map(t => t.value).join("");
|
||||
return tokens;
|
||||
}
|
||||
|
||||
export function compileExpr(expr: string, scope: { [key: string]: QWebVar }): string {
|
||||
return compileExprToArray(expr, scope)
|
||||
.map(t => t.value)
|
||||
.join("");
|
||||
}
|
||||
|
||||
+18
-6
@@ -58,7 +58,7 @@ export function makeHandlerCode(
|
||||
ctx.rootContext.shouldDefineUtils = true;
|
||||
const comp = `utils.getComponent(context)`;
|
||||
if (args) {
|
||||
let argId = ctx.generateID();
|
||||
const argId = ctx.generateID();
|
||||
ctx.addLine(`let args${argId} = [${ctx.formatExpression(args)}];`);
|
||||
code = `${comp}['${name}'](...args${argId}, e);`;
|
||||
putInCache = false;
|
||||
@@ -67,8 +67,9 @@ export function makeHandlerCode(
|
||||
}
|
||||
} else {
|
||||
// if we get here, then it is an expression
|
||||
// we need to capture every variable in it
|
||||
putInCache = false;
|
||||
code = ctx.formatExpression(value);
|
||||
code = ctx.captureExpression(value);
|
||||
}
|
||||
const modCode = mods.map(mod => modcodes[mod]).join("");
|
||||
let handler = `function (e) {if (!context.__owl__.isMounted){return}${modCode}${code}}`;
|
||||
@@ -303,9 +304,20 @@ QWeb.addDirective({
|
||||
QWeb.addDirective({
|
||||
name: "key",
|
||||
priority: 45,
|
||||
atNodeEncounter({ ctx, value }) {
|
||||
let id = ctx.generateID();
|
||||
ctx.addLine(`const nodeKey${id} = ${ctx.formatExpression(value)};`);
|
||||
ctx.currentKey = `nodeKey${id}`;
|
||||
atNodeEncounter({ ctx, value, node }) {
|
||||
if (ctx.loopNumber === 0) {
|
||||
ctx.keyStack.push(ctx.rootContext.hasKey0);
|
||||
ctx.rootContext.hasKey0 = true;
|
||||
}
|
||||
ctx.addLine("{");
|
||||
ctx.indent();
|
||||
ctx.addLine(`let key${ctx.loopNumber} = ${ctx.formatExpression(value)};`);
|
||||
},
|
||||
finalize({ ctx }) {
|
||||
ctx.dedent();
|
||||
ctx.addLine("}");
|
||||
if (ctx.loopNumber === 0) {
|
||||
ctx.rootContext.hasKey0 = ctx.keyStack.pop() as boolean;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
+31
-7
@@ -86,6 +86,10 @@ interface Utils {
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
function isComponent(obj) {
|
||||
return obj && obj.hasOwnProperty("__owl__");
|
||||
}
|
||||
|
||||
const UTILS: Utils = {
|
||||
zero: Symbol("zero"),
|
||||
toObj(expr) {
|
||||
@@ -122,10 +126,21 @@ const UTILS: Utils = {
|
||||
.join("");
|
||||
},
|
||||
getComponent(obj) {
|
||||
while (obj && !obj.hasOwnProperty("__owl__")) {
|
||||
while (obj && !isComponent(obj)) {
|
||||
obj = obj.__proto__;
|
||||
}
|
||||
return obj;
|
||||
},
|
||||
getScope(obj, property: string) {
|
||||
const obj0 = obj;
|
||||
while (obj && !obj.hasOwnProperty(property)) {
|
||||
const newObj = obj.__proto__;
|
||||
if (!newObj || isComponent(newObj)) {
|
||||
return obj0;
|
||||
}
|
||||
obj = newObj;
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -383,7 +398,12 @@ export class QWeb extends EventBus {
|
||||
});
|
||||
}
|
||||
|
||||
_compile(name: string, elem: Element, parentContext?: CompilationContext): CompiledTemplate {
|
||||
_compile(
|
||||
name: string,
|
||||
elem: Element,
|
||||
parentContext?: CompilationContext,
|
||||
defineKey?: boolean
|
||||
): CompiledTemplate {
|
||||
const isDebug = elem.attributes.hasOwnProperty("t-debug");
|
||||
const ctx = new CompilationContext(name);
|
||||
if (elem.tagName !== "t") {
|
||||
@@ -396,6 +416,10 @@ export class QWeb extends EventBus {
|
||||
ctx.hasParentWidget = true;
|
||||
ctx.shouldDefineResult = false;
|
||||
ctx.addLine(`let c${ctx.parentNode} = extra.parentNode;`);
|
||||
if (defineKey) {
|
||||
ctx.addLine(`let key0 = extra.key || "";`);
|
||||
ctx.hasKey0 = true;
|
||||
}
|
||||
}
|
||||
this._compileNode(elem, ctx);
|
||||
|
||||
@@ -473,9 +497,6 @@ export class QWeb extends EventBus {
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (ctx !== ctx.rootContext) {
|
||||
ctx = ctx.subContext("currentKey", ctx.currentKey);
|
||||
}
|
||||
|
||||
const firstLetter = node.tagName[0];
|
||||
if (firstLetter === firstLetter.toUpperCase()) {
|
||||
@@ -750,8 +771,8 @@ export class QWeb extends EventBus {
|
||||
}
|
||||
}
|
||||
let nodeID = ctx.generateID();
|
||||
let nodeKey = ctx.currentKey || nodeID;
|
||||
const parts = [`key:${nodeKey}`];
|
||||
let key = ctx.loopNumber || ctx.hasKey0 ? `\`\${key${ctx.loopNumber}}_${nodeID}\`` : nodeID;
|
||||
const parts = [`key:${key}`];
|
||||
if (attrs.length + tattrs.length > 0) {
|
||||
parts.push(`attrs:{${attrs.join(",")}}`);
|
||||
}
|
||||
@@ -780,6 +801,9 @@ export class QWeb extends EventBus {
|
||||
ctx.addLine(`let vn${nodeID} = h('${node.nodeName}', p${nodeID}, c${nodeID});`);
|
||||
if (ctx.parentNode) {
|
||||
ctx.addLine(`c${ctx.parentNode}.push(vn${nodeID});`);
|
||||
} else if (ctx.loopNumber || ctx.hasKey0) {
|
||||
ctx.rootContext.shouldDefineResult = true;
|
||||
ctx.addLine(`result = vn${nodeID};`);
|
||||
}
|
||||
|
||||
return nodeID;
|
||||
|
||||
+1
-1
@@ -132,7 +132,7 @@ export function useStore(selector, options: SelectorOptions = {}): any {
|
||||
__destroy.call(component, parent);
|
||||
};
|
||||
|
||||
if (typeof result !== "object") {
|
||||
if (typeof result !== "object" || result === null) {
|
||||
return result;
|
||||
}
|
||||
return new Proxy(result, {
|
||||
|
||||
+17
@@ -1,4 +1,5 @@
|
||||
import { QWeb } from "./qweb/index";
|
||||
import { registerSheet } from "./component/styles";
|
||||
|
||||
/**
|
||||
* Owl Tags
|
||||
@@ -25,3 +26,19 @@ export function xml(strings, ...args) {
|
||||
QWeb.registerTemplate(name, value);
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* CSS tag helper for defining inline stylesheets. With this, one can simply define
|
||||
* an inline stylesheet with just the following code:
|
||||
* ```js
|
||||
* class A extends Component {
|
||||
* static style = css`.component-a { color: red; }`;
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
export function css(strings, ...args) {
|
||||
const name = `__sheet__${QWeb.nextId++}`;
|
||||
const value = String.raw(strings, ...args);
|
||||
registerSheet(name, value);
|
||||
return name;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -229,14 +229,14 @@ exports[`t-slot directive slots are rendered with proper context, part 2 1`] = `
|
||||
// Template name: \\"Link\\"
|
||||
let scope = Object.create(context);
|
||||
let h = this.h;
|
||||
let _12 = scope['props'].to;
|
||||
let c13 = [], p13 = {key:13,attrs:{href: _12}};
|
||||
let vn13 = h('a', p13, c13);
|
||||
const slot14 = this.constructor.slots[context.__owl__.slotId + '_' + 'default'];
|
||||
if (slot14) {
|
||||
slot14.call(this, context.__owl__.scope, Object.assign({}, extra, {parentNode: c13, parent: extra.parent || context}));
|
||||
let _11 = scope['props'].to;
|
||||
let c12 = [], p12 = {key:12,attrs:{href: _11}};
|
||||
let vn12 = h('a', p12, c12);
|
||||
const slot13 = this.constructor.slots[context.__owl__.slotId + '_' + 'default'];
|
||||
if (slot13) {
|
||||
slot13.call(this, context.__owl__.scope, Object.assign({}, extra, {parentNode: c12, parent: extra.parent || context}));
|
||||
}
|
||||
return vn13;
|
||||
return vn12;
|
||||
}"
|
||||
`;
|
||||
|
||||
@@ -263,42 +263,42 @@ exports[`t-slot directive slots are rendered with proper context, part 2 2`] = `
|
||||
}
|
||||
let _length4 = _4.length;
|
||||
let _origScope6 = scope;
|
||||
scope = Object.assign(Object.create(context), scope);
|
||||
scope = Object.create(scope);
|
||||
for (let i1 = 0; i1 < _length4; i1++) {
|
||||
scope.user_first = i1 === 0
|
||||
scope.user_last = i1 === _length4 - 1
|
||||
scope.user_index = i1
|
||||
scope.user = _4[i1]
|
||||
scope.user_value = _5[i1]
|
||||
const nodeKey7 = scope['user'].id;
|
||||
let c8 = [], p8 = {key:nodeKey7};
|
||||
let vn8 = h('li', p8, c8);
|
||||
c2.push(vn8);
|
||||
let key1 = scope['user'].id;
|
||||
let c7 = [], p7 = {key:\`\${key1}_7\`};
|
||||
let vn7 = h('li', p7, c7);
|
||||
c2.push(vn7);
|
||||
// Component 'Link'
|
||||
let k10 = \`__10__\` + nodeKey7;
|
||||
let w9 = k10 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[k10]] : false;
|
||||
let props9 = {to:'/user/'+scope['user'].id};
|
||||
if (w9 && w9.__owl__.currentFiber && !w9.__owl__.vnode) {
|
||||
w9.destroy();
|
||||
w9 = false;
|
||||
let k9 = \`__9__\${key1}__\`;
|
||||
let w8 = k9 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[k9]] : false;
|
||||
let props8 = {to:'/user/'+scope['user'].id};
|
||||
if (w8 && w8.__owl__.currentFiber && !w8.__owl__.vnode) {
|
||||
w8.destroy();
|
||||
w8 = false;
|
||||
}
|
||||
if (w9) {
|
||||
w9.__updateProps(props9, extra.fiber, Object.assign(Object.create(context), scope));
|
||||
let pvnode = w9.__owl__.pvnode;
|
||||
c8.push(pvnode);
|
||||
if (w8) {
|
||||
w8.__updateProps(props8, extra.fiber, Object.assign(Object.create(context), scope));
|
||||
let pvnode = w8.__owl__.pvnode;
|
||||
c7.push(pvnode);
|
||||
} else {
|
||||
let componentKey9 = \`Link\`;
|
||||
let W9 = context.constructor.components[componentKey9] || QWeb.components[componentKey9]|| scope['Link'];
|
||||
if (!W9) {throw new Error('Cannot find the definition of component \\"' + componentKey9 + '\\"')}
|
||||
w9 = new W9(parent, props9);
|
||||
parent.__owl__.cmap[k10] = w9.__owl__.id;
|
||||
w9.__owl__.slotId = 1;
|
||||
let fiber = w9.__prepare(extra.fiber, Object.assign(Object.create(context), scope), () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
|
||||
let pvnode = h('dummy', {key: k10, hook: {remove() {},destroy(vn) {w9.destroy();}}});
|
||||
c8.push(pvnode);
|
||||
w9.__owl__.pvnode = pvnode;
|
||||
let componentKey8 = \`Link\`;
|
||||
let W8 = context.constructor.components[componentKey8] || QWeb.components[componentKey8]|| scope['Link'];
|
||||
if (!W8) {throw new Error('Cannot find the definition of component \\"' + componentKey8 + '\\"')}
|
||||
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 pvnode = h('dummy', {key: k9, hook: {remove() {},destroy(vn) {w8.destroy();}}});
|
||||
c7.push(pvnode);
|
||||
w8.__owl__.pvnode = pvnode;
|
||||
}
|
||||
w9.__owl__.parentLastFiberId = extra.fiber.id;
|
||||
w8.__owl__.parentLastFiberId = extra.fiber.id;
|
||||
}
|
||||
scope = _origScope6;
|
||||
return vn1;
|
||||
@@ -311,11 +311,11 @@ exports[`t-slot directive slots are rendered with proper context, part 2 3`] = `
|
||||
// Template name: \\"slot_default_template\\"
|
||||
let scope = Object.create(context);
|
||||
let h = this.h;
|
||||
let c8 = extra.parentNode;
|
||||
c8.push({text: \`User \`});
|
||||
let _11 = scope['user'].name;
|
||||
if (_11 != null) {
|
||||
c8.push({text: _11});
|
||||
let c7 = extra.parentNode;
|
||||
c7.push({text: \`User \`});
|
||||
let _10 = scope['user'].name;
|
||||
if (_10 != null) {
|
||||
c7.push({text: _10});
|
||||
}
|
||||
}"
|
||||
`;
|
||||
@@ -326,14 +326,14 @@ exports[`t-slot directive slots are rendered with proper context, part 3 1`] = `
|
||||
// Template name: \\"Link\\"
|
||||
let scope = Object.create(context);
|
||||
let h = this.h;
|
||||
let _11 = scope['props'].to;
|
||||
let c12 = [], p12 = {key:12,attrs:{href: _11}};
|
||||
let vn12 = h('a', p12, c12);
|
||||
const slot13 = this.constructor.slots[context.__owl__.slotId + '_' + 'default'];
|
||||
if (slot13) {
|
||||
slot13.call(this, context.__owl__.scope, Object.assign({}, extra, {parentNode: c12, parent: extra.parent || context}));
|
||||
let _10 = scope['props'].to;
|
||||
let c11 = [], p11 = {key:11,attrs:{href: _10}};
|
||||
let vn11 = h('a', p11, c11);
|
||||
const slot12 = this.constructor.slots[context.__owl__.slotId + '_' + 'default'];
|
||||
if (slot12) {
|
||||
slot12.call(this, context.__owl__.scope, Object.assign({}, extra, {parentNode: c11, parent: extra.parent || context}));
|
||||
}
|
||||
return vn12;
|
||||
return vn11;
|
||||
}"
|
||||
`;
|
||||
|
||||
@@ -360,43 +360,43 @@ exports[`t-slot directive slots are rendered with proper context, part 3 2`] = `
|
||||
}
|
||||
let _length4 = _4.length;
|
||||
let _origScope6 = scope;
|
||||
scope = Object.assign(Object.create(context), scope);
|
||||
scope = Object.create(scope);
|
||||
for (let i1 = 0; i1 < _length4; i1++) {
|
||||
scope.user_first = i1 === 0
|
||||
scope.user_last = i1 === _length4 - 1
|
||||
scope.user_index = i1
|
||||
scope.user = _4[i1]
|
||||
scope.user_value = _5[i1]
|
||||
const nodeKey7 = scope['user'].id;
|
||||
let c8 = [], p8 = {key:nodeKey7};
|
||||
let vn8 = h('li', p8, c8);
|
||||
c2.push(vn8);
|
||||
scope.userdescr = 'User '+scope['user'].name;
|
||||
let key1 = scope['user'].id;
|
||||
let c7 = [], p7 = {key:\`\${key1}_7\`};
|
||||
let vn7 = h('li', p7, c7);
|
||||
c2.push(vn7);
|
||||
utils.getScope(scope, 'userdescr').userdescr = 'User '+scope['user'].name;
|
||||
// Component 'Link'
|
||||
let k10 = \`__10__\` + nodeKey7;
|
||||
let w9 = k10 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[k10]] : false;
|
||||
let props9 = {to:'/user/'+scope['user'].id};
|
||||
if (w9 && w9.__owl__.currentFiber && !w9.__owl__.vnode) {
|
||||
w9.destroy();
|
||||
w9 = false;
|
||||
let k9 = \`__9__\${key1}__\`;
|
||||
let w8 = k9 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[k9]] : false;
|
||||
let props8 = {to:'/user/'+scope['user'].id};
|
||||
if (w8 && w8.__owl__.currentFiber && !w8.__owl__.vnode) {
|
||||
w8.destroy();
|
||||
w8 = false;
|
||||
}
|
||||
if (w9) {
|
||||
w9.__updateProps(props9, extra.fiber, Object.assign(Object.create(context), scope));
|
||||
let pvnode = w9.__owl__.pvnode;
|
||||
c8.push(pvnode);
|
||||
if (w8) {
|
||||
w8.__updateProps(props8, extra.fiber, Object.assign(Object.create(context), scope));
|
||||
let pvnode = w8.__owl__.pvnode;
|
||||
c7.push(pvnode);
|
||||
} else {
|
||||
let componentKey9 = \`Link\`;
|
||||
let W9 = context.constructor.components[componentKey9] || QWeb.components[componentKey9]|| scope['Link'];
|
||||
if (!W9) {throw new Error('Cannot find the definition of component \\"' + componentKey9 + '\\"')}
|
||||
w9 = new W9(parent, props9);
|
||||
parent.__owl__.cmap[k10] = w9.__owl__.id;
|
||||
w9.__owl__.slotId = 1;
|
||||
let fiber = w9.__prepare(extra.fiber, Object.assign(Object.create(context), scope), () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
|
||||
let pvnode = h('dummy', {key: k10, hook: {remove() {},destroy(vn) {w9.destroy();}}});
|
||||
c8.push(pvnode);
|
||||
w9.__owl__.pvnode = pvnode;
|
||||
let componentKey8 = \`Link\`;
|
||||
let W8 = context.constructor.components[componentKey8] || QWeb.components[componentKey8]|| scope['Link'];
|
||||
if (!W8) {throw new Error('Cannot find the definition of component \\"' + componentKey8 + '\\"')}
|
||||
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 pvnode = h('dummy', {key: k9, hook: {remove() {},destroy(vn) {w8.destroy();}}});
|
||||
c7.push(pvnode);
|
||||
w8.__owl__.pvnode = pvnode;
|
||||
}
|
||||
w9.__owl__.parentLastFiberId = extra.fiber.id;
|
||||
w8.__owl__.parentLastFiberId = extra.fiber.id;
|
||||
}
|
||||
scope = _origScope6;
|
||||
return vn1;
|
||||
@@ -409,9 +409,9 @@ exports[`t-slot directive slots are rendered with proper context, part 3 3`] = `
|
||||
// Template name: \\"slot_default_template\\"
|
||||
let scope = Object.create(context);
|
||||
let h = this.h;
|
||||
let c8 = extra.parentNode;
|
||||
let c7 = extra.parentNode;
|
||||
if (scope.userdescr != null) {
|
||||
c8.push({text: scope.userdescr});
|
||||
c7.push({text: scope.userdescr});
|
||||
}
|
||||
}"
|
||||
`;
|
||||
|
||||
@@ -147,6 +147,21 @@ describe("basic widget properties", () => {
|
||||
expect(fixture.innerHTML).toBe("<div>1<button>Inc</button></div>");
|
||||
});
|
||||
|
||||
test("can handle empty props", async () => {
|
||||
class Child extends Component<any, any> {
|
||||
static template = xml`<span><t t-esc="props.val"/></span>`;
|
||||
}
|
||||
class Parent extends Component<any, any> {
|
||||
static template = xml`<div><Child val=""/></div>`;
|
||||
static components = { Child };
|
||||
}
|
||||
|
||||
const parent = new Parent();
|
||||
await parent.mount(fixture);
|
||||
expect(env.qweb.templates[Parent.template].fn.toString()).toMatchSnapshot();
|
||||
expect(fixture.innerHTML).toBe("<div><span></span></div>");
|
||||
});
|
||||
|
||||
test("cannot be clicked on and updated if not in DOM", async () => {
|
||||
class Counter extends Component<any, any> {
|
||||
static template = xml`
|
||||
@@ -322,6 +337,36 @@ describe("basic widget properties", () => {
|
||||
console.warn = warn;
|
||||
});
|
||||
|
||||
test("reconciliation alg works for t-foreach in t-foreach, 2", async () => {
|
||||
class Child extends Component<any, any> {
|
||||
static template = xml`<div><t t-esc="props.row + '_' + props.col"/></div>`;
|
||||
}
|
||||
|
||||
class Parent extends Component<any, any> {
|
||||
static template = xml`
|
||||
<div>
|
||||
<p t-foreach="state.rows" t-as="row" t-key="row">
|
||||
<p t-foreach="state.cols" t-as="col" t-key="col">
|
||||
<Child row="row" col="col"/>
|
||||
</p>
|
||||
</p>
|
||||
</div>`;
|
||||
static components = { Child };
|
||||
state = useState({ rows: [1, 2], cols: ["a", "b"] });
|
||||
}
|
||||
|
||||
const widget = new Parent();
|
||||
await widget.mount(fixture);
|
||||
expect(fixture.innerHTML).toBe(
|
||||
"<div><p><p><div>1_a</div></p><p><div>1_b</div></p></p><p><p><div>2_a</div></p><p><div>2_b</div></p></p></div>"
|
||||
);
|
||||
widget.state.rows = [2, 1];
|
||||
await nextTick();
|
||||
expect(fixture.innerHTML).toBe(
|
||||
"<div><p><p><div>2_a</div></p><p><div>2_b</div></p></p><p><p><div>1_a</div></p><p><div>1_b</div></p></p></div>"
|
||||
);
|
||||
});
|
||||
|
||||
test("same t-keys in two different places", async () => {
|
||||
class Child extends Component<any, any> {
|
||||
static template = xml`<span><t t-esc="props.blip"/></span>`;
|
||||
@@ -339,6 +384,7 @@ describe("basic widget properties", () => {
|
||||
const widget = new Parent();
|
||||
await widget.mount(fixture);
|
||||
expect(fixture.innerHTML).toBe("<div><div><span>1</span></div><div><span>2</span></div></div>");
|
||||
expect(env.qweb.templates[Parent.template].fn.toString()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test("t-key on a component with t-if, and a sibling component", async () => {
|
||||
@@ -2624,6 +2670,359 @@ describe("other directives with t-component", () => {
|
||||
await nextTick(); // wait for changes triggered in mounted to be applied
|
||||
expect(fixture.innerHTML).toBe("<div><span>B0</span><span>B1</span></div>");
|
||||
});
|
||||
|
||||
test("t-set outside modified in t-foreach", async () => {
|
||||
class SomeWidget extends Component<any, any> {
|
||||
static template = xml`
|
||||
<div>
|
||||
<t t-set="iter" t-value="0"/>
|
||||
<t t-foreach="state.values" t-as="val" t-key="val">
|
||||
<p>InLoop: <t t-esc="iter"/></p>
|
||||
<t t-set="iter" t-value="iter + 1"/>
|
||||
</t>
|
||||
<p>EndLoop: <t t-esc="iter"/></p>
|
||||
</div>`;
|
||||
|
||||
state = useState({ values: ["a", "b"] });
|
||||
}
|
||||
const widget = new SomeWidget();
|
||||
await widget.mount(fixture);
|
||||
|
||||
expect(fixture.innerHTML).toBe("<div><p>InLoop: 0</p><p>InLoop: 1</p><p>EndLoop: 2</p></div>");
|
||||
expect(QWeb.TEMPLATES[SomeWidget.template].fn.toString()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test("t-set outside modified in t-if", async () => {
|
||||
class SomeWidget extends Component<any, any> {
|
||||
static template = xml`
|
||||
<div>
|
||||
<t t-set="iter" t-value="0"/>
|
||||
<t t-set="flag" t-value="state.flag" />
|
||||
<t t-if="flag === 'if'">
|
||||
<t t-set="iter" t-value="2"/>
|
||||
</t>
|
||||
<t t-elif="flag === 'elif'">
|
||||
<t t-set="iter" t-value="3"/>
|
||||
</t>
|
||||
<t t-else="">
|
||||
<t t-set="iter" t-value="4"/>
|
||||
</t>
|
||||
<p><t t-esc="iter"/></p>
|
||||
</div>`;
|
||||
|
||||
state = { flag: "if" };
|
||||
}
|
||||
const widget = new SomeWidget();
|
||||
await widget.mount(fixture);
|
||||
|
||||
expect(fixture.innerHTML).toBe("<div><p>2</p></div>");
|
||||
widget.state.flag = "elif";
|
||||
await widget.render();
|
||||
expect(fixture.innerHTML).toBe("<div><p>3</p></div>");
|
||||
widget.state.flag = "false";
|
||||
await widget.render();
|
||||
expect(fixture.innerHTML).toBe("<div><p>4</p></div>");
|
||||
});
|
||||
|
||||
test("t-set in t-if", async () => {
|
||||
// Weird that code block within 'if' leaks outside of it
|
||||
// Python does the same
|
||||
class SomeWidget extends Component<any, any> {
|
||||
static template = xml`
|
||||
<div>
|
||||
<t t-set="flag" t-value="state.flag" />
|
||||
<t t-if="flag === 'if'">
|
||||
<t t-set="iter" t-value="2"/>
|
||||
</t>
|
||||
<t t-elif="flag === 'elif'">
|
||||
<t t-set="iter" t-value="3"/>
|
||||
</t>
|
||||
<t t-else="">
|
||||
<t t-set="iter" t-value="4"/>
|
||||
</t>
|
||||
<p><t t-esc="iter"/></p>
|
||||
</div>`;
|
||||
|
||||
state = { flag: "if" };
|
||||
}
|
||||
const widget = new SomeWidget();
|
||||
await widget.mount(fixture);
|
||||
|
||||
expect(fixture.innerHTML).toBe("<div><p>2</p></div>");
|
||||
widget.state.flag = "elif";
|
||||
await widget.render();
|
||||
expect(fixture.innerHTML).toBe("<div><p>3</p></div>");
|
||||
widget.state.flag = "false";
|
||||
await widget.render();
|
||||
expect(fixture.innerHTML).toBe("<div><p>4</p></div>");
|
||||
});
|
||||
|
||||
test("t-set can't alter component", async () => {
|
||||
class SomeWidget extends Component<any, any> {
|
||||
static template = xml`
|
||||
<div>
|
||||
<p><t t-esc="iter"/></p>
|
||||
<t t-set="iter" t-value="5"/>
|
||||
<p><t t-esc="iter"/></p>
|
||||
</div>`;
|
||||
|
||||
iter = 1;
|
||||
}
|
||||
const widget = new SomeWidget();
|
||||
await widget.mount(fixture);
|
||||
|
||||
expect(fixture.innerHTML).toBe("<div><p>1</p><p>5</p></div>");
|
||||
expect(widget.iter).toBe(1);
|
||||
expect(QWeb.TEMPLATES[SomeWidget.template].fn.toString()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test("t-set can't alter from within callee", async () => {
|
||||
env.qweb.addTemplate(
|
||||
"ChildWidget",
|
||||
`<div><t t-esc="iter"/><t t-set="iter" t-value="'called'"/><t t-esc="iter"/></div>`
|
||||
);
|
||||
class SomeWidget extends Component<any, any> {
|
||||
static template = xml`
|
||||
<div>
|
||||
<t t-set="iter" t-value="'source'"/>
|
||||
<p><t t-esc="iter"/></p>
|
||||
<t t-call="ChildWidget"/>
|
||||
<p><t t-esc="iter"/></p>
|
||||
</div>`;
|
||||
}
|
||||
const widget = new SomeWidget();
|
||||
await widget.mount(fixture);
|
||||
|
||||
expect(fixture.innerHTML).toBe("<div><p>source</p><div>sourcecalled</div><p>source</p></div>");
|
||||
expect(QWeb.TEMPLATES[SomeWidget.template].fn.toString()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test("t-set can't alter in t-call body", async () => {
|
||||
env.qweb.addTemplate(
|
||||
"ChildWidget",
|
||||
`<div><t t-esc="iter"/><t t-set="iter" t-value="'called'"/><t t-esc="iter"/></div>`
|
||||
);
|
||||
class SomeWidget extends Component<any, any> {
|
||||
static template = xml`
|
||||
<div>
|
||||
<t t-set="iter" t-value="'source'"/>
|
||||
<p><t t-esc="iter"/></p>
|
||||
<t t-call="ChildWidget">
|
||||
<t t-set="iter" t-value="'inCall'"/>
|
||||
</t>
|
||||
<p><t t-esc="iter"/></p>
|
||||
</div>`;
|
||||
}
|
||||
const widget = new SomeWidget();
|
||||
await widget.mount(fixture);
|
||||
|
||||
expect(fixture.innerHTML).toBe("<div><p>source</p><div>inCallcalled</div><p>source</p></div>");
|
||||
expect(QWeb.TEMPLATES[SomeWidget.template].fn.toString()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test("slot setted value (with t-set) not accessible with t-esc", async () => {
|
||||
class ChildWidget extends Component<any, any> {
|
||||
static template = xml`<div><t t-esc="iter"/><t t-set="iter" t-value="'called'"/><t t-esc="iter"/></div>`;
|
||||
}
|
||||
class SomeWidget extends Component<any, any> {
|
||||
static components = { ChildWidget };
|
||||
static template = xml`
|
||||
<div>
|
||||
<t t-set="iter" t-value="'source'"/>
|
||||
<p><t t-esc="iter"/></p>
|
||||
<ChildWidget>
|
||||
<t t-set="iter" t-value="'inCall'"/>
|
||||
</ChildWidget>
|
||||
<p><t t-esc="iter"/></p>
|
||||
</div>`;
|
||||
}
|
||||
const widget = new SomeWidget();
|
||||
await widget.mount(fixture);
|
||||
|
||||
expect(fixture.innerHTML).toBe("<div><p>source</p><div>called</div><p>source</p></div>");
|
||||
expect(QWeb.TEMPLATES[SomeWidget.template].fn.toString()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test("t-set not altered by child widget", async () => {
|
||||
let child;
|
||||
class ChildWidget extends Component<any, any> {
|
||||
static template = xml`<div><t t-esc="iter"/><t t-set="iter" t-value="'called'"/><t t-esc="iter"/></div>`;
|
||||
iter = "child";
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
child = this;
|
||||
}
|
||||
}
|
||||
class SomeWidget extends Component<any, any> {
|
||||
static components = { ChildWidget };
|
||||
static template = xml`
|
||||
<div>
|
||||
<t t-set="iter" t-value="'source'"/>
|
||||
<p><t t-esc="iter"/></p>
|
||||
<ChildWidget/>
|
||||
<p><t t-esc="iter"/></p>
|
||||
</div>`;
|
||||
}
|
||||
const widget = new SomeWidget();
|
||||
await widget.mount(fixture);
|
||||
|
||||
expect(fixture.innerHTML).toBe("<div><p>source</p><div>childcalled</div><p>source</p></div>");
|
||||
expect(child.iter).toBe("child");
|
||||
expect(QWeb.TEMPLATES[SomeWidget.template].fn.toString()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test("t-set outside modified in t-foreach increment-after operator", async () => {
|
||||
class SomeWidget extends Component<any, any> {
|
||||
static template = xml`
|
||||
<div>
|
||||
<t t-set="iter" t-value="0"/>
|
||||
<t t-foreach="state.values" t-as="val" t-key="val">
|
||||
<p>InLoop: <t t-esc="iter"/></p>
|
||||
<t t-set="iter" t-value="iter++"/>
|
||||
</t>
|
||||
<p>EndLoop: <t t-esc="iter"/></p>
|
||||
</div>`;
|
||||
|
||||
state = useState({ values: ["a", "b"] });
|
||||
}
|
||||
const widget = new SomeWidget();
|
||||
await widget.mount(fixture);
|
||||
expect(fixture.innerHTML).toBe("<div><p>InLoop: 0</p><p>InLoop: 1</p><p>EndLoop: 0</p></div>");
|
||||
});
|
||||
|
||||
test("t-set outside modified in t-foreach increment-before operator", async () => {
|
||||
class SomeWidget extends Component<any, any> {
|
||||
static template = xml`
|
||||
<div>
|
||||
<t t-set="iter" t-value="0"/>
|
||||
<t t-foreach="state.values" t-as="val" t-key="val">
|
||||
<p>InLoop: <t t-esc="iter"/></p>
|
||||
<t t-set="iter" t-value="++iter"/>
|
||||
</t>
|
||||
<p>EndLoop: <t t-esc="iter"/></p>
|
||||
</div>`;
|
||||
|
||||
state = useState({ values: ["a", "b"] });
|
||||
}
|
||||
const widget = new SomeWidget();
|
||||
await widget.mount(fixture);
|
||||
expect(fixture.innerHTML).toBe("<div><p>InLoop: 0</p><p>InLoop: 1</p><p>EndLoop: 1</p></div>");
|
||||
});
|
||||
|
||||
test("t-on expression in t-foreach", async () => {
|
||||
class SomeWidget extends Component<any, any> {
|
||||
static template = xml`
|
||||
<div>
|
||||
<div t-foreach="state.values" t-as="val" t-key="val">
|
||||
<t t-esc="val_index"/>: <t t-esc="val + ''"/>
|
||||
<button t-on-click="otherState.vals.push(val)">Expr</button>
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
state = useState({ values: ["a", "b"] });
|
||||
otherState = { vals: [] };
|
||||
}
|
||||
const widget = new SomeWidget();
|
||||
await widget.mount(fixture);
|
||||
|
||||
expect(fixture.innerHTML).toBe(
|
||||
"<div><div>0: a<button>Expr</button></div><div>1: b<button>Expr</button></div></div>"
|
||||
);
|
||||
expect(widget.otherState.vals).toStrictEqual([]);
|
||||
const buttons = fixture.querySelectorAll("button");
|
||||
buttons[0].click();
|
||||
buttons[1].click();
|
||||
expect(widget.otherState.vals).toStrictEqual(["a", "b"]);
|
||||
expect(QWeb.TEMPLATES[SomeWidget.template].fn.toString()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test("t-on expression in t-foreach with t-set", async () => {
|
||||
class SomeWidget extends Component<any, any> {
|
||||
static template = xml`
|
||||
<div>
|
||||
<t t-set="bossa" t-value="'nova'"/>
|
||||
<div t-foreach="state.values" t-as="val" t-key="val">
|
||||
<t t-set="bossa" t-value="bossa + '_' + val_index" />
|
||||
<t t-esc="val_index"/>: <t t-esc="val + ''"/>
|
||||
<button t-on-click="otherState.vals.push(val + '_' + bossa)">Expr</button>
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
state = useState({ values: ["a", "b"] });
|
||||
otherState = { vals: [] };
|
||||
}
|
||||
const widget = new SomeWidget();
|
||||
await widget.mount(fixture);
|
||||
|
||||
expect(fixture.innerHTML).toBe(
|
||||
"<div><div>0: a<button>Expr</button></div><div>1: b<button>Expr</button></div></div>"
|
||||
);
|
||||
expect(widget.otherState.vals).toStrictEqual([]);
|
||||
const buttons = fixture.querySelectorAll("button");
|
||||
buttons[0].click();
|
||||
buttons[1].click();
|
||||
expect(widget.otherState.vals).toStrictEqual(["a_nova_0", "b_nova_0_1"]);
|
||||
expect(QWeb.TEMPLATES[SomeWidget.template].fn.toString()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test("t-on method call in t-foreach", async () => {
|
||||
class SomeWidget extends Component<any, any> {
|
||||
static template = xml`
|
||||
<div>
|
||||
<div t-foreach="state.values" t-as="val" t-key="val">
|
||||
<t t-esc="val_index"/>: <t t-esc="val + ''"/>
|
||||
<button t-on-click="addVal(val)">meth call</button>
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
state = useState({ values: ["a", "b"] });
|
||||
otherState = { vals: new Array<string>() };
|
||||
|
||||
addVal(val: string) {
|
||||
this.otherState.vals.push(val);
|
||||
}
|
||||
}
|
||||
const widget = new SomeWidget();
|
||||
await widget.mount(fixture);
|
||||
|
||||
expect(fixture.innerHTML).toBe(
|
||||
"<div><div>0: a<button>meth call</button></div><div>1: b<button>meth call</button></div></div>"
|
||||
);
|
||||
expect(widget.otherState.vals).toStrictEqual([]);
|
||||
const buttons = fixture.querySelectorAll("button");
|
||||
buttons[0].click();
|
||||
buttons[1].click();
|
||||
expect(widget.otherState.vals).toStrictEqual(["a", "b"]);
|
||||
expect(QWeb.TEMPLATES[SomeWidget.template].fn.toString()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test("t-on expression captured in t-foreach", async () => {
|
||||
class SomeWidget extends Component<any, any> {
|
||||
static template = xml`
|
||||
<div>
|
||||
<t t-set="iter" t-value="0" />
|
||||
<div t-foreach="arr" t-as="val" t-key="val">
|
||||
<button t-on-click="otherState.vals.push(iter + '_' + iter)">expr</button>
|
||||
<t t-set="iter" t-value="iter + 1" />
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
arr = ["a", "b"];
|
||||
otherState = { vals: new Array<string>() };
|
||||
}
|
||||
const widget = new SomeWidget();
|
||||
await widget.mount(fixture);
|
||||
|
||||
expect(fixture.innerHTML).toBe(
|
||||
"<div><div><button>expr</button></div><div><button>expr</button></div></div>"
|
||||
);
|
||||
expect(widget.otherState.vals).toStrictEqual([]);
|
||||
const buttons = fixture.querySelectorAll("button");
|
||||
buttons[0].click();
|
||||
buttons[1].click();
|
||||
expect(widget.otherState.vals).toStrictEqual(["0_0", "1_1"]);
|
||||
expect(QWeb.TEMPLATES[SomeWidget.template].fn.toString()).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
describe("random stuff/miscellaneous", () => {
|
||||
@@ -4418,6 +4817,28 @@ describe("t-model directive", () => {
|
||||
expect(comp.state[2].f).toBe(false);
|
||||
expect(env.qweb.templates[SomeComponent.template].fn.toString()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test("two inputs in a div with a t-key", async () => {
|
||||
class SomeComponent extends Component<any, any> {
|
||||
static template = xml`
|
||||
<div t-key="'key'">
|
||||
<input class="a" t-if="state.flag"/>
|
||||
<input class="b" t-if="!state.flag"/>
|
||||
</div>
|
||||
`;
|
||||
state = useState({ flag: true });
|
||||
}
|
||||
const comp = new SomeComponent();
|
||||
await comp.mount(fixture);
|
||||
expect(fixture.innerHTML).toBe('<div><input class="a"></div>');
|
||||
expect(env.qweb.templates[SomeComponent.template].fn.toString()).toMatchSnapshot();
|
||||
fixture.querySelector("input")!.value = "asdf";
|
||||
expect(fixture.querySelector("input")!.value).toBe("asdf");
|
||||
comp.state.flag = false;
|
||||
await nextTick();
|
||||
expect(fixture.innerHTML).toBe('<div><input class="b"></div>');
|
||||
expect(fixture.querySelector("input")!.value).toBe("");
|
||||
});
|
||||
});
|
||||
|
||||
describe("environment and plugins", () => {
|
||||
@@ -5599,6 +6020,26 @@ describe("t-call", () => {
|
||||
expect(env.qweb.subTemplates["sub"].toString()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test("t-call in t-foreach and children component", async () => {
|
||||
env.qweb.addTemplate("sub", `<Child val="val"/>`);
|
||||
class Child extends Component<any, any> {
|
||||
static template = xml`<span><t t-esc="props.val"/></span>`;
|
||||
}
|
||||
class Parent extends Component<any, any> {
|
||||
static components = { Child };
|
||||
static template = xml`
|
||||
<div>
|
||||
<t t-foreach="['a', 'b', 'c']" t-as="val" t-key="val">
|
||||
<t t-call="sub"/>
|
||||
</t>
|
||||
</div>`;
|
||||
}
|
||||
const parent = new Parent();
|
||||
await parent.mount(fixture);
|
||||
|
||||
expect(fixture.innerHTML).toBe("<div><span>a</span><span>b</span><span>c</span></div>");
|
||||
});
|
||||
|
||||
test("parent is set within t-call with no parentNode", async () => {
|
||||
env.qweb.addTemplate("sub", `<Child/>`);
|
||||
let child;
|
||||
@@ -5638,4 +6079,29 @@ describe("t-call", () => {
|
||||
|
||||
fixture.querySelector("p")!.click();
|
||||
});
|
||||
|
||||
test("sub components in two t-calls", async () => {
|
||||
class Child extends Component<any, any> {
|
||||
static template = xml`<span><t t-esc="props.val"/></span>`;
|
||||
}
|
||||
|
||||
env.qweb.addTemplate("sub", `<Child val="state.val"/>`);
|
||||
class Parent extends Component<any, any> {
|
||||
static template = xml`
|
||||
<div>
|
||||
<t t-if="state.val===1">
|
||||
<t t-call="sub"/>
|
||||
</t>
|
||||
<div t-else=""><t t-call="sub"/></div>
|
||||
</div>`;
|
||||
static components = { Child };
|
||||
state = useState({ val: 1 });
|
||||
}
|
||||
const parent = new Parent();
|
||||
await parent.mount(fixture);
|
||||
expect(fixture.innerHTML).toBe("<div><span>1</span></div>");
|
||||
parent.state.val = 2;
|
||||
await nextTick();
|
||||
expect(fixture.innerHTML).toBe("<div><div><span>2</span></div></div>");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -19,13 +19,6 @@ let env: Env;
|
||||
beforeEach(() => {
|
||||
fixture = makeTestFixture();
|
||||
env = makeTestEnv();
|
||||
env.qweb.addTemplate("Component<any,any>", "<div></div>");
|
||||
env.qweb.addTemplate(
|
||||
"Counter",
|
||||
`<div><t t-esc="state.counter"/><button t-on-click="inc">Inc</button></div>`
|
||||
);
|
||||
env.qweb.addTemplate("WidgetA", `<div>Hello<t t-component="b"/></div>`);
|
||||
env.qweb.addTemplate("WidgetB", `<div>world</div>`);
|
||||
Component.env = env;
|
||||
});
|
||||
|
||||
|
||||
@@ -0,0 +1,170 @@
|
||||
import { Component, Env } from "../../src/component/component";
|
||||
import { processSheet } from "../../src/component/styles";
|
||||
import { xml, css } from "../../src/tags";
|
||||
import { makeTestFixture, makeTestEnv } from "../helpers";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Setup and helpers
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// We create before each test:
|
||||
// - fixture: a div, appended to the DOM, intended to be the target of dom
|
||||
// manipulations. Note that it is removed after each test.
|
||||
// - env: an Env, necessary to create new components
|
||||
|
||||
let fixture: HTMLElement;
|
||||
let env: Env;
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = makeTestFixture();
|
||||
env = makeTestEnv();
|
||||
Component.env = env;
|
||||
document.head.innerHTML = "";
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
fixture.remove();
|
||||
});
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Tests
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
describe("styles and component", () => {
|
||||
test("can define an inline stylesheet", async () => {
|
||||
class App extends Component<any, any> {
|
||||
static template = xml`<div class="app">text</div>`;
|
||||
static style = css`
|
||||
.app {
|
||||
color: red;
|
||||
}
|
||||
`;
|
||||
}
|
||||
expect(document.head.innerHTML).toBe("");
|
||||
const app = new App();
|
||||
|
||||
expect(document.head.innerHTML).toBe(`<style component=\"App\">.app {
|
||||
color: red;
|
||||
}</style>`);
|
||||
|
||||
await app.mount(fixture);
|
||||
const style = getComputedStyle(app.el!);
|
||||
expect(style.color).toBe("red");
|
||||
expect(fixture.innerHTML).toBe('<div class="app">text</div>');
|
||||
});
|
||||
|
||||
test("inherited components properly apply css", async () => {
|
||||
class App extends Component<any, any> {
|
||||
static template = xml`<div class="app">text</div>`;
|
||||
static style = css`
|
||||
.app {
|
||||
color: red;
|
||||
}
|
||||
`;
|
||||
}
|
||||
class SubApp extends App {
|
||||
static style = css`
|
||||
.app {
|
||||
font-weight: bold;
|
||||
}
|
||||
`;
|
||||
}
|
||||
expect(document.head.innerHTML).toBe("");
|
||||
const app = new SubApp();
|
||||
|
||||
expect(document.head.innerHTML).toBe(`<style component=\"SubApp\">.app {
|
||||
font-weight: bold;
|
||||
}</style><style component=\"App\">.app {
|
||||
color: red;
|
||||
}</style>`);
|
||||
|
||||
await app.mount(fixture);
|
||||
const style = getComputedStyle(app.el!);
|
||||
expect(style.color).toBe("red");
|
||||
expect(style.fontWeight).toBe("bold");
|
||||
expect(fixture.innerHTML).toBe('<div class="app">text</div>');
|
||||
});
|
||||
|
||||
test("get a meaningful error message if css helper is missing", async () => {
|
||||
class App extends Component<any, any> {
|
||||
static template = xml`<div class="app">text</div>`;
|
||||
static style = `.app {color: red;}`;
|
||||
}
|
||||
let error;
|
||||
try {
|
||||
new App();
|
||||
} catch (e) {
|
||||
error = e;
|
||||
}
|
||||
expect(error).toBeDefined();
|
||||
expect(error.message).toBe(
|
||||
"Invalid css stylesheet for component 'App'. Did you forget to use the 'css' tag helper?"
|
||||
);
|
||||
});
|
||||
|
||||
test("inline stylesheets are processed", async () => {
|
||||
class App extends Component<any, any> {
|
||||
static template = xml`<div class="app">text</div>`;
|
||||
static style = css`
|
||||
.app {
|
||||
color: red;
|
||||
.some-class {
|
||||
font-weight: bold;
|
||||
width: 40px;
|
||||
}
|
||||
|
||||
display: block;
|
||||
}
|
||||
`;
|
||||
}
|
||||
new App();
|
||||
|
||||
expect(document.head.querySelector("style")!.innerHTML).toBe(`.app {
|
||||
color: red;
|
||||
}
|
||||
.app .some-class {
|
||||
font-weight: bold;
|
||||
width: 40px;
|
||||
}
|
||||
.app {
|
||||
display: block;
|
||||
}`);
|
||||
});
|
||||
|
||||
test("properly handle rules with commas", async () => {
|
||||
const sheet = processSheet(`.parent-a, .parent-b {
|
||||
.child-a, .child-b {
|
||||
color: red;
|
||||
}
|
||||
}`);
|
||||
|
||||
expect(sheet)
|
||||
.toBe(`.parent-a .child-a, .parent-a .child-b, .parent-b .child-a, .parent-b .child-b {
|
||||
color: red;
|
||||
}`);
|
||||
});
|
||||
|
||||
test("handle & selector", async () => {
|
||||
let sheet = processSheet(`.btn {
|
||||
&.danger {
|
||||
color: red;
|
||||
}
|
||||
}`);
|
||||
|
||||
expect(sheet).toBe(`.btn.danger {
|
||||
color: red;
|
||||
}`);
|
||||
|
||||
sheet = processSheet(`.some-class {
|
||||
&.btn {
|
||||
.other-class ~ & {
|
||||
color: red;
|
||||
}
|
||||
}
|
||||
}`);
|
||||
|
||||
expect(sheet).toBe(`.other-class ~ .some-class.btn {
|
||||
color: red;
|
||||
}`);
|
||||
});
|
||||
});
|
||||
+37
-1
@@ -9,7 +9,8 @@ import {
|
||||
onWillPatch,
|
||||
onWillStart,
|
||||
onWillUpdateProps,
|
||||
useSubEnv
|
||||
useSubEnv,
|
||||
useExternalListener
|
||||
} from "../src/hooks";
|
||||
import { xml } from "../src/tags";
|
||||
|
||||
@@ -592,4 +593,39 @@ describe("hooks", () => {
|
||||
expect(fixture.innerHTML).toBe("<div><span>2</span></div>");
|
||||
expect(steps).toEqual(["onWillStart", "onWillUpdateProps"]);
|
||||
});
|
||||
|
||||
test("useExternalListener", async () => {
|
||||
let n = 0;
|
||||
|
||||
class MyComponent extends Component<any, any> {
|
||||
static template = xml`<span><t t-esc="props.value"/></span>`;
|
||||
constructor(parent, props) {
|
||||
super(parent, props);
|
||||
useExternalListener(window as any, "click", this.increment);
|
||||
}
|
||||
increment() {
|
||||
n++;
|
||||
}
|
||||
}
|
||||
class App extends Component<any, any> {
|
||||
static template = xml`<div><MyComponent t-if="state.flag"/></div>`;
|
||||
static components = { MyComponent };
|
||||
state = useState({ flag: false });
|
||||
}
|
||||
|
||||
const app = new App();
|
||||
await app.mount(fixture);
|
||||
|
||||
expect(n).toBe(0);
|
||||
window.dispatchEvent(new Event("click"));
|
||||
expect(n).toBe(0);
|
||||
app.state.flag = true;
|
||||
await nextTick();
|
||||
window.dispatchEvent(new Event("click"));
|
||||
expect(n).toBe(1);
|
||||
app.state.flag = false;
|
||||
await nextTick();
|
||||
window.dispatchEvent(new Event("click"));
|
||||
expect(n).toBe(1);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -421,13 +421,14 @@ exports[`foreach does not pollute the rendering context 1`] = `
|
||||
}
|
||||
let _length3 = _3.length;
|
||||
let _origScope5 = scope;
|
||||
scope = Object.assign(Object.create(context), 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});
|
||||
@@ -455,20 +456,20 @@ exports[`foreach iterate on items (on a element node) 1`] = `
|
||||
}
|
||||
let _length3 = _3.length;
|
||||
let _origScope5 = scope;
|
||||
scope = Object.assign(Object.create(context), 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]
|
||||
const nodeKey6 = scope['item'];
|
||||
let c7 = [], p7 = {key:nodeKey6};
|
||||
let vn7 = h('span', p7, c7);
|
||||
c1.push(vn7);
|
||||
let _8 = scope['item'];
|
||||
if (_8 != null) {
|
||||
c7.push({text: _8});
|
||||
let key1 = scope['item'];
|
||||
let c6 = [], p6 = {key:\`\${key1}_6\`};
|
||||
let vn6 = h('span', p6, c6);
|
||||
c1.push(vn6);
|
||||
let _7 = scope['item'];
|
||||
if (_7 != null) {
|
||||
c6.push({text: _7});
|
||||
}
|
||||
}
|
||||
scope = _origScope5;
|
||||
@@ -493,13 +494,14 @@ exports[`foreach iterate on items 1`] = `
|
||||
}
|
||||
let _length3 = _3.length;
|
||||
let _origScope5 = scope;
|
||||
scope = Object.assign(Object.create(context), 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;
|
||||
c1.push({text: \` [\`});
|
||||
let _6 = scope['item_index'];
|
||||
if (_6 != null) {
|
||||
@@ -539,13 +541,14 @@ exports[`foreach iterate, dict param 1`] = `
|
||||
}
|
||||
let _length3 = _3.length;
|
||||
let _origScope5 = scope;
|
||||
scope = Object.assign(Object.create(context), 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;
|
||||
c1.push({text: \` [\`});
|
||||
let _6 = scope['item_index'];
|
||||
if (_6 != null) {
|
||||
@@ -585,13 +588,14 @@ exports[`foreach iterate, position 1`] = `
|
||||
}
|
||||
let _length3 = _3.length;
|
||||
let _origScope5 = scope;
|
||||
scope = Object.assign(Object.create(context), scope);
|
||||
scope = Object.create(scope);
|
||||
for (let i1 = 0; i1 < _length3; i1++) {
|
||||
scope.elem_first = i1 === 0
|
||||
scope.elem_last = i1 === _length3 - 1
|
||||
scope.elem_index = i1
|
||||
scope.elem = _3[i1]
|
||||
scope.elem_value = _4[i1]
|
||||
let key1 = i1;
|
||||
c1.push({text: \` -\`});
|
||||
if (scope['elem_first']) {
|
||||
c1.push({text: \` first\`});
|
||||
@@ -628,13 +632,14 @@ exports[`foreach t-foreach in t-forach 1`] = `
|
||||
}
|
||||
let _length3 = _3.length;
|
||||
let _origScope5 = scope;
|
||||
scope = Object.assign(Object.create(context), scope);
|
||||
scope = Object.create(scope);
|
||||
for (let i1 = 0; i1 < _length3; i1++) {
|
||||
scope.number_first = i1 === 0
|
||||
scope.number_last = i1 === _length3 - 1
|
||||
scope.number_index = i1
|
||||
scope.number = _3[i1]
|
||||
scope.number_value = _4[i1]
|
||||
let key1 = i1;
|
||||
let _6 = scope['letters'];
|
||||
if (!_6) { throw new Error('QWeb error: Invalid loop expression')}
|
||||
let _7 = _8 = _6;
|
||||
@@ -644,13 +649,14 @@ exports[`foreach t-foreach in t-forach 1`] = `
|
||||
}
|
||||
let _length7 = _7.length;
|
||||
let _origScope9 = scope;
|
||||
scope = Object.assign(Object.create(context), scope);
|
||||
scope = Object.create(scope);
|
||||
for (let i2 = 0; i2 < _length7; i2++) {
|
||||
scope.letter_first = i2 === 0
|
||||
scope.letter_last = i2 === _length7 - 1
|
||||
scope.letter_index = i2
|
||||
scope.letter = _7[i2]
|
||||
scope.letter_value = _8[i2]
|
||||
let key2 = i2;
|
||||
c1.push({text: \` [\`});
|
||||
let _10 = scope['number'];
|
||||
if (_10 != null) {
|
||||
@@ -686,14 +692,15 @@ exports[`foreach warn if no key in some case 1`] = `
|
||||
}
|
||||
let _length3 = _3.length;
|
||||
let _origScope5 = scope;
|
||||
scope = Object.assign(Object.create(context), 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 c6 = [], p6 = {key:6};
|
||||
let key1 = i1;
|
||||
let c6 = [], p6 = {key:\`\${key1}_6\`};
|
||||
let vn6 = h('span', p6, c6);
|
||||
c1.push(vn6);
|
||||
let _7 = scope['item'];
|
||||
@@ -727,7 +734,7 @@ exports[`loading templates can load a few templates from a xml string 1`] = `
|
||||
let h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
let vn1 = h('ul', p1, c1);
|
||||
this.subTemplates['items'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context)}));
|
||||
this.subTemplates['items'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__4__'}));
|
||||
return vn1;
|
||||
}"
|
||||
`;
|
||||
@@ -750,14 +757,15 @@ exports[`misc global 1`] = `
|
||||
}
|
||||
let _length3 = _3.length;
|
||||
let _origScope5 = scope;
|
||||
scope = Object.assign(Object.create(context), scope);
|
||||
scope = Object.create(scope);
|
||||
for (let i1 = 0; i1 < _length3; i1++) {
|
||||
scope.value_first = i1 === 0
|
||||
scope.value_last = i1 === _length3 - 1
|
||||
scope.value_index = i1
|
||||
scope.value = _3[i1]
|
||||
scope.value_value = _4[i1]
|
||||
let c6 = [], p6 = {key:6};
|
||||
let key1 = i1;
|
||||
let c6 = [], p6 = {key:\`\${key1}_6\`};
|
||||
let vn6 = h('span', p6, c6);
|
||||
c1.push(vn6);
|
||||
let _7 = scope['value'];
|
||||
@@ -774,23 +782,27 @@ exports[`misc global 1`] = `
|
||||
scope = Object.assign(Object.create(context), scope);
|
||||
{
|
||||
let c__0 = [];
|
||||
scope.foo = 'aaa';
|
||||
utils.getScope(scope, 'foo').foo = 'aaa';
|
||||
scope[utils.zero] = c__0;
|
||||
}
|
||||
this.subTemplates['_callee-uses-foo'].call(this, scope, Object.assign({}, extra, {parentNode: c__0, parent: utils.getComponent(context)}));
|
||||
let k14 = \`__14__\${key1}__\`;
|
||||
this.subTemplates['_callee-uses-foo'].call(this, scope, Object.assign({}, extra, {parentNode: c__0, parent: utils.getComponent(context), key: k14}));
|
||||
scope = _origScope13;
|
||||
}
|
||||
this.subTemplates['_callee-uses-foo'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c__0, parent: utils.getComponent(context)}));
|
||||
scope.foo = 'bbb';
|
||||
this.subTemplates['_callee-uses-foo'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c__0, parent: utils.getComponent(context)}));
|
||||
let k15 = \`__15__\${key1}__\`;
|
||||
this.subTemplates['_callee-uses-foo'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c__0, parent: utils.getComponent(context), key: k15}));
|
||||
utils.getScope(scope, 'foo').foo = 'bbb';
|
||||
let k16 = \`__16__\${key1}__\`;
|
||||
this.subTemplates['_callee-uses-foo'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c__0, parent: utils.getComponent(context), key: k16}));
|
||||
scope[utils.zero] = c__0;
|
||||
}
|
||||
this.subTemplates['_callee-asc'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context)}));
|
||||
let k17 = \`__17__\${key1}__\`;
|
||||
this.subTemplates['_callee-asc'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: k17}));
|
||||
scope = _origScope10;
|
||||
}
|
||||
}
|
||||
scope = _origScope5;
|
||||
this.subTemplates['_callee-asc-toto'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context)}));
|
||||
this.subTemplates['_callee-asc-toto'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__20__'}));
|
||||
return vn1;
|
||||
}"
|
||||
`;
|
||||
@@ -1029,7 +1041,7 @@ exports[`t-call (template calling basic caller 1`] = `
|
||||
let h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
let vn1 = h('div', p1, c1);
|
||||
this.subTemplates['_basic-callee'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context)}));
|
||||
this.subTemplates['_basic-callee'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__3__'}));
|
||||
return vn1;
|
||||
}"
|
||||
`;
|
||||
@@ -1040,7 +1052,8 @@ exports[`t-call (template calling basic caller 2`] = `
|
||||
// Template name: \\"_basic-callee\\"
|
||||
let h = this.h;
|
||||
let c1 = extra.parentNode;
|
||||
let c2 = [], p2 = {key:2};
|
||||
let key0 = extra.key || \\"\\";
|
||||
let c2 = [], p2 = {key:\`\${key0}_2\`};
|
||||
let vn2 = h('span', p2, c2);
|
||||
c1.push(vn2);
|
||||
c2.push({text: \`ok\`});
|
||||
@@ -1056,7 +1069,7 @@ exports[`t-call (template calling basic caller, no parent node 1`] = `
|
||||
let result;
|
||||
let h = this.h;
|
||||
result = []
|
||||
this.subTemplates['_basic-callee'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: result, parent: utils.getComponent(context)}));
|
||||
this.subTemplates['_basic-callee'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: result, parent: utils.getComponent(context), key: '__3__'}));
|
||||
result = result[0]
|
||||
return result;
|
||||
}"
|
||||
@@ -1068,7 +1081,8 @@ exports[`t-call (template calling basic caller, no parent node 2`] = `
|
||||
// Template name: \\"_basic-callee\\"
|
||||
let h = this.h;
|
||||
let c1 = extra.parentNode;
|
||||
let c2 = [], p2 = {key:2};
|
||||
let key0 = extra.key || \\"\\";
|
||||
let c2 = [], p2 = {key:\`\${key0}_2\`};
|
||||
let vn2 = h('div', p2, c2);
|
||||
c1.push(vn2);
|
||||
c2.push({text: \`ok\`});
|
||||
@@ -1100,7 +1114,7 @@ exports[`t-call (template calling call with several sub nodes on same line 1`] =
|
||||
c5.push({text: \`yay\`});
|
||||
scope[utils.zero] = c__0;
|
||||
}
|
||||
this.subTemplates['SubTemplate'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context)}));
|
||||
this.subTemplates['SubTemplate'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__6__'}));
|
||||
scope = _origScope3;
|
||||
}
|
||||
return vn1;
|
||||
@@ -1115,7 +1129,8 @@ exports[`t-call (template calling call with several sub nodes on same line 2`] =
|
||||
let scope = Object.create(context);
|
||||
let h = this.h;
|
||||
let c1 = extra.parentNode;
|
||||
let c2 = [], p2 = {key:2};
|
||||
let key0 = extra.key || \\"\\";
|
||||
let c2 = [], p2 = {key:\`\${key0}_2\`};
|
||||
let vn2 = h('div', p2, c2);
|
||||
c1.push(vn2);
|
||||
c2.push(...scope[utils.zero]);
|
||||
@@ -1132,23 +1147,23 @@ exports[`t-call (template calling cascading t-call t-raw='0' 1`] = `
|
||||
let c1 = [], p1 = {key:1};
|
||||
let vn1 = h('div', p1, c1);
|
||||
{
|
||||
let _origScope10 = scope;
|
||||
let _origScope12 = scope;
|
||||
scope = Object.assign(Object.create(context), scope);
|
||||
{
|
||||
let c__0 = [];
|
||||
let c11 = [], p11 = {key:11};
|
||||
let vn11 = h('span', p11, c11);
|
||||
c__0.push(vn11);
|
||||
c11.push({text: \`hey\`});
|
||||
let c13 = [], p13 = {key:13};
|
||||
let vn13 = h('span', p13, c13);
|
||||
c__0.push(vn13);
|
||||
c13.push({text: \`hey\`});
|
||||
c__0.push({text: \` \`});
|
||||
let c12 = [], p12 = {key:12};
|
||||
let vn12 = h('span', p12, c12);
|
||||
c__0.push(vn12);
|
||||
c12.push({text: \`yay\`});
|
||||
let c14 = [], p14 = {key:14};
|
||||
let vn14 = h('span', p14, c14);
|
||||
c__0.push(vn14);
|
||||
c14.push({text: \`yay\`});
|
||||
scope[utils.zero] = c__0;
|
||||
}
|
||||
this.subTemplates['SubTemplate'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context)}));
|
||||
scope = _origScope10;
|
||||
this.subTemplates['SubTemplate'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__15__'}));
|
||||
scope = _origScope12;
|
||||
}
|
||||
return vn1;
|
||||
}"
|
||||
@@ -1164,7 +1179,7 @@ exports[`t-call (template calling inherit context 1`] = `
|
||||
let c1 = [], p1 = {key:1};
|
||||
let vn1 = h('div', p1, c1);
|
||||
scope.foo = 1;
|
||||
this.subTemplates['_callee-uses-foo'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context)}));
|
||||
this.subTemplates['_callee-uses-foo'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__2__'}));
|
||||
return vn1;
|
||||
}"
|
||||
`;
|
||||
@@ -1183,7 +1198,7 @@ exports[`t-call (template calling recursive template, part 1 1`] = `
|
||||
c1.push(vn2);
|
||||
c2.push({text: \`hey\`});
|
||||
if (false) {
|
||||
this.subTemplates['recursive'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context)}));
|
||||
this.subTemplates['recursive'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__6__'}));
|
||||
}
|
||||
return vn1;
|
||||
}"
|
||||
@@ -1197,15 +1212,17 @@ exports[`t-call (template calling recursive template, part 1 2`] = `
|
||||
let scope = Object.create(context);
|
||||
let h = this.h;
|
||||
let c1 = extra.parentNode;
|
||||
let c3 = [], p3 = {key:3};
|
||||
let key0 = extra.key || \\"\\";
|
||||
let c3 = [], p3 = {key:\`\${key0}_3\`};
|
||||
let vn3 = h('div', p3, c3);
|
||||
c1.push(vn3);
|
||||
let c4 = [], p4 = {key:4};
|
||||
let c4 = [], p4 = {key:\`\${key0}_4\`};
|
||||
let vn4 = h('span', p4, c4);
|
||||
c3.push(vn4);
|
||||
c4.push({text: \`hey\`});
|
||||
if (false) {
|
||||
this.subTemplates['recursive'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c3, parent: utils.getComponent(context)}));
|
||||
let k5 = \`__5__\${key0}__\`;
|
||||
this.subTemplates['recursive'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c3, parent: utils.getComponent(context), key: k5}));
|
||||
}
|
||||
}"
|
||||
`;
|
||||
@@ -1220,15 +1237,15 @@ exports[`t-call (template calling recursive template, part 2 1`] = `
|
||||
let c1 = [], p1 = {key:1};
|
||||
let vn1 = h('div', p1, c1);
|
||||
{
|
||||
let _origScope10 = scope;
|
||||
let _origScope11 = scope;
|
||||
scope = Object.assign(Object.create(context), scope);
|
||||
{
|
||||
let c__0 = [];
|
||||
scope.node = scope['root'];
|
||||
utils.getScope(scope, 'node').node = scope['root'];
|
||||
scope[utils.zero] = c__0;
|
||||
}
|
||||
this.subTemplates['nodeTemplate'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context)}));
|
||||
scope = _origScope10;
|
||||
this.subTemplates['nodeTemplate'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__12__'}));
|
||||
scope = _origScope11;
|
||||
}
|
||||
return vn1;
|
||||
}"
|
||||
@@ -1242,10 +1259,11 @@ exports[`t-call (template calling recursive template, part 2 2`] = `
|
||||
let scope = Object.create(context);
|
||||
let h = this.h;
|
||||
let c1 = extra.parentNode;
|
||||
let c2 = [], p2 = {key:2};
|
||||
let key0 = extra.key || \\"\\";
|
||||
let c2 = [], p2 = {key:\`\${key0}_2\`};
|
||||
let vn2 = h('div', p2, c2);
|
||||
c1.push(vn2);
|
||||
let c3 = [], p3 = {key:3};
|
||||
let c3 = [], p3 = {key:\`\${key0}_3\`};
|
||||
let vn3 = h('p', p3, c3);
|
||||
c2.push(vn3);
|
||||
let _4 = scope['node'].val;
|
||||
@@ -1261,22 +1279,24 @@ exports[`t-call (template calling recursive template, part 2 2`] = `
|
||||
}
|
||||
let _length6 = _6.length;
|
||||
let _origScope8 = scope;
|
||||
scope = Object.assign(Object.create(context), scope);
|
||||
scope = Object.create(scope);
|
||||
for (let i1 = 0; i1 < _length6; i1++) {
|
||||
scope.subtree_first = i1 === 0
|
||||
scope.subtree_last = i1 === _length6 - 1
|
||||
scope.subtree_index = i1
|
||||
scope.subtree = _6[i1]
|
||||
scope.subtree_value = _7[i1]
|
||||
let key1 = i1;
|
||||
{
|
||||
let _origScope9 = scope;
|
||||
scope = Object.assign(Object.create(context), scope);
|
||||
{
|
||||
let c__0 = [];
|
||||
scope.node = scope['subtree'];
|
||||
utils.getScope(scope, 'node').node = scope['subtree'];
|
||||
scope[utils.zero] = c__0;
|
||||
}
|
||||
this.subTemplates['nodeTemplate'].call(this, scope, Object.assign({}, extra, {parentNode: c2, parent: utils.getComponent(context)}));
|
||||
let k10 = \`__10__\${key0}__\${key1}__\`;
|
||||
this.subTemplates['nodeTemplate'].call(this, scope, Object.assign({}, extra, {parentNode: c2, parent: utils.getComponent(context), key: k10}));
|
||||
scope = _origScope9;
|
||||
}
|
||||
}
|
||||
@@ -1294,15 +1314,15 @@ exports[`t-call (template calling recursive template, part 3 1`] = `
|
||||
let c1 = [], p1 = {key:1};
|
||||
let vn1 = h('div', p1, c1);
|
||||
{
|
||||
let _origScope10 = scope;
|
||||
let _origScope11 = scope;
|
||||
scope = Object.assign(Object.create(context), scope);
|
||||
{
|
||||
let c__0 = [];
|
||||
scope.node = scope['root'];
|
||||
utils.getScope(scope, 'node').node = scope['root'];
|
||||
scope[utils.zero] = c__0;
|
||||
}
|
||||
this.subTemplates['nodeTemplate'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context)}));
|
||||
scope = _origScope10;
|
||||
this.subTemplates['nodeTemplate'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__12__'}));
|
||||
scope = _origScope11;
|
||||
}
|
||||
return vn1;
|
||||
}"
|
||||
@@ -1316,10 +1336,11 @@ exports[`t-call (template calling recursive template, part 3 2`] = `
|
||||
let scope = Object.create(context);
|
||||
let h = this.h;
|
||||
let c1 = extra.parentNode;
|
||||
let c2 = [], p2 = {key:2};
|
||||
let key0 = extra.key || \\"\\";
|
||||
let c2 = [], p2 = {key:\`\${key0}_2\`};
|
||||
let vn2 = h('div', p2, c2);
|
||||
c1.push(vn2);
|
||||
let c3 = [], p3 = {key:3};
|
||||
let c3 = [], p3 = {key:\`\${key0}_3\`};
|
||||
let vn3 = h('p', p3, c3);
|
||||
c2.push(vn3);
|
||||
let _4 = scope['node'].val;
|
||||
@@ -1335,22 +1356,24 @@ exports[`t-call (template calling recursive template, part 3 2`] = `
|
||||
}
|
||||
let _length6 = _6.length;
|
||||
let _origScope8 = scope;
|
||||
scope = Object.assign(Object.create(context), scope);
|
||||
scope = Object.create(scope);
|
||||
for (let i1 = 0; i1 < _length6; i1++) {
|
||||
scope.subtree_first = i1 === 0
|
||||
scope.subtree_last = i1 === _length6 - 1
|
||||
scope.subtree_index = i1
|
||||
scope.subtree = _6[i1]
|
||||
scope.subtree_value = _7[i1]
|
||||
let key1 = i1;
|
||||
{
|
||||
let _origScope9 = scope;
|
||||
scope = Object.assign(Object.create(context), scope);
|
||||
{
|
||||
let c__0 = [];
|
||||
scope.node = scope['subtree'];
|
||||
utils.getScope(scope, 'node').node = scope['subtree'];
|
||||
scope[utils.zero] = c__0;
|
||||
}
|
||||
this.subTemplates['nodeTemplate'].call(this, scope, Object.assign({}, extra, {parentNode: c2, parent: utils.getComponent(context)}));
|
||||
let k10 = \`__10__\${key0}__\${key1}__\`;
|
||||
this.subTemplates['nodeTemplate'].call(this, scope, Object.assign({}, extra, {parentNode: c2, parent: utils.getComponent(context), key: k10}));
|
||||
scope = _origScope9;
|
||||
}
|
||||
}
|
||||
@@ -1372,10 +1395,10 @@ exports[`t-call (template calling scoped parameters 1`] = `
|
||||
scope = Object.assign(Object.create(context), scope);
|
||||
{
|
||||
let c__0 = [];
|
||||
scope.foo = 42;
|
||||
utils.getScope(scope, 'foo').foo = 42;
|
||||
scope[utils.zero] = c__0;
|
||||
}
|
||||
this.subTemplates['_basic-callee'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context)}));
|
||||
this.subTemplates['_basic-callee'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__3__'}));
|
||||
scope = _origScope2;
|
||||
}
|
||||
if (scope.foo != null) {
|
||||
@@ -1395,7 +1418,7 @@ exports[`t-call (template calling t-call with t-if 1`] = `
|
||||
let c1 = [], p1 = {key:1};
|
||||
let vn1 = h('div', p1, c1);
|
||||
if (scope['flag']) {
|
||||
this.subTemplates['sub'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context)}));
|
||||
this.subTemplates['sub'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__3__'}));
|
||||
}
|
||||
return vn1;
|
||||
}"
|
||||
@@ -1407,7 +1430,8 @@ exports[`t-call (template calling t-call with t-if 2`] = `
|
||||
// Template name: \\"sub\\"
|
||||
let h = this.h;
|
||||
let c1 = extra.parentNode;
|
||||
let c2 = [], p2 = {key:2};
|
||||
let key0 = extra.key || \\"\\";
|
||||
let c2 = [], p2 = {key:\`\${key0}_2\`};
|
||||
let vn2 = h('span', p2, c2);
|
||||
c1.push(vn2);
|
||||
c2.push({text: \`ok\`});
|
||||
@@ -1432,23 +1456,25 @@ exports[`t-call (template calling t-call with t-set inside and outside 1`] = `
|
||||
}
|
||||
let _length3 = _3.length;
|
||||
let _origScope5 = scope;
|
||||
scope = Object.assign(Object.create(context), scope);
|
||||
scope = Object.create(scope);
|
||||
for (let i1 = 0; i1 < _length3; i1++) {
|
||||
scope.v_first = i1 === 0
|
||||
scope.v_last = i1 === _length3 - 1
|
||||
scope.v_index = i1
|
||||
scope.v = _3[i1]
|
||||
scope.v_value = _4[i1]
|
||||
scope.val = scope['v'].val;
|
||||
let key1 = i1;
|
||||
utils.getScope(scope, 'val').val = scope['v'].val;
|
||||
{
|
||||
let _origScope8 = scope;
|
||||
scope = Object.assign(Object.create(context), scope);
|
||||
{
|
||||
let c__0 = [];
|
||||
scope.val3 = scope.val*3;
|
||||
utils.getScope(scope, 'val3').val3 = scope.val*3;
|
||||
scope[utils.zero] = c__0;
|
||||
}
|
||||
this.subTemplates['sub'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context)}));
|
||||
let k9 = \`__9__\${key1}__\`;
|
||||
this.subTemplates['sub'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: k9}));
|
||||
scope = _origScope8;
|
||||
}
|
||||
}
|
||||
@@ -1467,7 +1493,7 @@ exports[`t-call (template calling t-call with t-set inside and outside. 2 1`] =
|
||||
let c1 = [], p1 = {key:1};
|
||||
let vn1 = h('p', p1, c1);
|
||||
scope.w = 'fromwrapper';
|
||||
this.subTemplates['main'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context)}));
|
||||
this.subTemplates['main'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__11__'}));
|
||||
return vn1;
|
||||
}"
|
||||
`;
|
||||
@@ -1483,19 +1509,19 @@ exports[`t-call (template calling t-call, conditional and t-set in t-call body 1
|
||||
let vn1 = h('div', p1, c1);
|
||||
scope.v1 = 'elif';
|
||||
if (scope.v1==='if') {
|
||||
this.subTemplates['callee1'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context)}));
|
||||
this.subTemplates['callee1'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__3__'}));
|
||||
}
|
||||
else if (scope.v1==='elif') {
|
||||
{
|
||||
let _origScope5 = scope;
|
||||
let _origScope6 = scope;
|
||||
scope = Object.assign(Object.create(context), scope);
|
||||
{
|
||||
let c__0 = [];
|
||||
scope.v = 'success';
|
||||
utils.getScope(scope, 'v').v = 'success';
|
||||
scope[utils.zero] = c__0;
|
||||
}
|
||||
this.subTemplates['callee2'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context)}));
|
||||
scope = _origScope5;
|
||||
this.subTemplates['callee2'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__7__'}));
|
||||
scope = _origScope6;
|
||||
}
|
||||
}
|
||||
return vn1;
|
||||
@@ -1511,7 +1537,7 @@ exports[`t-call (template calling t-call, global templates 1`] = `
|
||||
let h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
let vn1 = h('div', p1, c1);
|
||||
this.subTemplates['john'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context)}));
|
||||
this.subTemplates['john'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__3__'}));
|
||||
return vn1;
|
||||
}"
|
||||
`;
|
||||
@@ -1533,7 +1559,7 @@ exports[`t-call (template calling with unused body 1`] = `
|
||||
scope[utils.zero] = c__0;
|
||||
}
|
||||
result = []
|
||||
this.subTemplates['_basic-callee'].call(this, scope, Object.assign({}, extra, {parentNode: result, parent: utils.getComponent(context)}));
|
||||
this.subTemplates['_basic-callee'].call(this, scope, Object.assign({}, extra, {parentNode: result, parent: utils.getComponent(context), key: '__4__'}));
|
||||
result = result[0]
|
||||
scope = _origScope3;
|
||||
}
|
||||
@@ -1554,11 +1580,11 @@ exports[`t-call (template calling with unused setbody 1`] = `
|
||||
scope = Object.assign(Object.create(context), scope);
|
||||
{
|
||||
let c__0 = [];
|
||||
scope.qux = 3;
|
||||
utils.getScope(scope, 'qux').qux = 3;
|
||||
scope[utils.zero] = c__0;
|
||||
}
|
||||
result = []
|
||||
this.subTemplates['_basic-callee'].call(this, scope, Object.assign({}, extra, {parentNode: result, parent: utils.getComponent(context)}));
|
||||
this.subTemplates['_basic-callee'].call(this, scope, Object.assign({}, extra, {parentNode: result, parent: utils.getComponent(context), key: '__4__'}));
|
||||
result = result[0]
|
||||
scope = _origScope3;
|
||||
}
|
||||
@@ -1583,7 +1609,7 @@ exports[`t-call (template calling with used body 1`] = `
|
||||
scope[utils.zero] = c__0;
|
||||
}
|
||||
result = []
|
||||
this.subTemplates['_callee-printsbody'].call(this, scope, Object.assign({}, extra, {parentNode: result, parent: utils.getComponent(context)}));
|
||||
this.subTemplates['_callee-printsbody'].call(this, scope, Object.assign({}, extra, {parentNode: result, parent: utils.getComponent(context), key: '__4__'}));
|
||||
result = result[0]
|
||||
scope = _origScope3;
|
||||
}
|
||||
@@ -1605,10 +1631,10 @@ exports[`t-call (template calling with used set body 1`] = `
|
||||
scope = Object.assign(Object.create(context), scope);
|
||||
{
|
||||
let c__0 = [];
|
||||
scope.foo = 'ok';
|
||||
utils.getScope(scope, 'foo').foo = 'ok';
|
||||
scope[utils.zero] = c__0;
|
||||
}
|
||||
this.subTemplates['_callee-uses-foo'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context)}));
|
||||
this.subTemplates['_callee-uses-foo'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__4__'}));
|
||||
scope = _origScope3;
|
||||
}
|
||||
return vn1;
|
||||
@@ -1808,7 +1834,7 @@ exports[`t-esc t-esc=0 is escaped 1`] = `
|
||||
c4.push({text: \`escaped\`});
|
||||
scope[utils.zero] = c__0;
|
||||
}
|
||||
this.subTemplates['test'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context)}));
|
||||
this.subTemplates['test'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__5__'}));
|
||||
scope = _origScope3;
|
||||
}
|
||||
return vn1;
|
||||
@@ -2096,15 +2122,20 @@ exports[`t-key can use t-key directive on a node 1`] = `
|
||||
) {
|
||||
// Template name: \\"test\\"
|
||||
let scope = Object.create(context);
|
||||
let result;
|
||||
let h = this.h;
|
||||
const nodeKey1 = scope['beer'].id;
|
||||
let c2 = [], p2 = {key:nodeKey1};
|
||||
let vn2 = h('div', p2, c2);
|
||||
let _3 = scope['beer'].name;
|
||||
if (_3 != null) {
|
||||
c2.push({text: _3});
|
||||
{
|
||||
let key0 = scope['beer'].id;
|
||||
let c1 = [], p1 = {key:\`\${key0}_1\`};
|
||||
let vn1 = h('div', p1, c1);
|
||||
result = vn1;
|
||||
result = vn1;
|
||||
let _2 = scope['beer'].name;
|
||||
if (_2 != null) {
|
||||
c1.push({text: _2});
|
||||
}
|
||||
}
|
||||
return vn2;
|
||||
return result;
|
||||
}"
|
||||
`;
|
||||
|
||||
@@ -2125,20 +2156,20 @@ exports[`t-key t-key directive in a list 1`] = `
|
||||
}
|
||||
let _length3 = _3.length;
|
||||
let _origScope5 = scope;
|
||||
scope = Object.assign(Object.create(context), scope);
|
||||
scope = Object.create(scope);
|
||||
for (let i1 = 0; i1 < _length3; i1++) {
|
||||
scope.beer_first = i1 === 0
|
||||
scope.beer_last = i1 === _length3 - 1
|
||||
scope.beer_index = i1
|
||||
scope.beer = _3[i1]
|
||||
scope.beer_value = _4[i1]
|
||||
const nodeKey6 = scope['beer'].id;
|
||||
let c7 = [], p7 = {key:nodeKey6};
|
||||
let vn7 = h('li', p7, c7);
|
||||
c1.push(vn7);
|
||||
let _8 = scope['beer'].name;
|
||||
if (_8 != null) {
|
||||
c7.push({text: _8});
|
||||
let key1 = scope['beer'].id;
|
||||
let c6 = [], p6 = {key:\`\${key1}_6\`};
|
||||
let vn6 = h('li', p6, c6);
|
||||
c1.push(vn6);
|
||||
let _7 = scope['beer'].name;
|
||||
if (_7 != null) {
|
||||
c6.push({text: _7});
|
||||
}
|
||||
}
|
||||
scope = _origScope5;
|
||||
@@ -2229,23 +2260,23 @@ exports[`t-on can bind handlers with loop variable as argument 1`] = `
|
||||
}
|
||||
let _length3 = _3.length;
|
||||
let _origScope5 = scope;
|
||||
scope = Object.assign(Object.create(context), scope);
|
||||
scope = Object.create(scope);
|
||||
for (let i1 = 0; i1 < _length3; i1++) {
|
||||
scope.action_first = i1 === 0
|
||||
scope.action_last = i1 === _length3 - 1
|
||||
scope.action_index = i1
|
||||
scope.action = _3[i1]
|
||||
scope.action_value = _4[i1]
|
||||
const nodeKey6 = scope['action_index'];
|
||||
let c7 = [], p7 = {key:nodeKey6};
|
||||
let vn7 = h('li', p7, c7);
|
||||
c1.push(vn7);
|
||||
let c8 = [], p8 = {key:nodeKey6,on:{}};
|
||||
let vn8 = h('a', p8, c8);
|
||||
c7.push(vn8);
|
||||
let args9 = [scope['action']];
|
||||
p8.on['click'] = function (e) {if (!context.__owl__.isMounted){return}utils.getComponent(context)['activate'](...args9, e);};
|
||||
c8.push({text: \`link\`});
|
||||
let key1 = scope['action_index'];
|
||||
let c6 = [], p6 = {key:\`\${key1}_6\`};
|
||||
let vn6 = h('li', p6, c6);
|
||||
c1.push(vn6);
|
||||
let c7 = [], p7 = {key:\`\${key1}_7\`,on:{}};
|
||||
let vn7 = h('a', p7, c7);
|
||||
c6.push(vn7);
|
||||
let args8 = [scope['action']];
|
||||
p7.on['click'] = function (e) {if (!context.__owl__.isMounted){return}utils.getComponent(context)['activate'](...args8, e);};
|
||||
c7.push({text: \`link\`});
|
||||
}
|
||||
scope = _origScope5;
|
||||
return vn1;
|
||||
@@ -2369,7 +2400,8 @@ exports[`t-on t-on with inline statement (function call) 1`] = `
|
||||
let h = this.h;
|
||||
let c1 = [], p1 = {key:1,on:{}};
|
||||
let vn1 = h('button', p1, c1);
|
||||
p1.on['click'] = function (e) {if (!context.__owl__.isMounted){return}scope['state'].incrementCounter(2)};
|
||||
const state_2 = scope['state'];
|
||||
p1.on['click'] = function (e) {if (!context.__owl__.isMounted){return}state_2.incrementCounter(2)};
|
||||
c1.push({text: \`Click\`});
|
||||
return vn1;
|
||||
}"
|
||||
@@ -2383,7 +2415,8 @@ exports[`t-on t-on with inline statement 1`] = `
|
||||
let h = this.h;
|
||||
let c1 = [], p1 = {key:1,on:{}};
|
||||
let vn1 = h('button', p1, c1);
|
||||
p1.on['click'] = function (e) {if (!context.__owl__.isMounted){return}scope['state'].counter++};
|
||||
const state_2 = scope['state'];
|
||||
p1.on['click'] = function (e) {if (!context.__owl__.isMounted){return}state_2.counter++};
|
||||
c1.push({text: \`Click\`});
|
||||
return vn1;
|
||||
}"
|
||||
@@ -2397,7 +2430,8 @@ exports[`t-on t-on with inline statement, part 2 1`] = `
|
||||
let h = this.h;
|
||||
let c1 = [], p1 = {key:1,on:{}};
|
||||
let vn1 = h('button', p1, c1);
|
||||
p1.on['click'] = function (e) {if (!context.__owl__.isMounted){return}scope['state'].flag=!scope['state'].flag};
|
||||
const state_2 = scope['state'];
|
||||
p1.on['click'] = function (e) {if (!context.__owl__.isMounted){return}state_2.flag=!state_2.flag};
|
||||
c1.push({text: \`Toggle\`});
|
||||
return vn1;
|
||||
}"
|
||||
@@ -2411,7 +2445,9 @@ exports[`t-on t-on with inline statement, part 3 1`] = `
|
||||
let h = this.h;
|
||||
let c1 = [], p1 = {key:1,on:{}};
|
||||
let vn1 = h('button', p1, c1);
|
||||
p1.on['click'] = function (e) {if (!context.__owl__.isMounted){return}scope['state'].n=scope['someFunction'](3)};
|
||||
const state_2 = scope['state'];
|
||||
const someFunction_2 = scope['someFunction'];
|
||||
p1.on['click'] = function (e) {if (!context.__owl__.isMounted){return}state_2.n=someFunction_2(3)};
|
||||
c1.push({text: \`Toggle\`});
|
||||
return vn1;
|
||||
}"
|
||||
@@ -2486,24 +2522,27 @@ exports[`t-on t-on with prevent modifier in t-foreach 1`] = `
|
||||
}
|
||||
let _length3 = _3.length;
|
||||
let _origScope5 = scope;
|
||||
scope = Object.assign(Object.create(context), scope);
|
||||
scope = Object.create(scope);
|
||||
for (let i1 = 0; i1 < _length3; i1++) {
|
||||
scope.project_first = i1 === 0
|
||||
scope.project_last = i1 === _length3 - 1
|
||||
scope.project_index = i1
|
||||
scope.project = _3[i1]
|
||||
scope.project_value = _4[i1]
|
||||
const nodeKey6 = scope['project'];
|
||||
let _7 = '#';
|
||||
let c8 = [], p8 = {key:nodeKey6,attrs:{href: _7},on:{}};
|
||||
let vn8 = h('a', p8, c8);
|
||||
c1.push(vn8);
|
||||
let args9 = [scope['project'].id];
|
||||
p8.on['click'] = function (e) {if (!context.__owl__.isMounted){return}e.preventDefault();utils.getComponent(context)['onEdit'](...args9, e);};
|
||||
c8.push({text: \` Edit \`});
|
||||
let _10 = scope['project'].name;
|
||||
if (_10 != null) {
|
||||
c8.push({text: _10});
|
||||
let key1 = i1;
|
||||
{
|
||||
let key1 = scope['project'];
|
||||
let _6 = '#';
|
||||
let c7 = [], p7 = {key:\`\${key1}_7\`,attrs:{href: _6},on:{}};
|
||||
let vn7 = h('a', p7, c7);
|
||||
c1.push(vn7);
|
||||
let args8 = [scope['project'].id];
|
||||
p7.on['click'] = function (e) {if (!context.__owl__.isMounted){return}e.preventDefault();utils.getComponent(context)['onEdit'](...args8, e);};
|
||||
c7.push({text: \` Edit \`});
|
||||
let _9 = scope['project'].name;
|
||||
if (_9 != null) {
|
||||
c7.push({text: _9});
|
||||
}
|
||||
}
|
||||
}
|
||||
scope = _origScope5;
|
||||
@@ -2571,7 +2610,7 @@ exports[`t-on t-on with t-call 1`] = `
|
||||
let h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
let vn1 = h('div', p1, c1);
|
||||
this.subTemplates['sub'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context)}));
|
||||
this.subTemplates['sub'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__4__'}));
|
||||
return vn1;
|
||||
}"
|
||||
`;
|
||||
@@ -2585,7 +2624,7 @@ exports[`t-on t-on, with arguments and t-call 1`] = `
|
||||
let h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
let vn1 = h('div', p1, c1);
|
||||
this.subTemplates['sub'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context)}));
|
||||
this.subTemplates['sub'].call(this, Object.assign(Object.create(context), scope), Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__4__'}));
|
||||
return vn1;
|
||||
}"
|
||||
`;
|
||||
@@ -2729,29 +2768,32 @@ exports[`t-ref refs in a loop 1`] = `
|
||||
}
|
||||
let _length3 = _3.length;
|
||||
let _origScope5 = scope;
|
||||
scope = Object.assign(Object.create(context), 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]
|
||||
const nodeKey6 = scope['item'];
|
||||
let c7 = [], p7 = {key:nodeKey6};
|
||||
let vn7 = h('div', p7, c7);
|
||||
c1.push(vn7);
|
||||
const ref8 = (scope['item']);
|
||||
p7.hook = {
|
||||
create: (_, n) => {
|
||||
context.__owl__.refs[ref8] = n.elm;
|
||||
},
|
||||
destroy: () => {
|
||||
delete context.__owl__.refs[ref8];
|
||||
},
|
||||
};
|
||||
let _9 = scope['item'];
|
||||
if (_9 != null) {
|
||||
c7.push({text: _9});
|
||||
let key1 = i1;
|
||||
{
|
||||
let key1 = scope['item'];
|
||||
let c6 = [], p6 = {key:\`\${key1}_6\`};
|
||||
let vn6 = h('div', p6, c6);
|
||||
c1.push(vn6);
|
||||
const ref7 = (scope['item']);
|
||||
p6.hook = {
|
||||
create: (_, n) => {
|
||||
context.__owl__.refs[ref7] = n.elm;
|
||||
},
|
||||
destroy: () => {
|
||||
delete context.__owl__.refs[ref7];
|
||||
},
|
||||
};
|
||||
let _8 = scope['item'];
|
||||
if (_8 != null) {
|
||||
c6.push({text: _8});
|
||||
}
|
||||
}
|
||||
}
|
||||
scope = _origScope5;
|
||||
@@ -2953,6 +2995,7 @@ exports[`t-set t-set should reuse variable if possible 1`] = `
|
||||
"function anonymous(context, extra
|
||||
) {
|
||||
// Template name: \\"test\\"
|
||||
let utils = this.constructor.utils;
|
||||
let scope = Object.create(context);
|
||||
let h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
@@ -2967,25 +3010,25 @@ exports[`t-set t-set should reuse variable if possible 1`] = `
|
||||
}
|
||||
let _length3 = _3.length;
|
||||
let _origScope5 = scope;
|
||||
scope = Object.assign(Object.create(context), scope);
|
||||
scope = Object.create(scope);
|
||||
for (let i1 = 0; i1 < _length3; i1++) {
|
||||
scope.elem_first = i1 === 0
|
||||
scope.elem_last = i1 === _length3 - 1
|
||||
scope.elem_index = i1
|
||||
scope.elem = _3[i1]
|
||||
scope.elem_value = _4[i1]
|
||||
const nodeKey6 = scope['elem_index'];
|
||||
let c7 = [], p7 = {key:nodeKey6};
|
||||
let vn7 = h('div', p7, c7);
|
||||
c1.push(vn7);
|
||||
let c8 = [], p8 = {key:nodeKey6};
|
||||
let vn8 = h('span', p8, c8);
|
||||
c7.push(vn8);
|
||||
c8.push({text: \`v\`});
|
||||
let key1 = scope['elem_index'];
|
||||
let c6 = [], p6 = {key:\`\${key1}_6\`};
|
||||
let vn6 = h('div', p6, c6);
|
||||
c1.push(vn6);
|
||||
let c7 = [], p7 = {key:\`\${key1}_7\`};
|
||||
let vn7 = h('span', p7, c7);
|
||||
c6.push(vn7);
|
||||
c7.push({text: \`v\`});
|
||||
if (scope.v != null) {
|
||||
c8.push({text: scope.v});
|
||||
c7.push({text: scope.v});
|
||||
}
|
||||
scope.v = scope['elem'];
|
||||
utils.getScope(scope, 'v').v = scope['elem'];
|
||||
}
|
||||
scope = _origScope5;
|
||||
return vn1;
|
||||
|
||||
@@ -56,6 +56,12 @@ describe("tokenizer", () => {
|
||||
{ type: "OPERATOR", value: "..." },
|
||||
{ type: "VALUE", value: "1" }
|
||||
]);
|
||||
|
||||
expect(tokenize("a in b")).toEqual([
|
||||
{ type: "SYMBOL", value: "a" },
|
||||
{ type: "OPERATOR", value: "in " },
|
||||
{ type: "SYMBOL", value: "b" }
|
||||
]);
|
||||
});
|
||||
|
||||
test("strings", () => {
|
||||
@@ -134,6 +140,7 @@ describe("expression evaluation", () => {
|
||||
expect(compileExpr("color === 'black'", {})).toBe("scope['color']==='black'");
|
||||
expect(compileExpr("'li_'+item", {})).toBe("'li_'+scope['item']");
|
||||
expect(compileExpr("state.val > 1", {})).toBe("scope['state'].val>1");
|
||||
expect(compileExpr("a in b", {})).toBe("scope['a']in scope['b']");
|
||||
});
|
||||
|
||||
test("boolean operations", () => {
|
||||
|
||||
@@ -11,33 +11,35 @@ exports[`RouteComponent can render simple cases 1`] = `
|
||||
let result;
|
||||
let h = this.h;
|
||||
if (scope['routeComponent']) {
|
||||
const nodeKey4 = scope['env'].router.currentRouteName;
|
||||
// Component 'routeComponent'
|
||||
let k6 = \`__6__\` + nodeKey4;
|
||||
let w5 = k6 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[k6]] : false;
|
||||
let vn7 = {};
|
||||
result = vn7;
|
||||
let props5 = Object.assign({}, scope['env'].router.currentParams);
|
||||
if (w5 && w5.__owl__.currentFiber && !w5.__owl__.vnode) {
|
||||
w5.destroy();
|
||||
w5 = false;
|
||||
{
|
||||
let key0 = scope['env'].router.currentRouteName;
|
||||
// Component 'routeComponent'
|
||||
let k5 = \`__5__\${key0}__\`;
|
||||
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);
|
||||
if (w4 && w4.__owl__.currentFiber && !w4.__owl__.vnode) {
|
||||
w4.destroy();
|
||||
w4 = false;
|
||||
}
|
||||
if (w4) {
|
||||
w4.__updateProps(props4, extra.fiber, undefined);
|
||||
let pvnode = w4.__owl__.pvnode;
|
||||
utils.defineProxy(vn6, pvnode);
|
||||
} else {
|
||||
let componentKey4 = \`routeComponent\`;
|
||||
let W4 = context.constructor.components[componentKey4] || QWeb.components[componentKey4]|| scope['routeComponent'];
|
||||
if (!W4) {throw new Error('Cannot find the definition of component \\"' + componentKey4 + '\\"')}
|
||||
w4 = new W4(parent, props4);
|
||||
parent.__owl__.cmap[k5] = w4.__owl__.id;
|
||||
let fiber = w4.__prepare(extra.fiber, undefined, () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
|
||||
let pvnode = h('dummy', {key: k5, hook: {remove() {},destroy(vn) {w4.destroy();}}});
|
||||
utils.defineProxy(vn6, pvnode);
|
||||
w4.__owl__.pvnode = pvnode;
|
||||
}
|
||||
w4.__owl__.parentLastFiberId = extra.fiber.id;
|
||||
}
|
||||
if (w5) {
|
||||
w5.__updateProps(props5, extra.fiber, undefined);
|
||||
let pvnode = w5.__owl__.pvnode;
|
||||
utils.defineProxy(vn7, pvnode);
|
||||
} else {
|
||||
let componentKey5 = \`routeComponent\`;
|
||||
let W5 = context.constructor.components[componentKey5] || QWeb.components[componentKey5]|| scope['routeComponent'];
|
||||
if (!W5) {throw new Error('Cannot find the definition of component \\"' + componentKey5 + '\\"')}
|
||||
w5 = new W5(parent, props5);
|
||||
parent.__owl__.cmap[k6] = w5.__owl__.id;
|
||||
let fiber = w5.__prepare(extra.fiber, undefined, () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
|
||||
let pvnode = h('dummy', {key: k6, hook: {remove() {},destroy(vn) {w5.destroy();}}});
|
||||
utils.defineProxy(vn7, pvnode);
|
||||
w5.__owl__.pvnode = pvnode;
|
||||
}
|
||||
w5.__owl__.parentLastFiberId = extra.fiber.id;
|
||||
}
|
||||
return result;
|
||||
}"
|
||||
|
||||
@@ -49,10 +49,13 @@ describe("connecting a component to store", () => {
|
||||
});
|
||||
|
||||
test("useStore can observe primitive types and call onUpdate", async () => {
|
||||
const state = { isBoolean: false };
|
||||
const state = { isBoolean: false, nullValue: null };
|
||||
const actions = {
|
||||
setTrue({ state }) {
|
||||
state.isBoolean = true;
|
||||
},
|
||||
setNotNull({ state }) {
|
||||
state.nullValue = "ok";
|
||||
}
|
||||
};
|
||||
const store = new Store({ state, actions });
|
||||
@@ -61,8 +64,10 @@ describe("connecting a component to store", () => {
|
||||
static template = xml`
|
||||
<div>
|
||||
<span t-if="isBoolean">ok</span>
|
||||
<span t-if="nullValue !== null">not null</span>
|
||||
</div>`;
|
||||
isBoolean: boolean;
|
||||
nullValue: string;
|
||||
constructor() {
|
||||
super();
|
||||
this.isBoolean = useStore(state => state.isBoolean, {
|
||||
@@ -70,6 +75,11 @@ describe("connecting a component to store", () => {
|
||||
this.isBoolean = isBoolean;
|
||||
}
|
||||
});
|
||||
this.nullValue = useStore(state => state.nullValue, {
|
||||
onUpdate: nullValue => {
|
||||
this.nullValue = nullValue;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,6 +92,10 @@ describe("connecting a component to store", () => {
|
||||
store.dispatch("setTrue");
|
||||
await nextTick();
|
||||
expect(fixture.innerHTML).toBe("<div><span>ok</span></div>");
|
||||
|
||||
store.dispatch("setNotNull");
|
||||
await nextTick();
|
||||
expect(fixture.innerHTML).toBe("<div><span>ok</span><span>not null</span></div>");
|
||||
});
|
||||
|
||||
test("map works on the result of useStore when the resulting array changes for a bigger one", async () => {
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
/**
|
||||
* We can only make one test per file, since the debug tool modify in place
|
||||
* the owl object in a way that is difficult to undo.
|
||||
*/
|
||||
|
||||
import { debugOwl } from "../../tools/debug";
|
||||
import * as owl from "../../src/index";
|
||||
|
||||
import { Component, Env } from "../../src/component/component";
|
||||
import { xml } from "../../src/tags";
|
||||
import { makeTestFixture, makeTestEnv } from "../helpers";
|
||||
|
||||
let fixture: HTMLElement = makeTestFixture();
|
||||
let env: Env = makeTestEnv();
|
||||
Component.env = env;
|
||||
|
||||
debugOwl(owl, { logScheduler: true });
|
||||
|
||||
test("log a sub component with non stringifiable props", async () => {
|
||||
const steps: string[] = [];
|
||||
const log = console.log;
|
||||
console.log = arg => steps.push(arg);
|
||||
|
||||
class Child extends Component<any, any> {
|
||||
static template = xml`<span><t t-esc="props.obj.val"/></span>`;
|
||||
}
|
||||
|
||||
const circularObject: any = { val: 1 };
|
||||
circularObject.circularObject = circularObject;
|
||||
|
||||
class Parent extends Component<any, any> {
|
||||
static template = xml`<div><Child obj="obj"/></div>`;
|
||||
static components = { Child };
|
||||
obj = circularObject;
|
||||
}
|
||||
|
||||
const parent = new Parent(null, {});
|
||||
await parent.mount(fixture);
|
||||
parent.unmount();
|
||||
|
||||
expect(steps).toEqual([
|
||||
"[OWL_DEBUG] Parent<id=1> constructor, props={}",
|
||||
"[OWL_DEBUG] Parent<id=1> mount",
|
||||
"[OWL_DEBUG] Parent<id=1> willStart",
|
||||
"[OWL_DEBUG] scheduler: start running tasks queue",
|
||||
"[OWL_DEBUG] Parent<id=1> rendering template",
|
||||
"[OWL_DEBUG] Child<id=2> constructor, props=<JSON error>",
|
||||
"[OWL_DEBUG] Child<id=2> willStart",
|
||||
"[OWL_DEBUG] Child<id=2> rendering template",
|
||||
"[OWL_DEBUG] Child<id=2> mounted",
|
||||
"[OWL_DEBUG] Parent<id=1> mounted",
|
||||
"[OWL_DEBUG] scheduler: stop running tasks queue",
|
||||
"[OWL_DEBUG] Parent<id=1> willUnmount",
|
||||
"[OWL_DEBUG] Child<id=2> willUnmount"
|
||||
]);
|
||||
console.log = log;
|
||||
});
|
||||
+6
-1
@@ -35,7 +35,12 @@
|
||||
});
|
||||
|
||||
function toStr(obj) {
|
||||
let str = JSON.stringify(obj || {});
|
||||
let str;
|
||||
try {
|
||||
str = JSON.stringify(obj || {});
|
||||
} catch (e) {
|
||||
str = "<JSON error>";
|
||||
}
|
||||
if (str.length > 200) {
|
||||
str = str.slice(0, 200) + "...";
|
||||
}
|
||||
|
||||
@@ -455,7 +455,7 @@ const actions = {
|
||||
todo.completed = !todo.completed;
|
||||
},
|
||||
clearCompleted({ state, dispatch }) {
|
||||
for (let todo of state.todos) {
|
||||
for (let todo of state.todos.slice()) {
|
||||
if (todo.completed) {
|
||||
dispatch("removeTodo", todo.id);
|
||||
}
|
||||
@@ -1089,20 +1089,20 @@ const RESPONSIVE_XML = `<templates>
|
||||
<button>Button!</button>
|
||||
</div>
|
||||
|
||||
<t t-name="maincontent">
|
||||
<FormView />
|
||||
<Chatter />
|
||||
</t>
|
||||
<div t-name="App" class="app" t-att-class="{mobile: env.isMobile, desktop: !env.isMobile}">
|
||||
<t t-set="maincontent">
|
||||
<FormView />
|
||||
<Chatter />
|
||||
</t>
|
||||
<Navbar/>
|
||||
<ControlPanel/>
|
||||
<div class="content-wrapper" t-if="!env.isMobile">
|
||||
<div class="content">
|
||||
<t t-raw="maincontent"/>
|
||||
<t t-call="maincontent"/>
|
||||
</div>
|
||||
</div>
|
||||
<t t-else="">
|
||||
<t t-raw="maincontent"/>
|
||||
<t t-call="maincontent"/>
|
||||
</t>
|
||||
</div>
|
||||
</templates>
|
||||
|
||||
+2
-1
@@ -13,7 +13,8 @@
|
||||
"noImplicitReturns": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"strictPropertyInitialization": true,
|
||||
"strictNullChecks": true
|
||||
"strictNullChecks": true,
|
||||
"declarationDir": "dist/types"
|
||||
},
|
||||
"include": ["src/**/*.ts","src/*.ts"]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user