Compare commits

..

2 Commits

Author SHA1 Message Date
Samuel Degueldre b2cfefd008 [FIX] portal: correctly mount portal content in target created by mount
Previously, blah blah blah
2022-10-19 14:39:44 +02:00
Samuel Degueldre 30d6994836 [FIX] blockdom: correctly reorder children in heterogeneous t-foreach
Currently, the `moveBefore` method on VNodes assumes that the `other`
VNode it receives is of the same type, and that the entire VNode tree
below that other VNode has the exact same structure. While this is
correct in most cases, it breaks down when there is a VToggler somewhere
in the VNode tree, as the structure below a VToggler can be very
different from the structure below another VToggler that was created
from the same compiled code. For example, two iterations of a t-foreach
that contains a <t t-component="..."/> may spawn different components,
and different components obviously have different structures.

One way to fix this is to remove the assumption that the structure of
the `this` block tree in moveBefore is the same as the structure
of the `other` block tree, and instead, always give the concrete DOM
node before which we want to move the current VNode instead of giving it
a VNode and an afterNode as a fallback. One problem with this solution
is that it degrades performance in the "standard" case, where a
t-foreach contains no VToggler anywhere in its block tree, as retrieving
the first concrete DOM node requires calling firstNode() which
recursively traverses the entire tree.

To avoid this performance penalty in the standard case, we opt to only
go down this route whenever we encounter a VToggler when calling
`moveBefore`. This requires that we maintain two separate methods, one
to move a VNode before another VNode of assumed similar structure, which
is basically the current implementation of `moveBefore` for all VNode
types except VToggler, and one implementation that moves a VNode before
a concrete DOM node. This method needs to be implemented for all VNode
types, as all VNode types can be descendants of a VToggler. This method
will only be called from one place: the `moveBeforeVNode` method of the
toggler, which is the point where we realize that the assumption of
identical structure breaks down.

Co-authored-by: Bruno Boi <boi@odoo.com>
2022-10-19 13:11:44 +02:00
64 changed files with 651 additions and 7688 deletions
-47
View File
@@ -1,47 +0,0 @@
{
"env": {
"browser": true,
"node": true,
"es2022": true
},
"parser": "@typescript-eslint/parser",
"plugins": ["@typescript-eslint"],
"parserOptions": {
"sourceType": "module"
},
"root": true,
"rules": {
"no-restricted-globals": ["error", "event", "self"],
"no-const-assign": ["error"],
"no-debugger": ["error"],
"no-dupe-class-members": ["error"],
"no-dupe-keys": ["error"],
"no-dupe-args": ["error"],
"no-dupe-else-if": ["error"],
"no-unsafe-negation": ["error"],
"no-duplicate-imports": ["error"],
"valid-typeof": ["error"],
"@typescript-eslint/no-unused-vars": ["error", { "vars": "all", "args": "none", "ignoreRestSiblings": false, "caughtErrors": "all" }],
"no-restricted-syntax": [
"error",
{
"selector": "MemberExpression[object.name='test'][property.name='only']",
"message": "test.only(...) is forbidden",
},
{
"selector": "MemberExpression[object.name='describe'][property.name='only']",
"message": "describe.only(...) is forbidden",
}
],
},
"globals": {
"describe": true,
"expect": true,
"test": true,
"beforeEach": true,
"beforeAll": true,
"afterEach": true,
"afterAll": true,
"jest": true,
},
}
+1 -2
View File
@@ -22,8 +22,7 @@ jobs:
uses: actions/setup-node@v1 uses: actions/setup-node@v1
with: with:
node-version: ${{ matrix.node-version }} node-version: ${{ matrix.node-version }}
- run: npm ci - run: npm install
- run: npm run test - run: npm run test
- run: npm run check-formatting - run: npm run check-formatting
- run: npm run lint
- run: npm run build - run: npm run build
+3
View File
@@ -14,6 +14,9 @@ npm-debug.log*
yarn-debug.log* yarn-debug.log*
yarn-error.log* yarn-error.log*
package-lock.json
yarn.lock
#ide's #ide's
.vscode .vscode
.idea .idea
+3 -7
View File
@@ -174,9 +174,9 @@ class Root extends Component {
``` ```
The template contains a [`t-foreach`](../reference/templates.md#loops) loop to iterate The template contains a [`t-foreach`](../reference/templates.md#loops) loop to iterate
through the tasks. It can find the `tasks` list from the component, since the rendering through the tasks. It can find the `tasks` list from the component, since the
context contains the properties of the component. Note that we use the `id` of each task component is the rendering context. Note that we use the `id` of each task as a
as a `t-key`, which is very common. There are two css classes: `task-list` and `task`, `t-key`, which is very common. There are two css classes: `task-list` and `task`,
that we will use in the next section. that we will use in the next section.
Finally, notice the use of the `t-att-checked` attribute: Finally, notice the use of the `t-att-checked` attribute:
@@ -502,10 +502,6 @@ deleteTask(task) {
Notice that the `onDelete` prop is defined with a `.bind` suffix: this is a special Notice that the `onDelete` prop is defined with a `.bind` suffix: this is a special
suffix that makes sure the function callback is bound to the component. suffix that makes sure the function callback is bound to the component.
Notice also that we have two functions named `deleteTask`. The one in the Task
component just delegates the work to the Root component that owns the task list
via the `onDelete` property.
## 10. Using a store ## 10. Using a store
Looking at the code, it is apparent that all the code handling tasks is scattered Looking at the code, it is apparent that all the code handling tasks is scattered
+64 -328
View File
@@ -2,394 +2,130 @@
## Content ## Content
- [Introduction](#introduction) - [Overview](#overview)
- [`useState`](#usestate) - [`useState`](#usestate)
- [`reactive`](#reactive) - [`reactive`](#reactive)
- [`Escape hatches`](#escape-hatches) - [`markRaw`](#markraw)
- [`Advanced usage`](#advanced-usage) - [`toRaw`](#toraw)
## Introduction ## Overview
Reactivity is a big topic in javascript frameworks. The goal is to provide a Reactivity is a big topic in javascript frameworks. The goal is to provide a
simple way to manipulate state, in such a way that the interface updates automatically simple way to manipulate state, in such a way that the interface automatically
according to state changes, and to do so in a performant manner. update accordingly to state changes. Also, we obviously want this to happen in
a performant way.
To this end, Owl provides a proxy-based reactivity system, based on the `reactive` primitive. To solve this issue, Owl provides two reactivity primitives:
The `reactive` function takes an object as a first argument, and an optional callback as its second
argument, it returns a proxy of the object. This proxy tracks what properties are read - `reactive`, which returns a proxy to its first argument, and tracks all read/update
through the proxy, and calls the provided callback whenever one of these properties is changed operation going through it,
through any reactive version of the same object. It does so in depth, by returning reactive versions - `useState`: a hook, that internally uses `reactive`, and is linked to its
of the subobjects when they are read. owner component: any read operation will be tracked (key by key), and any
updates to these tracked values will cause the component to be rerendered.
Most of the time, the `useState` hook is the best solution.
Since version 2.0, Owl applies the fine grained reactivity at the component
level: reactive objects received as props are automatically subscribed to by the
component, so Owl can track which part of these props are consumed by each
component, and is therefore able to only rerender the impacted components.
## `useState` ## `useState`
While the `reactive` primitive is very powerful, its usage in components follow a very standard pattern: Let us start by an example of how `useState` could be used:
components want to be rerendered when part of the state which they depend on for rendering changes. To
this end, owl provides a standard hook: `useState`. To put it simply, this hook simply calls reactive
with the provided object, and the current component's render function as its callback. This will cause
it to rerender whenever any part of the state object that has been read by this component is modified.
Here is a simple example of how `useState` can be used:
```js ```js
class Counter extends Component { class Counter extends Component {
static template = xml` static template = xml`
<div t-on-click="() => this.state.value++"> <div t-on-click="increment">
<t t-esc="state.value"/> <t t-esc="state.value"/>
</div>`; </div>`;
setup() { setup() {
this.state = useState({ value: 0 }); this.state = useState({ value: 0 });
} }
}
```
This component reads `state.value` when it renders, subscribing it to changes to that key. Whenever increment() {
the value changes, Owl will update the component. Note that there is nothing special about the this.state.value++;
`state` property, you can name your state variables whatever you want, and you can have multiple of
them on the same component if it makes sense to do so. This also allows `useState` to be used in custom
hooks that may require state that is specific to that hook.
### Reactive props
Since version 2.0, Owl renders are no longer "deep" by default: a component is only rerendered by its
parent if its props have changed (using a simple equality test). What if the contents of a props have
changed in a deeper property? If that prop is reactive, owl will rerender the child components that
need to be updated automatically, and only those components, it does so by reobserving reactive
objects passed as props to components. Consider the following example:
```js
class Counter extends Component {
static template = xml`
<div t-on-click="() => props.state.value++">
<t t-esc="props.state.value"/>
</div>`;
}
class Parent extends Component {
static template = xml`
<Counter state="this.state"/>
<button t-on-click="() => this.state.value = 0">Reset counter</button>
<button t-on-click="() => this.state.test++" t-esc="this.state.test"/>`;
setup() {
this.state = useState({ value: 0, test: 1 });
} }
} }
``` ```
When clicking on the counter button, only the Counter rerenders, because the Parent has never read If one were to use a simple state object, Owl would not be aware that the value
the "value" key in the state. When clicking on the "Reset Counter" button, the same thing happens: was changed and that the component should be rerendered. With the `useState`
only the Counter component rerenders. What matters is not _where_ the state is updated, but which hook, `this.state` is now a reactive object, so this component works as expected.
parts of the state are updated, and which components depend on them. This is achieved by Owl by
automatically calling `useState` on reactive objects passed as props to a child component.
When clicking on the last button, the parent is rerendered, but the child does not care about the
`test` key: it has not read it. The props that we give it (`this.state`) have also not changed,
as such, the parent updates but the child doesn't.
For most day-to-day operations, `useState` should cover all of your needs. If
you are curious about more advanced use cases and technical details, read on.
### Debugging subscriptions
Owl provides a way to show which reactive objects and keys a component is subscribed to: you can
look at `component.__owl__.subscriptions`. Note that this is on the internal `__owl__` field, and
should not be used in any type of production code as the name of this property or any of its properties
or methods are subject to change at any point, even in stable versions of Owl, and may become available
only in debug mode in the future.
## `reactive` ## `reactive`
The `reactive` function is the basic reactivity primitive. It takes an object The `reactive` function is the basic reactivity primitive. It takes an object
or an array as first argument, and optionally, a function as the second argument. or an array as first argument, and optionally, a function as the second argument.
The function is called whenever any tracked value is updated. The function will be called whenever any tracked value is updated.
```js ```js
const obj = reactive({ a: 1 }, () => console.log("changed")); const obj = reactive({ a: 1 }, () => console.log("changed"));
obj.a = 2; // does not log anything: the 'a' key has not been read yet obj.a = 2; // does not log anything: the 'a' key was not read
console.log(obj.a); // logs 2 and reads the 'a' key => it is now tracked console.log(obj.a); // log 2, and reads the 'a' key => it is now tracked
obj.a = 3; // logs 'changed' because we updated a tracked value obj.a = 3; // log 'changed' because we updated a tracked value
``` ```
An important property of reactive objects is that they can be reobserved: this An important property of reactive objects is that they can be reobserved: this
will create an independent proxy that tracks another set of keys: will create an independant proxy that tracks another set of keys:
```js ```js
const obj1 = reactive({ a: 1, b: 2 }, () => console.log("observer 1")); const obj1 = reactive({ a: 1, b: 2 }, () => console.log("observer 1"));
const obj2 = reactive(obj1, () => console.log("observer 2")); const obj2 = reactive(obj1, () => console.log("observer 2"));
console.log(obj1.a); // logs 1, and reads the 'a' key => it is now tracked by observer 1 console.log(obj1.a); // log 1, and reads the 'a' key => it is now tracked by observer 1
console.log(obj2.b); // logs 2, and 'b' is now tracked by observer 2 console.log(obj1.b); // log 2, and 'b' is now tracked by observer 1
obj2.a = 3; // only logs 'observer1', because observer2 does not track a console.log(obj2.b); // log 2, and 'b' is now tracked by observer 1
obj2.b = 3; // only logs 'observer2', because observer1 does not track b obj2.a = 3; // log 'observer1', because observer2 does not track a
console.log(obj2.a, obj1.b); // logs 3 and 3, while the object is observed independently, it is still a single object obj2.b = 3; // log 'observer1' and 'observer2'
``` ```
Because `useState` returns a normal reactive object, it is possible to call `reactive` on the result Obviously, one can use `reactive` on the result of a `useState` if wanted, this
of a `useState` to observe changes to that object while outside the context of a component, or to is the proper way to watch for some state changes.
call `useState` on reactive objects created outside of components. In those cases, one needs to be
careful with regards to the lifetime of those reactive objects, as holding references to these
objects may prevent garbage collection of the component and its data even if Owl has destroyed it.
### Subscriptions are ephemereal ## `markRaw`
Subscription to state changes are ephemereal, whenever an observer is notified that a state object Marks an object so that it is ignored by the reactivity system. This function returns its argument.
has changed, all of its subscriptions are cleared, meaning that if it still cares about it, it
should read the properties it cares about again. For example:
```js ```js
const obj = reactive({ a: 1 }, () => console.log("observer called")); const someObject = markRaw(...);
console.log(obj.a); // logs 1, and reads the 'a' key => it is now tracked by the observer
obj.a = 3; // logs 'observer1' and clears the subscriptions of the observer
obj.a = 4; // doesn't log anything, the key is no longer observed
```
This may seem counter-intuitive, but it makes perfect sense in the context of components:
```js
class DoubleCounter extends Component {
static template = xml`
<t t-esc="state.selected + ': ' + state[state.selected].value"/>
<button t-on-click="() => this.state.count1++">increment count 1</button>
<button t-on-click="() => this.state.count2++">increment count 2</button>
<button t-on-click="changeCounter">Switch counter</button>
`;
setup() {
this.state = useState({ selected: "count1", count1: 0, count2: 0 });
}
changeCounter() {
this.state.selected = this.state.selected === "count1" ? "count2" : "count1";
}
}
```
In this component, if we increment the value of the second counter, the component will not rerender,
which makes sense as rerendering will have no effect, as the second counter is not displayed. If we
toggle the component to display the second counter, we now no longer want the component to rerender
when the value of the first counter changes, and this is what happens: a component only rerenders
when there are changes to pieces of state that have been read during or after the previous render.
If a piece of state has not been read in the last render, we know that its value won't influence the
rendered output, and so we can ignore it.
### reactive `Map` and `Set`
The reactivity system has special support built-in for the standard container types `Map` and `Set`.
They behave like one would expect: reading a key subscribes the observer to that key, adding or
removing an item to them notifies observers that have used any of the iterators on that reactive
object, such as `.entries()` or `.keys()`, likewise with clearing them.
## Escape hatches
Sometimes, it is desirable to bypass the reactivity system. Creating proxies when interacting with
reactive objects is expensive, and while on the whole, the performance benefit that we get by
rerendering only the parts of the interface that need it outweighs that cost, in some cases, we want
to be able to opt out of creating them in the first place. This is the purpose of `markRaw`:
### `markRaw`
Marks an object so that it is ignored by the reactivity system, meaning that if this object is ever
part of a of a reactive object, it will be returned as is, and no keys in that object will be
observed.
```js
const someObject = markRaw({ b: 1 });
const state = useState({ const state = useState({
a: 1, a: 1,
obj: someObject, obj: someObject
}); });
console.log(state.obj.b); // attempt to subscribe to the "b" key in someObject // here, state.obj === someObject
state.obj.b = 2; // No rerender will occur here
console.log(someObject === state.obj); // true
``` ```
This is useful in some rare cases. One such example would be if you want to use an array of objects This is useful in some rare cases. For example, some complex and large object such
that is potentially large to render a list, but those objects are known to be immutable: that going through the reactivity system may cause a non trivial performance slowdown.
```js
this.items = useState([
{ label: "some text", value: 42 },
// ... 1000 total objects
]);
```
in the template:
```xml
<t t-foreach="items" t-as="item" t-key="item.label" t-esc="item.label + item.value"/>
```
Here, on every render, we go and read one thousand keys from a reactive object, which causes
one thousand reactive objects to be created. If we know that the content of these objects
cannot change, this is wasted work. If instead all of these objects are marked as raw, we avoid
all of this work while keeping the ability to lean on the reactivity to track the presence and
identity of these objects:
```js
this.items = useState([
markRaw({ label: "some text", value: 42 }),
// ... 1000 total objects
]);
```
However, use this function with caution: this is an escape hatch from the reactivity However, use this function with caution: this is an escape hatch from the reactivity
system, and as such, using it may cause subtle and unintended issues! For example: system, and as such, using it may cause subtle and unintended issues!
## `toRaw`
Given a reactive object, this function returns the underlying, non-reactive,
corresponding object.
```js ```js
// This will cause a rerender // in setup
this.items.push(markRaw({ label: "another label", value: 1337 })); const state = useState({ value: 1 });
// THIS WILL NOT CAUSE A RENDER! // later:
this.items[17].value = 3; const rawState = toRaw(this.state);
// The UI is now desynced from component's state until the next render caused by something else rawState.value = 3; // will NOT be picked up by the reactivity system!!!
``` ```
In short: only use `markRaw` if your application is slowing down noticeably and profiling reveals Here again, this is useful in some situations where we want to explicitely bypass
that a lot of time is spent creating useless reactive objects. Owl, but using this function means that the responsability of coordinating
state update is given to the user code, instead of Owl. Subtle bugs may arise!
### `toRaw` Also, normal (non-reactive objects) will be directly returned by `toRaw`:
While `markRaw` marks an object so that it is never made reactive, `toRaw` takes an object and
returns the underlying non-reactive object. It can be useful in some niche cases. In particular,
because the reactivity system returns a proxy, the returned object does not compare equal to the
original object:
```js ```js
const obj = {}; const obj = { a: 1 };
const reactiveObj = reactive(obj); console.log(toRaw(obj) === obj); // true
console.log(obj === reactiveObj); // false
console.log(obj === toRaw(reactiveObj)); // true
``` ```
It can also be useful during debugging, as unfolding proxies recursively in debuggers can be confusing.
## Advanced usage
The following is a collection of small snippets that leverage the reactivity system in
"non-standard" ways to help you understand its power and where using it might make your code simpler.
### Notification manager
Showing notifications is a pretty common need in web applications, you may want to show a
notification from any other component within the application, and the notifications should stack on
top of one another regardless of which component spawned them, here is how we can leverage the
reactivity to accomplish this:
```js
let notificationId = 1;
const notifications = reactive({});
class NotificationContainer extends Component {
static template = xml`
<t t-foreach="notifications" t-as="notification" t-key="notification_key" t-esc="notification"/>
`;
setup() {
this.notifications = useState(notifications);
}
}
export function addNotification(label) {
const id = notificationId++;
notifications[id] = label;
return () => {
delete notifications[id];
};
}
```
Here, the `notifications` variable is a reactive object. Notice how we didn't give `reactive` a
callback: this is because in this case, all we care about is that adding or removing notifications
in the `addNotification` function goes through the reactivity system. The `NotificationContainer`
component reobserves this object with `useState`, and is updated whenever notifications are
added or removed.
### Store
Centralizing application state is a pretty common want/need in web applications. Because of the way
the reactivity system works, you can treat any reactive object as a store, and if you call `useState`
on it, components automatically observe only the part of the store that they're interested in:
```js
export const store = reactive({
list: [],
add(item) {
this.list.push(item);
},
});
export function useStore() {
return useState(store);
}
```
In any component:
```js
import { useStore } from "./store";
class List extends Component {
static template = xml`
<t t-foreach="store.list" t-as="item" t-key="item" t-esc="item"/>
`;
setup() {
this.store = useStore();
}
}
```
Anywhere in the application:
```js
import { store } from "./store";
// Will cause any instance of the List component in the app to update
store.add("New list item!");
```
Notice how we can make objects with methods into reactive objects, and when these methods are used
to mutate the store contents, it works as expected. And while stores are generally one-off objects,
it is entirely possible to make class instances reactive:
```js
class Store {
list = [];
add(item) {
this.list.push(item);
}
}
// Essentially equivalent to the previous code
export const store = reactive(new Store());
```
Which can be useful to unit test the class separately.
### Local storage synchronization
Sometimes, you want to persist some state accross reloads, you can do this by storing it in the
`localStorage`, but what if you want to update the `localStorage` item every time the state changes,
so that you don't have to manually synchronize the states? Well, you can use the reactivity system
to write a custom hook that will do that for you:
```js
function useStoredState(key, initialState) {
const state = JSON.parse(localStorage.getItem(key)) || initialState;
const store = (obj) => localStorage.setItem(key, JSON.stringify(obj));
const reactiveState = reactive(state, () => store(reactiveState));
store(reactiveState);
return useState(state);
}
class MyComponent extends Component {
setup() {
this.state = useStoredState("MyComponent.state", { value: 1 });
}
}
```
One important thing to notice is that both times we call `store`, we call it with `reactiveState`,
not `state`: we need `store` to read the keys through a reactive object for it to correctly
subscribe to state changes. Notice also that we call `store` the first time by hand, as otherwise it
will not be subscribed to anything, and no amount of change in the object will cause the reactive
callback to be invoked.
+4 -3
View File
@@ -115,7 +115,7 @@ It is useful to explain the various rules that apply on these expressions:
<div><p t-if="console.log(1)">NOT valid</p></div> <div><p t-if="console.log(1)">NOT valid</p></div>
``` ```
2. it can use anything in the rendering context (which typically contains the properties of the component): 2. it can use anything in the rendering context (typically, the component):
```xml ```xml
<p t-if="user.birthday === today()">Happy bithday!</p> <p t-if="user.birthday === today()">Happy bithday!</p>
@@ -541,8 +541,9 @@ This can be used to define variables scoped to a sub template:
``` ```
Note: by default, the rendering context for a sub template is simply the current Note: by default, the rendering context for a sub template is simply the current
rendering context. However, it may be useful to be able to specify a specific rendering context (so, the current component). However, it may be useful to be
object as context. This can be done by using the `t-call-context` directive: able to specify a specific object as context. This can be done by using the
`t-call-context` directive:
```xml ```xml
<t t-call="other-template" t-call-context="obj"/> <t t-call="other-template" t-call-context="obj"/>
-5711
View File
File diff suppressed because it is too large Load Diff
+7 -7
View File
@@ -1,6 +1,6 @@
{ {
"name": "@odoo/owl", "name": "@odoo/owl",
"version": "2.0.7", "version": "2.0.0",
"description": "Odoo Web Library (OWL)", "description": "Odoo Web Library (OWL)",
"main": "dist/owl.cjs.js", "main": "dist/owl.cjs.js",
"module": "dist/owl.es.js", "module": "dist/owl.es.js",
@@ -25,7 +25,6 @@
"playground:watch": "npm-run-all --parallel playground:serve \"build:* -- --watch\"", "playground:watch": "npm-run-all --parallel playground:serve \"build:* -- --watch\"",
"prettier": "prettier {src/*.ts,src/**/*.ts,tests/*.ts,tests/**/*.ts,doc/*.md,doc/**/*.md} --write", "prettier": "prettier {src/*.ts,src/**/*.ts,tests/*.ts,tests/**/*.ts,doc/*.md,doc/**/*.md} --write",
"check-formatting": "prettier {src/*.ts,src/**/*.ts,tests/*.ts,tests/**/*.ts,doc/*.md,doc/**/*.md} --check", "check-formatting": "prettier {src/*.ts,src/**/*.ts,tests/*.ts,tests/**/*.ts,doc/*.md,doc/**/*.md} --check",
"lint": "eslint src/**/*.ts tests/**/*.ts",
"publish": "npm run build && npm publish", "publish": "npm run build && npm publish",
"release": "node tools/release.js", "release": "node tools/release.js",
"compile_templates": "node tools/compile_xml.js" "compile_templates": "node tools/compile_xml.js"
@@ -43,25 +42,26 @@
"devDependencies": { "devDependencies": {
"@types/jest": "^27.0.1", "@types/jest": "^27.0.1",
"@types/node": "^14.11.8", "@types/node": "^14.11.8",
"@typescript-eslint/eslint-plugin": "5.48.1",
"@typescript-eslint/parser": "5.48.1",
"chalk": "^3.0.0", "chalk": "^3.0.0",
"cpx": "^1.5.0",
"current-git-branch": "^1.1.0", "current-git-branch": "^1.1.0",
"eslint": "8.31.0", "git-rev-sync": "^1.12.0",
"git-rev-sync": "^3.0.2",
"github-api": "^3.3.0", "github-api": "^3.3.0",
"jest": "^27.1.0", "jest": "^27.1.0",
"jest-diff": "^27.3.1", "jest-diff": "^27.3.1",
"jest-environment-jsdom": "^27.1.0", "jest-environment-jsdom": "^27.1.0",
"live-server": "^1.2.1",
"npm-run-all": "^4.1.5", "npm-run-all": "^4.1.5",
"prettier": "2.4.1", "prettier": "2.4.1",
"rollup": "^2.56.3", "rollup": "^2.56.3",
"rollup-plugin-dts": "^4.2.2", "rollup-plugin-dts": "^4.2.2",
"rollup-plugin-terser": "^7.0.2", "rollup-plugin-terser": "^7.0.2",
"rollup-plugin-typescript2": "^0.31.1", "rollup-plugin-typescript2": "^0.31.1",
"sass": "^1.16.1",
"source-map-support": "^0.5.10", "source-map-support": "^0.5.10",
"ts-jest": "^27.0.5", "ts-jest": "^27.0.5",
"typescript": "4.5.2" "typescript": "4.5.2",
"uglify-es": "^3.3.9"
}, },
"jest": { "jest": {
"testEnvironment": "jsdom", "testEnvironment": "jsdom",
+73 -118
View File
@@ -15,7 +15,6 @@ import {
ASTLog, ASTLog,
ASTMulti, ASTMulti,
ASTSlot, ASTSlot,
ASTSlotDefinition,
ASTTCall, ASTTCall,
ASTTCallBlock, ASTTCallBlock,
ASTTEsc, ASTTEsc,
@@ -138,14 +137,12 @@ interface Context {
index: number | string; index: number | string;
forceNewBlock: boolean; forceNewBlock: boolean;
preventRoot?: boolean; preventRoot?: boolean;
ignoreRoot?: boolean;
isLast?: boolean; isLast?: boolean;
translate: boolean; translate: boolean;
tKeyExpr: string | null; tKeyExpr: string | null;
nameSpace?: string; nameSpace?: string;
tModelSelectedExpr?: string; tModelSelectedExpr?: string;
ctxVar?: string; ctxVar?: string;
slotVar?: string;
} }
function createContext(parentCtx: Context, params?: Partial<Context>): Context { function createContext(parentCtx: Context, params?: Partial<Context>): Context {
@@ -158,7 +155,6 @@ function createContext(parentCtx: Context, params?: Partial<Context>): Context {
tKeyExpr: null, tKeyExpr: null,
nameSpace: parentCtx.nameSpace, nameSpace: parentCtx.nameSpace,
tModelSelectedExpr: parentCtx.tModelSelectedExpr, tModelSelectedExpr: parentCtx.tModelSelectedExpr,
slotVar: parentCtx.slotVar
}, },
params params
); );
@@ -195,7 +191,7 @@ class CodeTarget {
let result: string[] = []; let result: string[] = [];
result.push(`function ${this.name}(ctx, node, key = "") {`); result.push(`function ${this.name}(ctx, node, key = "") {`);
if (this.hasRef) { if (this.hasRef) {
result.push(` const refs = this.__owl__.refs;`); result.push(` const refs = ctx.__owl__.refs;`);
for (let name in this.refInfo) { for (let name in this.refInfo) {
const [id, expr] = this.refInfo[name]; const [id, expr] = this.refInfo[name];
result.push(` const ${id} = ${expr};`); result.push(` const ${id} = ${expr};`);
@@ -298,7 +294,7 @@ export class CodeGenerator {
for (let block of this.blocks) { for (let block of this.blocks) {
if (block.dom) { if (block.dom) {
let xmlString = block.asXmlString(); let xmlString = block.asXmlString();
xmlString = xmlString.replace(/\\/g, "\\\\").replace(/`/g, "\\`"); xmlString = xmlString.replace(/`/g, "\\`");
if (block.dynamicTagName) { if (block.dynamicTagName) {
xmlString = xmlString.replace(/^<\w+/, `<\${tag || '${block.dom.nodeName}'}`); xmlString = xmlString.replace(/^<\w+/, `<\${tag || '${block.dom.nodeName}'}`);
xmlString = xmlString.replace(/\w+>$/, `\${tag || '${block.dom.nodeName}'}>`); xmlString = xmlString.replace(/\w+>$/, `\${tag || '${block.dom.nodeName}'}>`);
@@ -362,7 +358,7 @@ export class CodeGenerator {
): BlockDescription { ): BlockDescription {
const hasRoot = this.target.hasRoot; const hasRoot = this.target.hasRoot;
const block = new BlockDescription(this.target, type); const block = new BlockDescription(this.target, type);
if (!hasRoot && !ctx.preventRoot && !ctx.ignoreRoot) { if (!hasRoot && !ctx.preventRoot) {
this.target.hasRoot = true; this.target.hasRoot = true;
block.isRoot = true; block.isRoot = true;
} }
@@ -388,9 +384,6 @@ export class CodeGenerator {
blockExpr = `toggler(${ctx.tKeyExpr}, ${blockExpr})`; blockExpr = `toggler(${ctx.tKeyExpr}, ${blockExpr})`;
} }
if (ctx.ignoreRoot) {
return;
}
if (block.isRoot && !ctx.preventRoot) { if (block.isRoot && !ctx.preventRoot) {
if (this.target.on) { if (this.target.on) {
blockExpr = this.wrapWithEventCatcher(blockExpr, this.target.on); blockExpr = this.wrapWithEventCatcher(blockExpr, this.target.on);
@@ -472,8 +465,6 @@ export class CodeGenerator {
return this.compileLog(ast, ctx); return this.compileLog(ast, ctx);
case ASTType.TSlot: case ASTType.TSlot:
return this.compileTSlot(ast, ctx); return this.compileTSlot(ast, ctx);
case ASTType.TSetSlot:
return this.compileTSetSlot(ast, ctx);
case ASTType.TTranslation: case ASTType.TTranslation:
return this.compileTTranslation(ast, ctx); return this.compileTTranslation(ast, ctx);
case ASTType.TPortal: case ASTType.TPortal:
@@ -615,6 +606,39 @@ export class CodeGenerator {
} }
} }
// event handlers
for (let ev in ast.on) {
const name = this.generateHandlerCode(ev, ast.on[ev]);
const idx = block!.insertData(name, "hdlr");
attrs[`block-handler-${idx}`] = ev;
}
// t-ref
if (ast.ref) {
this.target.hasRef = true;
const isDynamic = INTERP_REGEXP.test(ast.ref);
if (isDynamic) {
const str = replaceDynamicParts(ast.ref, (expr) => this.captureExpression(expr, true));
const idx = block!.insertData(`(el) => refs[${str}] = el`, "ref");
attrs["block-ref"] = String(idx);
} else {
let name = ast.ref;
if (name in this.target.refInfo) {
// ref has already been defined
this.helpers.add("multiRefSetter");
const info = this.target.refInfo[name];
const index = block!.data.push(info[0]) - 1;
attrs["block-ref"] = String(index);
info[1] = `multiRefSetter(refs, \`${name}\`)`;
} else {
let id = generateId("ref");
this.target.refInfo[name] = [id, `(el) => refs[\`${name}\`] = el`];
const index = block!.data.push(id) - 1;
attrs["block-ref"] = String(index);
}
}
}
// t-model // t-model
let tModelSelectedExpr; let tModelSelectedExpr;
if (ast.model) { if (ast.model) {
@@ -641,15 +665,7 @@ export class CodeGenerator {
let idx: number; let idx: number;
if (specialInitTargetAttr) { if (specialInitTargetAttr) {
let targetExpr = targetAttr in attrs && `'${attrs[targetAttr]}'`; idx = block!.insertData(`${fullExpression} === '${attrs[targetAttr]}'`, "attr");
if (!targetExpr && ast.attrs) {
// look at the dynamic attribute counterpart
const dynamicTgExpr = ast.attrs[`t-att-${targetAttr}`];
if (dynamicTgExpr) {
targetExpr = compileExpr(dynamicTgExpr);
}
}
idx = block!.insertData(`${fullExpression} === ${targetExpr}`, "attr");
attrs[`block-attribute-${idx}`] = specialInitTargetAttr; attrs[`block-attribute-${idx}`] = specialInitTargetAttr;
} else if (hasDynamicChildren) { } else if (hasDynamicChildren) {
const bValueId = generateId("bValue"); const bValueId = generateId("bValue");
@@ -669,41 +685,6 @@ export class CodeGenerator {
attrs[`block-handler-${idx}`] = eventType; attrs[`block-handler-${idx}`] = eventType;
} }
// event handlers
for (let ev in ast.on) {
const name = this.generateHandlerCode(ev, ast.on[ev]);
const idx = block!.insertData(name, "hdlr");
attrs[`block-handler-${idx}`] = ev;
}
// t-ref
if (ast.ref) {
this.target.hasRef = true;
const isDynamic = INTERP_REGEXP.test(ast.ref);
if (isDynamic) {
this.helpers.add("singleRefSetter");
const str = replaceDynamicParts(ast.ref, (expr) => this.captureExpression(expr, true));
const idx = block!.insertData(`singleRefSetter(refs, ${str})`, "ref");
attrs["block-ref"] = String(idx);
} else {
let name = ast.ref;
if (name in this.target.refInfo) {
// ref has already been defined
this.helpers.add("multiRefSetter");
const info = this.target.refInfo[name];
const index = block!.data.push(info[0]) - 1;
attrs["block-ref"] = String(index);
info[1] = `multiRefSetter(refs, \`${name}\`)`;
} else {
let id = generateId("ref");
this.helpers.add("singleRefSetter");
this.target.refInfo[name] = [id, `singleRefSetter(refs, \`${name}\`)`];
const index = block!.data.push(id) - 1;
attrs["block-ref"] = String(index);
}
}
}
const dom = xmlDoc.createElement(ast.tag); const dom = xmlDoc.createElement(ast.tag);
for (const [attr, val] of Object.entries(attrs)) { for (const [attr, val] of Object.entries(attrs)) {
if (!(attr === "class" && val === "")) { if (!(attr === "class" && val === "")) {
@@ -1123,7 +1104,7 @@ export class CodeGenerator {
if (suffix === "bind") { if (suffix === "bind") {
this.helpers.add("bind"); this.helpers.add("bind");
name = _name; name = _name;
value = `bind(this, ${value || undefined})`; value = `bind(ctx, ${value || undefined})`;
} else { } else {
throw new OwlError("Invalid prop suffix"); throw new OwlError("Invalid prop suffix");
} }
@@ -1153,38 +1134,52 @@ export class CodeGenerator {
const props: string[] = ast.props ? this.formatPropObject(ast.props) : []; const props: string[] = ast.props ? this.formatPropObject(ast.props) : [];
// slots // slots
let slotVar: string = ""; let slotDef: string = "";
if (ast.body) { if (ast.slots) {
slotVar = generateId("slots"); let ctxStr = "ctx";
this.helpers.add("markRaw"); if (this.target.loopLevel || !this.hasSafeContext) {
if (ast.body.type === ASTType.TSetSlot) { ctxStr = generateId("ctx");
const subCtx = createContext(ctx, { slotVar: undefined, ignoreRoot: true }); this.helpers.add("capture");
const slotInfo = this.compileAST(ast.body, subCtx); this.define(ctxStr, `capture(ctx)`);
this.target.addLine(`let ${slotVar} = markRaw({'${ast.body.name}': ${slotInfo}});`);
} else {
this.target.addLine(`let ${slotVar} = markRaw({});`);
const subCtx = createContext(ctx, { slotVar, ignoreRoot: true });
this.compileAST(ast.body, subCtx);
} }
let slotStr: string[] = [];
for (let slotName in ast.slots) {
const slotAst = ast.slots[slotName];
const params = [];
if (slotAst.content) {
const name = this.compileInNewTarget("slot", slotAst.content, ctx, slotAst.on);
params.push(`__render: ${name}, __ctx: ${ctxStr}`);
}
const scope = ast.slots[slotName].scope;
if (scope) {
params.push(`__scope: "${scope}"`);
}
if (ast.slots[slotName].attrs) {
params.push(...this.formatPropObject(ast.slots[slotName].attrs!));
}
const slotInfo = `{${params.join(", ")}}`;
slotStr.push(`'${slotName}': ${slotInfo}`);
}
slotDef = `{${slotStr.join(", ")}}`;
} }
if (slotVar && !(ast.dynamicProps || hasSlotsProp)) { if (slotDef && !(ast.dynamicProps || hasSlotsProp)) {
props.push(`slots: ${slotVar}`); this.helpers.add("markRaw");
props.push(`slots: markRaw(${slotDef})`);
} }
let propString = this.getPropString(props, ast.dynamicProps); let propString = this.getPropString(props, ast.dynamicProps);
let propVar: string; let propVar: string;
if ((slotVar && (ast.dynamicProps || hasSlotsProp)) || this.dev) { if ((slotDef && (ast.dynamicProps || hasSlotsProp)) || this.dev) {
propVar = generateId("props"); propVar = generateId("props");
this.define(propVar!, propString); this.define(propVar!, propString);
propString = propVar!; propString = propVar!;
} }
if (slotVar && (ast.dynamicProps || hasSlotsProp)) { if (slotDef && (ast.dynamicProps || hasSlotsProp)) {
this.helpers.add("markRaw"); this.helpers.add("markRaw");
this.addLine(`${propVar!}.slots = markRaw(Object.assign(${slotVar}, ${propVar!}.slots))`); this.addLine(`${propVar!}.slots = markRaw(Object.assign(${slotDef}, ${propVar!}.slots))`);
} }
// cmap key // cmap key
@@ -1215,18 +1210,11 @@ export class CodeGenerator {
id, id,
expr: `app.createComponent(${ expr: `app.createComponent(${
ast.isDynamic ? null : expr ast.isDynamic ? null : expr
}, ${!ast.isDynamic}, ${!!ast.body}, ${!!ast.dynamicProps}, ${ }, ${!ast.isDynamic}, ${!!ast.slots}, ${!!ast.dynamicProps}, ${
!ast.props && !ast.dynamicProps !ast.props && !ast.dynamicProps
})`, })`,
}); });
if (ast.isDynamic) {
// If the component class changes, this can cause delayed renders to go
// through if the key doesn't change. Use the component name for now.
// This means that two component classes with the same name isn't supported
// in t-component. We can generate a unique id per class later if needed.
keyArg = `(${expr}).name + ${keyArg}`;
}
let blockExpr = `${id}(${propString}, ${keyArg}, node, this, ${ast.isDynamic ? expr : null})`; let blockExpr = `${id}(${propString}, ${keyArg}, node, this, ${ast.isDynamic ? expr : null})`;
if (ast.isDynamic) { if (ast.isDynamic) {
blockExpr = `toggler(${expr}, ${blockExpr})`; blockExpr = `toggler(${expr}, ${blockExpr})`;
@@ -1258,39 +1246,6 @@ export class CodeGenerator {
return `${name}(${expr}, [${handlers.join(",")}])`; return `${name}(${expr}, [${handlers.join(",")}])`;
} }
compileTSetSlot(ast: ASTSlotDefinition, ctx: Context): string {
let ctxStr = "ctx";
if (this.target.loopLevel || !this.hasSafeContext) {
ctxStr = generateId("ctx");
this.helpers.add("capture");
this.define(ctxStr, `capture(ctx)`);
}
// let slotStr: string[] = [];
// for (let slotName in ast.slots) {
// const slotAst = ast.slots[slotName];
const params = [];
if (ast.content) {
const name = this.compileInNewTarget("slot", ast.content, ctx, ast.on);
params.push(`__render: ${name}.bind(this), __ctx: ${ctxStr}`);
}
// ast.scope
// const scope = ast.slots[slotName].scope;
if (ast.scope) {
params.push(`__scope: "${ast.scope}"`);
}
if (ast.attrs) {
params.push(...this.formatPropObject(ast.attrs!));
}
const slotInfo = `{${params.join(", ")}}`;
if (ctx.slotVar) {
this.target.addLine(`${ctx.slotVar}['${ast.name}'] = ${slotInfo};`)
}
// slotStr.push(`'${slotName}': ${slotInfo}`);
// }
// slotDef = `{${slotStr.join(", ")}}`;
return slotInfo;
}
compileTSlot(ast: ASTSlot, ctx: Context): string { compileTSlot(ast: ASTSlot, ctx: Context): string {
this.helpers.add("callSlot"); this.helpers.add("callSlot");
let { block } = ctx; let { block } = ctx;
@@ -1320,7 +1275,7 @@ export class CodeGenerator {
const scope = this.getPropString(props, dynProps); const scope = this.getPropString(props, dynProps);
if (ast.defaultContent) { if (ast.defaultContent) {
const name = this.compileInNewTarget("defaultContent", ast.defaultContent, ctx); const name = this.compileInNewTarget("defaultContent", ast.defaultContent, ctx);
blockString = `callSlot(ctx, node, ${key}, ${slotName}, ${dynamic}, ${scope}, ${name}.bind(this))`; blockString = `callSlot(ctx, node, ${key}, ${slotName}, ${dynamic}, ${scope}, ${name})`;
} else { } else {
if (dynamic) { if (dynamic) {
let name = generateId("slot"); let name = generateId("slot");
@@ -1370,7 +1325,7 @@ export class CodeGenerator {
}); });
const target = compileExpr(ast.target); const target = compileExpr(ast.target);
const blockString = `${id}({target: ${target},slots: {'default': {__render: ${name}.bind(this), __ctx: ${ctxStr}}}}, key + \`${key}\`, node, ctx, Portal)`; const blockString = `${id}({target: ${target},slots: {'default': {__render: ${name}, __ctx: ${ctxStr}}}}, key + \`${key}\`, node, ctx, Portal)`;
if (block) { if (block) {
this.insertAnchor(block); this.insertAnchor(block);
} }
+1 -1
View File
@@ -28,7 +28,7 @@ import { OwlError } from "../runtime/error_handling";
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
const RESERVED_WORDS = const RESERVED_WORDS =
"true,false,NaN,null,undefined,debugger,console,window,in,instanceof,new,function,return,eval,void,Math,RegExp,Array,Object,Date".split( "true,false,NaN,null,undefined,debugger,console,window,in,instanceof,new,function,return,this,eval,void,Math,RegExp,Array,Object,Date".split(
"," ","
); );
+67 -88
View File
@@ -23,7 +23,6 @@ export const enum ASTType {
TDebug, TDebug,
TLog, TLog,
TSlot, TSlot,
TSetSlot,
TCallBlock, TCallBlock,
TTranslation, TTranslation,
TPortal, TPortal,
@@ -121,9 +120,7 @@ export interface ASTTCall {
context: string | null; context: string | null;
} }
export interface ASTSlotDefinition { interface SlotDefinition {
type: ASTType.TSetSlot;
name: string;
content: AST | null; content: AST | null;
scope: string | null; scope: string | null;
on: EventHandlers | null; on: EventHandlers | null;
@@ -137,8 +134,7 @@ export interface ASTComponent {
dynamicProps: string | null; dynamicProps: string | null;
on: EventHandlers | null; on: EventHandlers | null;
props: { [name: string]: string } | null; props: { [name: string]: string } | null;
body: AST | null; slots: { [name: string]: SlotDefinition } | null;
// slots: { [name: string]: ASTSlotDefinition } | null;
} }
export interface ASTSlot { export interface ASTSlot {
@@ -190,7 +186,6 @@ export type AST =
| ASTTKey | ASTTKey
| ASTComponent | ASTComponent
| ASTSlot | ASTSlot
| ASTSlotDefinition
| ASTTCallBlock | ASTTCallBlock
| ASTLog | ASTLog
| ASTDebug | ASTDebug
@@ -243,7 +238,6 @@ function parseNode(node: Node, ctx: ParsingContext): AST | null {
parseTKey(node, ctx) || parseTKey(node, ctx) ||
parseTTranslation(node, ctx) || parseTTranslation(node, ctx) ||
parseTSlot(node, ctx) || parseTSlot(node, ctx) ||
parseTSetSlot(node, ctx) ||
parseTOutNode(node, ctx) || parseTOutNode(node, ctx) ||
parseComponent(node, ctx) || parseComponent(node, ctx) ||
parseDOMNode(node, ctx) || parseDOMNode(node, ctx) ||
@@ -579,12 +573,12 @@ function parseTCall(node: Element, ctx: ParsingContext): AST | null {
ast.content = [tcall]; ast.content = [tcall];
return ast; return ast;
} }
// if (ast && ast.type === ASTType.TComponent) { if (ast && ast.type === ASTType.TComponent) {
// return { return {
// ...ast, ...ast,
// slots: { default: { content: tcall, scope: null, on: null, attrs: null } }, slots: { default: { content: tcall, scope: null, on: null, attrs: null } },
// }; };
// } }
} }
const body = parseChildren(node, ctx); const body = parseChildren(node, ctx);
@@ -712,8 +706,8 @@ function parseComponent(node: Element, ctx: ParsingContext): AST | null {
const dynamicProps = node.getAttribute("t-props"); const dynamicProps = node.getAttribute("t-props");
node.removeAttribute("t-props"); node.removeAttribute("t-props");
// const defaultSlotScope = node.getAttribute("t-slot-scope"); const defaultSlotScope = node.getAttribute("t-slot-scope");
// node.removeAttribute("t-slot-scope"); node.removeAttribute("t-slot-scope");
let on: ASTComponent["on"] = null; let on: ASTComponent["on"] = null;
let props: ASTComponent["props"] = null; let props: ASTComponent["props"] = null;
@@ -733,17 +727,67 @@ function parseComponent(node: Element, ctx: ParsingContext): AST | null {
} }
} }
let body: ASTComponent["body"] = null; let slots: ASTComponent["slots"] | null = null;
// let slots: ASTComponent["slots"] | null = null;
if (node.hasChildNodes()) { if (node.hasChildNodes()) {
body = parseChildNodes(node, ctx); const clone = <Element>node.cloneNode(true);
if (!node.querySelector('[t-set-slot]')) {
body = {type: ASTType.TSetSlot, name: "default", content: body, on: null, attrs: null, scope: null} // named slots
const slotNodes = Array.from(clone.querySelectorAll("[t-set-slot]"));
for (let slotNode of slotNodes) {
if (slotNode.tagName !== "t") {
throw new OwlError(
`Directive 't-set-slot' can only be used on <t> nodes (used on a <${slotNode.tagName}>)`
);
}
const name = slotNode.getAttribute("t-set-slot")!;
// check if this is defined in a sub component (in which case it should
// be ignored)
let el = slotNode.parentElement!;
let isInSubComponent = false;
while (el !== clone) {
if (el!.hasAttribute("t-component") || el!.tagName[0] === el!.tagName[0].toUpperCase()) {
isInSubComponent = true;
break;
}
el = el.parentElement!;
}
if (isInSubComponent) {
continue;
}
slotNode.removeAttribute("t-set-slot");
slotNode.remove();
const slotAst = parseNode(slotNode, ctx);
let on: SlotDefinition["on"] = null;
let attrs: Attrs | null = null;
let scope: string | null = null;
for (let attributeName of slotNode.getAttributeNames()) {
const value = slotNode.getAttribute(attributeName)!;
if (attributeName === "t-slot-scope") {
scope = value;
continue;
} else if (attributeName.startsWith("t-on-")) {
on = on || {};
on[attributeName.slice(5)] = value;
} else {
attrs = attrs || {};
attrs[attributeName] = value;
}
}
slots = slots || {};
slots[name] = { content: slotAst, on, attrs, scope };
} }
// default slot
const defaultContent = parseChildNodes(clone, ctx);
slots = slots || {};
// t-set-slot="default" has priority over content
if (defaultContent && !slots.default) {
slots.default = { content: defaultContent, on, attrs: null, scope: defaultSlotScope };
}
} }
return { type: ASTType.TComponent, name, isDynamic, dynamicProps, props, body, on }; return { type: ASTType.TComponent, name, isDynamic, dynamicProps, props, slots, on };
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
@@ -777,71 +821,6 @@ function parseTSlot(node: Element, ctx: ParsingContext): AST | null {
}; };
} }
function parseTSetSlot(node: Element, ctx: ParsingContext): AST | null {
if (!node.hasAttribute("t-set-slot")) {
return null;
}
// const t = el.ownerDocument.createElement("t");
// const clone = <Element>node.cloneNode(true);
// // named slots
// const slotNodes = Array.from(clone.querySelectorAll("[t-set-slot]"));
// for (let slotNode of slotNodes) {
if (node.tagName !== "t") {
throw new OwlError(
`Directive 't-set-slot' can only be used on <t> nodes (used on a <${node.tagName}>)`
);
}
const name = node.getAttribute("t-set-slot")!;
// // check if this is defined in a sub component (in which case it should
// // be ignored)
// let el = slotNode.parentElement!;
// let isInSubComponent = false;
// while (el !== clone) {
// if (el!.hasAttribute("t-component") || el!.tagName[0] === el!.tagName[0].toUpperCase()) {
// isInSubComponent = true;
// break;
// }
// el = el.parentElement!;
// }
// if (isInSubComponent) {
// continue;
// }
node.removeAttribute("t-set-slot");
node.remove();
const slotAst = parseNode(node, ctx);
let on: ASTSlotDefinition["on"] = null;
let attrs: Attrs | null = null;
let scope: string | null = null;
for (let attributeName of node.getAttributeNames()) {
const value = node.getAttribute(attributeName)!;
if (attributeName === "t-slot-scope") {
scope = value;
continue;
} else if (attributeName.startsWith("t-on-")) {
on = on || {};
on[attributeName.slice(5)] = value;
} else {
attrs = attrs || {};
attrs[attributeName] = value;
}
}
// slots = slots || {};
return { type: ASTType.TSetSlot, name, content: slotAst, on, attrs, scope };
// }
// // default slot
// const defaultContent = parseChildNodes(clone, ctx);
// slots = slots || {};
// // t-set-slot="default" has priority over content
// if (defaultContent && !slots.default) {
// slots.default = { content: defaultContent, on, attrs: null, scope: defaultSlotScope };
// }
}
function parseTTranslation(node: Element, ctx: ParsingContext): AST | null { function parseTTranslation(node: Element, ctx: ParsingContext): AST | null {
if (node.getAttribute("t-translation") !== "off") { if (node.getAttribute("t-translation") !== "off") {
return null; return null;
+4 -27
View File
@@ -1,11 +1,12 @@
import { Component, ComponentConstructor, Props } from "./component"; import { Component, ComponentConstructor, Props } from "./component";
import { ComponentNode } from "./component_node"; import { ComponentNode } from "./component_node";
import { nodeErrorHandlers, OwlError, handleError } from "./error_handling"; import { nodeErrorHandlers, OwlError } from "./error_handling";
import { Fiber, RootFiber, MountOptions } from "./fibers"; import { Fiber, MountOptions } from "./fibers";
import { Scheduler } from "./scheduler"; import { Scheduler } from "./scheduler";
import { validateProps } from "./template_helpers"; import { validateProps } from "./template_helpers";
import { TemplateSet, TemplateSetConfig } from "./template_set"; import { TemplateSet, TemplateSetConfig } from "./template_set";
import { validateTarget } from "./utils"; import { validateTarget } from "./utils";
import { handleError } from "./error_handling";
// reimplement dev mode stuff see last change in 0f7a8289a6fb8387c3c1af41c6664b2a8448758f // reimplement dev mode stuff see last change in 0f7a8289a6fb8387c3c1af41c6664b2a8448758f
@@ -31,22 +32,6 @@ This is not suitable for production use.
See https://github.com/odoo/owl/blob/${hash}/doc/reference/app.md#configuration for more information.`; See https://github.com/odoo/owl/blob/${hash}/doc/reference/app.md#configuration for more information.`;
}; };
declare global {
interface Window {
__OWL_DEVTOOLS__: {
apps: Set<App>;
Fiber: typeof Fiber;
RootFiber: typeof RootFiber;
};
}
}
window.__OWL_DEVTOOLS__ ||= {
apps: new Set<App>(),
Fiber: Fiber,
RootFiber: RootFiber,
};
export class App< export class App<
T extends abstract new (...args: any) => any = any, T extends abstract new (...args: any) => any = any,
P extends object = any, P extends object = any,
@@ -64,7 +49,6 @@ export class App<
constructor(Root: ComponentConstructor<P, E>, config: AppConfig<P, E> = {}) { constructor(Root: ComponentConstructor<P, E>, config: AppConfig<P, E> = {}) {
super(config); super(config);
this.Root = Root; this.Root = Root;
window.__OWL_DEVTOOLS__.apps.add(this);
if (config.test) { if (config.test) {
this.dev = true; this.dev = true;
} }
@@ -126,7 +110,6 @@ export class App<
this.scheduler.flush(); this.scheduler.flush();
this.root.destroy(); this.root.destroy();
} }
window.__OWL_DEVTOOLS__.apps.delete(this);
} }
createComponent<P extends Props>( createComponent<P extends Props>(
@@ -168,13 +151,7 @@ export class App<
} else { } else {
// new component // new component
if (isStatic) { if (isStatic) {
const components = parent.constructor.components; C = parent.constructor.components[name as any];
if (!components) {
throw new OwlError(
`Cannot find the definition of component "${name}", missing static components key in parent`
);
}
C = components[name as any];
if (!C) { if (!C) {
throw new OwlError(`Cannot find the definition of component "${name}"`); throw new OwlError(`Cannot find the definition of component "${name}"`);
} else if (!(C.prototype instanceof Component)) { } else if (!(C.prototype instanceof Component)) {
+2 -3
View File
@@ -517,9 +517,8 @@ function createBlockClass(template: HTMLElement, ctx: BlockCtx): BlockClass {
return this.el!; return this.el!;
} }
moveBeforeDOMNode(node: Node | null, parent = this.parentEl) { moveBeforeDOMNode(node: Node | null) {
this.parentEl = parent; nodeInsertBefore.call(this.parentEl, this.el!, node);
nodeInsertBefore.call(parent, this.el!, node);
} }
moveBeforeVNode(other: Block | null, afterNode: Node | null) { moveBeforeVNode(other: Block | null, afterNode: Node | null) {
+4 -5
View File
@@ -46,7 +46,7 @@ export function createCatcher(eventsSpec: EventsSpec): Catcher {
const target = ev.target; const target = ev.target;
let currentNode: any = self.child.firstNode(); let currentNode: any = self.child.firstNode();
const afterNode = self.afterNode; const afterNode = self.afterNode;
while (currentNode && currentNode !== afterNode) { while (currentNode !== afterNode) {
if (currentNode.contains(target)) { if (currentNode.contains(target)) {
return origFn.call(this, ev); return origFn.call(this, ev);
} }
@@ -56,10 +56,9 @@ export function createCatcher(eventsSpec: EventsSpec): Catcher {
} }
} }
moveBeforeDOMNode(node: Node | null, parent = this.parentEl) { moveBeforeDOMNode(node: Node | null) {
this.parentEl = parent; this.child.moveBeforeDOMNode(node);
this.child.moveBeforeDOMNode(node, parent); this.parentEl!.insertBefore(this.afterNode!, node);
parent!.insertBefore(this.afterNode!, node);
} }
moveBeforeVNode(other: VCatcher | null, afterNode: Node | null) { moveBeforeVNode(other: VCatcher | null, afterNode: Node | null) {
+2 -2
View File
@@ -29,8 +29,8 @@ class VHtml {
} }
} }
moveBeforeDOMNode(node: Node | null, parent = this.parentEl) { moveBeforeDOMNode(node: Node | null) {
this.parentEl = parent; const parent = this.parentEl;
for (let elem of this.content) { for (let elem of this.content) {
nodeInsertBefore.call(parent, elem, node); nodeInsertBefore.call(parent, elem, node);
} }
+1 -1
View File
@@ -10,7 +10,7 @@ export { createCatcher } from "./event_catcher";
export interface VNode<T = any> { export interface VNode<T = any> {
mount(parent: HTMLElement, afterNode: Node | null): void; mount(parent: HTMLElement, afterNode: Node | null): void;
moveBeforeDOMNode(node: Node | null, parent?: HTMLElement): void; moveBeforeDOMNode(node: Node | null): void;
moveBeforeVNode(other: T | null, afterNode: Node | null): void; moveBeforeVNode(other: T | null, afterNode: Node | null): void;
patch(other: T, withBeforeRemove: boolean): void; patch(other: T, withBeforeRemove: boolean): void;
beforeRemove(): void; beforeRemove(): void;
+3 -4
View File
@@ -38,13 +38,12 @@ class VList {
this.parentEl = parent; this.parentEl = parent;
} }
moveBeforeDOMNode(node: Node | null, parent = this.parentEl) { moveBeforeDOMNode(node: Node | null) {
this.parentEl = parent;
const children = this.children; const children = this.children;
for (let i = 0, l = children.length; i < l; i++) { for (let i = 0, l = children.length; i < l; i++) {
children[i].moveBeforeDOMNode(node, parent); children[i].moveBeforeDOMNode(node);
} }
parent!.insertBefore(this.anchor!, node); this.parentEl!.insertBefore(this.anchor!, node);
} }
moveBeforeVNode(other: VList | null, afterNode: Node | null) { moveBeforeVNode(other: VList | null, afterNode: Node | null) {
+3 -3
View File
@@ -38,14 +38,14 @@ export class VMulti {
this.parentEl = parent; this.parentEl = parent;
} }
moveBeforeDOMNode(node: Node | null, parent = this.parentEl) { moveBeforeDOMNode(node: Node | null) {
this.parentEl = parent;
const children = this.children; const children = this.children;
const parent = this.parentEl;
const anchors = this.anchors; const anchors = this.anchors;
for (let i = 0, l = children.length; i < l; i++) { for (let i = 0, l = children.length; i < l; i++) {
let child = children[i]; let child = children[i];
if (child) { if (child) {
child.moveBeforeDOMNode(node, parent); child.moveBeforeDOMNode(node);
} else { } else {
const anchor = anchors![i]; const anchor = anchors![i];
nodeInsertBefore.call(parent, anchor, node); nodeInsertBefore.call(parent, anchor, node);
+2 -3
View File
@@ -23,9 +23,8 @@ abstract class VSimpleNode {
this.el = node; this.el = node;
} }
moveBeforeDOMNode(node: Node | null, parent = this.parentEl) { moveBeforeDOMNode(node: Node | null) {
this.parentEl = parent; nodeInsertBefore.call(this.parentEl, this.el!, node);
nodeInsertBefore.call(parent, this.el!, node);
} }
moveBeforeVNode(other: VText | null, afterNode: Node | null) { moveBeforeVNode(other: VText | null, afterNode: Node | null) {
+2 -2
View File
@@ -20,8 +20,8 @@ class VToggler {
this.child.mount(parent, afterNode); this.child.mount(parent, afterNode);
} }
moveBeforeDOMNode(node: Node | null, parent?: HTMLElement) { moveBeforeDOMNode(node: Node | null) {
this.child.moveBeforeDOMNode(node, parent); this.child.moveBeforeDOMNode(node);
} }
moveBeforeVNode(other: VToggler | null, afterNode: Node | null) { moveBeforeVNode(other: VToggler | null, afterNode: Node | null) {
+14 -9
View File
@@ -3,7 +3,14 @@ import { BDom, VNode } from "./blockdom";
import { Component, ComponentConstructor, Props } from "./component"; import { Component, ComponentConstructor, Props } from "./component";
import { fibersInError, OwlError } from "./error_handling"; import { fibersInError, OwlError } from "./error_handling";
import { Fiber, makeChildFiber, makeRootFiber, MountFiber, MountOptions } from "./fibers"; import { Fiber, makeChildFiber, makeRootFiber, MountFiber, MountOptions } from "./fibers";
import { clearReactivesForCallback, getSubscriptions, reactive, targets } from "./reactivity"; import {
clearReactivesForCallback,
getSubscriptions,
NonReactive,
Reactive,
reactive,
TARGET,
} from "./reactivity";
import { STATUS } from "./status"; import { STATUS } from "./status";
import { batched, Callback } from "./utils"; import { batched, Callback } from "./utils";
@@ -45,7 +52,7 @@ const batchedRenderFunctions = new WeakMap<ComponentNode, Callback>();
* relevant changes * relevant changes
* @see reactive * @see reactive
*/ */
export function useState<T extends object>(state: T): T { export function useState<T extends object>(state: T): Reactive<T> | NonReactive<T> {
const node = getCurrent(); const node = getCurrent();
let render = batchedRenderFunctions.get(node)!; let render = batchedRenderFunctions.get(node)!;
if (!render) { if (!render) {
@@ -109,13 +116,12 @@ export class ComponentNode<P extends Props = any, E = any> implements VNode<Comp
this.childEnv = env; this.childEnv = env;
for (const key in props) { for (const key in props) {
const prop = props[key]; const prop = props[key];
if (prop && typeof prop === "object" && targets.has(prop)) { if (prop && typeof prop === "object" && prop[TARGET]) {
props[key] = useState(prop); props[key] = useState(prop);
} }
} }
this.component = new C(props, env, this); this.component = new C(props, env, this);
const ctx = Object.assign(Object.create(this.component), { this: this.component }); this.renderFn = app.getTemplate(C.template).bind(this.component, this.component, this);
this.renderFn = app.getTemplate(C.template).bind(this.component, ctx, this);
this.component.setup(); this.component.setup();
currentNode = null; currentNode = null;
} }
@@ -234,7 +240,7 @@ export class ComponentNode<P extends Props = any, E = any> implements VNode<Comp
currentNode = this; currentNode = this;
for (const key in props) { for (const key in props) {
const prop = props[key]; const prop = props[key];
if (prop && typeof prop === "object" && targets.has(prop)) { if (prop && typeof prop === "object" && prop[TARGET]) {
props[key] = useState(prop); props[key] = useState(prop);
} }
} }
@@ -300,8 +306,8 @@ export class ComponentNode<P extends Props = any, E = any> implements VNode<Comp
this.fiber = null; this.fiber = null;
} }
moveBeforeDOMNode(node: Node | null, parent?: HTMLElement): void { moveBeforeDOMNode(node: Node | null): void {
this.bdom!.moveBeforeDOMNode(node, parent); this.bdom!.moveBeforeDOMNode(node);
} }
moveBeforeVNode(other: ComponentNode<P, E> | null, afterNode: Node | null) { moveBeforeVNode(other: ComponentNode<P, E> | null, afterNode: Node | null) {
@@ -318,7 +324,6 @@ export class ComponentNode<P extends Props = any, E = any> implements VNode<Comp
} }
_patch() { _patch() {
let hasChildren = false; let hasChildren = false;
// eslint-disable-next-line @typescript-eslint/no-unused-vars
for (let _k in this.children) { for (let _k in this.children) {
hasChildren = true; hasChildren = true;
break; break;
+1 -1
View File
@@ -117,7 +117,7 @@ export function useEffect(effect: Effect, computeDependencies: () => any[] = ()
* `useExternalListener(window, 'click', this._doSomething);` * `useExternalListener(window, 'click', this._doSomething);`
* */ * */
export function useExternalListener( export function useExternalListener(
target: EventTarget, target: HTMLElement | typeof window,
eventName: string, eventName: string,
handler: EventListener, handler: EventListener,
eventParams?: AddEventListenerOptions eventParams?: AddEventListenerOptions
+1
View File
@@ -12,6 +12,7 @@ import {
comment, comment,
} from "./blockdom"; } from "./blockdom";
import { mainEventHandler } from "./event_handling"; import { mainEventHandler } from "./event_handling";
export type { Reactive } from "./reactivity";
config.shouldNormalizeDom = false; config.shouldNormalizeDom = false;
config.mainEventHandler = mainEventHandler; config.mainEventHandler = mainEventHandler;
+8 -12
View File
@@ -21,20 +21,16 @@ class VPortal extends VText implements Partial<VNode<VPortal>> {
this.target = document.querySelector(this.selector) as any; this.target = document.querySelector(this.selector) as any;
if (this.target) { if (this.target) {
this.content!.mount(this.target!, null); this.content!.mount(this.target!, null);
} else {
this.content!.mount(parent, anchor);
} }
} }
beforeRemove() { beforeRemove() {
this.content!.beforeRemove(); // this.target not being null means content is mounted
} if (this.target) {
remove() { this.content!.beforeRemove();
if (this.content) {
super.remove();
this.content!.remove(); this.content!.remove();
this.content = null;
} }
this.content = null;
} }
patch(other: VPortal) { patch(other: VPortal) {
@@ -73,9 +69,9 @@ export class Portal extends Component {
onMounted(() => { onMounted(() => {
const portal: VPortal = node.bdom; const portal: VPortal = node.bdom;
if (!portal.target) { if (!portal.target) {
const target: HTMLElement = document.querySelector(this.props.target); portal.target = document.querySelector(this.props.target);
if (target) { if (portal.target) {
portal.content!.moveBeforeDOMNode(target.firstChild, target); portal.content!.mount(portal.target, null);
} else { } else {
throw new OwlError("invalid portal target"); throw new OwlError("invalid portal target");
} }
@@ -84,7 +80,7 @@ export class Portal extends Component {
onWillUnmount(() => { onWillUnmount(() => {
const portal: VPortal = node.bdom; const portal: VPortal = node.bdom;
portal.remove(); portal.beforeRemove();
}); });
} }
} }
+51 -57
View File
@@ -1,22 +1,25 @@
import type { Callback } from "./utils"; import { Callback } from "./utils";
import { OwlError } from "./error_handling"; import { OwlError } from "./error_handling";
// Allows to get the target of a Reactive (used for making a new Reactive from the underlying object)
export const TARGET = Symbol("Target");
// Escape hatch to prevent reactivity system to turn something into a reactive
const SKIP = Symbol("Skip");
// Special key to subscribe to, to be notified of key creation/deletion // Special key to subscribe to, to be notified of key creation/deletion
const KEYCHANGES = Symbol("Key changes"); const KEYCHANGES = Symbol("Key changes");
// Used to specify the absence of a callback, can be used as WeakMap key but
// should only be used as a sentinel value and never called.
const NO_CALLBACK = () => {
throw new Error("Called NO_CALLBACK. Owl is broken, please report this to the maintainers.");
};
// The following types only exist to signify places where objects are expected
// to be reactive or not, they provide no type checking benefit over "object"
type Target = object; type Target = object;
type Reactive<T extends Target> = T;
type Collection = Set<any> | Map<any, any> | WeakMap<any, any>; type Collection = Set<any> | Map<any, any> | WeakMap<any, any>;
type CollectionRawType = "Set" | "Map" | "WeakMap"; type CollectionRawType = "Set" | "Map" | "WeakMap";
export type Reactive<T extends Target = Target> = T & {
[TARGET]: any;
};
export type NonReactive<T extends Target = Target> = T & {
[SKIP]: any;
};
const objectToString = Object.prototype.toString; const objectToString = Object.prototype.toString;
const objectHasOwnProperty = Object.prototype.hasOwnProperty; const objectHasOwnProperty = Object.prototype.hasOwnProperty;
@@ -33,7 +36,7 @@ const COLLECTION_RAWTYPES = new Set(["Set", "Map", "WeakMap"]);
* @returns the raw type of the object * @returns the raw type of the object
*/ */
function rawType(obj: any) { function rawType(obj: any) {
return objectToString.call(toRaw(obj)).slice(8, -1); return objectToString.call(obj).slice(8, -1);
} }
/** /**
* Checks whether a given value can be made into a reactive object. * Checks whether a given value can be made into a reactive object.
@@ -58,16 +61,15 @@ function possiblyReactive(val: any, cb: Callback) {
return canBeMadeReactive(val) ? reactive(val, cb) : val; return canBeMadeReactive(val) ? reactive(val, cb) : val;
} }
const skipped = new WeakSet<Target>();
/** /**
* Mark an object or array so that it is ignored by the reactivity system * Mark an object or array so that it is ignored by the reactivity system
* *
* @param value the value to mark * @param value the value to mark
* @returns the object itself * @returns the object itself
*/ */
export function markRaw<T extends Target>(value: T): T { export function markRaw<T extends Target>(value: T): NonReactive<T> {
skipped.add(value); (value as any)[SKIP] = true;
return value; return value as NonReactive<T>;
} }
/** /**
@@ -76,8 +78,8 @@ export function markRaw<T extends Target>(value: T): T {
* @param value a reactive value * @param value a reactive value
* @returns the underlying value * @returns the underlying value
*/ */
export function toRaw<T extends Target, U extends Reactive<T>>(value: U | T): T { export function toRaw<T extends object>(value: Reactive<T>): T {
return targets.has(value) ? (targets.get(value) as T) : value; return value[TARGET] || value;
} }
const targetToKeysToCallbacks = new WeakMap<Target, Map<PropertyKey, Set<Callback>>>(); const targetToKeysToCallbacks = new WeakMap<Target, Map<PropertyKey, Set<Callback>>>();
@@ -91,9 +93,6 @@ const targetToKeysToCallbacks = new WeakMap<Target, Map<PropertyKey, Set<Callbac
* @param callback the function to call when the key changes * @param callback the function to call when the key changes
*/ */
function observeTargetKey(target: Target, key: PropertyKey, callback: Callback): void { function observeTargetKey(target: Target, key: PropertyKey, callback: Callback): void {
if (callback === NO_CALLBACK) {
return;
}
if (!targetToKeysToCallbacks.get(target)) { if (!targetToKeysToCallbacks.get(target)) {
targetToKeysToCallbacks.set(target, new Map()); targetToKeysToCallbacks.set(target, new Map());
} }
@@ -148,11 +147,8 @@ export function clearReactivesForCallback(callback: Callback): void {
if (!observedKeys) { if (!observedKeys) {
continue; continue;
} }
for (const [key, callbacks] of observedKeys.entries()) { for (const callbacks of observedKeys.values()) {
callbacks.delete(callback); callbacks.delete(callback);
if (!callbacks.size) {
observedKeys.delete(key);
}
} }
} }
targetsToClear.clear(); targetsToClear.clear();
@@ -162,20 +158,14 @@ export function getSubscriptions(callback: Callback) {
const targets = callbacksToTargets.get(callback) || []; const targets = callbacksToTargets.get(callback) || [];
return [...targets].map((target) => { return [...targets].map((target) => {
const keysToCallbacks = targetToKeysToCallbacks.get(target); const keysToCallbacks = targetToKeysToCallbacks.get(target);
let keys = []; return {
if (keysToCallbacks) { target,
for (const [key, cbs] of keysToCallbacks) { keys: keysToCallbacks ? [...keysToCallbacks.keys()] : [],
if (cbs.has(callback)) { };
keys.push(key);
}
}
}
return { target, keys };
}); });
} }
// Maps reactive objects to the underlying target
export const targets = new WeakMap<Reactive<Target>, Target>(); const reactiveCache = new WeakMap<Target, WeakMap<Callback, Reactive>>();
const reactiveCache = new WeakMap<Target, WeakMap<Callback, Reactive<Target>>>();
/** /**
* Creates a reactive proxy for an object. Reading data on the reactive object * Creates a reactive proxy for an object. Reading data on the reactive object
* subscribes to changes to the data. Writing data on the object will cause the * subscribes to changes to the data. Writing data on the object will cause the
@@ -190,7 +180,7 @@ const reactiveCache = new WeakMap<Target, WeakMap<Callback, Reactive<Target>>>()
* Subscriptions: * Subscriptions:
* + Reading a property on an object will subscribe you to changes in the value * + Reading a property on an object will subscribe you to changes in the value
* of that property. * of that property.
* + Accessing an object's keys (eg with Object.keys or with `for..in`) will * + Accessing an object keys (eg with Object.keys or with `for..in`) will
* subscribe you to the creation/deletion of keys. Checking the presence of a * subscribe you to the creation/deletion of keys. Checking the presence of a
* key on the object with 'in' has the same effect. * key on the object with 'in' has the same effect.
* - getOwnPropertyDescriptor does not currently subscribe you to the property. * - getOwnPropertyDescriptor does not currently subscribe you to the property.
@@ -203,16 +193,19 @@ const reactiveCache = new WeakMap<Target, WeakMap<Callback, Reactive<Target>>>()
* reactive has changed * reactive has changed
* @returns a proxy that tracks changes to it * @returns a proxy that tracks changes to it
*/ */
export function reactive<T extends Target>(target: T, callback: Callback = NO_CALLBACK): T { export function reactive<T extends Target>(
target: T,
callback: Callback = () => {}
): Reactive<T> | NonReactive<T> {
if (!canBeMadeReactive(target)) { if (!canBeMadeReactive(target)) {
throw new OwlError(`Cannot make the given value reactive`); throw new OwlError(`Cannot make the given value reactive`);
} }
if (skipped.has(target)) { if (SKIP in target) {
return target; return target as NonReactive<T>;
} }
if (targets.has(target)) { const originalTarget = (target as Reactive)[TARGET];
// target is reactive, create a reactive on the underlying object instead if (originalTarget) {
return reactive(targets.get(target) as T, callback); return reactive(originalTarget, callback);
} }
if (!reactiveCache.has(target)) { if (!reactiveCache.has(target)) {
reactiveCache.set(target, new WeakMap()); reactiveCache.set(target, new WeakMap());
@@ -225,7 +218,6 @@ export function reactive<T extends Target>(target: T, callback: Callback = NO_CA
: basicProxyHandler<T>(callback); : basicProxyHandler<T>(callback);
const proxy = new Proxy(target, handler as ProxyHandler<T>) as Reactive<T>; const proxy = new Proxy(target, handler as ProxyHandler<T>) as Reactive<T>;
reactivesForTarget.set(callback, proxy); reactivesForTarget.set(callback, proxy);
targets.set(proxy, target);
} }
return reactivesForTarget.get(callback) as Reactive<T>; return reactivesForTarget.get(callback) as Reactive<T>;
} }
@@ -237,29 +229,29 @@ export function reactive<T extends Target>(target: T, callback: Callback = NO_CA
*/ */
function basicProxyHandler<T extends Target>(callback: Callback): ProxyHandler<T> { function basicProxyHandler<T extends Target>(callback: Callback): ProxyHandler<T> {
return { return {
get(target, key, receiver) { get(target: any, key: PropertyKey, proxy: Reactive<T>) {
if (key === TARGET) {
return target;
}
// non-writable non-configurable properties cannot be made reactive // non-writable non-configurable properties cannot be made reactive
const desc = Object.getOwnPropertyDescriptor(target, key); const desc = Object.getOwnPropertyDescriptor(target, key);
if (desc && !desc.writable && !desc.configurable) { if (desc && !desc.writable && !desc.configurable) {
return Reflect.get(target, key, receiver); return Reflect.get(target, key, proxy);
} }
observeTargetKey(target, key, callback); observeTargetKey(target, key, callback);
return possiblyReactive(Reflect.get(target, key, receiver), callback); return possiblyReactive(Reflect.get(target, key, proxy), callback);
}, },
set(target, key, value, receiver) { set(target, key, value, proxy) {
const hadKey = objectHasOwnProperty.call(target, key); const isNewKey = !objectHasOwnProperty.call(target, key);
const originalValue = Reflect.get(target, key, receiver); const originalValue = Reflect.get(target, key, proxy);
const ret = Reflect.set(target, key, value, receiver); const ret = Reflect.set(target, key, value, proxy);
if (!hadKey && objectHasOwnProperty.call(target, key)) { if (isNewKey) {
notifyReactives(target, KEYCHANGES); notifyReactives(target, KEYCHANGES);
} }
// While Array length may trigger the set trap, it's not actually set by this // While Array length may trigger the set trap, it's not actually set by this
// method but is updated behind the scenes, and the trap is not called with the // method but is updated behind the scenes, and the trap is not called with the
// new value. We disable the "same-value-optimization" for it because of that. // new value. We disable the "same-value-optimization" for it because of that.
if ( if (originalValue !== value || (Array.isArray(target) && key === "length")) {
originalValue !== Reflect.get(target, key, receiver) ||
(key === "length" && Array.isArray(target))
) {
notifyReactives(target, key); notifyReactives(target, key);
} }
return ret; return ret;
@@ -452,8 +444,10 @@ function collectionsProxyHandler<T extends Collection>(
// property is read. // property is read.
const specialHandlers = rawTypeToFuncHandlers[targetRawType](target, callback); const specialHandlers = rawTypeToFuncHandlers[targetRawType](target, callback);
return Object.assign(basicProxyHandler(callback), { return Object.assign(basicProxyHandler(callback), {
// FIXME: probably broken when part of prototype chain since we ignore the receiver
get(target: any, key: PropertyKey) { get(target: any, key: PropertyKey) {
if (key === TARGET) {
return target;
}
if (objectHasOwnProperty.call(specialHandlers, key)) { if (objectHasOwnProperty.call(specialHandlers, key)) {
return (specialHandlers as any)[key]; return (specialHandlers as any)[key];
} }
+6 -15
View File
@@ -32,14 +32,14 @@ function callSlot(
if (__scope) { if (__scope) {
slotScope[__scope] = extra; slotScope[__scope] = extra;
} }
const slotBDom = __render ? __render(slotScope, parent, key) : null; const slotBDom = __render ? __render.call(__ctx.__owl__.component, slotScope, parent, key) : null;
if (defaultContent) { if (defaultContent) {
let child1: BDom | undefined = undefined; let child1: BDom | undefined = undefined;
let child2: BDom | undefined = undefined; let child2: BDom | undefined = undefined;
if (slotBDom) { if (slotBDom) {
child1 = dynamic ? toggler(name, slotBDom) : slotBDom; child1 = dynamic ? toggler(name, slotBDom) : slotBDom;
} else { } else {
child2 = defaultContent(ctx, parent, key); child2 = defaultContent.call(ctx.__owl__.component, ctx, parent, key);
} }
return multi([child1, child2]); return multi([child1, child2]);
} }
@@ -47,7 +47,8 @@ function callSlot(
} }
function capture(ctx: any): any { function capture(ctx: any): any {
const result = ObjectCreate(ctx); const component = ctx.__owl__.component;
const result = ObjectCreate(component);
for (let k in ctx) { for (let k in ctx) {
result[k] = ctx[k]; result[k] = ctx[k];
} }
@@ -170,7 +171,8 @@ let boundFunctions = new WeakMap();
const WeakMapGet = WeakMap.prototype.get; const WeakMapGet = WeakMap.prototype.get;
const WeakMapSet = WeakMap.prototype.set; const WeakMapSet = WeakMap.prototype.set;
function bind(component: any, fn: Function): Function { function bind(ctx: any, fn: Function): Function {
let component = ctx.__owl__.component;
let boundFnMap = WeakMapGet.call(boundFunctions, component); let boundFnMap = WeakMapGet.call(boundFunctions, component);
if (!boundFnMap) { if (!boundFnMap) {
boundFnMap = new WeakMap(); boundFnMap = new WeakMap();
@@ -202,16 +204,6 @@ function multiRefSetter(refs: RefMap, name: string): RefSetter {
}; };
} }
function singleRefSetter(refs: RefMap, name: string): RefSetter {
let _el: HTMLElement | null = null;
return (el) => {
if (el || refs[name] === _el) {
refs[name] = el;
_el = el;
}
};
}
/** /**
* Validate the component props (or next props) against the (static) props * Validate the component props (or next props) against the (static) props
* description. This is potentially an expensive operation: it may needs to * description. This is potentially an expensive operation: it may needs to
@@ -270,7 +262,6 @@ export const helpers = {
prepareList, prepareList,
setContextValue, setContextValue,
multiRefSetter, multiRefSetter,
singleRefSetter,
shallowEqual, shallowEqual,
toNumber, toNumber,
validateProps, validateProps,
-2
View File
@@ -1,5 +1,4 @@
import { OwlError } from "./error_handling"; import { OwlError } from "./error_handling";
import { toRaw } from "./reactivity";
type BaseType = type BaseType =
| typeof String | typeof String
@@ -85,7 +84,6 @@ export function validateSchema(obj: { [key: string]: any }, schema: Schema): str
if (Array.isArray(schema)) { if (Array.isArray(schema)) {
schema = toSchema(schema); schema = toSchema(schema);
} }
obj = toRaw(obj);
let errors = []; let errors = [];
// check if each value in obj has correct shape // check if each value in obj has correct shape
for (let key in obj) { for (let key in obj) {
@@ -430,19 +430,6 @@ exports[`attributes static attributes on void elements 1`] = `
}" }"
`; `;
exports[`attributes static attributes with backslash or backtick 1`] = `
"function anonymous(app, bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let block1 = createBlock(\`<div foo=\\"\\\\\\\\a\\" bar=\\"\\\\\\\\n\\" baz=\\"\\\\\`\\"/>\`);
return function template(ctx, node, key = \\"\\") {
return block1();
}
}"
`;
exports[`attributes static attributes with backticks 1`] = ` exports[`attributes static attributes with backticks 1`] = `
"function anonymous(app, bdom, helpers "function anonymous(app, bdom, helpers
) { ) {
@@ -519,9 +519,8 @@ exports[`t-on t-on, with arguments and t-call 2`] = `
let block1 = createBlock(\`<p block-handler-0=\\"click\\">lucas</p>\`); let block1 = createBlock(\`<p block-handler-0=\\"click\\">lucas</p>\`);
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
const v1 = ctx['this']; const v1 = ctx['value'];
const v2 = ctx['value']; let hdlr1 = [()=>this.update(v1), ctx];
let hdlr1 = [()=>v1.update(v2), ctx];
return block1([hdlr1]); return block1([hdlr1]);
} }
}" }"
@@ -182,7 +182,7 @@ exports[`misc other complex template 1`] = `
"function anonymous(app, bdom, helpers "function anonymous(app, bdom, helpers
) { ) {
let { text, createBlock, list, multi, html, toggler, comment } = bdom; let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { prepareList, withKey, singleRefSetter } = helpers; let { prepareList, withKey } = helpers;
const callTemplate_1 = app.getTemplate(\`LOAD_INFOS_TEMPLATE\`); const callTemplate_1 = app.getTemplate(\`LOAD_INFOS_TEMPLATE\`);
const comp1 = app.createComponent(\`BundlesList\`, true, false, false, false); const comp1 = app.createComponent(\`BundlesList\`, true, false, false, false);
const comp2 = app.createComponent(\`BundlesList\`, true, false, false, false); const comp2 = app.createComponent(\`BundlesList\`, true, false, false, false);
@@ -217,9 +217,9 @@ exports[`misc other complex template 1`] = `
let block25 = createBlock(\`<div><block-child-0/><block-child-1/></div>\`); let block25 = createBlock(\`<div><block-child-0/><block-child-1/></div>\`);
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
const refs = this.__owl__.refs; const refs = ctx.__owl__.refs;
const ref1 = singleRefSetter(refs, \`search_input\`); const ref1 = (el) => refs[\`search_input\`] = el;
const ref2 = singleRefSetter(refs, \`settings_menu\`); const ref2 = (el) => refs[\`settings_menu\`] = el;
let b2,b4,b14,b17,b22,b23,b24,b25; let b2,b4,b14,b17,b22,b23,b24,b25;
let attr1 = \`/runbot/\${ctx['project'].slug}\`; let attr1 = \`/runbot/\${ctx['project'].slug}\`;
let txt1 = ctx['project'].name; let txt1 = ctx['project'].name;
+16 -22
View File
@@ -4,14 +4,13 @@ exports[`t-ref can get a dynamic ref on a node 1`] = `
"function anonymous(app, bdom, helpers "function anonymous(app, bdom, helpers
) { ) {
let { text, createBlock, list, multi, html, toggler, comment } = bdom; let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { singleRefSetter } = helpers;
let block1 = createBlock(\`<div><span block-ref=\\"0\\"/></div>\`); let block1 = createBlock(\`<div><span block-ref=\\"0\\"/></div>\`);
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
const refs = this.__owl__.refs; const refs = ctx.__owl__.refs;
const v1 = ctx['id']; const v1 = ctx['id'];
let ref1 = singleRefSetter(refs, \`myspan\${v1}\`); let ref1 = (el) => refs[\`myspan\${v1}\`] = el;
return block1([ref1]); return block1([ref1]);
} }
}" }"
@@ -21,14 +20,13 @@ exports[`t-ref can get a dynamic ref on a node, alternate syntax 1`] = `
"function anonymous(app, bdom, helpers "function anonymous(app, bdom, helpers
) { ) {
let { text, createBlock, list, multi, html, toggler, comment } = bdom; let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { singleRefSetter } = helpers;
let block1 = createBlock(\`<div><span block-ref=\\"0\\"/></div>\`); let block1 = createBlock(\`<div><span block-ref=\\"0\\"/></div>\`);
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
const refs = this.__owl__.refs; const refs = ctx.__owl__.refs;
const v1 = ctx['id']; const v1 = ctx['id'];
let ref1 = singleRefSetter(refs, \`myspan\${v1}\`); let ref1 = (el) => refs[\`myspan\${v1}\`] = el;
return block1([ref1]); return block1([ref1]);
} }
}" }"
@@ -38,13 +36,12 @@ exports[`t-ref can get a ref on a node 1`] = `
"function anonymous(app, bdom, helpers "function anonymous(app, bdom, helpers
) { ) {
let { text, createBlock, list, multi, html, toggler, comment } = bdom; let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { singleRefSetter } = helpers;
let block1 = createBlock(\`<div><span block-ref=\\"0\\"/></div>\`); let block1 = createBlock(\`<div><span block-ref=\\"0\\"/></div>\`);
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
const refs = this.__owl__.refs; const refs = ctx.__owl__.refs;
const ref1 = singleRefSetter(refs, \`myspan\`); const ref1 = (el) => refs[\`myspan\`] = el;
return block1([ref1]); return block1([ref1]);
} }
}" }"
@@ -69,13 +66,12 @@ exports[`t-ref ref in a t-call 2`] = `
"function anonymous(app, bdom, helpers "function anonymous(app, bdom, helpers
) { ) {
let { text, createBlock, list, multi, html, toggler, comment } = bdom; let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { singleRefSetter } = helpers;
let block1 = createBlock(\`<div>1<span block-ref=\\"0\\"/>2</div>\`); let block1 = createBlock(\`<div>1<span block-ref=\\"0\\"/>2</div>\`);
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
const refs = this.__owl__.refs; const refs = ctx.__owl__.refs;
const ref1 = singleRefSetter(refs, \`name\`); const ref1 = (el) => refs[\`name\`] = el;
return block1([ref1]); return block1([ref1]);
} }
}" }"
@@ -85,14 +81,13 @@ exports[`t-ref ref in a t-if 1`] = `
"function anonymous(app, bdom, helpers "function anonymous(app, bdom, helpers
) { ) {
let { text, createBlock, list, multi, html, toggler, comment } = bdom; let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { singleRefSetter } = helpers;
let block1 = createBlock(\`<div><block-child-0/></div>\`); let block1 = createBlock(\`<div><block-child-0/></div>\`);
let block2 = createBlock(\`<span block-ref=\\"0\\"/>\`); let block2 = createBlock(\`<span block-ref=\\"0\\"/>\`);
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
const refs = this.__owl__.refs; const refs = ctx.__owl__.refs;
const ref1 = singleRefSetter(refs, \`name\`); const ref1 = (el) => refs[\`name\`] = el;
let b2; let b2;
if (ctx['condition']) { if (ctx['condition']) {
b2 = block2([ref1]); b2 = block2([ref1]);
@@ -106,13 +101,13 @@ exports[`t-ref refs in a loop 1`] = `
"function anonymous(app, bdom, helpers "function anonymous(app, bdom, helpers
) { ) {
let { text, createBlock, list, multi, html, toggler, comment } = bdom; let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { prepareList, singleRefSetter, withKey } = helpers; let { prepareList, withKey } = helpers;
let block1 = createBlock(\`<div><block-child-0/></div>\`); let block1 = createBlock(\`<div><block-child-0/></div>\`);
let block3 = createBlock(\`<div block-ref=\\"0\\"><block-text-1/></div>\`); let block3 = createBlock(\`<div block-ref=\\"0\\"><block-text-1/></div>\`);
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
const refs = this.__owl__.refs; const refs = ctx.__owl__.refs;
ctx = Object.create(ctx); ctx = Object.create(ctx);
const [k_block2, v_block2, l_block2, c_block2] = prepareList(ctx['items']);; const [k_block2, v_block2, l_block2, c_block2] = prepareList(ctx['items']);;
for (let i1 = 0; i1 < l_block2; i1++) { for (let i1 = 0; i1 < l_block2; i1++) {
@@ -120,7 +115,7 @@ exports[`t-ref refs in a loop 1`] = `
const key1 = ctx['item']; const key1 = ctx['item'];
const tKey_1 = ctx['item']; const tKey_1 = ctx['item'];
const v1 = ctx['item']; const v1 = ctx['item'];
let ref1 = singleRefSetter(refs, (v1)); let ref1 = (el) => refs[(v1)] = el;
let txt1 = ctx['item']; let txt1 = ctx['item'];
c_block2[i1] = withKey(block3([ref1, txt1]), tKey_1 + key1); c_block2[i1] = withKey(block3([ref1, txt1]), tKey_1 + key1);
} }
@@ -134,15 +129,14 @@ exports[`t-ref two refs, one in a t-if 1`] = `
"function anonymous(app, bdom, helpers "function anonymous(app, bdom, helpers
) { ) {
let { text, createBlock, list, multi, html, toggler, comment } = bdom; let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { singleRefSetter } = helpers;
let block1 = createBlock(\`<div><block-child-0/><p block-ref=\\"0\\"/></div>\`); let block1 = createBlock(\`<div><block-child-0/><p block-ref=\\"0\\"/></div>\`);
let block2 = createBlock(\`<span block-ref=\\"0\\"/>\`); let block2 = createBlock(\`<span block-ref=\\"0\\"/>\`);
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
const refs = this.__owl__.refs; const refs = ctx.__owl__.refs;
const ref1 = singleRefSetter(refs, \`name\`); const ref1 = (el) => refs[\`name\`] = el;
const ref2 = singleRefSetter(refs, \`p\`); const ref2 = (el) => refs[\`p\`] = el;
let b2; let b2;
if (ctx['condition']) { if (ctx['condition']) {
b2 = block2([ref1]); b2 = block2([ref1]);
-5
View File
@@ -12,11 +12,6 @@ describe("attributes", () => {
expect(renderToString(template)).toBe(`<div foo="a" bar="b" baz="c"></div>`); expect(renderToString(template)).toBe(`<div foo="a" bar="b" baz="c"></div>`);
}); });
test("static attributes with backslash or backtick", () => {
const template = `<div foo="\\a" bar="\\n" baz="\`"/>`;
expect(renderToString(template)).toBe(`<div foo="\\a" bar="\\n" baz="\`"></div>`);
});
test("two classes", () => { test("two classes", () => {
const template = `<div class="a b"/>`; const template = `<div class="a b"/>`;
expect(renderToString(template)).toBe(`<div class="a b"></div>`); expect(renderToString(template)).toBe(`<div class="a b"></div>`);
-3
View File
@@ -284,9 +284,6 @@ describe("t-on", () => {
expect(this).toBe(owner); expect(this).toBe(owner);
expect(val).toBe(444); expect(val).toBe(444);
}, },
get this() {
return owner;
},
value: 444, value: 444,
}; };
+1 -2
View File
@@ -114,8 +114,7 @@ describe("t-ref", () => {
app.addTemplate("main", main); app.addTemplate("main", main);
app.addTemplate("sub", sub); app.addTemplate("sub", sub);
const comp = { __owl__: { refs } }; const bdom = app.getTemplate("main")({ __owl__: { refs } }, {});
const bdom = app.getTemplate("main").call(comp, comp, {});
mount(bdom, document.createElement("div")); mount(bdom, document.createElement("div"));
expect(refs.name.tagName).toBe("SPAN"); expect(refs.name.tagName).toBe("SPAN");
@@ -8,7 +8,7 @@ exports[`basics GrandChild display is controlled by its GrandParent 1`] = `
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
const Comp1 = ctx['myComp']; const Comp1 = ctx['myComp'];
return toggler(Comp1, comp1({displayGrandChild: ctx['displayGrandChild']}, (Comp1).name + key + \`__1\`, node, this, Comp1)); return toggler(Comp1, comp1({displayGrandChild: ctx['displayGrandChild']}, key + \`__1\`, node, this, Comp1));
} }
}" }"
`; `;
@@ -1213,45 +1213,6 @@ exports[`delayed fiber does not get rendered if it was cancelled 4`] = `
}" }"
`; `;
exports[`delayed render does not go through when t-component value changed 1`] = `
"function anonymous(app, bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
const comp1 = app.createComponent(null, false, false, false, true);
return function template(ctx, node, key = \\"\\") {
const b2 = text(\`A\`);
const Comp1 = ctx['state'].component;
const b3 = toggler(Comp1, comp1({}, (Comp1).name + key + \`__1\`, node, this, Comp1));
return multi([b2, b3]);
}
}"
`;
exports[`delayed render does not go through when t-component value changed 2`] = `
"function anonymous(app, bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
return function template(ctx, node, key = \\"\\") {
const b2 = text(\`B\`);
const b3 = text(ctx['state'].val);
return multi([b2, b3]);
}
}"
`;
exports[`delayed render does not go through when t-component value changed 3`] = `
"function anonymous(app, bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
return function template(ctx, node, key = \\"\\") {
return text(\`C\`);
}
}"
`;
exports[`delayed rendering, but then initial rendering is cancelled by yet another render 1`] = ` exports[`delayed rendering, but then initial rendering is cancelled by yet another render 1`] = `
"function anonymous(app, bdom, helpers "function anonymous(app, bdom, helpers
) { ) {
@@ -1815,7 +1776,7 @@ exports[`t-foreach with dynamic async component 1`] = `
let b3; let b3;
if (ctx['arr']) { if (ctx['arr']) {
const Comp1 = ctx['myComp']; const Comp1 = ctx['myComp'];
b3 = toggler(Comp1, comp1({key: ctx['arr'][0]}, (Comp1).name + key + \`__1__\${key1}\`, node, this, Comp1)); b3 = toggler(Comp1, comp1({key: ctx['arr'][0]}, key + \`__1__\${key1}\`, node, this, Comp1));
} }
c_block1[i1] = withKey(multi([b3]), key1); c_block1[i1] = withKey(multi([b3]), key1);
} }
@@ -1849,7 +1810,7 @@ exports[`t-key on dom node having a component 1`] = `
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
const tKey_1 = ctx['key']; const tKey_1 = ctx['key'];
const Comp1 = ctx['myComp']; const Comp1 = ctx['myComp'];
const b2 = toggler(tKey_1, toggler(Comp1, comp1({key: ctx['key']}, (Comp1).name + tKey_1 + key + \`__1\`, node, this, Comp1))); const b2 = toggler(tKey_1, toggler(Comp1, comp1({key: ctx['key']}, tKey_1 + key + \`__1\`, node, this, Comp1)));
return toggler(tKey_1, block1([], [b2])); return toggler(tKey_1, block1([], [b2]));
} }
}" }"
@@ -1875,7 +1836,7 @@ exports[`t-key on dynamic async component (toggler is never patched) 1`] = `
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
const tKey_1 = ctx['key']; const tKey_1 = ctx['key'];
const Comp1 = ctx['myComp']; const Comp1 = ctx['myComp'];
return toggler(tKey_1, toggler(Comp1, comp1({key: ctx['key']}, (Comp1).name + tKey_1 + key + \`__1\`, node, this, Comp1))); return toggler(tKey_1, toggler(Comp1, comp1({key: ctx['key']}, tKey_1 + key + \`__1\`, node, this, Comp1)));
} }
}" }"
`; `;
@@ -38,21 +38,6 @@ exports[`basics display a nice error if it cannot find component 1`] = `
}" }"
`; `;
exports[`basics display a nice error if the components key is missing with subcomponents 1`] = `
"function anonymous(app, bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
const comp1 = app.createComponent(\`MissingChild\`, true, false, false, true);
let block1 = createBlock(\`<div><block-child-0/></div>\`);
return function template(ctx, node, key = \\"\\") {
const b2 = comp1({}, key + \`__1\`, node, this, null);
return block1([], [b2]);
}
}"
`;
exports[`basics no component catching error lead to full app destruction 1`] = ` exports[`basics no component catching error lead to full app destruction 1`] = `
"function anonymous(app, bdom, helpers "function anonymous(app, bdom, helpers
) { ) {
@@ -268,7 +253,7 @@ exports[`can catch errors can catch an error in a component render function 1`]
} }
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
const b3 = comp2({slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx}})}, key + \`__2\`, node, this, null); const b3 = comp2({slots: markRaw({'default': {__render: slot1, __ctx: ctx}})}, key + \`__2\`, node, this, null);
return block1([], [b3]); return block1([], [b3]);
} }
}" }"
@@ -323,7 +308,7 @@ exports[`can catch errors can catch an error in the constructor call of a compon
} }
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
const b3 = comp2({slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx}})}, key + \`__2\`, node, this, null); const b3 = comp2({slots: markRaw({'default': {__render: slot1, __ctx: ctx}})}, key + \`__2\`, node, this, null);
return block1([], [b3]); return block1([], [b3]);
} }
}" }"
@@ -367,7 +352,7 @@ exports[`can catch errors can catch an error in the constructor call of a compon
} }
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
const b5 = comp3({slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx}})}, key + \`__3\`, node, this, null); const b5 = comp3({slots: markRaw({'default': {__render: slot1, __ctx: ctx}})}, key + \`__3\`, node, this, null);
return block1([], [b5]); return block1([], [b5]);
} }
}" }"
@@ -447,7 +432,7 @@ exports[`can catch errors can catch an error in the initial call of a component
} }
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
const b3 = comp2({slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx}})}, key + \`__2\`, node, this, null); const b3 = comp2({slots: markRaw({'default': {__render: slot1, __ctx: ctx}})}, key + \`__2\`, node, this, null);
return block1([], [b3]); return block1([], [b3]);
} }
}" }"
@@ -504,7 +489,7 @@ exports[`can catch errors can catch an error in the initial call of a component
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
let b3; let b3;
if (ctx['state'].flag) { if (ctx['state'].flag) {
b3 = comp2({slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx}})}, key + \`__2\`, node, this, null); b3 = comp2({slots: markRaw({'default': {__render: slot1, __ctx: ctx}})}, key + \`__2\`, node, this, null);
} }
return block1([], [b3]); return block1([], [b3]);
} }
@@ -653,7 +638,7 @@ exports[`can catch errors can catch an error in the mounted call 1`] = `
} }
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
const b3 = comp2({slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx}})}, key + \`__2\`, node, this, null); const b3 = comp2({slots: markRaw({'default': {__render: slot1, __ctx: ctx}})}, key + \`__2\`, node, this, null);
return block1([], [b3]); return block1([], [b3]);
} }
}" }"
@@ -708,7 +693,7 @@ exports[`can catch errors can catch an error in the willPatch call 1`] = `
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
let txt1 = ctx['state'].message; let txt1 = ctx['state'].message;
const b3 = comp2({slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx}})}, key + \`__2\`, node, this, null); const b3 = comp2({slots: markRaw({'default': {__render: slot1, __ctx: ctx}})}, key + \`__2\`, node, this, null);
return block1([txt1], [b3]); return block1([txt1], [b3]);
} }
}" }"
@@ -763,7 +748,7 @@ exports[`can catch errors can catch an error in the willStart call 1`] = `
} }
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
const b3 = comp2({slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx}})}, key + \`__2\`, node, this, null); const b3 = comp2({slots: markRaw({'default': {__render: slot1, __ctx: ctx}})}, key + \`__2\`, node, this, null);
return block1([], [b3]); return block1([], [b3]);
} }
}" }"
@@ -820,7 +805,7 @@ exports[`can catch errors can catch an error origination from a child's willStar
} }
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
const b5 = comp3({slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx}})}, key + \`__3\`, node, this, null); const b5 = comp3({slots: markRaw({'default': {__render: slot1, __ctx: ctx}})}, key + \`__3\`, node, this, null);
return block1([], [b5]); return block1([], [b5]);
} }
}" }"
@@ -931,7 +916,7 @@ exports[`can catch errors catching error, rethrow, render parent -- a main comp
function slot1(ctx, node, key = \\"\\") { function slot1(ctx, node, key = \\"\\") {
const Comp1 = ctx['cp'].Comp; const Comp1 = ctx['cp'].Comp;
return toggler(Comp1, comp1({}, (Comp1).name + key + \`__1\`, node, this, Comp1)); return toggler(Comp1, comp1({}, key + \`__1\`, node, this, Comp1));
} }
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
@@ -940,10 +925,9 @@ exports[`can catch errors catching error, rethrow, render parent -- a main comp
for (let i1 = 0; i1 < l_block1; i1++) { for (let i1 = 0; i1 < l_block1; i1++) {
ctx[\`cp\`] = v_block1[i1]; ctx[\`cp\`] = v_block1[i1];
const key1 = ctx['cp'].id; const key1 = ctx['cp'].id;
const v1 = ctx['this']; const v1 = ctx['cp'];
const v2 = ctx['cp'];
const ctx1 = capture(ctx); const ctx1 = capture(ctx);
c_block1[i1] = withKey(comp2({onError: ()=>v1.cleanUp(v2.id),slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__2__\${key1}\`, node, this, null), key1); c_block1[i1] = withKey(comp2({onError: ()=>this.cleanUp(v1.id),slots: markRaw({'default': {__render: slot1, __ctx: ctx1}})}, key + \`__2__\${key1}\`, node, this, null), key1);
} }
return list(c_block1); return list(c_block1);
} }
@@ -1010,19 +994,18 @@ exports[`can catch errors catching in child makes parent render 1`] = `
function slot1(ctx, node, key = \\"\\") { function slot1(ctx, node, key = \\"\\") {
const Comp1 = ctx['elem'][1]; const Comp1 = ctx['elem'][1];
return toggler(Comp1, comp1({id: ctx['elem'][0]}, (Comp1).name + key + \`__1\`, node, this, Comp1)); return toggler(Comp1, comp1({id: ctx['elem'][0]}, key + \`__1\`, node, this, Comp1));
} }
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
ctx = Object.create(ctx); ctx = Object.create(ctx);
const [k_block1, v_block1, l_block1, c_block1] = prepareList(Object.entries(ctx['this'].elements));; const [k_block1, v_block1, l_block1, c_block1] = prepareList(Object.entries(this.elements));;
for (let i1 = 0; i1 < l_block1; i1++) { for (let i1 = 0; i1 < l_block1; i1++) {
ctx[\`elem\`] = v_block1[i1]; ctx[\`elem\`] = v_block1[i1];
const key1 = ctx['elem'][0]; const key1 = ctx['elem'][0];
const v1 = ctx['this']; const v1 = ctx['elem'];
const v2 = ctx['elem'];
const ctx1 = capture(ctx); const ctx1 = capture(ctx);
c_block1[i1] = withKey(comp2({onError: (_error)=>v1.onError(v2[0],_error),slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__2__\${key1}\`, node, this, null), key1); c_block1[i1] = withKey(comp2({onError: (_error)=>this.onError(v1[0],_error),slots: markRaw({'default': {__render: slot1, __ctx: ctx1}})}, key + \`__2__\${key1}\`, node, this, null), key1);
} }
return list(c_block1); return list(c_block1);
} }
@@ -1085,7 +1068,7 @@ exports[`can catch errors error in mounted on a component with a sibling (proper
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
const b2 = comp1({}, key + \`__1\`, node, this, null); const b2 = comp1({}, key + \`__1\`, node, this, null);
const b4 = comp3({slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx}})}, key + \`__3\`, node, this, null); const b4 = comp3({slots: markRaw({'default': {__render: slot1, __ctx: ctx}})}, key + \`__3\`, node, this, null);
return block1([], [b2, b4]); return block1([], [b2, b4]);
} }
}" }"
@@ -1157,7 +1140,7 @@ exports[`can catch errors onError in class inheritance is called if rethrown 2`]
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
let b2,b3; let b2,b3;
if (!ctx['state'].error) { if (!ctx['state'].error) {
b2 = text(ctx['this'].will.crash); b2 = text(this.will.crash);
} else { } else {
b3 = text(ctx['state'].error); b3 = text(ctx['state'].error);
} }
@@ -1188,7 +1171,7 @@ exports[`can catch errors onError in class inheritance is not called if no rethr
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
let b2,b3; let b2,b3;
if (!ctx['state'].error) { if (!ctx['state'].error) {
b2 = text(ctx['this'].will.crash); b2 = text(this.will.crash);
} else { } else {
b3 = text(ctx['state'].error); b3 = text(ctx['state'].error);
} }
@@ -1220,7 +1203,7 @@ exports[`errors and promises a rendering error in a sub component will reject th
let block1 = createBlock(\`<div><block-text-0/></div>\`); let block1 = createBlock(\`<div><block-text-0/></div>\`);
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
let txt1 = ctx['this'].will.crash; let txt1 = this.will.crash;
return block1([txt1]); return block1([txt1]);
} }
}" }"
@@ -1234,7 +1217,7 @@ exports[`errors and promises a rendering error will reject the mount promise 1`]
let block1 = createBlock(\`<div><block-text-0/></div>\`); let block1 = createBlock(\`<div><block-text-0/></div>\`);
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
let txt1 = ctx['this'].will.crash; let txt1 = this.will.crash;
return block1([txt1]); return block1([txt1]);
} }
}" }"
@@ -1279,7 +1262,7 @@ exports[`errors and promises a rendering error will reject the render promise 1`
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
let b2; let b2;
if (ctx['flag']) { if (ctx['flag']) {
b2 = text(ctx['this'].will.crash); b2 = text(this.will.crash);
} }
return block1([], [b2]); return block1([], [b2]);
} }
@@ -4,15 +4,14 @@ exports[`hooks autofocus hook input in a t-if 1`] = `
"function anonymous(app, bdom, helpers "function anonymous(app, bdom, helpers
) { ) {
let { text, createBlock, list, multi, html, toggler, comment } = bdom; let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { singleRefSetter } = helpers;
let block1 = createBlock(\`<div><input block-ref=\\"0\\"/><block-child-0/></div>\`); let block1 = createBlock(\`<div><input block-ref=\\"0\\"/><block-child-0/></div>\`);
let block2 = createBlock(\`<input block-ref=\\"0\\"/>\`); let block2 = createBlock(\`<input block-ref=\\"0\\"/>\`);
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
const refs = this.__owl__.refs; const refs = ctx.__owl__.refs;
const ref1 = singleRefSetter(refs, \`input1\`); const ref1 = (el) => refs[\`input1\`] = el;
const ref2 = singleRefSetter(refs, \`input2\`); const ref2 = (el) => refs[\`input2\`] = el;
let b2; let b2;
if (ctx['state'].flag) { if (ctx['state'].flag) {
b2 = block2([ref2]); b2 = block2([ref2]);
@@ -26,14 +25,13 @@ exports[`hooks autofocus hook simple input 1`] = `
"function anonymous(app, bdom, helpers "function anonymous(app, bdom, helpers
) { ) {
let { text, createBlock, list, multi, html, toggler, comment } = bdom; let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { singleRefSetter } = helpers;
let block1 = createBlock(\`<div><input block-ref=\\"0\\"/><input block-ref=\\"1\\"/></div>\`); let block1 = createBlock(\`<div><input block-ref=\\"0\\"/><input block-ref=\\"1\\"/></div>\`);
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
const refs = this.__owl__.refs; const refs = ctx.__owl__.refs;
const ref1 = singleRefSetter(refs, \`input1\`); const ref1 = (el) => refs[\`input1\`] = el;
const ref2 = singleRefSetter(refs, \`input2\`); const ref2 = (el) => refs[\`input2\`] = el;
return block1([ref1, ref2]); return block1([ref1, ref2]);
} }
}" }"
@@ -266,13 +264,12 @@ exports[`hooks useEffect hook effect can depend on stuff in dom 1`] = `
"function anonymous(app, bdom, helpers "function anonymous(app, bdom, helpers
) { ) {
let { text, createBlock, list, multi, html, toggler, comment } = bdom; let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { singleRefSetter } = helpers;
let block2 = createBlock(\`<div block-ref=\\"0\\"/>\`); let block2 = createBlock(\`<div block-ref=\\"0\\"/>\`);
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
const refs = this.__owl__.refs; const refs = ctx.__owl__.refs;
const ref1 = singleRefSetter(refs, \`div\`); const ref1 = (el) => refs[\`div\`] = el;
let b2; let b2;
if (ctx['state'].value) { if (ctx['state'].value) {
b2 = block2([ref1]); b2 = block2([ref1]);
@@ -356,13 +353,12 @@ exports[`hooks useRef hook: basic use 1`] = `
"function anonymous(app, bdom, helpers "function anonymous(app, bdom, helpers
) { ) {
let { text, createBlock, list, multi, html, toggler, comment } = bdom; let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { singleRefSetter } = helpers;
let block1 = createBlock(\`<div><button block-ref=\\"0\\"><block-text-1/></button></div>\`); let block1 = createBlock(\`<div><button block-ref=\\"0\\"><block-text-1/></button></div>\`);
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
const refs = this.__owl__.refs; const refs = ctx.__owl__.refs;
const ref1 = singleRefSetter(refs, \`button\`); const ref1 = (el) => refs[\`button\`] = el;
let txt1 = ctx['value']; let txt1 = ctx['value'];
return block1([ref1, txt1]); return block1([ref1, txt1]);
} }
@@ -283,7 +283,7 @@ exports[`bound functions is referentially equal after update 1`] = `
const comp1 = app.createComponent(\`Child\`, true, false, false, false); const comp1 = app.createComponent(\`Child\`, true, false, false, false);
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
return comp1({val: ctx['state'].val,fn: bind(this, ctx['someFunction'])}, key + \`__1\`, node, this, null); return comp1({val: ctx['state'].val,fn: bind(ctx, ctx['someFunction'])}, key + \`__1\`, node, this, null);
} }
}" }"
`; `;
@@ -307,7 +307,7 @@ exports[`can bind function prop with bind suffix 1`] = `
const comp1 = app.createComponent(\`Child\`, true, false, false, false); const comp1 = app.createComponent(\`Child\`, true, false, false, false);
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
return comp1({doSomething: bind(this, ctx['doSomething'])}, key + \`__1\`, node, this, null); return comp1({doSomething: bind(ctx, ctx['doSomething'])}, key + \`__1\`, node, this, null);
} }
}" }"
`; `;
@@ -695,7 +695,7 @@ exports[`props validation can validate through slots 1`] = `
} }
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
const props2 = {slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx}})}; const props2 = {slots: markRaw({'default': {__render: slot1, __ctx: ctx}})};
helpers.validateProps(\`Wrapper\`, props2, this); helpers.validateProps(\`Wrapper\`, props2, this);
return comp2(props2, key + \`__2\`, node, this, null); return comp2(props2, key + \`__2\`, node, this, null);
} }
@@ -841,33 +841,6 @@ exports[`props validation props are validated whenever component is updated 2`]
}" }"
`; `;
exports[`props validation props validation does not cause additional subscription 1`] = `
"function anonymous(app, bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
const comp1 = app.createComponent(\`Child\`, true, false, false, false);
return function template(ctx, node, key = \\"\\") {
const props1 = {obj: ctx['obj']};
helpers.validateProps(\`Child\`, props1, this);
const b2 = comp1(props1, key + \`__1\`, node, this, null);
const b3 = text(ctx['obj'].otherValue);
return multi([b2, b3]);
}
}"
`;
exports[`props validation props validation does not cause additional subscription 2`] = `
"function anonymous(app, bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
return function template(ctx, node, key = \\"\\") {
return text(ctx['props'].obj.value);
}
}"
`;
exports[`props validation props: list of strings 1`] = ` exports[`props validation props: list of strings 1`] = `
"function anonymous(app, bdom, helpers "function anonymous(app, bdom, helpers
) { ) {
@@ -140,39 +140,3 @@ exports[`reactivity in lifecycle state changes in willUnmount do not trigger rer
} }
}" }"
`; `;
exports[`subscriptions subscriptions returns the keys and targets observed by the component 1`] = `
"function anonymous(app, bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
return function template(ctx, node, key = \\"\\") {
return text(ctx['state'].a);
}
}"
`;
exports[`subscriptions subscriptions returns the keys observed by the component 1`] = `
"function anonymous(app, bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
const comp1 = app.createComponent(\`Child\`, true, false, false, false);
return function template(ctx, node, key = \\"\\") {
const b2 = text(ctx['state'].a);
const b3 = comp1({state: ctx['state']}, key + \`__1\`, node, this, null);
return multi([b2, b3]);
}
}"
`;
exports[`subscriptions subscriptions returns the keys observed by the component 2`] = `
"function anonymous(app, bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
return function template(ctx, node, key = \\"\\") {
return text(ctx['props'].state.b);
}
}"
`;
@@ -4,13 +4,12 @@ exports[`refs basic use 1`] = `
"function anonymous(app, bdom, helpers "function anonymous(app, bdom, helpers
) { ) {
let { text, createBlock, list, multi, html, toggler, comment } = bdom; let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { singleRefSetter } = helpers;
let block1 = createBlock(\`<div block-ref=\\"0\\"/>\`); let block1 = createBlock(\`<div block-ref=\\"0\\"/>\`);
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
const refs = this.__owl__.refs; const refs = ctx.__owl__.refs;
const ref1 = singleRefSetter(refs, \`div\`); const ref1 = (el) => refs[\`div\`] = el;
return block1([ref1]); return block1([ref1]);
} }
}" }"
@@ -20,13 +19,13 @@ exports[`refs can use 2 refs with same name in a t-if/t-else situation 1`] = `
"function anonymous(app, bdom, helpers "function anonymous(app, bdom, helpers
) { ) {
let { text, createBlock, list, multi, html, toggler, comment } = bdom; let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { singleRefSetter, multiRefSetter } = helpers; let { multiRefSetter } = helpers;
let block2 = createBlock(\`<div block-ref=\\"0\\"/>\`); let block2 = createBlock(\`<div block-ref=\\"0\\"/>\`);
let block3 = createBlock(\`<span block-ref=\\"0\\"/>\`); let block3 = createBlock(\`<span block-ref=\\"0\\"/>\`);
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
const refs = this.__owl__.refs; const refs = ctx.__owl__.refs;
const ref1 = multiRefSetter(refs, \`coucou\`); const ref1 = multiRefSetter(refs, \`coucou\`);
let b2,b3; let b2,b3;
if (ctx['state'].value) { if (ctx['state'].value) {
@@ -43,14 +42,13 @@ exports[`refs refs and recursive templates 1`] = `
"function anonymous(app, bdom, helpers "function anonymous(app, bdom, helpers
) { ) {
let { text, createBlock, list, multi, html, toggler, comment } = bdom; let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { singleRefSetter } = helpers;
const comp1 = app.createComponent(\`Test\`, true, false, false, false); const comp1 = app.createComponent(\`Test\`, true, false, false, false);
let block1 = createBlock(\`<p block-ref=\\"0\\"><block-text-1/><block-child-0/></p>\`); let block1 = createBlock(\`<p block-ref=\\"0\\"><block-text-1/><block-child-0/></p>\`);
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
const refs = this.__owl__.refs; const refs = ctx.__owl__.refs;
const ref1 = singleRefSetter(refs, \`root\`); const ref1 = (el) => refs[\`root\`] = el;
let b2; let b2;
let txt1 = ctx['props'].tree.value; let txt1 = ctx['props'].tree.value;
if (ctx['props'].tree.child) { if (ctx['props'].tree.child) {
@@ -61,41 +59,19 @@ exports[`refs refs and recursive templates 1`] = `
}" }"
`; `;
exports[`refs refs and t-key 1`] = `
"function anonymous(app, bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { singleRefSetter } = helpers;
let block2 = createBlock(\`<button block-handler-0=\\"click\\"/>\`);
let block3 = createBlock(\`<p block-ref=\\"0\\"/>\`);
return function template(ctx, node, key = \\"\\") {
const refs = this.__owl__.refs;
const ref1 = singleRefSetter(refs, \`root\`);
const v1 = ctx['state'];
let hdlr1 = [()=>v1.renderId++, ctx];
const b2 = block2([hdlr1]);
const tKey_1 = ctx['state'].renderId;
const b3 = toggler(tKey_1, block3([ref1]));
return multi([b2, b3]);
}
}"
`;
exports[`refs refs are properly bound in slots 1`] = ` exports[`refs refs are properly bound in slots 1`] = `
"function anonymous(app, bdom, helpers "function anonymous(app, bdom, helpers
) { ) {
let { text, createBlock, list, multi, html, toggler, comment } = bdom; let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { capture, singleRefSetter, markRaw } = helpers; let { capture, markRaw } = helpers;
const comp1 = app.createComponent(\`Dialog\`, true, true, false, true); const comp1 = app.createComponent(\`Dialog\`, true, true, false, true);
let block1 = createBlock(\`<div><span class=\\"counter\\"><block-text-0/></span><block-child-0/></div>\`); let block1 = createBlock(\`<div><span class=\\"counter\\"><block-text-0/></span><block-child-0/></div>\`);
let block2 = createBlock(\`<button block-handler-0=\\"click\\" block-ref=\\"1\\">do something</button>\`); let block2 = createBlock(\`<button block-handler-0=\\"click\\" block-ref=\\"1\\">do something</button>\`);
function slot1(ctx, node, key = \\"\\") { function slot1(ctx, node, key = \\"\\") {
const refs = this.__owl__.refs; const refs = ctx.__owl__.refs;
const ref1 = singleRefSetter(refs, \`myButton\`); const ref1 = (el) => refs[\`myButton\`] = el;
let hdlr1 = [ctx['doSomething'], ctx]; let hdlr1 = [ctx['doSomething'], ctx];
return block2([hdlr1, ref1]); return block2([hdlr1, ref1]);
} }
@@ -103,7 +79,7 @@ exports[`refs refs are properly bound in slots 1`] = `
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
let txt1 = ctx['state'].val; let txt1 = ctx['state'].val;
const ctx1 = capture(ctx); const ctx1 = capture(ctx);
const b3 = comp1({slots: markRaw({'footer': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__1\`, node, this, null); const b3 = comp1({slots: markRaw({'footer': {__render: slot1, __ctx: ctx1}})}, key + \`__1\`, node, this, null);
return block1([txt1], [b3]); return block1([txt1], [b3]);
} }
}" }"
@@ -128,13 +104,13 @@ exports[`refs throws if there are 2 same refs at the same time 1`] = `
"function anonymous(app, bdom, helpers "function anonymous(app, bdom, helpers
) { ) {
let { text, createBlock, list, multi, html, toggler, comment } = bdom; let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { singleRefSetter, multiRefSetter } = helpers; let { multiRefSetter } = helpers;
let block2 = createBlock(\`<div block-ref=\\"0\\"/>\`); let block2 = createBlock(\`<div block-ref=\\"0\\"/>\`);
let block3 = createBlock(\`<span block-ref=\\"0\\"/>\`); let block3 = createBlock(\`<span block-ref=\\"0\\"/>\`);
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
const refs = this.__owl__.refs; const refs = ctx.__owl__.refs;
const ref1 = multiRefSetter(refs, \`coucou\`); const ref1 = multiRefSetter(refs, \`coucou\`);
const b2 = block2([ref1]); const b2 = block2([ref1]);
const b3 = block3([ref1]); const b3 = block3([ref1]);
+92 -144
View File
@@ -28,7 +28,7 @@ exports[`slots can define a default content 2`] = `
} }
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
const b3 = callSlot(ctx, node, key, 'default', false, {}, defaultContent1.bind(this)); const b3 = callSlot(ctx, node, key, 'default', false, {}, defaultContent1);
return block1([], [b3]); return block1([], [b3]);
} }
}" }"
@@ -55,7 +55,7 @@ exports[`slots can define and call slots 1`] = `
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx); const ctx1 = capture(ctx);
const b4 = comp1({slots: markRaw({'header': {__render: slot1.bind(this), __ctx: ctx1}, 'footer': {__render: slot2.bind(this), __ctx: ctx1}})}, key + \`__1\`, node, this, null); const b4 = comp1({slots: markRaw({'header': {__render: slot1, __ctx: ctx1}, 'footer': {__render: slot2, __ctx: ctx1}})}, key + \`__1\`, node, this, null);
return block1([], [b4]); return block1([], [b4]);
} }
}" }"
@@ -90,7 +90,7 @@ exports[`slots can define and call slots with bound params 1`] = `
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx); const ctx1 = capture(ctx);
return comp1({slots: markRaw({'abc': {__render: slot1.bind(this), __ctx: ctx1, getValue: bind(this, ctx['getValue'])}})}, key + \`__1\`, node, this, null); return comp1({slots: markRaw({'abc': {__render: slot1, __ctx: ctx1, getValue: bind(ctx, ctx['getValue'])}})}, key + \`__1\`, node, this, null);
} }
}" }"
`; `;
@@ -130,7 +130,7 @@ exports[`slots can define and call slots with params 1`] = `
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx); const ctx1 = capture(ctx);
const b4 = comp1({slots: markRaw({'header': {__render: slot1.bind(this), __ctx: ctx1, param: ctx['var']}, 'footer': {__render: slot2.bind(this), __ctx: ctx1, param: '5'}})}, key + \`__1\`, node, this, null); const b4 = comp1({slots: markRaw({'header': {__render: slot1, __ctx: ctx1, param: ctx['var']}, 'footer': {__render: slot2, __ctx: ctx1, param: '5'}})}, key + \`__1\`, node, this, null);
return block1([], [b4]); return block1([], [b4]);
} }
}" }"
@@ -158,22 +158,22 @@ exports[`slots can render node with t-ref and Component in same slot 1`] = `
"function anonymous(app, bdom, helpers "function anonymous(app, bdom, helpers
) { ) {
let { text, createBlock, list, multi, html, toggler, comment } = bdom; let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { singleRefSetter, markRaw } = helpers; let { markRaw } = helpers;
const comp1 = app.createComponent(\`Child\`, true, false, false, true); const comp1 = app.createComponent(\`Child\`, true, false, false, true);
const comp2 = app.createComponent(\`Child\`, true, true, false, true); const comp2 = app.createComponent(\`Child\`, true, true, false, true);
let block2 = createBlock(\`<div block-ref=\\"0\\"/>\`); let block2 = createBlock(\`<div block-ref=\\"0\\"/>\`);
function slot1(ctx, node, key = \\"\\") { function slot1(ctx, node, key = \\"\\") {
const refs = this.__owl__.refs; const refs = ctx.__owl__.refs;
const ref1 = singleRefSetter(refs, \`div\`); const ref1 = (el) => refs[\`div\`] = el;
const b2 = block2([ref1]); const b2 = block2([ref1]);
const b3 = comp1({}, key + \`__1\`, node, this, null); const b3 = comp1({}, key + \`__1\`, node, this, null);
return multi([b2, b3]); return multi([b2, b3]);
} }
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
return comp2({slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx}})}, key + \`__2\`, node, this, null); return comp2({slots: markRaw({'default': {__render: slot1, __ctx: ctx}})}, key + \`__2\`, node, this, null);
} }
}" }"
`; `;
@@ -226,7 +226,7 @@ exports[`slots can use component in default-content of t-slot 2`] = `
} }
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
return callSlot(ctx, node, key, 'default', false, {}, defaultContent1.bind(this)); return callSlot(ctx, node, key, 'default', false, {}, defaultContent1);
} }
}" }"
`; `;
@@ -266,7 +266,7 @@ exports[`slots can use t-call in default-content of t-slot 2`] = `
} }
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
return callSlot(ctx, node, key, 'default', false, {}, defaultContent1.bind(this)); return callSlot(ctx, node, key, 'default', false, {}, defaultContent1);
} }
}" }"
`; `;
@@ -282,47 +282,6 @@ exports[`slots can use t-call in default-content of t-slot 3`] = `
}" }"
`; `;
exports[`slots conditional slot 1`] = `
"function anonymous(app, bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { capture, markRaw } = helpers;
const comp1 = app.createComponent(\`Child\`, true, true, false, true);
function slot1(ctx, node, key = \\"\\") {
return text(\`blue\`);
}
function slot2(ctx, node, key = \\"\\") {
return text(\`red\`);
}
return function template(ctx, node, key = \\"\\") {
let slots1 = {};
if (ctx['state'].flag) {
const ctx1 = capture(ctx);
slots1['abc'] = {__render: slot1.bind(this), __ctx: ctx1};
} else {
const ctx2 = capture(ctx);
slots1['abc'] = {__render: slot2.bind(this), __ctx: ctx2};
}
return comp1({slots: markRaw(slots1)}, key + \`__1\`, node, this, null);
}
}"
`;
exports[`slots conditional slot 2`] = `
"function anonymous(app, bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { callSlot } = helpers;
return function template(ctx, node, key = \\"\\") {
return callSlot(ctx, node, key, 'abc', false, {});
}
}"
`;
exports[`slots content is the default slot (variation) 1`] = ` exports[`slots content is the default slot (variation) 1`] = `
"function anonymous(app, bdom, helpers "function anonymous(app, bdom, helpers
) { ) {
@@ -337,7 +296,7 @@ exports[`slots content is the default slot (variation) 1`] = `
} }
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
return comp1({slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx}})}, key + \`__1\`, node, this, null); return comp1({slots: markRaw({'default': {__render: slot1, __ctx: ctx}})}, key + \`__1\`, node, this, null);
} }
}" }"
`; `;
@@ -369,7 +328,7 @@ exports[`slots content is the default slot 1`] = `
} }
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
const b3 = comp1({slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx}})}, key + \`__1\`, node, this, null); const b3 = comp1({slots: markRaw({'default': {__render: slot1, __ctx: ctx}})}, key + \`__1\`, node, this, null);
return block1([], [b3]); return block1([], [b3]);
} }
}" }"
@@ -405,7 +364,7 @@ exports[`slots default content is not rendered if named slot is provided 1`] = `
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx); const ctx1 = capture(ctx);
const b3 = comp1({slots: markRaw({'header': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__1\`, node, this, null); const b3 = comp1({slots: markRaw({'header': {__render: slot1, __ctx: ctx1}})}, key + \`__1\`, node, this, null);
return block1([], [b3]); return block1([], [b3]);
} }
}" }"
@@ -424,7 +383,7 @@ exports[`slots default content is not rendered if named slot is provided 2`] = `
} }
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
const b3 = callSlot(ctx, node, key, 'header', false, {}, defaultContent1.bind(this)); const b3 = callSlot(ctx, node, key, 'header', false, {}, defaultContent1);
return block1([], [b3]); return block1([], [b3]);
} }
}" }"
@@ -444,7 +403,7 @@ exports[`slots default content is not rendered if slot is provided 1`] = `
} }
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
const b3 = comp1({slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx}})}, key + \`__1\`, node, this, null); const b3 = comp1({slots: markRaw({'default': {__render: slot1, __ctx: ctx}})}, key + \`__1\`, node, this, null);
return block1([], [b3]); return block1([], [b3]);
} }
}" }"
@@ -463,7 +422,7 @@ exports[`slots default content is not rendered if slot is provided 2`] = `
} }
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
const b3 = callSlot(ctx, node, key, 'default', false, {}, defaultContent1.bind(this)); const b3 = callSlot(ctx, node, key, 'default', false, {}, defaultContent1);
return block1([], [b3]); return block1([], [b3]);
} }
}" }"
@@ -484,7 +443,7 @@ exports[`slots default slot next to named slot, with default content 1`] = `
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx); const ctx1 = capture(ctx);
const b3 = comp1({slots: markRaw({'footer': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__1\`, node, this, null); const b3 = comp1({slots: markRaw({'footer': {__render: slot1, __ctx: ctx1}})}, key + \`__1\`, node, this, null);
return block1([], [b3]); return block1([], [b3]);
} }
}" }"
@@ -507,8 +466,8 @@ exports[`slots default slot next to named slot, with default content 2`] = `
} }
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
const b3 = callSlot(ctx, node, key, 'default', false, {}, defaultContent1.bind(this)); const b3 = callSlot(ctx, node, key, 'default', false, {}, defaultContent1);
const b5 = callSlot(ctx, node, key, 'footer', false, {}, defaultContent2.bind(this)); const b5 = callSlot(ctx, node, key, 'footer', false, {}, defaultContent2);
return block1([], [b3, b5]); return block1([], [b3, b5]);
} }
}" }"
@@ -526,7 +485,7 @@ exports[`slots default slot with params with - in it 1`] = `
} }
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
return comp1({slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx, __scope: \\"slotScope\\"}})}, key + \`__1\`, node, this, null); return comp1({slots: markRaw({'default': {__render: slot1, __ctx: ctx, __scope: \\"slotScope\\"}})}, key + \`__1\`, node, this, null);
} }
}" }"
`; `;
@@ -561,7 +520,7 @@ exports[`slots default slot with slot scope: shorthand syntax 1`] = `
} }
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
return comp1({slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx, __scope: \\"slotScope\\"}})}, key + \`__1\`, node, this, null); return comp1({slots: markRaw({'default': {__render: slot1, __ctx: ctx, __scope: \\"slotScope\\"}})}, key + \`__1\`, node, this, null);
} }
}" }"
`; `;
@@ -593,7 +552,7 @@ exports[`slots default slot work with text nodes (variation) 1`] = `
} }
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
return comp1({slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx}})}, key + \`__1\`, node, this, null); return comp1({slots: markRaw({'default': {__render: slot1, __ctx: ctx}})}, key + \`__1\`, node, this, null);
} }
}" }"
`; `;
@@ -624,7 +583,7 @@ exports[`slots default slot work with text nodes 1`] = `
} }
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
const b3 = comp1({slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx}})}, key + \`__1\`, node, this, null); const b3 = comp1({slots: markRaw({'default': {__render: slot1, __ctx: ctx}})}, key + \`__1\`, node, this, null);
return block1([], [b3]); return block1([], [b3]);
} }
}" }"
@@ -661,7 +620,7 @@ exports[`slots dynamic slot in multiple locations 1`] = `
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx); const ctx1 = capture(ctx);
return comp2({location: ctx['state'].location,slots: markRaw({'coffee': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__2\`, node, this, null); return comp2({location: ctx['state'].location,slots: markRaw({'coffee': {__render: slot1, __ctx: ctx1}})}, key + \`__2\`, node, this, null);
} }
}" }"
`; `;
@@ -727,7 +686,7 @@ exports[`slots dynamic t-slot call 1`] = `
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx); const ctx1 = capture(ctx);
const b6 = comp1({slots: markRaw({'slot1': {__render: slot1.bind(this), __ctx: ctx1}, 'slot2': {__render: slot2.bind(this), __ctx: ctx1}})}, key + \`__1\`, node, this, null); const b6 = comp1({slots: markRaw({'slot1': {__render: slot1, __ctx: ctx1}, 'slot2': {__render: slot2, __ctx: ctx1}})}, key + \`__1\`, node, this, null);
return block1([], [b6]); return block1([], [b6]);
} }
}" }"
@@ -774,7 +733,7 @@ exports[`slots dynamic t-slot call with default 1`] = `
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx); const ctx1 = capture(ctx);
const b6 = comp1({slots: markRaw({'slot1': {__render: slot1.bind(this), __ctx: ctx1}, 'slot2': {__render: slot2.bind(this), __ctx: ctx1}})}, key + \`__1\`, node, this, null); const b6 = comp1({slots: markRaw({'slot1': {__render: slot1, __ctx: ctx1}, 'slot2': {__render: slot2, __ctx: ctx1}})}, key + \`__1\`, node, this, null);
return block1([], [b6]); return block1([], [b6]);
} }
}" }"
@@ -794,7 +753,7 @@ exports[`slots dynamic t-slot call with default 2`] = `
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
let hdlr1 = [ctx['toggle'], ctx]; let hdlr1 = [ctx['toggle'], ctx];
const b3 = callSlot(ctx, node, key + \`__1\`, (ctx['current'].slot), true, {}, defaultContent1.bind(this)); const b3 = callSlot(ctx, node, key + \`__1\`, (ctx['current'].slot), true, {}, defaultContent1);
return block1([hdlr1], [b3]); return block1([hdlr1], [b3]);
} }
}" }"
@@ -812,7 +771,7 @@ exports[`slots fun: two calls to the same slot 1`] = `
} }
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
return comp1({slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx}})}, key + \`__1\`, node, this, null); return comp1({slots: markRaw({'default': {__render: slot1, __ctx: ctx}})}, key + \`__1\`, node, this, null);
} }
}" }"
`; `;
@@ -907,7 +866,7 @@ exports[`slots mix of slots, t-call, t-call with body, and giving own props chil
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx); const ctx1 = capture(ctx);
return comp1({slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__2\`, node, this, null); return comp1({slots: markRaw({'default': {__render: slot1, __ctx: ctx1}})}, key + \`__2\`, node, this, null);
} }
}" }"
`; `;
@@ -993,7 +952,7 @@ exports[`slots multiple roots are allowed in a default slot 1`] = `
} }
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
const b5 = comp1({slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx}})}, key + \`__1\`, node, this, null); const b5 = comp1({slots: markRaw({'default': {__render: slot1, __ctx: ctx}})}, key + \`__1\`, node, this, null);
return block1([], [b5]); return block1([], [b5]);
} }
}" }"
@@ -1033,7 +992,7 @@ exports[`slots multiple roots are allowed in a named slot 1`] = `
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx); const ctx1 = capture(ctx);
const b5 = comp1({slots: markRaw({'content': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__1\`, node, this, null); const b5 = comp1({slots: markRaw({'content': {__render: slot1, __ctx: ctx1}})}, key + \`__1\`, node, this, null);
return block1([], [b5]); return block1([], [b5]);
} }
}" }"
@@ -1073,7 +1032,7 @@ exports[`slots multiple slots containing components 1`] = `
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx); const ctx1 = capture(ctx);
return comp3({slots: markRaw({'s1': {__render: slot1.bind(this), __ctx: ctx1}, 's2': {__render: slot2.bind(this), __ctx: ctx1}})}, key + \`__3\`, node, this, null); return comp3({slots: markRaw({'s1': {__render: slot1, __ctx: ctx1}, 's2': {__render: slot2, __ctx: ctx1}})}, key + \`__3\`, node, this, null);
} }
}" }"
`; `;
@@ -1127,7 +1086,7 @@ exports[`slots named slot inside slot 1`] = `
function slot2(ctx, node, key = \\"\\") { function slot2(ctx, node, key = \\"\\") {
const ctx2 = capture(ctx); const ctx2 = capture(ctx);
return comp1({slots: markRaw({'brol': {__render: slot3.bind(this), __ctx: ctx2}})}, key + \`__1\`, node, this, null); return comp1({slots: markRaw({'brol': {__render: slot3, __ctx: ctx2}})}, key + \`__1\`, node, this, null);
} }
function slot3(ctx, node, key = \\"\\") { function slot3(ctx, node, key = \\"\\") {
@@ -1137,7 +1096,7 @@ exports[`slots named slot inside slot 1`] = `
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx); const ctx1 = capture(ctx);
const b5 = comp2({slots: markRaw({'brol': {__render: slot1.bind(this), __ctx: ctx1}, 'default': {__render: slot2.bind(this), __ctx: ctx1}})}, key + \`__2\`, node, this, null); const b5 = comp2({slots: markRaw({'brol': {__render: slot1, __ctx: ctx1}, 'default': {__render: slot2, __ctx: ctx1}})}, key + \`__2\`, node, this, null);
return block1([], [b5]); return block1([], [b5]);
} }
}" }"
@@ -1178,7 +1137,7 @@ exports[`slots named slot inside slot, part 3 1`] = `
function slot2(ctx, node, key = \\"\\") { function slot2(ctx, node, key = \\"\\") {
const ctx2 = capture(ctx); const ctx2 = capture(ctx);
return comp1({slots: markRaw({'brol': {__render: slot3.bind(this), __ctx: ctx2}})}, key + \`__1\`, node, this, null); return comp1({slots: markRaw({'brol': {__render: slot3, __ctx: ctx2}})}, key + \`__1\`, node, this, null);
} }
function slot3(ctx, node, key = \\"\\") { function slot3(ctx, node, key = \\"\\") {
@@ -1188,7 +1147,7 @@ exports[`slots named slot inside slot, part 3 1`] = `
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx); const ctx1 = capture(ctx);
const b5 = comp2({slots: markRaw({'brol': {__render: slot1.bind(this), __ctx: ctx1}, 'default': {__render: slot2.bind(this), __ctx: ctx1}})}, key + \`__2\`, node, this, null); const b5 = comp2({slots: markRaw({'brol': {__render: slot1, __ctx: ctx1}, 'default': {__render: slot2, __ctx: ctx1}})}, key + \`__2\`, node, this, null);
return block1([], [b5]); return block1([], [b5]);
} }
}" }"
@@ -1238,7 +1197,7 @@ exports[`slots named slots can define a default content 2`] = `
} }
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
const b3 = callSlot(ctx, node, key, 'header', false, {}, defaultContent1.bind(this)); const b3 = callSlot(ctx, node, key, 'header', false, {}, defaultContent1);
return block1([], [b3]); return block1([], [b3]);
} }
}" }"
@@ -1263,7 +1222,7 @@ exports[`slots named slots inside slot, again 1`] = `
function slot2(ctx, node, key = \\"\\") { function slot2(ctx, node, key = \\"\\") {
const ctx2 = capture(ctx); const ctx2 = capture(ctx);
return comp1({slots: markRaw({'brol2': {__render: slot3.bind(this), __ctx: ctx2}})}, key + \`__1\`, node, this, null); return comp1({slots: markRaw({'brol2': {__render: slot3, __ctx: ctx2}})}, key + \`__1\`, node, this, null);
} }
function slot3(ctx, node, key = \\"\\") { function slot3(ctx, node, key = \\"\\") {
@@ -1273,7 +1232,7 @@ exports[`slots named slots inside slot, again 1`] = `
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx); const ctx1 = capture(ctx);
const b5 = comp2({slots: markRaw({'brol1': {__render: slot1.bind(this), __ctx: ctx1}, 'default': {__render: slot2.bind(this), __ctx: ctx1}})}, key + \`__2\`, node, this, null); const b5 = comp2({slots: markRaw({'brol1': {__render: slot1, __ctx: ctx1}, 'default': {__render: slot2, __ctx: ctx1}})}, key + \`__2\`, node, this, null);
return block1([], [b5]); return block1([], [b5]);
} }
}" }"
@@ -1296,8 +1255,8 @@ exports[`slots named slots inside slot, again 2`] = `
} }
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
const b3 = callSlot(ctx, node, key, 'brol1', false, {}, defaultContent1.bind(this)); const b3 = callSlot(ctx, node, key, 'brol1', false, {}, defaultContent1);
const b5 = callSlot(ctx, node, key, 'brol2', false, {}, defaultContent2.bind(this)); const b5 = callSlot(ctx, node, key, 'brol2', false, {}, defaultContent2);
const b6 = callSlot(ctx, node, key, 'default', false, {}); const b6 = callSlot(ctx, node, key, 'default', false, {});
return block1([], [b3, b5, b6]); return block1([], [b3, b5, b6]);
} }
@@ -1316,7 +1275,7 @@ exports[`slots nested slots in same template 1`] = `
let block1 = createBlock(\`<span id=\\"parent\\"><block-child-0/></span>\`); let block1 = createBlock(\`<span id=\\"parent\\"><block-child-0/></span>\`);
function slot1(ctx, node, key = \\"\\") { function slot1(ctx, node, key = \\"\\") {
return comp2({slots: markRaw({'default': {__render: slot2.bind(this), __ctx: ctx}})}, key + \`__2\`, node, this, null); return comp2({slots: markRaw({'default': {__render: slot2, __ctx: ctx}})}, key + \`__2\`, node, this, null);
} }
function slot2(ctx, node, key = \\"\\") { function slot2(ctx, node, key = \\"\\") {
@@ -1324,7 +1283,7 @@ exports[`slots nested slots in same template 1`] = `
} }
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
const b4 = comp3({slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx}})}, key + \`__3\`, node, this, null); const b4 = comp3({slots: markRaw({'default': {__render: slot1, __ctx: ctx}})}, key + \`__3\`, node, this, null);
return block1([], [b4]); return block1([], [b4]);
} }
}" }"
@@ -1386,7 +1345,7 @@ exports[`slots nested slots: evaluation context and parented relationship 1`] =
} }
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
return comp2({slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx}})}, key + \`__2\`, node, this, null); return comp2({slots: markRaw({'default': {__render: slot1, __ctx: ctx}})}, key + \`__2\`, node, this, null);
} }
}" }"
`; `;
@@ -1403,7 +1362,7 @@ exports[`slots nested slots: evaluation context and parented relationship 2`] =
} }
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
return comp1({slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx}})}, key + \`__1\`, node, this, null); return comp1({slots: markRaw({'default': {__render: slot1, __ctx: ctx}})}, key + \`__1\`, node, this, null);
} }
}" }"
`; `;
@@ -1476,8 +1435,7 @@ exports[`slots simple default slot 1`] = `
} }
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
let slots1 = markRaw({'default': {__render: slot1.bind(this), __ctx: ctx}}); return comp1({slots: markRaw({'default': {__render: slot1, __ctx: ctx}})}, key + \`__1\`, node, this, null);
return comp1({slots: slots1}, key + \`__1\`, node, this, null);
} }
}" }"
`; `;
@@ -1515,7 +1473,7 @@ exports[`slots simple default slot with params 1`] = `
} }
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
return comp1({slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx}})}, key + \`__1\`, node, this, null); return comp1({slots: markRaw({'default': {__render: slot1, __ctx: ctx}})}, key + \`__1\`, node, this, null);
} }
}" }"
`; `;
@@ -1547,7 +1505,7 @@ exports[`slots simple default slot with params and bound function 1`] = `
} }
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
return comp1({slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx, __scope: \\"slotScope\\"}})}, key + \`__1\`, node, this, null); return comp1({slots: markRaw({'default': {__render: slot1, __ctx: ctx, __scope: \\"slotScope\\"}})}, key + \`__1\`, node, this, null);
} }
}" }"
`; `;
@@ -1559,7 +1517,7 @@ exports[`slots simple default slot with params and bound function 2`] = `
let { callSlot, bind } = helpers; let { callSlot, bind } = helpers;
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
return callSlot(ctx, node, key, 'default', false, {fn: bind(this, ctx['getValue'])}); return callSlot(ctx, node, key, 'default', false, {fn: bind(ctx, ctx['getValue'])});
} }
}" }"
`; `;
@@ -1576,8 +1534,7 @@ exports[`slots simple default slot, variation 1`] = `
} }
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
let slots1 = markRaw({'default': {__render: slot1.bind(this), __ctx: ctx}}); return comp1({slots: markRaw({'default': {__render: slot1, __ctx: ctx}})}, key + \`__1\`, node, this, null);
return comp1({slots: slots1}, key + \`__1\`, node, this, null);
} }
}" }"
`; `;
@@ -1613,7 +1570,7 @@ exports[`slots simple dynamic slot with slot scope 1`] = `
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx); const ctx1 = capture(ctx);
return comp1({slots: markRaw({'slotName': {__render: slot1.bind(this), __ctx: ctx1, __scope: \\"slotScope\\"}})}, key + \`__1\`, node, this, null); return comp1({slots: markRaw({'slotName': {__render: slot1, __ctx: ctx1, __scope: \\"slotScope\\"}})}, key + \`__1\`, node, this, null);
} }
}" }"
`; `;
@@ -1661,7 +1618,7 @@ exports[`slots simple named and empty slot -- 2 2`] = `
} }
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
const b3 = callSlot(ctx, node, key, 'myEmptySlot', false, {}, defaultContent1.bind(this)); const b3 = callSlot(ctx, node, key, 'myEmptySlot', false, {}, defaultContent1);
return block1([], [b3]); return block1([], [b3]);
} }
}" }"
@@ -1680,7 +1637,7 @@ exports[`slots simple named and empty slot 1`] = `
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx); const ctx1 = capture(ctx);
return comp1({slots: markRaw({'myEmptySlot': {}, 'default': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__1\`, node, this, null); return comp1({slots: markRaw({'myEmptySlot': {}, 'default': {__render: slot1, __ctx: ctx1}})}, key + \`__1\`, node, this, null);
} }
}" }"
`; `;
@@ -1720,7 +1677,7 @@ exports[`slots simple slot with slot scope 1`] = `
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx); const ctx1 = capture(ctx);
return comp1({slots: markRaw({'slotName': {__render: slot1.bind(this), __ctx: ctx1, __scope: \\"slotScope\\"}})}, key + \`__1\`, node, this, null); return comp1({slots: markRaw({'slotName': {__render: slot1, __ctx: ctx1, __scope: \\"slotScope\\"}})}, key + \`__1\`, node, this, null);
} }
}" }"
`; `;
@@ -1756,7 +1713,7 @@ exports[`slots slot and (inline) t-call 1`] = `
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx); const ctx1 = capture(ctx);
const b3 = comp1({slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__2\`, node, this, null); const b3 = comp1({slots: markRaw({'default': {__render: slot1, __ctx: ctx1}})}, key + \`__2\`, node, this, null);
return block1([], [b3]); return block1([], [b3]);
} }
}" }"
@@ -1806,7 +1763,7 @@ exports[`slots slot and t-call 1`] = `
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx); const ctx1 = capture(ctx);
const b3 = comp1({slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__2\`, node, this, null); const b3 = comp1({slots: markRaw({'default': {__render: slot1, __ctx: ctx1}})}, key + \`__2\`, node, this, null);
return block1([], [b3]); return block1([], [b3]);
} }
}" }"
@@ -1854,7 +1811,7 @@ exports[`slots slot and t-esc 1`] = `
} }
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
const b3 = comp1({slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx}})}, key + \`__1\`, node, this, null); const b3 = comp1({slots: markRaw({'default': {__render: slot1, __ctx: ctx}})}, key + \`__1\`, node, this, null);
return block1([], [b3]); return block1([], [b3]);
} }
}" }"
@@ -1892,7 +1849,7 @@ exports[`slots slot are properly rendered if inner props are changed 1`] = `
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
let hdlr1 = [ctx['inc'], ctx]; let hdlr1 = [ctx['inc'], ctx];
let txt1 = ctx['state'].val; let txt1 = ctx['state'].val;
const b3 = comp2({slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx}})}, key + \`__2\`, node, this, null); const b3 = comp2({slots: markRaw({'default': {__render: slot1, __ctx: ctx}})}, key + \`__2\`, node, this, null);
return block1([hdlr1, txt1], [b3]); return block1([hdlr1, txt1], [b3]);
} }
}" }"
@@ -1940,7 +1897,7 @@ exports[`slots slot content has different key from other content -- dynamic slot
} }
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
return comp2({slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx}})}, key + \`__2\`, node, this, null); return comp2({slots: markRaw({'default': {__render: slot1, __ctx: ctx}})}, key + \`__2\`, node, this, null);
} }
}" }"
`; `;
@@ -1988,7 +1945,7 @@ exports[`slots slot content has different key from other content -- static slot
} }
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
return comp2({slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx}})}, key + \`__2\`, node, this, null); return comp2({slots: markRaw({'default': {__render: slot1, __ctx: ctx}})}, key + \`__2\`, node, this, null);
} }
}" }"
`; `;
@@ -2035,14 +1992,13 @@ exports[`slots slot content is bound to caller (variation) 1`] = `
ctx = Object.create(ctx); ctx = Object.create(ctx);
ctx[isBoundary] = 1 ctx[isBoundary] = 1
setContextValue(ctx, \\"var\\", 1); setContextValue(ctx, \\"var\\", 1);
const v1 = ctx['this']; let hdlr1 = [()=>this.inc(), ctx];
let hdlr1 = [()=>v1.inc(), ctx];
return block1([hdlr1]); return block1([hdlr1]);
} }
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx); const ctx1 = capture(ctx);
return comp1({slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__1\`, node, this, null); return comp1({slots: markRaw({'default': {__render: slot1, __ctx: ctx1}})}, key + \`__1\`, node, this, null);
} }
}" }"
`; `;
@@ -2077,7 +2033,7 @@ exports[`slots slot content is bound to caller 1`] = `
} }
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
return comp1({slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx}})}, key + \`__1\`, node, this, null); return comp1({slots: markRaw({'default': {__render: slot1, __ctx: ctx}})}, key + \`__1\`, node, this, null);
} }
}" }"
`; `;
@@ -2112,7 +2068,7 @@ exports[`slots slot in multiple locations 1`] = `
} }
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
return comp2({location: ctx['state'].location,slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx}})}, key + \`__2\`, node, this, null); return comp2({location: ctx['state'].location,slots: markRaw({'default': {__render: slot1, __ctx: ctx}})}, key + \`__2\`, node, this, null);
} }
}" }"
`; `;
@@ -2167,7 +2123,7 @@ exports[`slots slot in t-foreach locations 1`] = `
} }
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
return comp2({list: ctx['state'].list,slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx}})}, key + \`__2\`, node, this, null); return comp2({list: ctx['state'].list,slots: markRaw({'default': {__render: slot1, __ctx: ctx}})}, key + \`__2\`, node, this, null);
} }
}" }"
`; `;
@@ -2224,7 +2180,7 @@ exports[`slots slot preserves properly parented relationship 1`] = `
} }
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
const b3 = comp2({slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx}})}, key + \`__2\`, node, this, null); const b3 = comp2({slots: markRaw({'default': {__render: slot1, __ctx: ctx}})}, key + \`__2\`, node, this, null);
return block1([], [b3]); return block1([], [b3]);
} }
}" }"
@@ -2269,7 +2225,7 @@ exports[`slots slot preserves properly parented relationship, even through t-cal
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx); const ctx1 = capture(ctx);
const b3 = comp1({slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__2\`, node, this, null); const b3 = comp1({slots: markRaw({'default': {__render: slot1, __ctx: ctx1}})}, key + \`__2\`, node, this, null);
return block1([], [b3]); return block1([], [b3]);
} }
}" }"
@@ -2330,7 +2286,7 @@ exports[`slots slot with slot scope and t-props 1`] = `
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx); const ctx1 = capture(ctx);
return comp1({slots: markRaw({'slotName': {__render: slot1.bind(this), __ctx: ctx1, __scope: \\"info\\"}})}, key + \`__1\`, node, this, null); return comp1({slots: markRaw({'slotName': {__render: slot1, __ctx: ctx1, __scope: \\"info\\"}})}, key + \`__1\`, node, this, null);
} }
}" }"
`; `;
@@ -2359,7 +2315,7 @@ exports[`slots slots and wrapper components 1`] = `
} }
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
return comp1({slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx}})}, key + \`__1\`, node, this, null); return comp1({slots: markRaw({'default': {__render: slot1, __ctx: ctx}})}, key + \`__1\`, node, this, null);
} }
}" }"
`; `;
@@ -2403,14 +2359,13 @@ exports[`slots slots are properly bound to correct component 2`] = `
ctx = Object.create(ctx); ctx = Object.create(ctx);
ctx[isBoundary] = 1 ctx[isBoundary] = 1
setContextValue(ctx, \\"var\\", 1); setContextValue(ctx, \\"var\\", 1);
const v1 = ctx['this']; let hdlr1 = [()=>this.increment(), ctx];
let hdlr1 = [()=>v1.increment(), ctx];
let txt1 = ctx['state'].value; let txt1 = ctx['state'].value;
return block1([hdlr1, txt1]); return block1([hdlr1, txt1]);
} }
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
return callSlot(ctx, node, key, 'default', false, {}, defaultContent1.bind(this)); return callSlot(ctx, node, key, 'default', false, {}, defaultContent1);
} }
}" }"
`; `;
@@ -2433,7 +2388,7 @@ exports[`slots slots are rendered with proper context 1`] = `
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
let txt1 = ctx['state'].val; let txt1 = ctx['state'].val;
const ctx1 = capture(ctx); const ctx1 = capture(ctx);
const b3 = comp1({slots: markRaw({'footer': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__1\`, node, this, null); const b3 = comp1({slots: markRaw({'footer': {__render: slot1, __ctx: ctx1}})}, key + \`__1\`, node, this, null);
return block1([txt1], [b3]); return block1([txt1], [b3]);
} }
}" }"
@@ -2477,7 +2432,7 @@ exports[`slots slots are rendered with proper context, part 2 1`] = `
ctx[\`user\`] = v_block2[i1]; ctx[\`user\`] = v_block2[i1];
const key1 = ctx['user'].id; const key1 = ctx['user'].id;
const ctx1 = capture(ctx); const ctx1 = capture(ctx);
const b7 = comp1({to: '/user/'+ctx['user'].id,slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__1__\${key1}\`, node, this, null); const b7 = comp1({to: '/user/'+ctx['user'].id,slots: markRaw({'default': {__render: slot1, __ctx: ctx1}})}, key + \`__1__\${key1}\`, node, this, null);
c_block2[i1] = withKey(block3([], [b7]), key1); c_block2[i1] = withKey(block3([], [b7]), key1);
} }
const b2 = list(c_block2); const b2 = list(c_block2);
@@ -2526,7 +2481,7 @@ exports[`slots slots are rendered with proper context, part 3 1`] = `
const key1 = ctx['user'].id; const key1 = ctx['user'].id;
setContextValue(ctx, \\"userdescr\\", 'User '+ctx['user'].name); setContextValue(ctx, \\"userdescr\\", 'User '+ctx['user'].name);
const ctx1 = capture(ctx); const ctx1 = capture(ctx);
const b5 = comp1({to: '/user/'+ctx['user'].id,slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__1__\${key1}\`, node, this, null); const b5 = comp1({to: '/user/'+ctx['user'].id,slots: markRaw({'default': {__render: slot1, __ctx: ctx1}})}, key + \`__1__\${key1}\`, node, this, null);
c_block2[i1] = withKey(block3([], [b5]), key1); c_block2[i1] = withKey(block3([], [b5]), key1);
} }
const b2 = list(c_block2); const b2 = list(c_block2);
@@ -2569,7 +2524,7 @@ exports[`slots slots are rendered with proper context, part 4 1`] = `
ctx[isBoundary] = 1 ctx[isBoundary] = 1
setContextValue(ctx, \\"userdescr\\", 'User '+ctx['state'].user.name); setContextValue(ctx, \\"userdescr\\", 'User '+ctx['state'].user.name);
const ctx1 = capture(ctx); const ctx1 = capture(ctx);
const b3 = comp1({to: '/user/'+ctx['state'].user.id,slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__1\`, node, this, null); const b3 = comp1({to: '/user/'+ctx['state'].user.id,slots: markRaw({'default': {__render: slot1, __ctx: ctx1}})}, key + \`__1\`, node, this, null);
return block1([], [b3]); return block1([], [b3]);
} }
}" }"
@@ -2611,7 +2566,7 @@ exports[`slots slots in slots, with vars 1`] = `
ctx[isBoundary] = 1 ctx[isBoundary] = 1
setContextValue(ctx, \\"test\\", ctx['state'].name); setContextValue(ctx, \\"test\\", ctx['state'].name);
const ctx1 = capture(ctx); const ctx1 = capture(ctx);
const b3 = comp1({slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__1\`, node, this, null); const b3 = comp1({slots: markRaw({'default': {__render: slot1, __ctx: ctx1}})}, key + \`__1\`, node, this, null);
return block1([], [b3]); return block1([], [b3]);
} }
}" }"
@@ -2631,7 +2586,7 @@ exports[`slots slots in slots, with vars 2`] = `
} }
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
const b3 = comp1({slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx}})}, key + \`__1\`, node, this, null); const b3 = comp1({slots: markRaw({'default': {__render: slot1, __ctx: ctx}})}, key + \`__1\`, node, this, null);
return block1([], [b3]); return block1([], [b3]);
} }
}" }"
@@ -2673,7 +2628,7 @@ exports[`slots slots in t-foreach and re-rendering 1`] = `
ctx[\`n_index\`] = i1; ctx[\`n_index\`] = i1;
const key1 = ctx['n_index']; const key1 = ctx['n_index'];
const ctx1 = capture(ctx); const ctx1 = capture(ctx);
c_block2[i1] = withKey(comp1({slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__1__\${key1}\`, node, this, null), key1); c_block2[i1] = withKey(comp1({slots: markRaw({'default': {__render: slot1, __ctx: ctx1}})}, key + \`__1__\${key1}\`, node, this, null), key1);
} }
const b2 = list(c_block2); const b2 = list(c_block2);
return block1([], [b2]); return block1([], [b2]);
@@ -2728,7 +2683,7 @@ exports[`slots slots in t-foreach in t-foreach 1`] = `
ctx[\`node2\`] = v_block6[i2]; ctx[\`node2\`] = v_block6[i2];
const key2 = ctx['node2'].key; const key2 = ctx['node2'].key;
const ctx1 = capture(ctx); const ctx1 = capture(ctx);
c_block6[i2] = withKey(comp1({slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__1__\${key1}__\${key2}\`, node, this, null), key2); c_block6[i2] = withKey(comp1({slots: markRaw({'default': {__render: slot1, __ctx: ctx1}})}, key + \`__1__\${key1}__\${key2}\`, node, this, null), key2);
} }
ctx = ctx.__proto__; ctx = ctx.__proto__;
const b6 = list(c_block6); const b6 = list(c_block6);
@@ -2780,7 +2735,7 @@ exports[`slots slots in t-foreach with t-set and re-rendering 1`] = `
const key1 = ctx['n_index']; const key1 = ctx['n_index'];
setContextValue(ctx, \\"dummy\\", ctx['n_index']); setContextValue(ctx, \\"dummy\\", ctx['n_index']);
const ctx1 = capture(ctx); const ctx1 = capture(ctx);
c_block2[i1] = withKey(comp1({slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__1__\${key1}\`, node, this, null), key1); c_block2[i1] = withKey(comp1({slots: markRaw({'default': {__render: slot1, __ctx: ctx1}})}, key + \`__1__\${key1}\`, node, this, null), key1);
} }
const b2 = list(c_block2); const b2 = list(c_block2);
return block1([], [b2]); return block1([], [b2]);
@@ -2820,7 +2775,7 @@ exports[`slots t-debug on a t-set-slot (defining a slot) 1`] = `
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx); const ctx1 = capture(ctx);
const b3 = comp1({slots: markRaw({'content': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__1\`, node, this, null); const b3 = comp1({slots: markRaw({'content': {__render: slot1, __ctx: ctx1}})}, key + \`__1\`, node, this, null);
return block1([], [b3]); return block1([], [b3]);
} }
}" }"
@@ -2859,7 +2814,7 @@ exports[`slots t-set t-value in a slot 1`] = `
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx); const ctx1 = capture(ctx);
const b3 = comp1({slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__1\`, node, this, null); const b3 = comp1({slots: markRaw({'default': {__render: slot1, __ctx: ctx1}})}, key + \`__1\`, node, this, null);
return block1([], [b3]); return block1([], [b3]);
} }
}" }"
@@ -2884,23 +2839,16 @@ exports[`slots t-set-slot=default has priority over rest of the content 1`] = `
"function anonymous(app, bdom, helpers "function anonymous(app, bdom, helpers
) { ) {
let { text, createBlock, list, multi, html, toggler, comment } = bdom; let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { markRaw, capture } = helpers; let { capture, markRaw } = helpers;
const comp1 = app.createComponent(\`Child\`, true, true, false, true); const comp1 = app.createComponent(\`Child\`, true, true, false, true);
function slot1(ctx, node, key = \\"\\") { function slot1(ctx, node, key = \\"\\") {
const b2 = text(\` some text \`);
const ctx2 = capture(ctx);
return multi([b2]);
}
function slot2(ctx, node, key = \\"\\") {
return text(\`some other text\`); return text(\`some other text\`);
} }
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx); const ctx1 = capture(ctx);
let slots1 = markRaw({'default': {__render: slot1.bind(this), __ctx: ctx1}}); return comp1({slots: markRaw({'default': {__render: slot1, __ctx: ctx1}})}, key + \`__1\`, node, this, null);
return comp1({slots: slots1}, key + \`__1\`, node, this, null);
} }
}" }"
`; `;
@@ -2958,7 +2906,7 @@ exports[`slots t-slot in recursive templates 1`] = `
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx); const ctx1 = capture(ctx);
return comp1({slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__2\`, node, this, null); return comp1({slots: markRaw({'default': {__render: slot1, __ctx: ctx1}})}, key + \`__2\`, node, this, null);
} }
}" }"
`; `;
@@ -2993,7 +2941,7 @@ exports[`slots t-slot nested within another slot 1`] = `
} }
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
const b3 = comp2({slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx}})}, key + \`__2\`, node, this, null); const b3 = comp2({slots: markRaw({'default': {__render: slot1, __ctx: ctx}})}, key + \`__2\`, node, this, null);
return block1([], [b3]); return block1([], [b3]);
} }
}" }"
@@ -3010,7 +2958,7 @@ exports[`slots t-slot nested within another slot 2`] = `
let block1 = createBlock(\`<span id=\\"c2\\"><block-child-0/></span>\`); let block1 = createBlock(\`<span id=\\"c2\\"><block-child-0/></span>\`);
function slot1(ctx, node, key = \\"\\") { function slot1(ctx, node, key = \\"\\") {
return comp1({slots: markRaw({'default': {__render: slot2.bind(this), __ctx: ctx}})}, key + \`__1\`, node, this, null); return comp1({slots: markRaw({'default': {__render: slot2, __ctx: ctx}})}, key + \`__1\`, node, this, null);
} }
function slot2(ctx, node, key = \\"\\") { function slot2(ctx, node, key = \\"\\") {
@@ -3018,7 +2966,7 @@ exports[`slots t-slot nested within another slot 2`] = `
} }
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
const b4 = comp2({slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx}})}, key + \`__2\`, node, this, null); const b4 = comp2({slots: markRaw({'default': {__render: slot1, __ctx: ctx}})}, key + \`__2\`, node, this, null);
return block1([], [b4]); return block1([], [b4]);
} }
}" }"
@@ -3081,7 +3029,7 @@ exports[`slots t-slot scope context 1`] = `
} }
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
return comp1({slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx}})}, key + \`__1\`, node, this, null); return comp1({slots: markRaw({'default': {__render: slot1, __ctx: ctx}})}, key + \`__1\`, node, this, null);
} }
}" }"
`; `;
@@ -3102,7 +3050,7 @@ exports[`slots t-slot scope context 2`] = `
} }
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
return comp1({slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx}})}, key + \`__1\`, node, this, null); return comp1({slots: markRaw({'default': {__render: slot1, __ctx: ctx}})}, key + \`__1\`, node, this, null);
} }
}" }"
`; `;
@@ -3136,7 +3084,7 @@ exports[`slots t-slot within dynamic t-call 1`] = `
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx); const ctx1 = capture(ctx);
const b3 = comp1({slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__2\`, node, this, null); const b3 = comp1({slots: markRaw({'default': {__render: slot1, __ctx: ctx1}})}, key + \`__2\`, node, this, null);
return block1([], [b3]); return block1([], [b3]);
} }
}" }"
@@ -3200,7 +3148,7 @@ exports[`slots template can just return a slot 1`] = `
} }
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
const b3 = comp2({slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx}})}, key + \`__2\`, node, this, null); const b3 = comp2({slots: markRaw({'default': {__render: slot1, __ctx: ctx}})}, key + \`__2\`, node, this, null);
return block1([], [b3]); return block1([], [b3]);
} }
}" }"
@@ -363,7 +363,7 @@ exports[`style and class handling error in subcomponent with class 2`] = `
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
let attr1 = ctx['props'].class; let attr1 = ctx['props'].class;
let txt1 = ctx['this'].will.crash; let txt1 = this.will.crash;
return block1([attr1, txt1]); return block1([attr1, txt1]);
} }
}" }"
@@ -109,8 +109,7 @@ exports[`t-call handlers are properly bound through a dynamic t-call 2`] = `
let block1 = createBlock(\`<p block-handler-0=\\"click\\">lucas</p>\`); let block1 = createBlock(\`<p block-handler-0=\\"click\\">lucas</p>\`);
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
const v1 = ctx['this']; let hdlr1 = [()=>this.update(), ctx];
let hdlr1 = [()=>v1.update(), ctx];
return block1([hdlr1]); return block1([hdlr1]);
} }
}" }"
@@ -169,9 +168,8 @@ exports[`t-call handlers with arguments are properly bound through a t-call 2`]
let block1 = createBlock(\`<p block-handler-0=\\"click\\">lucas</p>\`); let block1 = createBlock(\`<p block-handler-0=\\"click\\">lucas</p>\`);
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
const v1 = ctx['this']; const v1 = ctx['a'];
const v2 = ctx['a']; let hdlr1 = [()=>this.update(v1), ctx];
let hdlr1 = [()=>v1.update(v2), ctx];
return block1([hdlr1]); return block1([hdlr1]);
} }
}" }"
@@ -289,7 +287,7 @@ exports[`t-call recursive t-call binding this -- static t-call 2`] = `
ctx[isBoundary] = 1 ctx[isBoundary] = 1
let b2; let b2;
if (ctx['level']<2) { if (ctx['level']<2) {
let hdlr1 = [\\"stop\\", ctx['onClicked'].bind(ctx['this']), ctx]; let hdlr1 = [\\"stop\\", ctx['onClicked'].bind(this), ctx];
let txt1 = ctx['level']; let txt1 = ctx['level'];
const b3 = block3([hdlr1, txt1]); const b3 = block3([hdlr1, txt1]);
ctx = Object.create(ctx); ctx = Object.create(ctx);
@@ -516,134 +514,3 @@ exports[`t-call t-call with t-call-context, simple use 2`] = `
} }
}" }"
`; `;
exports[`t-call t-call-context: ComponentNode is not looked up in the context 1`] = `
"function anonymous(app, bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
const callTemplate_1 = app.getTemplate(\`someTemplate\`);
return function template(ctx, node, key = \\"\\") {
let ctx1 = {method:function(){}};
return callTemplate_1.call(this, ctx1, node, key + \`__1\`);
}
}"
`;
exports[`t-call t-call-context: ComponentNode is not looked up in the context 2`] = `
"function anonymous(app, bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { singleRefSetter, bind, capture, isBoundary, withDefault, setContextValue, markRaw } = helpers;
const comp1 = app.createComponent(\`Child\`, true, true, false, false);
let block2 = createBlock(\`<div block-ref=\\"0\\">outside slot</div>\`);
let block4 = createBlock(\`<div block-ref=\\"0\\">I'm the default slot</div>\`);
let block5 = createBlock(\`<div><block-text-0/></div>\`);
function slot1(ctx, node, key = \\"\\") {
const refs = this.__owl__.refs;
const ref2 = singleRefSetter(refs, \`myRef2\`);
ctx = Object.create(ctx);
ctx[isBoundary] = 1
const b4 = block4([ref2]);
setContextValue(ctx, \\"test\\", 3);
let txt1 = ctx['test'];
const b5 = block5([txt1]);
return multi([b4, b5]);
}
return function template(ctx, node, key = \\"\\") {
const refs = this.__owl__.refs;
const ref1 = singleRefSetter(refs, \`myRef\`);
const b2 = block2([ref1]);
const ctx1 = capture(ctx);
const b6 = comp1({prop: bind(this, ctx['method']),slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__1\`, node, this, null);
return multi([b2, b6]);
}
}"
`;
exports[`t-call t-call-context: ComponentNode is not looked up in the context 3`] = `
"function anonymous(app, bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { callSlot } = helpers;
return function template(ctx, node, key = \\"\\") {
return callSlot(ctx, node, key, 'default', false, {});
}
}"
`;
exports[`t-call t-call-context: slots don't make component available again when context is captured 1`] = `
"function anonymous(app, bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
const callTemplate_1 = app.getTemplate(\`template\`);
return function template(ctx, node, key = \\"\\") {
let ctx1 = {};
return callTemplate_1.call(this, ctx1, node, key + \`__1\`);
}
}"
`;
exports[`t-call t-call-context: slots don't make component available again when context is captured 2`] = `
"function anonymous(app, bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { isBoundary, withDefault, setContextValue, capture, markRaw } = helpers;
const comp1 = app.createComponent(\`Child\`, true, true, false, true);
function slot1(ctx, node, key = \\"\\") {
return text(ctx['someValue']);
}
return function template(ctx, node, key = \\"\\") {
ctx = Object.create(ctx);
ctx[isBoundary] = 1
setContextValue(ctx, \\"dummy\\", 0);
const ctx1 = capture(ctx);
const props1 = {slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx1}})};
helpers.validateProps(\`Child\`, props1, this);
return comp1(props1, key + \`__1\`, node, this, null);
}
}"
`;
exports[`t-call t-call-context: slots don't make component available again when context is captured 3`] = `
"function anonymous(app, bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { callSlot } = helpers;
return function template(ctx, node, key = \\"\\") {
return callSlot(ctx, node, key, 'default', false, {});
}
}"
`;
exports[`t-call t-call-context: this is not available inside t-call-context 1`] = `
"function anonymous(app, bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
const callTemplate_1 = app.getTemplate(\`someTemplate\`);
return function template(ctx, node, key = \\"\\") {
let ctx1 = {};
return callTemplate_1.call(this, ctx1, node, key + \`__1\`);
}
}"
`;
exports[`t-call t-call-context: this is not available inside t-call-context 2`] = `
"function anonymous(app, bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
return function template(ctx, node, key = \\"\\") {
return text(ctx['this']);
}
}"
`;
@@ -10,7 +10,7 @@ exports[`t-component can switch between dynamic components without the need for
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
const Comp1 = ctx['constructor'].components[ctx['state'].child]; const Comp1 = ctx['constructor'].components[ctx['state'].child];
const b2 = toggler(Comp1, comp1({}, (Comp1).name + key + \`__1\`, node, this, Comp1)); const b2 = toggler(Comp1, comp1({}, key + \`__1\`, node, this, Comp1));
return block1([], [b2]); return block1([], [b2]);
} }
}" }"
@@ -51,7 +51,7 @@ exports[`t-component can use dynamic components (the class) if given (with diffe
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
const tKey_1 = ctx['state'].child; const tKey_1 = ctx['state'].child;
const Comp1 = ctx['myComponent']; const Comp1 = ctx['myComponent'];
return toggler(tKey_1, toggler(Comp1, comp1({}, (Comp1).name + tKey_1 + key + \`__1\`, node, this, Comp1))); return toggler(tKey_1, toggler(Comp1, comp1({}, tKey_1 + key + \`__1\`, node, this, Comp1)));
} }
}" }"
`; `;
@@ -91,7 +91,7 @@ exports[`t-component can use dynamic components (the class) if given 1`] = `
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
const tKey_1 = ctx['state'].child; const tKey_1 = ctx['state'].child;
const Comp1 = ctx['myComponent']; const Comp1 = ctx['myComponent'];
return toggler(tKey_1, toggler(Comp1, comp1({}, (Comp1).name + tKey_1 + key + \`__1\`, node, this, Comp1))); return toggler(tKey_1, toggler(Comp1, comp1({}, tKey_1 + key + \`__1\`, node, this, Comp1)));
} }
}" }"
`; `;
@@ -132,7 +132,7 @@ exports[`t-component modifying a sub widget 1`] = `
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
const Comp1 = ctx['Counter']; const Comp1 = ctx['Counter'];
const b2 = toggler(Comp1, comp1({}, (Comp1).name + key + \`__1\`, node, this, Comp1)); const b2 = toggler(Comp1, comp1({}, key + \`__1\`, node, this, Comp1));
return block1([], [b2]); return block1([], [b2]);
} }
}" }"
@@ -162,7 +162,7 @@ exports[`t-component switching dynamic component 1`] = `
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
const Comp1 = ctx['Child']; const Comp1 = ctx['Child'];
return toggler(Comp1, comp1({}, (Comp1).name + key + \`__1\`, node, this, Comp1)); return toggler(Comp1, comp1({}, key + \`__1\`, node, this, Comp1));
} }
}" }"
`; `;
@@ -199,7 +199,7 @@ exports[`t-component t-component works in simple case 1`] = `
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
const Comp1 = ctx['Child']; const Comp1 = ctx['Child'];
return toggler(Comp1, comp1({}, (Comp1).name + key + \`__1\`, node, this, Comp1)); return toggler(Comp1, comp1({}, key + \`__1\`, node, this, Comp1));
} }
}" }"
`; `;
@@ -176,7 +176,7 @@ exports[`list of components order is correct when slots are not of same type 1`]
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx); const ctx1 = capture(ctx);
return comp1({slots: markRaw({'a': {__render: slot1.bind(this), __ctx: ctx1, active: !ctx['state'].active}, 'b': {__render: slot2.bind(this), __ctx: ctx1, active: true}, 'c': {__render: slot3.bind(this), __ctx: ctx1, active: ctx['state'].active}})}, key + \`__1\`, node, this, null); return comp1({slots: markRaw({'a': {__render: slot1, __ctx: ctx1, active: !ctx['state'].active}, 'b': {__render: slot2, __ctx: ctx1, active: true}, 'c': {__render: slot3, __ctx: ctx1, active: ctx['state'].active}})}, key + \`__1\`, node, this, null);
} }
}" }"
`; `;
@@ -120,15 +120,15 @@ exports[`t-model directive can also define t-on directive on same event, part 1
let { text, createBlock, list, multi, html, toggler, comment } = bdom; let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { toNumber } = helpers; let { toNumber } = helpers;
let block1 = createBlock(\`<div><input block-attribute-0=\\"value\\" block-handler-1=\\"input\\" block-handler-2=\\"input\\"/></div>\`); let block1 = createBlock(\`<div><input block-handler-0=\\"input\\" block-attribute-1=\\"value\\" block-handler-2=\\"input\\"/></div>\`);
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
let hdlr1 = [ctx['onInput'], ctx];
const bExpr1 = ctx['state']; const bExpr1 = ctx['state'];
const expr1 = 'text'; const expr1 = 'text';
let attr1 = bExpr1[expr1]; let attr1 = bExpr1[expr1];
let hdlr1 = [(ev) => { bExpr1[expr1] = ev.target.value; }]; let hdlr2 = [(ev) => { bExpr1[expr1] = ev.target.value; }];
let hdlr2 = [ctx['onInput'], ctx]; return block1([hdlr1, attr1, hdlr2]);
return block1([attr1, hdlr1, hdlr2]);
} }
}" }"
`; `;
@@ -139,25 +139,25 @@ exports[`t-model directive can also define t-on directive on same event, part 2
let { text, createBlock, list, multi, html, toggler, comment } = bdom; let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { toNumber } = helpers; let { toNumber } = helpers;
let block1 = createBlock(\`<div><input type=\\"radio\\" id=\\"one\\" value=\\"One\\" block-attribute-0=\\"checked\\" block-handler-1=\\"click\\" block-handler-2=\\"click\\"/><input type=\\"radio\\" id=\\"two\\" value=\\"Two\\" block-attribute-3=\\"checked\\" block-handler-4=\\"click\\" block-handler-5=\\"click\\"/><input type=\\"radio\\" id=\\"three\\" value=\\"Three\\" block-attribute-6=\\"checked\\" block-handler-7=\\"click\\" block-handler-8=\\"click\\"/></div>\`); let block1 = createBlock(\`<div><input type=\\"radio\\" id=\\"one\\" value=\\"One\\" block-handler-0=\\"click\\" block-attribute-1=\\"checked\\" block-handler-2=\\"click\\"/><input type=\\"radio\\" id=\\"two\\" value=\\"Two\\" block-handler-3=\\"click\\" block-attribute-4=\\"checked\\" block-handler-5=\\"click\\"/><input type=\\"radio\\" id=\\"three\\" value=\\"Three\\" block-handler-6=\\"click\\" block-attribute-7=\\"checked\\" block-handler-8=\\"click\\"/></div>\`);
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
let hdlr1 = [ctx['onClick'], ctx];
const bExpr1 = ctx['state']; const bExpr1 = ctx['state'];
const expr1 = 'choice'; const expr1 = 'choice';
let attr1 = bExpr1[expr1] === 'One'; let attr1 = bExpr1[expr1] === 'One';
let hdlr1 = [(ev) => { bExpr1[expr1] = ev.target.value; }]; let hdlr2 = [(ev) => { bExpr1[expr1] = ev.target.value; }];
let hdlr2 = [ctx['onClick'], ctx]; let hdlr3 = [ctx['onClick'], ctx];
const bExpr2 = ctx['state']; const bExpr2 = ctx['state'];
const expr2 = 'choice'; const expr2 = 'choice';
let attr2 = bExpr2[expr2] === 'Two'; let attr2 = bExpr2[expr2] === 'Two';
let hdlr3 = [(ev) => { bExpr2[expr2] = ev.target.value; }]; let hdlr4 = [(ev) => { bExpr2[expr2] = ev.target.value; }];
let hdlr4 = [ctx['onClick'], ctx]; let hdlr5 = [ctx['onClick'], ctx];
const bExpr3 = ctx['state']; const bExpr3 = ctx['state'];
const expr3 = 'choice'; const expr3 = 'choice';
let attr3 = bExpr3[expr3] === 'Three'; let attr3 = bExpr3[expr3] === 'Three';
let hdlr5 = [(ev) => { bExpr3[expr3] = ev.target.value; }]; let hdlr6 = [(ev) => { bExpr3[expr3] = ev.target.value; }];
let hdlr6 = [ctx['onClick'], ctx]; return block1([hdlr1, attr1, hdlr2, hdlr3, attr2, hdlr4, hdlr5, attr3, hdlr6]);
return block1([attr1, hdlr1, hdlr2, attr2, hdlr3, hdlr4, attr3, hdlr5, hdlr6]);
} }
}" }"
`; `;
@@ -410,25 +410,6 @@ exports[`t-model directive on an textarea 1`] = `
}" }"
`; `;
exports[`t-model directive t-model is applied before t-on-input 1`] = `
"function anonymous(app, bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { toNumber } = helpers;
let block1 = createBlock(\`<div><input block-attribute-0=\\"value\\" block-handler-1=\\"input\\" block-handler-2=\\"input\\"/></div>\`);
return function template(ctx, node, key = \\"\\") {
const bExpr1 = ctx['state'];
const expr1 = 'text';
let attr1 = bExpr1[expr1];
let hdlr1 = [(ev) => { bExpr1[expr1] = ev.target.value; }];
let hdlr2 = [ctx['onInput'], ctx];
return block1([attr1, hdlr1, hdlr2]);
}
}"
`;
exports[`t-model directive t-model on an input with an undefined value 1`] = ` exports[`t-model directive t-model on an input with an undefined value 1`] = `
"function anonymous(app, bdom, helpers "function anonymous(app, bdom, helpers
) { ) {
@@ -569,36 +550,6 @@ exports[`t-model directive t-model with dynamic values on select options in fore
}" }"
`; `;
exports[`t-model directive t-model with radio button group in t-foreach 1`] = `
"function anonymous(app, bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { prepareList, toNumber, withKey } = helpers;
let block1 = createBlock(\`<div id=\\"get_data\\" block-handler-0=\\"click\\"><block-child-0/></div>\`);
let block3 = createBlock(\`<input type=\\"radio\\" name=\\"radio_group\\" block-attribute-0=\\"value\\" block-attribute-1=\\"id\\" block-attribute-2=\\"checked\\" block-handler-3=\\"click\\"/>\`);
return function template(ctx, node, key = \\"\\") {
let hdlr1 = [ctx['getData'], ctx];
ctx = Object.create(ctx);
const [k_block2, v_block2, l_block2, c_block2] = prepareList(ctx['options']);;
for (let i1 = 0; i1 < l_block2; i1++) {
ctx[\`opt\`] = v_block2[i1];
const key1 = ctx['opt'];
let attr1 = new String((ctx['opt']) || \\"\\");
let attr2 = ctx['opt'];
const bExpr1 = ctx['state'];
const expr1 = 'group';
let attr3 = bExpr1[expr1] === ctx['opt'];
let hdlr2 = [(ev) => { bExpr1[expr1] = ev.target.value; }];
c_block2[i1] = withKey(block3([attr1, attr2, attr3, hdlr2]), key1);
}
const b2 = list(c_block2);
return block1([hdlr1], [b2]);
}
}"
`;
exports[`t-model directive two inputs in a div alternating with a t-if 1`] = ` exports[`t-model directive two inputs in a div alternating with a t-if 1`] = `
"function anonymous(app, bdom, helpers "function anonymous(app, bdom, helpers
) { ) {
@@ -111,9 +111,8 @@ exports[`t-on t-on method call in t-foreach 1`] = `
const key1 = ctx['val']; const key1 = ctx['val'];
let txt1 = ctx['val_index']; let txt1 = ctx['val_index'];
let txt2 = ctx['val']+''; let txt2 = ctx['val']+'';
const v1 = ctx['this']; const v1 = ctx['val'];
const v2 = ctx['val']; let hdlr1 = [()=>this.addVal(v1), ctx];
let hdlr1 = [()=>v1.addVal(v2), ctx];
c_block2[i1] = withKey(block3([txt1, txt2, hdlr1]), key1); c_block2[i1] = withKey(block3([txt1, txt2, hdlr1]), key1);
} }
const b2 = list(c_block2); const b2 = list(c_block2);
@@ -198,9 +197,8 @@ exports[`t-on t-on on components and t-foreach 1`] = `
for (let i1 = 0; i1 < l_block1; i1++) { for (let i1 = 0; i1 < l_block1; i1++) {
ctx[\`name\`] = v_block1[i1]; ctx[\`name\`] = v_block1[i1];
const key1 = ctx['name']; const key1 = ctx['name'];
const v1 = ctx['this']; const v1 = ctx['name'];
const v2 = ctx['name']; const hdlr1 = [()=>this.log(v1), ctx];
const hdlr1 = [()=>v1.log(v2), ctx];
c_block1[i1] = withKey(catcher1(comp1({value: ctx['name']}, key + \`__1__\${key1}\`, node, this, null), [hdlr1]), key1); c_block1[i1] = withKey(catcher1(comp1({value: ctx['name']}, key + \`__1__\${key1}\`, node, this, null), [hdlr1]), key1);
} }
return list(c_block1); return list(c_block1);
@@ -295,9 +293,8 @@ exports[`t-on t-on on components, with a handler update 1`] = `
ctx = Object.create(ctx); ctx = Object.create(ctx);
ctx[isBoundary] = 1 ctx[isBoundary] = 1
setContextValue(ctx, \\"name\\", ctx['state'].name); setContextValue(ctx, \\"name\\", ctx['state'].name);
const v1 = ctx['this']; const v1 = ctx['name'];
const v2 = ctx['name']; const hdlr1 = [()=>this.log(v1), ctx];
const hdlr1 = [()=>v1.log(v2), ctx];
return catcher1(comp1({value: ctx['name']}, key + \`__1\`, node, this, null), [hdlr1]); return catcher1(comp1({value: ctx['name']}, key + \`__1\`, node, this, null), [hdlr1]);
} }
}" }"
@@ -363,7 +360,7 @@ exports[`t-on t-on on slot, with 'prevent' modifier 1`] = `
} }
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
return comp1({slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx}})}, key + \`__1\`, node, this, null); return comp1({slots: markRaw({'default': {__render: slot1, __ctx: ctx}})}, key + \`__1\`, node, this, null);
} }
}" }"
`; `;
@@ -396,8 +393,7 @@ exports[`t-on t-on on t-set-slots 1`] = `
function slot1(ctx, node, key = \\"\\") { function slot1(ctx, node, key = \\"\\") {
const b6 = block6(); const b6 = block6();
const b7 = block7(); const b7 = block7();
const v1 = ctx['this']; const hdlr1 = [()=>this.state.count++, ctx];
const hdlr1 = [()=>v1.state.count++, ctx];
return catcher1(multi([b6, b7]), [hdlr1]); return catcher1(multi([b6, b7]), [hdlr1]);
} }
@@ -406,7 +402,7 @@ exports[`t-on t-on on t-set-slots 1`] = `
const b3 = text(ctx['state'].count); const b3 = text(ctx['state'].count);
const b4 = text(\`] \`); const b4 = text(\`] \`);
const ctx1 = capture(ctx); const ctx1 = capture(ctx);
const b8 = comp1({slots: markRaw({'myslot': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__1\`, node, this, null); const b8 = comp1({slots: markRaw({'myslot': {__render: slot1, __ctx: ctx1}})}, key + \`__1\`, node, this, null);
return multi([b2, b3, b4, b8]); return multi([b2, b3, b4, b8]);
} }
}" }"
@@ -438,7 +434,7 @@ exports[`t-on t-on on t-slots 1`] = `
} }
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
return comp1({slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx}})}, key + \`__1\`, node, this, null); return comp1({slots: markRaw({'default': {__render: slot1, __ctx: ctx}})}, key + \`__1\`, node, this, null);
} }
}" }"
`; `;
@@ -454,52 +450,9 @@ exports[`t-on t-on on t-slots 2`] = `
const b2 = text(\` [\`); const b2 = text(\` [\`);
const b3 = text(ctx['state'].count); const b3 = text(ctx['state'].count);
const b4 = text(\`] \`); const b4 = text(\`] \`);
const v1 = ctx['this']; const hdlr1 = [()=>this.state.count++, ctx];
const hdlr1 = [()=>v1.state.count++, ctx];
const b5 = catcher1(callSlot(ctx, node, key, 'default', false, {}), [hdlr1]); const b5 = catcher1(callSlot(ctx, node, key, 'default', false, {}), [hdlr1]);
return multi([b2, b3, b4, b5]); return multi([b2, b3, b4, b5]);
} }
}" }"
`; `;
exports[`t-on t-on when first component child is an empty component 1`] = `
"function anonymous(app, bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { createCatcher } = helpers;
const comp1 = app.createComponent(\`Child\`, true, false, false, false);
const catcher1 = createCatcher({\\"click\\":0});
let block1 = createBlock(\`<div block-handler-0=\\"click\\"><block-child-0/></div>\`);
return function template(ctx, node, key = \\"\\") {
let hdlr1 = [ctx['push'], ctx];
const hdlr2 = [()=>{}, ctx];
const b2 = catcher1(comp1({list: ctx['list']}, key + \`__1\`, node, this, null), [hdlr2]);
return block1([hdlr1], [b2]);
}
}"
`;
exports[`t-on t-on when first component child is an empty component 2`] = `
"function anonymous(app, bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { prepareList, withKey } = helpers;
let block2 = createBlock(\`<span><block-text-0/></span>\`);
return function template(ctx, node, key = \\"\\") {
ctx = Object.create(ctx);
const [k_block1, v_block1, l_block1, c_block1] = prepareList(ctx['props'].list);;
for (let i1 = 0; i1 < l_block1; i1++) {
ctx[\`c\`] = v_block1[i1];
ctx[\`c_index\`] = i1;
const key1 = ctx['c_index'];
let txt1 = ctx['c'];
c_block1[i1] = withKey(block2([txt1]), key1);
}
return list(c_block1);
}
}"
`;
@@ -22,7 +22,7 @@ exports[`t-set slot setted value (with t-set) not accessible with t-esc 1`] = `
setContextValue(ctx, \\"iter\\", 'source'); setContextValue(ctx, \\"iter\\", 'source');
let txt1 = ctx['iter']; let txt1 = ctx['iter'];
const ctx1 = capture(ctx); const ctx1 = capture(ctx);
const b2 = comp1({slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__1\`, node, this, null); const b2 = comp1({slots: markRaw({'default': {__render: slot1, __ctx: ctx1}})}, key + \`__1\`, node, this, null);
let txt2 = ctx['iter']; let txt2 = ctx['iter'];
return block1([txt1, txt2], [b2]); return block1([txt1, txt2], [b2]);
} }
@@ -71,7 +71,7 @@ exports[`t-set slots with a t-set with a component in body 1`] = `
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx); const ctx1 = capture(ctx);
return comp2({slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__2\`, node, this, null); return comp2({slots: markRaw({'default': {__render: slot1, __ctx: ctx1}})}, key + \`__2\`, node, this, null);
} }
}" }"
`; `;
@@ -128,7 +128,7 @@ exports[`t-set slots with an t-set with a component in body 1`] = `
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx); const ctx1 = capture(ctx);
return comp2({slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__2\`, node, this, null); return comp2({slots: markRaw({'default': {__render: slot1, __ctx: ctx1}})}, key + \`__2\`, node, this, null);
} }
}" }"
`; `;
@@ -179,7 +179,7 @@ exports[`t-set slots with an unused t-set with a component in body 1`] = `
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx); const ctx1 = capture(ctx);
return comp2({slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__2\`, node, this, null); return comp2({slots: markRaw({'default': {__render: slot1, __ctx: ctx1}})}, key + \`__2\`, node, this, null);
} }
}" }"
`; `;
-59
View File
@@ -1,7 +1,6 @@
import { import {
App, App,
Component, Component,
ComponentConstructor,
mount, mount,
onMounted, onMounted,
onRendered, onRendered,
@@ -4068,64 +4067,6 @@ test("renderings, destruction, patch, stuff, ... yet another variation", async (
expect(fixture.innerHTML).toBe("ABD<p>2</p>"); expect(fixture.innerHTML).toBe("ABD<p>2</p>");
}); });
test("delayed render does not go through when t-component value changed", async () => {
class C extends Component {
static template = xml`C`;
setup() {
useLogLifecycle("", true);
}
}
class B extends Component {
static template = xml`B<t t-esc="state.val"/>`;
state = useState({ val: 1 });
setup() {
useLogLifecycle("", true);
b = this;
}
}
let b: B;
class A extends Component {
static template = xml`A<t t-component="state.component"/>`;
state: { component: ComponentConstructor } = useState({ component: B });
setup() {
useLogLifecycle("", true);
}
}
const a = await mount(A, fixture);
expect(fixture.innerHTML).toBe("AB1");
expect([
"A:setup",
"A:willRender",
"B:setup",
"A:rendered",
"B:willRender",
"B:rendered",
"B:mounted",
"A:mounted",
]).toBeLogged();
// start a render in B
b!.state.val = 2;
// start a render in A, invalidating the scheduled render of B, which could crash if executed.
a.state.component = C;
await nextTick();
expect(fixture.innerHTML).toBe("AC");
expect([
"A:willRender",
"C:setup",
"A:rendered",
"C:willRender",
"C:rendered",
"A:willPatch",
"B:willUnmount",
"B:willDestroy",
"C:mounted",
"A:patched",
]).toBeLogged();
});
// test.skip("components with shouldUpdate=false", async () => { // test.skip("components with shouldUpdate=false", async () => {
// const state = { p: 1, cc: 10 }; // const state = { p: 1, cc: 10 };
-17
View File
@@ -126,23 +126,6 @@ describe("basics", () => {
); );
}); });
test("display a nice error if the components key is missing with subcomponents", async () => {
class Parent extends Component {
static template = xml`<div><MissingChild /></div>`;
}
const app = new App(Parent as typeof Component);
let error: Error;
const mountProm = app.mount(fixture).catch((e: Error) => (error = e));
await expect(nextAppError(app)).resolves.toThrow(
'Cannot find the definition of component "MissingChild", missing static components key in parent'
);
await mountProm;
expect(error!).toBeDefined();
expect(error!.message).toBe(
'Cannot find the definition of component "MissingChild", missing static components key in parent'
);
});
test("simple catchError", async () => { test("simple catchError", async () => {
class Boom extends Component { class Boom extends Component {
static template = xml`<div t-esc="a.b.c"/>`; static template = xml`<div t-esc="a.b.c"/>`;
+1 -24
View File
@@ -1,5 +1,5 @@
import { makeTestFixture, nextAppError, nextTick, snapshotEverything } from "../helpers"; import { makeTestFixture, nextAppError, nextTick, snapshotEverything } from "../helpers";
import { Component, onError, xml, mount, OwlError, useState } from "../../src"; import { Component, onError, xml, mount, OwlError } from "../../src";
import { App, DEV_MSG } from "../../src/runtime/app"; import { App, DEV_MSG } from "../../src/runtime/app";
import { validateProps } from "../../src/runtime/template_helpers"; import { validateProps } from "../../src/runtime/template_helpers";
import { Schema } from "../../src/runtime/validation"; import { Schema } from "../../src/runtime/validation";
@@ -682,29 +682,6 @@ describe("props validation", () => {
expect(error!.message).toBe("Invalid props for component 'SubComp': 'p' is missing"); expect(error!.message).toBe("Invalid props for component 'SubComp': 'p' is missing");
}); });
test("props validation does not cause additional subscription", async () => {
let obj = {
value: 1,
otherValue: 2,
};
class Child extends Component {
static props = {
obj: { type: Object, shape: { value: Number, otherValue: Number } },
};
static template = xml`<t t-esc="props.obj.value"/>`;
}
class Parent extends Component {
static template = xml`<Child obj="obj"/><t t-esc="obj.otherValue"/>`;
static components = { Child };
obj = useState(obj);
}
const app = new App(Parent, { test: true });
await app.mount(fixture);
expect(fixture.innerHTML).toBe("12");
expect(app.root!.subscriptions).toEqual([{ keys: ["otherValue"], target: obj }]);
});
test("props are validated whenever component is updated", async () => { test("props are validated whenever component is updated", async () => {
let error: Error; let error: Error;
class SubComp extends Component { class SubComp extends Component {
-32
View File
@@ -7,7 +7,6 @@ import {
onWillUnmount, onWillUnmount,
useState, useState,
xml, xml,
toRaw,
} from "../../src"; } from "../../src";
import { makeTestFixture, nextTick, snapshotEverything, useLogLifecycle } from "../helpers"; import { makeTestFixture, nextTick, snapshotEverything, useLogLifecycle } from "../helpers";
@@ -233,34 +232,3 @@ describe("reactivity in lifecycle", () => {
expect(fixture.innerHTML).toBe("34"); expect(fixture.innerHTML).toBe("34");
}); });
}); });
describe("subscriptions", () => {
test("subscriptions returns the keys and targets observed by the component", async () => {
class Comp extends Component {
static template = xml`<t t-esc="state.a"/>`;
state = useState({ a: 1, b: 2 });
}
const comp = await mount(Comp, fixture);
expect(fixture.innerHTML).toBe("1");
expect(comp.__owl__.subscriptions).toEqual([{ keys: ["a"], target: toRaw(comp.state) }]);
});
test("subscriptions returns the keys observed by the component", async () => {
class Child extends Component {
static template = xml`<t t-esc="props.state.b"/>`;
setup() {
child = this;
}
}
let child: Child;
class Parent extends Component {
static template = xml`<t t-esc="state.a"/><Child state="state"/>`;
static components = { Child };
state = useState({ a: 1, b: 2 });
}
const parent = await mount(Parent, fixture);
expect(fixture.innerHTML).toBe("12");
expect(parent.__owl__.subscriptions).toEqual([{ keys: ["a"], target: toRaw(parent.state) }]);
expect(child!.__owl__.subscriptions).toEqual([{ keys: ["b"], target: toRaw(parent.state) }]);
});
});
+2 -39
View File
@@ -1,14 +1,6 @@
import { import { App, Component, mount, onMounted, useRef, useState } from "../../src/index";
App,
Component,
mount,
onMounted,
onPatched,
useRef,
useState,
xml,
} from "../../src/index";
import { logStep, makeTestFixture, nextAppError, nextTick, snapshotEverything } from "../helpers"; import { logStep, makeTestFixture, nextAppError, nextTick, snapshotEverything } from "../helpers";
import { xml } from "../../src/index";
snapshotEverything(); snapshotEverything();
let fixture: HTMLElement; let fixture: HTMLElement;
@@ -136,33 +128,4 @@ describe("refs", () => {
expect(fixture.innerHTML).toBe("<p>a<p>b</p></p>"); expect(fixture.innerHTML).toBe("<p>a<p>b</p></p>");
expect(["<p>b</p>", "<p>a<p>b</p></p>"]).toBeLogged(); expect(["<p>b</p>", "<p>a<p>b</p></p>"]).toBeLogged();
}); });
test("refs and t-key", async () => {
let el;
class Test extends Component {
static components = {};
static template = xml`
<button t-on-click="() => state.renderId++" />
<p t-ref="root" t-key="state.renderId"/>`;
root = useRef("root");
state = useState({ renderId: 1 });
setup() {
onMounted(() => {
el = this.root.el;
});
onPatched(() => {
el = this.root.el;
});
}
}
await mount(Test, fixture);
expect(el).toBe(fixture.querySelector("p"));
const _el = el;
fixture.querySelector("button")!.click();
await nextTick();
expect(el).not.toBe(_el);
expect(el).toBe(fixture.querySelector("p"));
});
}); });
+69 -98
View File
@@ -62,7 +62,7 @@ describe("slots", () => {
expect(fixture.innerHTML).toBe("some other text"); expect(fixture.innerHTML).toBe("some other text");
}); });
test.skip("simple slot with slot scope", async () => { test("simple slot with slot scope", async () => {
let child: any; let child: any;
class Child extends Component { class Child extends Component {
static template = xml`<span><t t-slot="slotName" bool="state.bool"/></span>`; static template = xml`<span><t t-slot="slotName" bool="state.bool"/></span>`;
@@ -91,7 +91,7 @@ describe("slots", () => {
expect(fixture.innerHTML).toBe("<span>other text</span>"); expect(fixture.innerHTML).toBe("<span>other text</span>");
}); });
test.skip("slot with slot scope and t-props", async () => { test("slot with slot scope and t-props", async () => {
class Child extends Component { class Child extends Component {
static template = xml` static template = xml`
<t t-slot="slotName" t-props="info"/>`; <t t-slot="slotName" t-props="info"/>`;
@@ -113,7 +113,7 @@ describe("slots", () => {
expect(fixture.innerHTML).toBe("<p>1</p><p>2</p>"); expect(fixture.innerHTML).toBe("<p>1</p><p>2</p>");
}); });
test.skip("simple dynamic slot with slot scope", async () => { test("simple dynamic slot with slot scope", async () => {
let child: any; let child: any;
class Child extends Component { class Child extends Component {
static template = xml`<span><t t-slot="{{ 'slotName' }}" bool="state.bool"/></span>`; static template = xml`<span><t t-slot="{{ 'slotName' }}" bool="state.bool"/></span>`;
@@ -142,7 +142,7 @@ describe("slots", () => {
expect(fixture.innerHTML).toBe("<span>other text</span>"); expect(fixture.innerHTML).toBe("<span>other text</span>");
}); });
test.skip("simple named and empty slot", async () => { test("simple named and empty slot", async () => {
class Child extends Component { class Child extends Component {
static template = xml`<span><t t-slot="default" /><t t-slot="myEmptySlot"/></span>`; static template = xml`<span><t t-slot="default" /><t t-slot="myEmptySlot"/></span>`;
@@ -160,7 +160,7 @@ describe("slots", () => {
expect(fixture.innerHTML).toBe("<span>some text</span>"); expect(fixture.innerHTML).toBe("<span>some text</span>");
}); });
test.skip("simple named and empty slot -- 2", async () => { test("simple named and empty slot -- 2", async () => {
class Child extends Component { class Child extends Component {
static template = xml`<span><t t-slot="myEmptySlot">default empty</t></span>`; static template = xml`<span><t t-slot="myEmptySlot">default empty</t></span>`;
@@ -179,7 +179,7 @@ describe("slots", () => {
expect(fixture.innerHTML).toBe("<span>default empty</span>"); expect(fixture.innerHTML).toBe("<span>default empty</span>");
}); });
test.skip("default slot with slot scope: shorthand syntax", async () => { test("default slot with slot scope: shorthand syntax", async () => {
let child: any; let child: any;
class Child extends Component { class Child extends Component {
static template = xml`<span><t t-slot="default" bool="state.bool"/></span>`; static template = xml`<span><t t-slot="default" bool="state.bool"/></span>`;
@@ -206,7 +206,7 @@ describe("slots", () => {
expect(fixture.innerHTML).toBe("<span>other text</span>"); expect(fixture.innerHTML).toBe("<span>other text</span>");
}); });
test.skip("simple default slot with params", async () => { test("simple default slot with params", async () => {
class Child extends Component { class Child extends Component {
static template = xml`<span><t t-slot="default" bool="state.bool"/></span>`; static template = xml`<span><t t-slot="default" bool="state.bool"/></span>`;
state = useState({ bool: true }); state = useState({ bool: true });
@@ -230,7 +230,7 @@ describe("slots", () => {
expect(mockConsoleWarn).toBeCalledTimes(1); expect(mockConsoleWarn).toBeCalledTimes(1);
}); });
test.skip("simple default slot with params and bound function", async () => { test("simple default slot with params and bound function", async () => {
class Child extends Component { class Child extends Component {
static template = xml`<t t-slot="default" fn.bind="getValue"/>`; static template = xml`<t t-slot="default" fn.bind="getValue"/>`;
state = useState({ value: 123 }); state = useState({ value: 123 });
@@ -249,7 +249,7 @@ describe("slots", () => {
expect(fixture.innerHTML).toBe("123"); expect(fixture.innerHTML).toBe("123");
}); });
test.skip("default slot with params with - in it", async () => { test("default slot with params with - in it", async () => {
class Child extends Component { class Child extends Component {
static template = xml`<t t-slot="default" some-value="state.value"/>`; static template = xml`<t t-slot="default" some-value="state.value"/>`;
state = useState({ value: 123 }); state = useState({ value: 123 });
@@ -265,7 +265,7 @@ describe("slots", () => {
expect(fixture.innerHTML).toBe("123"); expect(fixture.innerHTML).toBe("123");
}); });
test.skip("fun: two calls to the same slot", async () => { test("fun: two calls to the same slot", async () => {
class Child extends Component { class Child extends Component {
static template = xml`<t t-slot="default"/><t t-slot="default"/>`; static template = xml`<t t-slot="default"/><t t-slot="default"/>`;
} }
@@ -279,7 +279,7 @@ describe("slots", () => {
expect(fixture.innerHTML).toBe("some textsome text"); expect(fixture.innerHTML).toBe("some textsome text");
}); });
test.skip("slot content is bound to caller", async () => { test("slot content is bound to caller", async () => {
class Child extends Component { class Child extends Component {
static template = xml`<span><t t-slot="default"/></span>`; static template = xml`<span><t t-slot="default"/></span>`;
} }
@@ -299,7 +299,7 @@ describe("slots", () => {
expect(parent.state.value).toBe(1); expect(parent.state.value).toBe(1);
}); });
test.skip("slot content is bound to caller (variation)", async () => { test("slot content is bound to caller (variation)", async () => {
class Child extends Component { class Child extends Component {
static template = xml`<span><t t-slot="default"/></span>`; static template = xml`<span><t t-slot="default"/></span>`;
} }
@@ -326,7 +326,7 @@ describe("slots", () => {
expect(parent.state.value).toBe(1); expect(parent.state.value).toBe(1);
}); });
test.skip("can define and call slots", async () => { test("can define and call slots", async () => {
class Dialog extends Component { class Dialog extends Component {
static template = xml` static template = xml`
<div> <div>
@@ -352,7 +352,7 @@ describe("slots", () => {
); );
}); });
test.skip("can define and call slots with params", async () => { test("can define and call slots with params", async () => {
class Dialog extends Component { class Dialog extends Component {
static template = xml` static template = xml`
<div> <div>
@@ -381,7 +381,7 @@ describe("slots", () => {
); );
}); });
test.skip("can define and call slots with bound params", async () => { test("can define and call slots with bound params", async () => {
class Child extends Component { class Child extends Component {
static template = xml` static template = xml`
<t t-slot="abc"/> <t t-slot="abc"/>
@@ -405,7 +405,7 @@ describe("slots", () => {
expect(fixture.innerHTML).toBe("abc444"); expect(fixture.innerHTML).toBe("abc444");
}); });
test.skip("no named slot content => just no children", async () => { test("no named slot content => just no children", async () => {
class Dialog extends Component { class Dialog extends Component {
static template = xml`<span><t t-slot="header"/></span>`; static template = xml`<span><t t-slot="header"/></span>`;
} }
@@ -418,7 +418,7 @@ describe("slots", () => {
expect(fixture.innerHTML).toBe("<span></span>"); expect(fixture.innerHTML).toBe("<span></span>");
}); });
test.skip("named slots can define a default content", async () => { test("named slots can define a default content", async () => {
class Dialog extends Component { class Dialog extends Component {
static template = xml` static template = xml`
<span> <span>
@@ -434,7 +434,7 @@ describe("slots", () => {
expect(fixture.innerHTML).toBe("<div><span>default content</span></div>"); expect(fixture.innerHTML).toBe("<div><span>default content</span></div>");
}); });
test.skip("can define a default content", async () => { test("can define a default content", async () => {
class Dialog extends Component { class Dialog extends Component {
static template = xml` static template = xml`
<span> <span>
@@ -450,7 +450,7 @@ describe("slots", () => {
expect(fixture.innerHTML).toBe("<div><span>default content</span></div>"); expect(fixture.innerHTML).toBe("<div><span>default content</span></div>");
}); });
test.skip("default content is not rendered if slot is provided", async () => { test("default content is not rendered if slot is provided", async () => {
class Dialog extends Component { class Dialog extends Component {
static template = xml` static template = xml`
<span> <span>
@@ -466,7 +466,7 @@ describe("slots", () => {
expect(fixture.innerHTML).toBe("<div><span>hey</span></div>"); expect(fixture.innerHTML).toBe("<div><span>hey</span></div>");
}); });
test.skip("default content is not rendered if named slot is provided", async () => { test("default content is not rendered if named slot is provided", async () => {
class Dialog extends Component { class Dialog extends Component {
static template = xml` static template = xml`
<span> <span>
@@ -482,7 +482,7 @@ describe("slots", () => {
expect(fixture.innerHTML).toBe("<div><span>hey</span></div>"); expect(fixture.innerHTML).toBe("<div><span>hey</span></div>");
}); });
test.skip("slots are properly bound to correct component", async () => { test("slots are properly bound to correct component", async () => {
let child: any = null; let child: any = null;
class Child extends Component { class Child extends Component {
// t-set t-value in template is to force compiler to protect the scope // t-set t-value in template is to force compiler to protect the scope
@@ -518,7 +518,7 @@ describe("slots", () => {
expect(fixture.innerHTML).toBe("<button>2</button>"); expect(fixture.innerHTML).toBe("<button>2</button>");
}); });
test.skip("slots are rendered with proper context", async () => { test("slots are rendered with proper context", async () => {
class Dialog extends Component { class Dialog extends Component {
static template = xml`<span><t t-slot="footer"/></span>`; static template = xml`<span><t t-slot="footer"/></span>`;
} }
@@ -554,7 +554,7 @@ describe("slots", () => {
); );
}); });
test.skip("slots are rendered with proper context, part 2", async () => { test("slots are rendered with proper context, part 2", async () => {
class Link extends Component { class Link extends Component {
static template = xml` static template = xml`
<a t-att-href="props.to"> <a t-att-href="props.to">
@@ -593,7 +593,7 @@ describe("slots", () => {
); );
}); });
test.skip("slots are rendered with proper context, part 3", async () => { test("slots are rendered with proper context, part 3", async () => {
class Link extends Component { class Link extends Component {
static template = xml` static template = xml`
<a t-att-href="props.to"> <a t-att-href="props.to">
@@ -632,7 +632,7 @@ describe("slots", () => {
); );
}); });
test.skip("slots are rendered with proper context, part 4", async () => { test("slots are rendered with proper context, part 4", async () => {
class Link extends Component { class Link extends Component {
static template = xml` static template = xml`
<a t-att-href="props.to"> <a t-att-href="props.to">
@@ -660,7 +660,7 @@ describe("slots", () => {
expect(fixture.innerHTML).toBe('<div><a href="/user/1">User David</a></div>'); expect(fixture.innerHTML).toBe('<div><a href="/user/1">User David</a></div>');
}); });
test.skip("content is the default slot", async () => { test("content is the default slot", async () => {
class Dialog extends Component { class Dialog extends Component {
static template = xml`<div><t t-slot="default"/></div>`; static template = xml`<div><t t-slot="default"/></div>`;
} }
@@ -678,7 +678,7 @@ describe("slots", () => {
expect(fixture.innerHTML).toBe("<div><div><span>sts rocks</span></div></div>"); expect(fixture.innerHTML).toBe("<div><div><span>sts rocks</span></div></div>");
}); });
test.skip("content is the default slot (variation)", async () => { test("content is the default slot (variation)", async () => {
class Dialog extends Component { class Dialog extends Component {
static template = xml`<t t-slot="default"/>`; static template = xml`<t t-slot="default"/>`;
} }
@@ -694,7 +694,7 @@ describe("slots", () => {
expect(fixture.innerHTML).toBe("<span>sts rocks</span>"); expect(fixture.innerHTML).toBe("<span>sts rocks</span>");
}); });
test.skip("default slot work with text nodes", async () => { test("default slot work with text nodes", async () => {
class Dialog extends Component { class Dialog extends Component {
static template = xml`<div><t t-slot="default"/></div>`; static template = xml`<div><t t-slot="default"/></div>`;
} }
@@ -710,7 +710,7 @@ describe("slots", () => {
expect(fixture.innerHTML).toBe("<div><div>sts rocks</div></div>"); expect(fixture.innerHTML).toBe("<div><div>sts rocks</div></div>");
}); });
test.skip("default slot work with text nodes (variation)", async () => { test("default slot work with text nodes (variation)", async () => {
class Dialog extends Component { class Dialog extends Component {
static template = xml`<t t-slot="default"/>`; static template = xml`<t t-slot="default"/>`;
} }
@@ -723,7 +723,7 @@ describe("slots", () => {
expect(fixture.innerHTML).toBe("sts rocks"); expect(fixture.innerHTML).toBe("sts rocks");
}); });
test.skip("multiple roots are allowed in a named slot", async () => { test("multiple roots are allowed in a named slot", async () => {
class Dialog extends Component { class Dialog extends Component {
static template = xml`<div><t t-slot="content"/></div>`; static template = xml`<div><t t-slot="content"/></div>`;
} }
@@ -744,7 +744,7 @@ describe("slots", () => {
expect(fixture.innerHTML).toBe("<div><div><span>sts</span><span>rocks</span></div></div>"); expect(fixture.innerHTML).toBe("<div><div><span>sts</span><span>rocks</span></div></div>");
}); });
test.skip("multiple roots are allowed in a default slot", async () => { test("multiple roots are allowed in a default slot", async () => {
class Dialog extends Component { class Dialog extends Component {
static template = xml`<div><t t-slot="default"/></div>`; static template = xml`<div><t t-slot="default"/></div>`;
} }
@@ -763,7 +763,7 @@ describe("slots", () => {
expect(fixture.innerHTML).toBe("<div><div><span>sts</span><span>rocks</span></div></div>"); expect(fixture.innerHTML).toBe("<div><div><span>sts</span><span>rocks</span></div></div>");
}); });
test.skip("missing slots are ignored", async () => { test("missing slots are ignored", async () => {
class Dialog extends Component { class Dialog extends Component {
static template = xml` static template = xml`
<span> <span>
@@ -782,7 +782,7 @@ describe("slots", () => {
expect(fixture.innerHTML).toBe("<div><span><span>some content</span></span></div>"); expect(fixture.innerHTML).toBe("<div><span><span>some content</span></span></div>");
}); });
test.skip("t-debug on a t-set-slot (defining a slot)", async () => { test("t-debug on a t-set-slot (defining a slot)", async () => {
const consoleLog = console.log; const consoleLog = console.log;
console.log = jest.fn(); console.log = jest.fn();
@@ -802,7 +802,7 @@ describe("slots", () => {
console.log = consoleLog; console.log = consoleLog;
}); });
test.skip("slot preserves properly parented relationship", async () => { test("slot preserves properly parented relationship", async () => {
class Child extends Component { class Child extends Component {
static template = xml`<t t-slot="default"/>`; static template = xml`<t t-slot="default"/>`;
} }
@@ -832,7 +832,7 @@ describe("slots", () => {
expect(childrenChildren[0]).toBeInstanceOf(GrandChild); expect(childrenChildren[0]).toBeInstanceOf(GrandChild);
}); });
test.skip("slot preserves properly parented relationship, even through t-call", async () => { test("slot preserves properly parented relationship, even through t-call", async () => {
class Child extends Component { class Child extends Component {
static template = xml`<t t-slot="default"/>`; static template = xml`<t t-slot="default"/>`;
} }
@@ -870,7 +870,7 @@ describe("slots", () => {
expect(childrenChildren[0]).toBeInstanceOf(GrandChild); expect(childrenChildren[0]).toBeInstanceOf(GrandChild);
}); });
test.skip("t-slot scope context", async () => { test("t-slot scope context", async () => {
expect.assertions(4); expect.assertions(4);
class Wrapper extends Component { class Wrapper extends Component {
@@ -916,7 +916,7 @@ describe("slots", () => {
await nextTick(); await nextTick();
}); });
test.skip("t-slot in recursive templates", async () => { test("t-slot in recursive templates", async () => {
class Wrapper extends Component { class Wrapper extends Component {
static template = xml` static template = xml`
<wrapper> <wrapper>
@@ -979,7 +979,7 @@ describe("slots", () => {
); );
}); });
test.skip("t-slot within dynamic t-call", async () => { test("t-slot within dynamic t-call", async () => {
class Child extends Component { class Child extends Component {
static template = xml`<div class="child"/>`; static template = xml`<div class="child"/>`;
} }
@@ -1021,7 +1021,7 @@ describe("slots", () => {
); );
}); });
test.skip("slots in t-foreach in t-foreach", async () => { test("slots in t-foreach in t-foreach", async () => {
class Child extends Component { class Child extends Component {
static template = xml` static template = xml`
<div><t t-slot="default" /></div> <div><t t-slot="default" /></div>
@@ -1084,7 +1084,7 @@ describe("slots", () => {
); );
}); });
test.skip("default slot next to named slot, with default content", async () => { test("default slot next to named slot, with default content", async () => {
class Dialog extends Component { class Dialog extends Component {
// We're using 2 slots here: a "default" one and a "footer", // We're using 2 slots here: a "default" one and a "footer",
// both having default children nodes. // both having default children nodes.
@@ -1125,7 +1125,7 @@ describe("slots", () => {
); );
}); });
test.skip("dynamic t-slot call", async () => { test("dynamic t-slot call", async () => {
class Toggler extends Component { class Toggler extends Component {
static template = xml`<button t-on-click="toggle"><t t-slot="{{current.slot}}"/></button>`; static template = xml`<button t-on-click="toggle"><t t-slot="{{current.slot}}"/></button>`;
current = useState({ slot: "slot1" }); current = useState({ slot: "slot1" });
@@ -1156,7 +1156,7 @@ describe("slots", () => {
expect(fixture.innerHTML).toBe("<div><button><p>slot1</p><span>content</span></button></div>"); expect(fixture.innerHTML).toBe("<div><button><p>slot1</p><span>content</span></button></div>");
}); });
test.skip("dynamic t-slot call with default", async () => { test("dynamic t-slot call with default", async () => {
class Toggler extends Component { class Toggler extends Component {
static template = xml` static template = xml`
<button t-on-click="toggle"> <button t-on-click="toggle">
@@ -1188,7 +1188,7 @@ describe("slots", () => {
expect(fixture.innerHTML).toBe("<div><button><h1>slot2</h1></button></div>"); expect(fixture.innerHTML).toBe("<div><button><h1>slot2</h1></button></div>");
}); });
test.skip("slot are properly rendered if inner props are changed", async () => { test("slot are properly rendered if inner props are changed", async () => {
class SomeComponent extends Component { class SomeComponent extends Component {
static template = xml` static template = xml`
<div> <div>
@@ -1222,7 +1222,7 @@ describe("slots", () => {
expect(fixture.innerHTML).toBe("<div><button>Inc[5]</button><div><div> SC:5</div></div></div>"); expect(fixture.innerHTML).toBe("<div><button>Inc[5]</button><div><div> SC:5</div></div></div>");
}); });
test.skip("slots and wrapper components", async () => { test("slots and wrapper components", async () => {
class Link extends Component { class Link extends Component {
static template = xml` static template = xml`
<a href="abc"> <a href="abc">
@@ -1240,7 +1240,7 @@ describe("slots", () => {
expect(fixture.innerHTML).toBe(`<a href="abc">hey</a>`); expect(fixture.innerHTML).toBe(`<a href="abc">hey</a>`);
}); });
test.skip("template can just return a slot", async () => { test("template can just return a slot", async () => {
class Child extends Component { class Child extends Component {
static template = xml`<span><t t-esc="props.value"/></span>`; static template = xml`<span><t t-esc="props.value"/></span>`;
} }
@@ -1265,7 +1265,7 @@ describe("slots", () => {
expect(fixture.innerHTML).toBe("<div><span>5</span></div>"); expect(fixture.innerHTML).toBe("<div><span>5</span></div>");
}); });
test.skip("multiple slots containing components", async () => { test("multiple slots containing components", async () => {
class C extends Component { class C extends Component {
static template = xml`<span><t t-esc="props.val"/></span>`; static template = xml`<span><t t-esc="props.val"/></span>`;
} }
@@ -1286,7 +1286,7 @@ describe("slots", () => {
expect(fixture.innerHTML).toBe(`<div><span>1</span><span>2</span></div>`); expect(fixture.innerHTML).toBe(`<div><span>1</span><span>2</span></div>`);
}); });
test.skip("slots in t-foreach and re-rendering", async () => { test("slots in t-foreach and re-rendering", async () => {
class Child extends Component { class Child extends Component {
static template = xml`<span><t t-esc="state.val"/><t t-slot="default"/></span>`; static template = xml`<span><t t-esc="state.val"/><t t-slot="default"/></span>`;
state = useState({ val: "A" }); state = useState({ val: "A" });
@@ -1312,7 +1312,7 @@ describe("slots", () => {
expect(fixture.innerHTML).toBe("<div><span>B0</span><span>B1</span></div>"); expect(fixture.innerHTML).toBe("<div><span>B0</span><span>B1</span></div>");
}); });
test.skip("slots in t-foreach with t-set and re-rendering", async () => { test("slots in t-foreach with t-set and re-rendering", async () => {
class Child extends Component { class Child extends Component {
static template = xml` static template = xml`
<span> <span>
@@ -1344,7 +1344,7 @@ describe("slots", () => {
expect(fixture.innerHTML).toBe("<div><span>B0</span><span>B1</span></div>"); expect(fixture.innerHTML).toBe("<div><span>B0</span><span>B1</span></div>");
}); });
test.skip("nested slots in same template", async () => { test("nested slots in same template", async () => {
let child: any = null; let child: any = null;
let child2: any = null; let child2: any = null;
let child3: any = null; let child3: any = null;
@@ -1398,7 +1398,7 @@ describe("slots", () => {
expect(children(parent)).toEqual([child]); expect(children(parent)).toEqual([child]);
}); });
test.skip("t-slot nested within another slot", async () => { test("t-slot nested within another slot", async () => {
let portal: any = null; let portal: any = null;
let modal: any = null; let modal: any = null;
let child3: any = null; let child3: any = null;
@@ -1451,7 +1451,7 @@ describe("slots", () => {
expect(children(modal)[0]).toBe(portal); expect(children(modal)[0]).toBe(portal);
}); });
test.skip("slots in slots, with vars", async () => { test("slots in slots, with vars", async () => {
class B extends Component { class B extends Component {
static template = xml`<span><t t-slot="default"/></span>`; static template = xml`<span><t t-slot="default"/></span>`;
} }
@@ -1480,7 +1480,7 @@ describe("slots", () => {
expect(fixture.innerHTML).toBe("<div><div><span><p>heyaaron</p></span></div></div>"); expect(fixture.innerHTML).toBe("<div><div><span><p>heyaaron</p></span></div></div>");
}); });
test.skip("t-set t-value in a slot", async () => { test("t-set t-value in a slot", async () => {
class Dialog extends Component { class Dialog extends Component {
static template = xml` static template = xml`
<span> <span>
@@ -1502,7 +1502,7 @@ describe("slots", () => {
expect(fixture.innerHTML).toBe("<div><span>dash</span></div>"); expect(fixture.innerHTML).toBe("<div><span>dash</span></div>");
}); });
test.skip("slot and t-esc", async () => { test("slot and t-esc", async () => {
class Dialog extends Component { class Dialog extends Component {
static template = xml`<span><t t-slot="default"/></span>`; static template = xml`<span><t t-slot="default"/></span>`;
} }
@@ -1515,7 +1515,7 @@ describe("slots", () => {
expect(fixture.innerHTML).toBe("<div><span>toph</span></div>"); expect(fixture.innerHTML).toBe("<div><span>toph</span></div>");
}); });
test.skip("slot and t-call", async () => { test("slot and t-call", async () => {
let sokka = xml`<p>sokka</p>`; let sokka = xml`<p>sokka</p>`;
class Dialog extends Component { class Dialog extends Component {
static template = xml`<span><t t-slot="default"/></span>`; static template = xml`<span><t t-slot="default"/></span>`;
@@ -1529,7 +1529,7 @@ describe("slots", () => {
expect(fixture.innerHTML).toBe("<div><span><p>sokka</p></span></div>"); expect(fixture.innerHTML).toBe("<div><span><p>sokka</p></span></div>");
}); });
test.skip("slot and (inline) t-call", async () => { test("slot and (inline) t-call", async () => {
let sokka = xml`<p>sokka</p>`; let sokka = xml`<p>sokka</p>`;
class Dialog extends Component { class Dialog extends Component {
static template = xml`<span><t t-slot="default"/></span>`; static template = xml`<span><t t-slot="default"/></span>`;
@@ -1543,7 +1543,7 @@ describe("slots", () => {
expect(fixture.innerHTML).toBe("<div><span><p>sokka</p></span></div>"); expect(fixture.innerHTML).toBe("<div><span><p>sokka</p></span></div>");
}); });
test.skip("nested slots: evaluation context and parented relationship", async () => { test("nested slots: evaluation context and parented relationship", async () => {
let slot: any = null; let slot: any = null;
let grandChild: any = null; let grandChild: any = null;
@@ -1578,7 +1578,7 @@ describe("slots", () => {
expect(children(grandChild)).toEqual([slot]); expect(children(grandChild)).toEqual([slot]);
}); });
test.skip("named slot inside slot", async () => { test("named slot inside slot", async () => {
class Child extends Component { class Child extends Component {
static template = xml` static template = xml`
<div> <div>
@@ -1608,7 +1608,7 @@ describe("slots", () => {
expect(fixture.innerHTML).toBe("<div><div><p>Ablip</p><div><p>Bblip</p></div></div></div>"); expect(fixture.innerHTML).toBe("<div><div><p>Ablip</p><div><p>Bblip</p></div></div></div>");
}); });
test.skip("named slots inside slot, again", async () => { test("named slots inside slot, again", async () => {
class Child extends Component { class Child extends Component {
static template = xml` static template = xml`
<child> <child>
@@ -1641,7 +1641,7 @@ describe("slots", () => {
); );
}); });
test.skip("named slot inside slot, part 3", async () => { test("named slot inside slot, part 3", async () => {
class Child extends Component { class Child extends Component {
static template = xml` static template = xml`
<div> <div>
@@ -1673,7 +1673,7 @@ describe("slots", () => {
expect(fixture.innerHTML).toBe("<div><div><p>Ablip</p><div><p>Bblip</p></div></div></div>"); expect(fixture.innerHTML).toBe("<div><div><p>Ablip</p><div><p>Bblip</p></div></div></div>");
}); });
test.skip("can render only empty slot", async () => { test("can render only empty slot", async () => {
class Parent extends Component { class Parent extends Component {
static template = xml`<t t-slot="default"/>`; static template = xml`<t t-slot="default"/>`;
} }
@@ -1688,7 +1688,7 @@ describe("slots", () => {
expect(fixture.innerHTML).toEqual(""); expect(fixture.innerHTML).toEqual("");
}); });
test.skip("can render node with t-ref and Component in same slot", async () => { test("can render node with t-ref and Component in same slot", async () => {
class Child extends Component { class Child extends Component {
static template = xml`<t t-slot="default"/>`; static template = xml`<t t-slot="default"/>`;
} }
@@ -1706,7 +1706,7 @@ describe("slots", () => {
expect(error).toBeNull(); expect(error).toBeNull();
}); });
test.skip("can use t-call in default-content of t-slot", async () => { test("can use t-call in default-content of t-slot", async () => {
const template = xml``; const template = xml``;
class Child extends Component { class Child extends Component {
static template = xml`<t t-slot="default"><t t-call="${template}"/></t>`; static template = xml`<t t-slot="default"><t t-call="${template}"/></t>`;
@@ -1719,7 +1719,7 @@ describe("slots", () => {
await mount(Parent, fixture); await mount(Parent, fixture);
}); });
test.skip("can use component in default-content of t-slot", async () => { test("can use component in default-content of t-slot", async () => {
class GrandChild extends Component { class GrandChild extends Component {
static template = xml``; static template = xml``;
} }
@@ -1735,7 +1735,7 @@ describe("slots", () => {
await mount(Parent, fixture); await mount(Parent, fixture);
}); });
test.skip("slot content has different key from other content -- static slot", async () => { test("slot content has different key from other content -- static slot", async () => {
class Child extends Component { class Child extends Component {
static template = xml`<div t-esc="props.parent" />`; static template = xml`<div t-esc="props.parent" />`;
} }
@@ -1757,7 +1757,7 @@ describe("slots", () => {
expect(fixture.innerHTML).toBe("<div>SlotDisplay</div><div>Parent</div>"); expect(fixture.innerHTML).toBe("<div>SlotDisplay</div><div>Parent</div>");
}); });
test.skip("slot content has different key from other content -- dynamic slot", async () => { test("slot content has different key from other content -- dynamic slot", async () => {
class Child extends Component { class Child extends Component {
static template = xml`<div t-esc="props.parent" />`; static template = xml`<div t-esc="props.parent" />`;
} }
@@ -1780,7 +1780,7 @@ describe("slots", () => {
expect(fixture.innerHTML).toBe("<div>SlotDisplay</div><div>Parent</div>"); expect(fixture.innerHTML).toBe("<div>SlotDisplay</div><div>Parent</div>");
}); });
test.skip("mix of slots, t-call, t-call with body, and giving own props child", async () => { test("mix of slots, t-call, t-call with body, and giving own props child", async () => {
expect.assertions(11); expect.assertions(11);
class C extends Component { class C extends Component {
@@ -1836,7 +1836,7 @@ describe("slots", () => {
expect(fixture.innerHTML).toBe("<button>inc</button>[B][C][A][sub1] [sub2334]"); expect(fixture.innerHTML).toBe("<button>inc</button>[B][C][A][sub1] [sub2334]");
}); });
test.skip("slot in multiple locations", async () => { test("slot in multiple locations", async () => {
class Child extends Component { class Child extends Component {
static template = xml`<div>child</div>`; static template = xml`<div>child</div>`;
} }
@@ -1869,7 +1869,7 @@ describe("slots", () => {
expect(fixture.innerHTML).toBe(" hello <div>child</div>"); expect(fixture.innerHTML).toBe(" hello <div>child</div>");
}); });
test.skip("dynamic slot in multiple locations", async () => { test("dynamic slot in multiple locations", async () => {
class Child extends Component { class Child extends Component {
static template = xml`<div>child</div>`; static template = xml`<div>child</div>`;
} }
@@ -1902,7 +1902,7 @@ describe("slots", () => {
expect(fixture.innerHTML).toBe("hello <div>child</div>"); expect(fixture.innerHTML).toBe("hello <div>child</div>");
}); });
test.skip("slot in t-foreach locations", async () => { test("slot in t-foreach locations", async () => {
class Child extends Component { class Child extends Component {
static template = xml`<div>child</div>`; static template = xml`<div>child</div>`;
} }
@@ -1933,33 +1933,4 @@ describe("slots", () => {
"<p>1 hello <div>child</div></p><p>2 hello <div>child</div></p>" "<p>1 hello <div>child</div></p><p>2 hello <div>child</div></p>"
); );
}); });
test.skip("conditional slot", async () => {
class Child extends Component {
static template = xml`<t t-slot="abc"/>`;
}
class Parent extends Component {
static template = xml`
<Child>
<t t-if="state.flag">
<t t-set-slot="abc">blue</t>
</t>
<t t-else="">
<t t-set-slot="abc">red</t>
</t>
</Child>`;
static components = { Child };
state = useState({ flag: true});
}
const parent = await mount(Parent, fixture);
expect(fixture.innerHTML).toBe("blue");
parent.state.flag = false;
await nextTick();
expect(fixture.innerHTML).toBe("red");
});
}); });
+1 -84
View File
@@ -304,7 +304,7 @@ describe("t-call", () => {
} }
await mount(Root, fixture, { await mount(Root, fixture, {
test: true, dev: true,
templates: ` templates: `
<templates> <templates>
<t t-name="someTemplate"> <t t-name="someTemplate">
@@ -315,87 +315,4 @@ describe("t-call", () => {
}); });
expect(fixture.innerHTML).toBe("childaaronchildlucas"); expect(fixture.innerHTML).toBe("childaaronchildlucas");
}); });
test("t-call-context: ComponentNode is not looked up in the context", async () => {
let child: any;
class Child extends Component {
static template = xml`<t t-slot="default"/>`;
static props = ["name"];
setup() {
child = this;
}
}
class Root extends Component {
static template = xml`
<t t-call="someTemplate" t-call-context="{method: function(){}}"/>`;
static components = { Child };
}
// The following things need a reference to the ComponentNode, historically
// we used to do this with ctx.__owl__, but this cannot work inside t-call-context
// - t-ref: node.refs[refName] = something
// - t-set: caused a call to capture, which used to use node.component
// - .bind: used to bind to node.component
const root = await mount(Root, fixture, {
templates: `
<templates>
<t t-name="someTemplate">
<div t-ref="myRef">outside slot</div>
<Child prop.bind="method">
<div t-ref="myRef2">I'm the default slot</div>
<t t-set="test" t-value="3"/>
<div t-esc="test"/>
</Child>
</t>
</templates>`,
});
expect(fixture.innerHTML).toBe(
"<div>outside slot</div><div>I'm the default slot</div><div>3</div>"
);
expect(Object.keys(child.__owl__.refs)).toEqual([]);
expect(Object.keys(root.__owl__.refs)).toEqual(["myRef", "myRef2"]);
});
test("t-call-context: slots don't make component available again when context is captured", async () => {
class Child extends Component {
static template = xml`<t t-slot="default"/>`;
}
class Root extends Component {
static template = xml`<t t-call="template" t-call-context="{}"/>`;
static components = { Child };
someValue = "Hello";
}
await mount(Root, fixture, {
test: true,
templates: `
<templates>
<t t-name="template">
<t t-set="dummy" t-value="0"/>
<Child>
<t t-esc="someValue"/>
</Child>
</t>
</templates>`,
});
expect(fixture.innerHTML).toBe("");
});
test("t-call-context: this is not available inside t-call-context", async () => {
class Root extends Component {
static template = xml`<t t-call="someTemplate" t-call-context="{}"/>`;
}
await mount(Root, fixture, {
templates: `
<templates>
<t t-name="someTemplate">
<t t-esc="this"/>
</t>
</templates>`,
});
expect(fixture.innerHTML).toBe("");
});
}); });
-50
View File
@@ -625,54 +625,4 @@ describe("t-model directive", () => {
await mount(Test, fixture); await mount(Test, fixture);
expect(fixture.querySelector("select")!.value).toEqual("b"); expect(fixture.querySelector("select")!.value).toEqual("b");
}); });
test("t-model is applied before t-on-input", async () => {
expect.assertions(3);
class SomeComponent extends Component {
static template = xml`
<div>
<input t-model="state['text']" t-on-input="onInput"/>
</div>
`;
state = useState({ text: "", other: "" });
onInput(ev: InputEvent) {
expect(this.state.text).toBe("Beam me up, Scotty");
expect((ev.target as HTMLInputElement).value).toBe("Beam me up, Scotty");
}
}
await mount(SomeComponent, fixture);
const input = fixture.querySelector("input")!;
await editInput(input, "Beam me up, Scotty");
});
test("t-model with radio button group in t-foreach", async () => {
expect.assertions(6);
const steps: string[] = [];
class SomeComponent extends Component {
static template = xml`
<div t-on-click="getData" id="get_data">
<t t-foreach="options" t-as="opt" t-key="opt">
<input type="radio" name="radio_group" t-model="state.group" t-att-value="opt" t-att-id="opt"/>
</t>
</div>
`;
state = useState({ group: "scotty" });
options = ["beam", "scotty"];
getData() {
steps.push(`group: ${this.state.group}`);
}
}
await mount(SomeComponent, fixture);
const divEl = fixture.querySelector("#get_data") as HTMLElement;
expect(fixture.querySelector("input:checked")!.getAttribute("id")).toBe("scotty");
divEl.click();
expect(steps).toEqual(["group: scotty"]);
fixture.querySelector("input")!.click();
expect(steps).toEqual(["group: scotty", "group: beam"]);
await nextTick();
expect(fixture.querySelector("input:checked")!.getAttribute("id")).toBe("beam");
divEl.click();
expect(steps).toEqual(["group: scotty", "group: beam", "group: beam"]);
});
}); });
-24
View File
@@ -41,30 +41,6 @@ describe("t-on", () => {
expect(steps).toEqual(["click"]); expect(steps).toEqual(["click"]);
}); });
test("t-on when first component child is an empty component", async () => {
class Child extends Component {
static template = xml`
<span t-foreach="props.list" t-as="c" t-key="c_index" t-esc="c"/>
`;
}
class Parent extends Component {
static template = xml`
<div t-on-click="push"><Child list="list" t-on-click="() => {}"/></div>
`;
static components = { Child };
list = useState([] as string[]);
push() {
this.list.push("foo");
}
}
const parent = await mount(Parent, fixture);
const el = elem(parent);
expect(el.innerHTML).toBe("");
el.click();
await nextTick();
expect(el.innerHTML).toBe("<span>foo</span>");
});
test("t-on expression in t-foreach", async () => { test("t-on expression in t-foreach", async () => {
class Comp extends Component { class Comp extends Component {
static template = xml` static template = xml`
+1 -1
View File
@@ -96,7 +96,7 @@ export function renderToBdom(template: string, context: any = {}, node?: any): B
createComponent() {}, createComponent() {},
createDynamicComponent() {}, createDynamicComponent() {},
}; };
return fn(app as any, blockDom, helpers).call(context, context, node); return fn(app as any, blockDom, helpers)(context, node);
} }
export function renderToString(template: string, context: any = {}, node?: any): string { export function renderToString(template: string, context: any = {}, node?: any): string {
+35 -35
View File
@@ -21,7 +21,7 @@ exports[`Portal Add and remove portals 1`] = `
ctx[\`portalId\`] = v_block1[i1]; ctx[\`portalId\`] = v_block1[i1];
const key1 = ctx['portalId']; const key1 = ctx['portalId'];
const ctx1 = capture(ctx); const ctx1 = capture(ctx);
c_block1[i1] = withKey(comp1({target: '#outside',slots: {'default': {__render: slot1.bind(this), __ctx: ctx1}}}, key + \`__1__\${key1}\`, node, ctx, Portal), key1); c_block1[i1] = withKey(comp1({target: '#outside',slots: {'default': {__render: slot1, __ctx: ctx1}}}, key + \`__1__\${key1}\`, node, ctx, Portal), key1);
} }
return list(c_block1); return list(c_block1);
} }
@@ -50,7 +50,7 @@ exports[`Portal Add and remove portals on div 1`] = `
ctx[\`portalId\`] = v_block1[i1]; ctx[\`portalId\`] = v_block1[i1];
const key1 = ctx['portalId']; const key1 = ctx['portalId'];
const ctx1 = capture(ctx); const ctx1 = capture(ctx);
c_block1[i1] = withKey(comp1({target: '#outside',slots: {'default': {__render: slot1.bind(this), __ctx: ctx1}}}, key + \`__1__\${key1}\`, node, ctx, Portal), key1); c_block1[i1] = withKey(comp1({target: '#outside',slots: {'default': {__render: slot1, __ctx: ctx1}}}, key + \`__1__\${key1}\`, node, ctx, Portal), key1);
} }
return list(c_block1); return list(c_block1);
} }
@@ -81,7 +81,7 @@ exports[`Portal Add and remove portals with t-foreach 1`] = `
const key1 = ctx['portalId']; const key1 = ctx['portalId'];
let txt1 = ctx['portalId']; let txt1 = ctx['portalId'];
const ctx1 = capture(ctx); const ctx1 = capture(ctx);
const b6 = comp1({target: '#outside',slots: {'default': {__render: slot1.bind(this), __ctx: ctx1}}}, key + \`__1__\${key1}\`, node, ctx, Portal); const b6 = comp1({target: '#outside',slots: {'default': {__render: slot1, __ctx: ctx1}}}, key + \`__1__\${key1}\`, node, ctx, Portal);
c_block1[i1] = withKey(block2([txt1], [b6]), key1); c_block1[i1] = withKey(block2([txt1], [b6]), key1);
} }
return list(c_block1); return list(c_block1);
@@ -113,7 +113,7 @@ exports[`Portal Add and remove portals with t-foreach and destroy 1`] = `
const key1 = ctx['portalId']; const key1 = ctx['portalId'];
let txt1 = ctx['portalId']; let txt1 = ctx['portalId'];
const ctx1 = capture(ctx); const ctx1 = capture(ctx);
const b6 = comp1({target: '#outside',slots: {'default': {__render: slot1.bind(this), __ctx: ctx1}}}, key + \`__1__\${key1}\`, node, ctx, Portal); const b6 = comp1({target: '#outside',slots: {'default': {__render: slot1, __ctx: ctx1}}}, key + \`__1__\${key1}\`, node, ctx, Portal);
c_block1[i1] = withKey(block2([txt1], [b6]), key1); c_block1[i1] = withKey(block2([txt1], [b6]), key1);
} }
return list(c_block1); return list(c_block1);
@@ -146,7 +146,7 @@ exports[`Portal Add and remove portals with t-foreach inside div 1`] = `
const key1 = ctx['portalId']; const key1 = ctx['portalId'];
let txt1 = ctx['portalId']; let txt1 = ctx['portalId'];
const ctx1 = capture(ctx); const ctx1 = capture(ctx);
const b7 = comp1({target: '#outside',slots: {'default': {__render: slot1.bind(this), __ctx: ctx1}}}, key + \`__1__\${key1}\`, node, ctx, Portal); const b7 = comp1({target: '#outside',slots: {'default': {__render: slot1, __ctx: ctx1}}}, key + \`__1__\${key1}\`, node, ctx, Portal);
c_block2[i1] = withKey(block3([txt1], [b7]), key1); c_block2[i1] = withKey(block3([txt1], [b7]), key1);
} }
const b2 = list(c_block2); const b2 = list(c_block2);
@@ -187,7 +187,7 @@ exports[`Portal Child and Portal 2`] = `
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
const b2 = block2(); const b2 = block2();
const b4 = comp1({target: '.portal',slots: {'default': {__render: slot1.bind(this), __ctx: ctx}}}, key + \`__1\`, node, ctx, Portal); const b4 = comp1({target: '.portal',slots: {'default': {__render: slot1, __ctx: ctx}}}, key + \`__1\`, node, ctx, Portal);
return multi([b2, b4]); return multi([b2, b4]);
} }
}" }"
@@ -208,7 +208,7 @@ exports[`Portal Portal composed with t-slot 1`] = `
} }
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
const b3 = comp2({slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx}})}, key + \`__2\`, node, this, null); const b3 = comp2({slots: markRaw({'default': {__render: slot1, __ctx: ctx}})}, key + \`__2\`, node, this, null);
return block1([], [b3]); return block1([], [b3]);
} }
}" }"
@@ -227,7 +227,7 @@ exports[`Portal Portal composed with t-slot 2`] = `
} }
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
return comp1({target: '#outside',slots: {'default': {__render: slot1.bind(this), __ctx: ctx}}}, key + \`__1\`, node, ctx, Portal); return comp1({target: '#outside',slots: {'default': {__render: slot1, __ctx: ctx}}}, key + \`__1\`, node, ctx, Portal);
} }
}" }"
`; `;
@@ -261,7 +261,7 @@ exports[`Portal basic use of portal 1`] = `
} }
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
const b3 = comp1({target: '#outside',slots: {'default': {__render: slot1.bind(this), __ctx: ctx}}}, key + \`__1\`, node, ctx, Portal); const b3 = comp1({target: '#outside',slots: {'default': {__render: slot1, __ctx: ctx}}}, key + \`__1\`, node, ctx, Portal);
return block1([], [b3]); return block1([], [b3]);
} }
}" }"
@@ -282,7 +282,7 @@ exports[`Portal basic use of portal in dev mode 1`] = `
} }
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
const b3 = comp1({target: '#outside',slots: {'default': {__render: slot1.bind(this), __ctx: ctx}}}, key + \`__1\`, node, ctx, Portal); const b3 = comp1({target: '#outside',slots: {'default': {__render: slot1, __ctx: ctx}}}, key + \`__1\`, node, ctx, Portal);
return block1([], [b3]); return block1([], [b3]);
} }
}" }"
@@ -303,7 +303,7 @@ exports[`Portal basic use of portal on div 1`] = `
} }
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
const b3 = comp1({target: '#outside',slots: {'default': {__render: slot1.bind(this), __ctx: ctx}}}, key + \`__1\`, node, ctx, Portal); const b3 = comp1({target: '#outside',slots: {'default': {__render: slot1, __ctx: ctx}}}, key + \`__1\`, node, ctx, Portal);
return block1([], [b3]); return block1([], [b3]);
} }
}" }"
@@ -324,7 +324,7 @@ exports[`Portal basic use of portal, variation 1`] = `
} }
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
const b3 = comp1({target: ctx['target'],slots: {'default': {__render: slot1.bind(this), __ctx: ctx}}}, key + \`__1\`, node, ctx, Portal); const b3 = comp1({target: ctx['target'],slots: {'default': {__render: slot1, __ctx: ctx}}}, key + \`__1\`, node, ctx, Portal);
return block1([], [b3]); return block1([], [b3]);
} }
}" }"
@@ -348,7 +348,7 @@ exports[`Portal conditional use of Portal (with sub Component) 1`] = `
let b2,b4; let b2,b4;
b2 = block2(); b2 = block2();
if (ctx['state'].hasPortal) { if (ctx['state'].hasPortal) {
b4 = comp2({target: '#outside',slots: {'default': {__render: slot1.bind(this), __ctx: ctx}}}, key + \`__2\`, node, ctx, Portal); b4 = comp2({target: '#outside',slots: {'default': {__render: slot1, __ctx: ctx}}}, key + \`__2\`, node, ctx, Portal);
} }
return multi([b2, b4]); return multi([b2, b4]);
} }
@@ -387,7 +387,7 @@ exports[`Portal conditional use of Portal 1`] = `
let b2,b4; let b2,b4;
b2 = block2(); b2 = block2();
if (ctx['state'].hasPortal) { if (ctx['state'].hasPortal) {
b4 = comp1({target: '#outside',slots: {'default': {__render: slot1.bind(this), __ctx: ctx}}}, key + \`__1\`, node, ctx, Portal); b4 = comp1({target: '#outside',slots: {'default': {__render: slot1, __ctx: ctx}}}, key + \`__1\`, node, ctx, Portal);
} }
return multi([b2, b4]); return multi([b2, b4]);
} }
@@ -432,7 +432,7 @@ exports[`Portal conditional use of Portal with child and div 2`] = `
ctx[\`elem\`] = v_block2[i1]; ctx[\`elem\`] = v_block2[i1];
const key1 = ctx['elem']; const key1 = ctx['elem'];
const ctx1 = capture(ctx); const ctx1 = capture(ctx);
c_block2[i1] = withKey(comp1({target: '#outside',slots: {'default': {__render: slot1.bind(this), __ctx: ctx1}}}, key + \`__1__\${key1}\`, node, ctx, Portal), key1); c_block2[i1] = withKey(comp1({target: '#outside',slots: {'default': {__render: slot1, __ctx: ctx1}}}, key + \`__1__\${key1}\`, node, ctx, Portal), key1);
} }
const b2 = list(c_block2); const b2 = list(c_block2);
return block1([], [b2]); return block1([], [b2]);
@@ -482,7 +482,7 @@ exports[`Portal conditional use of Portal with child and div, variation 2`] = `
ctx[\`elem\`] = v_block3[i1]; ctx[\`elem\`] = v_block3[i1];
const key1 = ctx['elem']; const key1 = ctx['elem'];
const ctx1 = capture(ctx); const ctx1 = capture(ctx);
c_block3[i1] = withKey(comp1({target: '#outside',slots: {'default': {__render: slot1.bind(this), __ctx: ctx1}}}, key + \`__1__\${key1}\`, node, ctx, Portal), key1); c_block3[i1] = withKey(comp1({target: '#outside',slots: {'default': {__render: slot1, __ctx: ctx1}}}, key + \`__1__\${key1}\`, node, ctx, Portal), key1);
} }
const b3 = list(c_block3); const b3 = list(c_block3);
return multi([b2, b3]); return multi([b2, b3]);
@@ -507,7 +507,7 @@ exports[`Portal conditional use of Portal with div 1`] = `
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
let b2; let b2;
if (ctx['state'].hasPortal) { if (ctx['state'].hasPortal) {
const b4 = comp1({target: '#outside',slots: {'default': {__render: slot1.bind(this), __ctx: ctx}}}, key + \`__1\`, node, ctx, Portal); const b4 = comp1({target: '#outside',slots: {'default': {__render: slot1, __ctx: ctx}}}, key + \`__1\`, node, ctx, Portal);
b2 = block2([], [b4]); b2 = block2([], [b4]);
} }
return multi([b2]); return multi([b2]);
@@ -532,7 +532,7 @@ exports[`Portal lifecycle hooks of portal sub component are properly called 1`]
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
let b3; let b3;
if (ctx['state'].hasChild) { if (ctx['state'].hasChild) {
b3 = comp2({target: '#outside',slots: {'default': {__render: slot1.bind(this), __ctx: ctx}}}, key + \`__2\`, node, ctx, Portal); b3 = comp2({target: '#outside',slots: {'default': {__render: slot1, __ctx: ctx}}}, key + \`__2\`, node, ctx, Portal);
} }
return block1([], [b3]); return block1([], [b3]);
} }
@@ -585,7 +585,7 @@ exports[`Portal portal and Child 2`] = `
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
const b2 = block2(); const b2 = block2();
const b4 = comp1({target: '.portal',slots: {'default': {__render: slot1.bind(this), __ctx: ctx}}}, key + \`__1\`, node, ctx, Portal); const b4 = comp1({target: '.portal',slots: {'default': {__render: slot1, __ctx: ctx}}}, key + \`__1\`, node, ctx, Portal);
return multi([b2, b4]); return multi([b2, b4]);
} }
}" }"
@@ -611,7 +611,7 @@ exports[`Portal portal could have dynamically no content 1`] = `
} }
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
const b4 = comp1({target: '#outside',slots: {'default': {__render: slot1.bind(this), __ctx: ctx}}}, key + \`__1\`, node, ctx, Portal); const b4 = comp1({target: '#outside',slots: {'default': {__render: slot1, __ctx: ctx}}}, key + \`__1\`, node, ctx, Portal);
return block1([], [b4]); return block1([], [b4]);
} }
}" }"
@@ -632,7 +632,7 @@ exports[`Portal portal destroys on crash 1`] = `
} }
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
const b3 = comp2({target: '#outside',slots: {'default': {__render: slot1.bind(this), __ctx: ctx}}}, key + \`__2\`, node, ctx, Portal); const b3 = comp2({target: '#outside',slots: {'default': {__render: slot1, __ctx: ctx}}}, key + \`__2\`, node, ctx, Portal);
return block1([], [b3]); return block1([], [b3]);
} }
}" }"
@@ -646,7 +646,7 @@ exports[`Portal portal destroys on crash 2`] = `
let block1 = createBlock(\`<span><block-text-0/></span>\`); let block1 = createBlock(\`<span><block-text-0/></span>\`);
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
let txt1 = ctx['props'].error&&ctx['this'].will.crash; let txt1 = ctx['props'].error&&this.will.crash;
return block1([txt1]); return block1([txt1]);
} }
}" }"
@@ -667,7 +667,7 @@ exports[`Portal portal with child and props 1`] = `
} }
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
const b3 = comp2({target: '#outside',slots: {'default': {__render: slot1.bind(this), __ctx: ctx}}}, key + \`__2\`, node, ctx, Portal); const b3 = comp2({target: '#outside',slots: {'default': {__render: slot1, __ctx: ctx}}}, key + \`__2\`, node, ctx, Portal);
return block1([], [b3]); return block1([], [b3]);
} }
}" }"
@@ -710,7 +710,7 @@ exports[`Portal portal with dynamic body 1`] = `
} }
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
const b5 = comp1({target: '#outside',slots: {'default': {__render: slot1.bind(this), __ctx: ctx}}}, key + \`__1\`, node, ctx, Portal); const b5 = comp1({target: '#outside',slots: {'default': {__render: slot1, __ctx: ctx}}}, key + \`__1\`, node, ctx, Portal);
return block1([], [b5]); return block1([], [b5]);
} }
}" }"
@@ -734,7 +734,7 @@ exports[`Portal portal with many children 1`] = `
} }
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
const b5 = comp1({target: '#outside',slots: {'default': {__render: slot1.bind(this), __ctx: ctx}}}, key + \`__1\`, node, ctx, Portal); const b5 = comp1({target: '#outside',slots: {'default': {__render: slot1, __ctx: ctx}}}, key + \`__1\`, node, ctx, Portal);
return block1([], [b5]); return block1([], [b5]);
} }
}" }"
@@ -758,7 +758,7 @@ exports[`Portal portal with no content 1`] = `
} }
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
const b4 = comp1({target: '#outside',slots: {'default': {__render: slot1.bind(this), __ctx: ctx}}}, key + \`__1\`, node, ctx, Portal); const b4 = comp1({target: '#outside',slots: {'default': {__render: slot1, __ctx: ctx}}}, key + \`__1\`, node, ctx, Portal);
return block1([], [b4]); return block1([], [b4]);
} }
}" }"
@@ -778,7 +778,7 @@ exports[`Portal portal with only text as content 1`] = `
} }
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
const b3 = comp1({target: '#outside',slots: {'default': {__render: slot1.bind(this), __ctx: ctx}}}, key + \`__1\`, node, ctx, Portal); const b3 = comp1({target: '#outside',slots: {'default': {__render: slot1, __ctx: ctx}}}, key + \`__1\`, node, ctx, Portal);
return block1([], [b3]); return block1([], [b3]);
} }
}" }"
@@ -799,7 +799,7 @@ exports[`Portal portal with target not in dom 1`] = `
} }
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
const b3 = comp1({target: '#does-not-exist',slots: {'default': {__render: slot1.bind(this), __ctx: ctx}}}, key + \`__1\`, node, ctx, Portal); const b3 = comp1({target: '#does-not-exist',slots: {'default': {__render: slot1, __ctx: ctx}}}, key + \`__1\`, node, ctx, Portal);
return block1([], [b3]); return block1([], [b3]);
} }
}" }"
@@ -820,7 +820,7 @@ exports[`Portal portal's parent's env is not polluted 1`] = `
} }
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
const b3 = comp2({target: '#outside',slots: {'default': {__render: slot1.bind(this), __ctx: ctx}}}, key + \`__2\`, node, ctx, Portal); const b3 = comp2({target: '#outside',slots: {'default': {__render: slot1, __ctx: ctx}}}, key + \`__2\`, node, ctx, Portal);
return block1([], [b3]); return block1([], [b3]);
} }
}" }"
@@ -875,7 +875,7 @@ exports[`Portal simple catchError with portal 2`] = `
} }
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
const b3 = comp1({target: '#outside',slots: {'default': {__render: slot1.bind(this), __ctx: ctx}}}, key + \`__1\`, node, ctx, Portal); const b3 = comp1({target: '#outside',slots: {'default': {__render: slot1, __ctx: ctx}}}, key + \`__1\`, node, ctx, Portal);
return block1([], [b3]); return block1([], [b3]);
} }
}" }"
@@ -896,7 +896,7 @@ exports[`Portal with target in template (after portal) 1`] = `
} }
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
const b3 = comp1({target: '#local-target',slots: {'default': {__render: slot1.bind(this), __ctx: ctx}}}, key + \`__1\`, node, ctx, Portal); const b3 = comp1({target: '#local-target',slots: {'default': {__render: slot1, __ctx: ctx}}}, key + \`__1\`, node, ctx, Portal);
return block1([], [b3]); return block1([], [b3]);
} }
}" }"
@@ -917,7 +917,7 @@ exports[`Portal with target in template (before portal) 1`] = `
} }
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
const b3 = comp1({target: '#local-target',slots: {'default': {__render: slot1.bind(this), __ctx: ctx}}}, key + \`__1\`, node, ctx, Portal); const b3 = comp1({target: '#local-target',slots: {'default': {__render: slot1, __ctx: ctx}}}, key + \`__1\`, node, ctx, Portal);
return block1([], [b3]); return block1([], [b3]);
} }
}" }"
@@ -938,7 +938,7 @@ exports[`Portal: Props validation target must be a valid selector 1`] = `
} }
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
const b3 = comp1({target: ' ',slots: {'default': {__render: slot1.bind(this), __ctx: ctx}}}, key + \`__1\`, node, ctx, Portal); const b3 = comp1({target: ' ',slots: {'default': {__render: slot1, __ctx: ctx}}}, key + \`__1\`, node, ctx, Portal);
return block1([], [b3]); return block1([], [b3]);
} }
}" }"
@@ -959,7 +959,7 @@ exports[`Portal: Props validation target must be a valid selector 2 1`] = `
} }
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
const b3 = comp1({target: 'aa',slots: {'default': {__render: slot1.bind(this), __ctx: ctx}}}, key + \`__1\`, node, ctx, Portal); const b3 = comp1({target: 'aa',slots: {'default': {__render: slot1, __ctx: ctx}}}, key + \`__1\`, node, ctx, Portal);
return block1([], [b3]); return block1([], [b3]);
} }
}" }"
@@ -980,7 +980,7 @@ exports[`Portal: UI/UX focus is kept across re-renders 1`] = `
} }
return function template(ctx, node, key = \\"\\") { return function template(ctx, node, key = \\"\\") {
const b3 = comp2({target: '#outside',slots: {'default': {__render: slot1.bind(this), __ctx: ctx}}}, key + \`__2\`, node, ctx, Portal); const b3 = comp2({target: '#outside',slots: {'default': {__render: slot1, __ctx: ctx}}}, key + \`__2\`, node, ctx, Portal);
return block1([], [b3]); return block1([], [b3]);
} }
}" }"
+3 -69
View File
@@ -9,7 +9,7 @@ import {
markRaw, markRaw,
toRaw, toRaw,
} from "../src"; } from "../src";
import { reactive, getSubscriptions } from "../src/runtime/reactivity"; import { reactive, Reactive } from "../src/runtime/reactivity";
import { batched } from "../src/runtime/utils"; import { batched } from "../src/runtime/utils";
import { import {
makeDeferred, makeDeferred,
@@ -1020,32 +1020,6 @@ describe("Reactivity", () => {
expect(n3).toBe(2); expect(n3).toBe(2);
}); });
test("reactive inside other: reading the inner reactive from outer doesn't affect the inner's subscriptions", async () => {
const getObservedKeys = (obj: any) => getSubscriptions(obj).flatMap(({ keys }) => keys);
let n1 = 0;
let n2 = 0;
const innerCb = () => n1++;
const outerCb = () => n2++;
const inner = createReactive({ a: 1 }, innerCb);
const outer = createReactive({ b: inner }, outerCb);
expect(n1).toBe(0);
expect(n2).toBe(0);
expect(getObservedKeys(innerCb)).toEqual([]);
expect(getObservedKeys(outerCb)).toEqual([]);
outer.b.a;
expect(getObservedKeys(innerCb)).toEqual([]);
expect(getObservedKeys(outerCb)).toEqual(["b", "a"]);
expect(n1).toBe(0);
expect(n2).toBe(0);
outer.b.a = 2;
expect(getObservedKeys(innerCb)).toEqual([]);
expect(getObservedKeys(outerCb)).toEqual([]);
expect(n1).toBe(0);
expect(n2).toBe(1);
});
// test("notification is not done after unregistration", async () => { // test("notification is not done after unregistration", async () => {
// let n = 0; // let n = 0;
// const observer = () => n++; // const observer = () => n++;
@@ -1168,46 +1142,6 @@ describe("Reactivity", () => {
expect(() => state.a).not.toThrow(); expect(() => state.a).not.toThrow();
expect(state.a).toBe(obj.a); expect(state.a).toBe(obj.a);
}); });
test("writing on object with reactive in prototype chain doesn't notify", async () => {
let n = 0;
const state = createReactive({ val: 0 }, () => n++);
const nonReactive = Object.create(state);
nonReactive.val++;
expect(n).toBe(0);
expect(toRaw(state)).toEqual({ val: 0 });
expect(toRaw(nonReactive)).toEqual({ val: 1 });
state.val++;
expect(n).toBe(1);
expect(toRaw(state)).toEqual({ val: 1 });
expect(toRaw(nonReactive)).toEqual({ val: 1 });
});
test("creating key on object with reactive in prototype chain doesn't notify", async () => {
let n = 0;
const parent = createReactive({}, () => n++);
const child = Object.create(parent);
Object.keys(parent); // Subscribe to key changes
child.val = 0;
expect(n).toBe(0);
});
test("reactive of object with reactive in prototype chain is not the object from the prototype chain", async () => {
const cb = () => {};
const parent = createReactive({ val: 0 }, cb);
const child = createReactive(Object.create(parent), cb);
expect(child).not.toBe(parent);
});
test("can create reactive of object with non-reactive in prototype chain", async () => {
let n = 0;
const parent = markRaw({ val: 0 });
const child = createReactive(Object.create(parent), () => n++);
child.val++;
expect(n).toBe(1);
expect(parent).toEqual({ val: 0 });
expect(child).toEqual({ val: 1 });
});
}); });
describe("Collections", () => { describe("Collections", () => {
@@ -1765,12 +1699,12 @@ describe("toRaw", () => {
const obj = { value: 1 }; const obj = { value: 1 };
const reactiveObj = reactive(obj); const reactiveObj = reactive(obj);
expect(reactiveObj).not.toBe(obj); expect(reactiveObj).not.toBe(obj);
expect(toRaw(reactiveObj)).toBe(obj); expect(toRaw(reactiveObj as Reactive<typeof obj>)).toBe(obj);
}); });
test("giving a non reactive to toRaw return the object itself", () => { test("giving a non reactive to toRaw return the object itself", () => {
const obj = { value: 1 }; const obj = { value: 1 };
expect(toRaw(obj)).toBe(obj); expect(toRaw(obj as Reactive<typeof obj>)).toBe(obj);
}); });
}); });
+1 -1
View File
@@ -48,7 +48,7 @@ function writeToFile(filepath, data) {
} }
// adapted from https://medium.com/@mhagemann/the-ultimate-way-to-slugify-a-url-string-in-javascript-b8e4a0d849e1 // adapted from https://medium.com/@mhagemann/the-ultimate-way-to-slugify-a-url-string-in-javascript-b8e4a0d849e1
const a = -_,:;"; const a = "·_,:;";
const p = new RegExp(a.split("").join("|"), "g"); const p = new RegExp(a.split("").join("|"), "g");
function slugify(str) { function slugify(str) {
+6 -35
View File
@@ -47,18 +47,6 @@ async function startRelease() {
let file = await ask(`Release notes (${REL_NOTES_FILE}): `); let file = await ask(`Release notes (${REL_NOTES_FILE}): `);
file = file || REL_NOTES_FILE; file = file || REL_NOTES_FILE;
let content; let content;
if (!fs.existsSync(`./${file}`)) {
let lastRelease = await getOutput("git log --grep='\\[REL\\]' -n 1 --pretty=%H");
const commitsSinceLastRelease = await getOutput(`git log ${lastRelease.trim()}..HEAD --pretty=%s`);
const commitsAsMdList = commitsSinceLastRelease.trim().split("\n").map(l => " - " + l).join("\n");
log(`${file} did not exist, created a template containing all commits since last release.`)
fs.writeFileSync(file, `# v${next}\n\n${commitsAsMdList}`);
const shouldContinue = await ask(`Check that the contents of ${file} is correct, then press y to continue: `);
if (shouldContinue.toLowerCase() !== "y") {
log("aborted");
return;
}
}
try { try {
content = await readFile("./" + file); content = await readFile("./" + file);
} catch (e) { } catch (e) {
@@ -116,7 +104,7 @@ async function startRelease() {
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
log(`Step 7/${STEPS}: Creating the release...`); log(`Step 7/${STEPS}: Creating the release...`);
const relaseResult = await execCommand(`gh release create v${next} dist/*.js ${draft} -F ${file}`); const relaseResult = await execCommand(`gh release create v${next} dist/*.js ${draft} -F ${REL_NOTES_FILE}`);
if (relaseResult !== 0) { if (relaseResult !== 0) {
logError("github release failed. Aborting."); logError("github release failed. Aborting.");
return; return;
@@ -132,7 +120,7 @@ async function startRelease() {
if (shouldUploadPlayground) { if (shouldUploadPlayground) {
log(`Bonus step: publishing new release on playground...`); log(`Bonus step: publishing new release on playground...`);
let owl_code = null; let owl_code = null;
let status = 0 status = 0
try { try {
owl_code = await readFile("dist/owl.iife.js"); owl_code = await readFile("dist/owl.iife.js");
@@ -142,8 +130,7 @@ async function startRelease() {
return; return;
} }
status |= await execCommand("git checkout gh-pages"); status += await execCommand("git checkout gh-pages");
status |= await execCommand("git pull --rebase");
if (status !== 0) { if (status !== 0) {
logError("Couldn't switch to gh-pages branch") logError("Couldn't switch to gh-pages branch")
@@ -157,9 +144,9 @@ async function startRelease() {
return; return;
} }
status |= await execCommand(`git commit -am "[IMP] update owl to v${next}"`); status += await execCommand(`git commit -am "[IMP] update owl to v${next}"`);
status |= await execCommand(`git push origin gh-pages`); status += await execCommand(`git push origin gh-pages`);
status |= await execCommand("git checkout -"); status += await execCommand("git checkout -");
if (status !== 0) { if (status !== 0) {
logError("Something went wrong for the playground update.") logError("Something went wrong for the playground update.")
} }
@@ -244,19 +231,3 @@ async function replaceInFile(file, from, to) {
}); });
}); });
} }
async function getOutput(command) {
return new Promise((resolve, reject) => {
const childProcess = exec(command, (err, stdout, stderr) => {
if (err) {
reject(err);
}
resolve(stdout);
});
childProcess.on("exit", code => {
if (code !== 0) {
reject(code);
}
});
});
}