Compare commits

...

10 Commits

Author SHA1 Message Date
Samuel Degueldre c30678f3ea [REL] v2.0.4
# v2.0.4

 - [IMP] app: expose live apps for the devtools
 - [FIX] compiler: support t-model radio group in t-foreach
 - [IMP] runtime: improve useExternalListener typing
2023-01-23 12:21:13 +01:00
Samuel Degueldre 6ca6717965 [IMP] tools: release scripts creates a template for release notes
This template contains the release version as a markdown title followed
by a markdown list with all the commit titles since the previous
release.
2023-01-23 12:19:14 +01:00
Julien Carion ad4adb930e [IMP] app: expose live apps for the devtools
This commit exposes owl apps in a global variable so that the owl
devtools can hook themselves on these apps. While the devtools are not
yet ready to be merged, exposing the apps will allow us to test the
devtools in production scenarios while polishing the development of
them.
2023-01-23 11:40:02 +01:00
Lucas Perais cea82e945d [FIX] compiler: t-model supports radio group in t-foreach
Have a radio group defined inside a t-foreach:
```xml
  <t t-foreach="values" t-as="val" t-key="val">
    <input name="radiogroup" t-att-value="val" t-model="state.radioGroup" />
  </t>
```

Before this commit the algorithm that set the "checked" attribute on the current active
radio button according to the state did not support having a dynamic value (`t-att-value`)

After this commit, this use case works as we go look in the dynamic attributes too.
2023-01-20 09:26:54 +01:00
Samuel Degueldre a69f8a39e7 [IMP] runtime: improve useExternalListener typing
Previously the type of the target for useExternalListener was
HTMLElement or Window, this makes document an invalid target. Here there
is no reason to use the EventTarget interface instead, as
useExternalListener only uses methods from that interface and should
work with any event target.

Closes odoo/owl#1323
2023-01-18 13:03:23 +01:00
Géry Debongnie 316eb06279 [REL] v2.0.3
# v2.0.2

Some small bug fixes

- fix: compiler: correctly escape backslashes when emitting block string
- fix: reactivity: don't subscribe to keys when making reactive
- fix: t-call-context: fix capture making component available in ctx
- fix: t-call-context: make `this` unavailable in rendering context
2023-01-12 16:29:11 +01:00
Samuel Degueldre df59ec49ae [FIX] t-call-context: make this unavailable in rendering context
t-call-context is a feature that's supposed to mask the rendering
context completely, but currently the component remains available
through `this`.

This commit stops treating `this` as a reserved word, so that it's
compiled to a lookup in the rendering context, and adds `this` to the
rendering context when binding the component's rendering function. With
these changes, `this` behaves the same as before when outside a
t-call-context, but when the rendering context is overriden, the
template can no longer access `this`. `this` still represents the
instance of the component inside of the rendering function since it's
needed by owl internally.

A side-effect of this change is that now the rendering context is no
longer the instance of the component by default, but is always an object
with the component in its prototype chain. This was already the case
before in some contexts (eg inside t-foreach, or inside components with
a t-set/t-call anywhere in its template). This can cause issues in rare
cases when a component method was called directly on the rendering
context, as before this change, the method's bound this would be the
component instance (except in a t-foreach, component with a
t-set/t-call, etc), while after this change it is now never the
component instance. When the method only reads on `this` there is no
issue as all the components properties are available on the rendering
contexts, but setting a value on `this` will write on the rendering
context and not the component which is likely a mistake.

While this is a breaking change, simply adding a t-set/t-call to any
template would break components that would be broken by this change,
with this in mind we decided to make this change anyway so that
developers get the error as early as possible in the development cycle
rather than having a seemingly inocuous change break code under them.
2023-01-12 09:38:22 +01:00
Samuel Degueldre 2a008a8679 [IMP] tooling: add eslint ci step to avoid stray .only and debugger 2023-01-11 15:47:09 +01:00
Samuel Degueldre 3d40533de1 [FIX] t-call-context: fix capture making component available in ctx
Previously, when using a component with a slot within a t-call with
t-call-context, the component would become available again inside the
slot despite the t-call-context. This was caused by the fact that the
capture helper function creates an object with the component as its
prototype which is incorrect. It should just use the previous context as
its prototype.
2023-01-09 09:28:10 +01:00
Samuel Degueldre 39329f80b2 [FIX] reactivity: don't subscribe to keys when making reactive
When attempting to create a reactive object, we first check if the
target can be made reactive, this is done with Object.toString, which
internally reads the Symbol.toStringTag on the underlying object. When
trying to make a reactive object from another, for example when
reobserving a reactive or when reading a reactive object from the
context of another, this would read the subscribe the original object to
the Symbol.toStringTag property.

This commit fixes that by calling Object.toString on the underlying
target object where applicable.
2022-12-08 14:22:39 +01:00
31 changed files with 6144 additions and 127 deletions
+47
View File
@@ -0,0 +1,47 @@
{
"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,
},
}
+2 -1
View File
@@ -22,7 +22,8 @@ jobs:
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- run: npm install
- run: npm ci
- run: npm run test
- run: npm run check-formatting
- run: npm run lint
- run: npm run build
-3
View File
@@ -14,9 +14,6 @@ npm-debug.log*
yarn-debug.log*
yarn-error.log*
package-lock.json
yarn.lock
#ide's
.vscode
.idea
+3 -3
View File
@@ -174,9 +174,9 @@ class Root extends Component {
```
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
component is the rendering context. Note that we use the `id` of each task as a
`t-key`, which is very common. There are two css classes: `task-list` and `task`,
through the tasks. It can find the `tasks` list from the component, since the rendering
context contains the properties of the component. Note that we use the `id` of each task
as a `t-key`, which is very common. There are two css classes: `task-list` and `task`,
that we will use in the next section.
Finally, notice the use of the `t-att-checked` attribute:
+3 -4
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>
```
2. it can use anything in the rendering context (typically, the component):
2. it can use anything in the rendering context (which typically contains the properties of the component):
```xml
<p t-if="user.birthday === today()">Happy bithday!</p>
@@ -541,9 +541,8 @@ 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
rendering context (so, the current component). However, it may be useful to be
able to specify a specific object as context. This can be done by using the
`t-call-context` directive:
rendering context. However, it may be useful to be able to specify a specific
object as context. This can be done by using the `t-call-context` directive:
```xml
<t t-call="other-template" t-call-context="obj"/>
+5711
View File
File diff suppressed because it is too large Load Diff
+6 -6
View File
@@ -1,6 +1,6 @@
{
"name": "@odoo/owl",
"version": "2.0.2",
"version": "2.0.4",
"description": "Odoo Web Library (OWL)",
"main": "dist/owl.cjs.js",
"module": "dist/owl.es.js",
@@ -25,6 +25,7 @@
"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",
"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",
"release": "node tools/release.js",
"compile_templates": "node tools/compile_xml.js"
@@ -42,26 +43,25 @@
"devDependencies": {
"@types/jest": "^27.0.1",
"@types/node": "^14.11.8",
"@typescript-eslint/eslint-plugin": "5.48.1",
"@typescript-eslint/parser": "5.48.1",
"chalk": "^3.0.0",
"cpx": "^1.5.0",
"current-git-branch": "^1.1.0",
"eslint": "8.31.0",
"git-rev-sync": "^1.12.0",
"github-api": "^3.3.0",
"jest": "^27.1.0",
"jest-diff": "^27.3.1",
"jest-environment-jsdom": "^27.1.0",
"live-server": "^1.2.1",
"npm-run-all": "^4.1.5",
"prettier": "2.4.1",
"rollup": "^2.56.3",
"rollup-plugin-dts": "^4.2.2",
"rollup-plugin-terser": "^7.0.2",
"rollup-plugin-typescript2": "^0.31.1",
"sass": "^1.16.1",
"source-map-support": "^0.5.10",
"ts-jest": "^27.0.5",
"typescript": "4.5.2",
"uglify-es": "^3.3.9"
"typescript": "4.5.2"
},
"jest": {
"testEnvironment": "jsdom",
+11 -3
View File
@@ -632,7 +632,15 @@ export class CodeGenerator {
let idx: number;
if (specialInitTargetAttr) {
idx = block!.insertData(`${fullExpression} === '${attrs[targetAttr]}'`, "attr");
let targetExpr = targetAttr in attrs && `'${attrs[targetAttr]}'`;
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;
} else if (hasDynamicChildren) {
const bValueId = generateId("bValue");
@@ -1140,7 +1148,7 @@ export class CodeGenerator {
if (this.target.loopLevel || !this.hasSafeContext) {
ctxStr = generateId("ctx");
this.helpers.add("capture");
this.define(ctxStr, `capture(ctx, this)`);
this.define(ctxStr, `capture(ctx)`);
}
let slotStr: string[] = [];
for (let slotName in ast.slots) {
@@ -1316,7 +1324,7 @@ export class CodeGenerator {
if (this.target.loopLevel || !this.hasSafeContext) {
ctxStr = generateId("ctx");
this.helpers.add("capture");
this.define(ctxStr, `capture(ctx, this)`);
this.define(ctxStr, `capture(ctx)`);
}
let id = generateId("comp");
this.staticDefs.push({
+1 -1
View File
@@ -28,7 +28,7 @@ import { OwlError } from "../runtime/error_handling";
//------------------------------------------------------------------------------
const RESERVED_WORDS =
"true,false,NaN,null,undefined,debugger,console,window,in,instanceof,new,function,return,this,eval,void,Math,RegExp,Array,Object,Date".split(
"true,false,NaN,null,undefined,debugger,console,window,in,instanceof,new,function,return,eval,void,Math,RegExp,Array,Object,Date".split(
","
);
+15 -2
View File
@@ -1,12 +1,11 @@
import { Component, ComponentConstructor, Props } from "./component";
import { ComponentNode } from "./component_node";
import { nodeErrorHandlers, OwlError } from "./error_handling";
import { nodeErrorHandlers, OwlError, handleError } from "./error_handling";
import { Fiber, MountOptions } from "./fibers";
import { Scheduler } from "./scheduler";
import { validateProps } from "./template_helpers";
import { TemplateSet, TemplateSetConfig } from "./template_set";
import { validateTarget } from "./utils";
import { handleError } from "./error_handling";
// reimplement dev mode stuff see last change in 0f7a8289a6fb8387c3c1af41c6664b2a8448758f
@@ -32,6 +31,18 @@ This is not suitable for production use.
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>;
};
}
}
window.__OWL_DEVTOOLS__ ||= {
apps: new Set<App>(),
};
export class App<
T extends abstract new (...args: any) => any = any,
P extends object = any,
@@ -49,6 +60,7 @@ export class App<
constructor(Root: ComponentConstructor<P, E>, config: AppConfig<P, E> = {}) {
super(config);
this.Root = Root;
window.__OWL_DEVTOOLS__.apps.add(this);
if (config.test) {
this.dev = true;
}
@@ -110,6 +122,7 @@ export class App<
this.scheduler.flush();
this.root.destroy();
}
window.__OWL_DEVTOOLS__.apps.delete(this);
}
createComponent<P extends Props>(
+3 -1
View File
@@ -114,7 +114,8 @@ export class ComponentNode<P extends Props = any, E = any> implements VNode<Comp
}
}
this.component = new C(props, env, this);
this.renderFn = app.getTemplate(C.template).bind(this.component, this.component, this);
const ctx = Object.assign(Object.create(this.component), { this: this.component });
this.renderFn = app.getTemplate(C.template).bind(this.component, ctx, this);
this.component.setup();
currentNode = null;
}
@@ -317,6 +318,7 @@ export class ComponentNode<P extends Props = any, E = any> implements VNode<Comp
}
_patch() {
let hasChildren = false;
// eslint-disable-next-line @typescript-eslint/no-unused-vars
for (let _k in this.children) {
hasChildren = true;
break;
+1 -1
View File
@@ -117,7 +117,7 @@ export function useEffect(effect: Effect, computeDependencies: () => any[] = ()
* `useExternalListener(window, 'click', this._doSomething);`
* */
export function useExternalListener(
target: HTMLElement | typeof window,
target: EventTarget,
eventName: string,
handler: EventListener,
eventParams?: AddEventListenerOptions
+1 -1
View File
@@ -28,7 +28,7 @@ const COLLECTION_RAWTYPES = new Set(["Set", "Map", "WeakMap"]);
* @returns the raw type of the object
*/
function rawType(obj: any) {
return objectToString.call(obj).slice(8, -1);
return objectToString.call(toRaw(obj)).slice(8, -1);
}
/**
* Checks whether a given value can be made into a reactive object.
+3 -3
View File
@@ -46,8 +46,8 @@ function callSlot(
return slotBDom || text("");
}
function capture(ctx: any, component: any): any {
const result = ObjectCreate(component);
function capture(ctx: any): any {
const result = ObjectCreate(ctx);
for (let k in ctx) {
result[k] = ctx[k];
}
@@ -114,7 +114,7 @@ class LazyValue {
constructor(fn: any, ctx: any, component: any, node: any, key: any) {
this.fn = fn;
this.ctx = capture(ctx, component);
this.ctx = capture(ctx);
this.component = component;
this.node = node;
this.key = key;
@@ -519,8 +519,9 @@ exports[`t-on t-on, with arguments and t-call 2`] = `
let block1 = createBlock(\`<p block-handler-0=\\"click\\">lucas</p>\`);
return function template(ctx, node, key = \\"\\") {
const v1 = ctx['value'];
let hdlr1 = [()=>this.update(v1), ctx];
const v1 = ctx['this'];
const v2 = ctx['value'];
let hdlr1 = [()=>v1.update(v2), ctx];
return block1([hdlr1]);
}
}"
+3
View File
@@ -284,6 +284,9 @@ describe("t-on", () => {
expect(this).toBe(owner);
expect(val).toBe(444);
},
get this() {
return owner;
},
value: 444,
};
@@ -940,9 +940,10 @@ exports[`can catch errors catching error, rethrow, render parent -- a main comp
for (let i1 = 0; i1 < l_block1; i1++) {
ctx[\`cp\`] = v_block1[i1];
const key1 = ctx['cp'].id;
const v1 = ctx['cp'];
const ctx1 = capture(ctx, this);
c_block1[i1] = withKey(comp2({onError: ()=>this.cleanUp(v1.id),slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__2__\${key1}\`, node, this, null), key1);
const v1 = ctx['this'];
const v2 = ctx['cp'];
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);
}
return list(c_block1);
}
@@ -1014,13 +1015,14 @@ exports[`can catch errors catching in child makes parent render 1`] = `
return function template(ctx, node, key = \\"\\") {
ctx = Object.create(ctx);
const [k_block1, v_block1, l_block1, c_block1] = prepareList(Object.entries(this.elements));;
const [k_block1, v_block1, l_block1, c_block1] = prepareList(Object.entries(ctx['this'].elements));;
for (let i1 = 0; i1 < l_block1; i1++) {
ctx[\`elem\`] = v_block1[i1];
const key1 = ctx['elem'][0];
const v1 = ctx['elem'];
const ctx1 = capture(ctx, this);
c_block1[i1] = withKey(comp2({onError: (_error)=>this.onError(v1[0],_error),slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__2__\${key1}\`, node, this, null), key1);
const v1 = ctx['this'];
const v2 = ctx['elem'];
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);
}
return list(c_block1);
}
@@ -1155,7 +1157,7 @@ exports[`can catch errors onError in class inheritance is called if rethrown 2`]
return function template(ctx, node, key = \\"\\") {
let b2,b3;
if (!ctx['state'].error) {
b2 = text(this.will.crash);
b2 = text(ctx['this'].will.crash);
} else {
b3 = text(ctx['state'].error);
}
@@ -1186,7 +1188,7 @@ exports[`can catch errors onError in class inheritance is not called if no rethr
return function template(ctx, node, key = \\"\\") {
let b2,b3;
if (!ctx['state'].error) {
b2 = text(this.will.crash);
b2 = text(ctx['this'].will.crash);
} else {
b3 = text(ctx['state'].error);
}
@@ -1218,7 +1220,7 @@ exports[`errors and promises a rendering error in a sub component will reject th
let block1 = createBlock(\`<div><block-text-0/></div>\`);
return function template(ctx, node, key = \\"\\") {
let txt1 = this.will.crash;
let txt1 = ctx['this'].will.crash;
return block1([txt1]);
}
}"
@@ -1232,7 +1234,7 @@ exports[`errors and promises a rendering error will reject the mount promise 1`]
let block1 = createBlock(\`<div><block-text-0/></div>\`);
return function template(ctx, node, key = \\"\\") {
let txt1 = this.will.crash;
let txt1 = ctx['this'].will.crash;
return block1([txt1]);
}
}"
@@ -1277,7 +1279,7 @@ exports[`errors and promises a rendering error will reject the render promise 1`
return function template(ctx, node, key = \\"\\") {
let b2;
if (ctx['flag']) {
b2 = text(this.will.crash);
b2 = text(ctx['this'].will.crash);
}
return block1([], [b2]);
}
@@ -78,7 +78,7 @@ exports[`refs refs are properly bound in slots 1`] = `
return function template(ctx, node, key = \\"\\") {
let txt1 = ctx['state'].val;
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
const b3 = comp1({slots: markRaw({'footer': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__1\`, node, this, null);
return block1([txt1], [b3]);
}
@@ -54,7 +54,7 @@ exports[`slots can define and call slots 1`] = `
}
return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx, this);
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);
return block1([], [b4]);
}
@@ -89,7 +89,7 @@ exports[`slots can define and call slots with bound params 1`] = `
}
return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx, this);
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);
}
}"
@@ -129,7 +129,7 @@ exports[`slots can define and call slots with params 1`] = `
}
return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx, this);
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);
return block1([], [b4]);
}
@@ -363,7 +363,7 @@ exports[`slots default content is not rendered if named slot is provided 1`] = `
}
return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
const b3 = comp1({slots: markRaw({'header': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__1\`, node, this, null);
return block1([], [b3]);
}
@@ -442,7 +442,7 @@ exports[`slots default slot next to named slot, with default content 1`] = `
}
return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
const b3 = comp1({slots: markRaw({'footer': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__1\`, node, this, null);
return block1([], [b3]);
}
@@ -619,7 +619,7 @@ exports[`slots dynamic slot in multiple locations 1`] = `
}
return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
return comp2({location: ctx['state'].location,slots: markRaw({'coffee': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__2\`, node, this, null);
}
}"
@@ -685,7 +685,7 @@ exports[`slots dynamic t-slot call 1`] = `
}
return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx, this);
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);
return block1([], [b6]);
}
@@ -732,7 +732,7 @@ exports[`slots dynamic t-slot call with default 1`] = `
}
return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx, this);
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);
return block1([], [b6]);
}
@@ -865,7 +865,7 @@ exports[`slots mix of slots, t-call, t-call with body, and giving own props chil
}
return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
return comp1({slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__2\`, node, this, null);
}
}"
@@ -991,7 +991,7 @@ exports[`slots multiple roots are allowed in a named slot 1`] = `
}
return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
const b5 = comp1({slots: markRaw({'content': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__1\`, node, this, null);
return block1([], [b5]);
}
@@ -1031,7 +1031,7 @@ exports[`slots multiple slots containing components 1`] = `
}
return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx, this);
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);
}
}"
@@ -1085,7 +1085,7 @@ exports[`slots named slot inside slot 1`] = `
}
function slot2(ctx, node, key = \\"\\") {
const ctx2 = capture(ctx, this);
const ctx2 = capture(ctx);
return comp1({slots: markRaw({'brol': {__render: slot3.bind(this), __ctx: ctx2}})}, key + \`__1\`, node, this, null);
}
@@ -1095,7 +1095,7 @@ exports[`slots named slot inside slot 1`] = `
}
return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx, this);
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);
return block1([], [b5]);
}
@@ -1136,7 +1136,7 @@ exports[`slots named slot inside slot, part 3 1`] = `
}
function slot2(ctx, node, key = \\"\\") {
const ctx2 = capture(ctx, this);
const ctx2 = capture(ctx);
return comp1({slots: markRaw({'brol': {__render: slot3.bind(this), __ctx: ctx2}})}, key + \`__1\`, node, this, null);
}
@@ -1146,7 +1146,7 @@ exports[`slots named slot inside slot, part 3 1`] = `
}
return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx, this);
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);
return block1([], [b5]);
}
@@ -1221,7 +1221,7 @@ exports[`slots named slots inside slot, again 1`] = `
}
function slot2(ctx, node, key = \\"\\") {
const ctx2 = capture(ctx, this);
const ctx2 = capture(ctx);
return comp1({slots: markRaw({'brol2': {__render: slot3.bind(this), __ctx: ctx2}})}, key + \`__1\`, node, this, null);
}
@@ -1231,7 +1231,7 @@ exports[`slots named slots inside slot, again 1`] = `
}
return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx, this);
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);
return block1([], [b5]);
}
@@ -1569,7 +1569,7 @@ exports[`slots simple dynamic slot with slot scope 1`] = `
}
return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
return comp1({slots: markRaw({'slotName': {__render: slot1.bind(this), __ctx: ctx1, __scope: \\"slotScope\\"}})}, key + \`__1\`, node, this, null);
}
}"
@@ -1599,7 +1599,7 @@ exports[`slots simple named and empty slot -- 2 1`] = `
const comp1 = app.createComponent(\`Child\`, true, true, false, true);
return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
return comp1({slots: markRaw({'myEmptySlot': {myProp: 'myProp text'}})}, key + \`__1\`, node, this, null);
}
}"
@@ -1636,7 +1636,7 @@ exports[`slots simple named and empty slot 1`] = `
}
return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
return comp1({slots: markRaw({'myEmptySlot': {}, 'default': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__1\`, node, this, null);
}
}"
@@ -1676,7 +1676,7 @@ exports[`slots simple slot with slot scope 1`] = `
}
return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
return comp1({slots: markRaw({'slotName': {__render: slot1.bind(this), __ctx: ctx1, __scope: \\"slotScope\\"}})}, key + \`__1\`, node, this, null);
}
}"
@@ -1712,7 +1712,7 @@ exports[`slots slot and (inline) t-call 1`] = `
}
return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
const b3 = comp1({slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__2\`, node, this, null);
return block1([], [b3]);
}
@@ -1762,7 +1762,7 @@ exports[`slots slot and t-call 1`] = `
}
return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
const b3 = comp1({slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__2\`, node, this, null);
return block1([], [b3]);
}
@@ -1992,12 +1992,13 @@ exports[`slots slot content is bound to caller (variation) 1`] = `
ctx = Object.create(ctx);
ctx[isBoundary] = 1
setContextValue(ctx, \\"var\\", 1);
let hdlr1 = [()=>this.inc(), ctx];
const v1 = ctx['this'];
let hdlr1 = [()=>v1.inc(), ctx];
return block1([hdlr1]);
}
return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
return comp1({slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__1\`, node, this, null);
}
}"
@@ -2224,7 +2225,7 @@ exports[`slots slot preserves properly parented relationship, even through t-cal
}
return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
const b3 = comp1({slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__2\`, node, this, null);
return block1([], [b3]);
}
@@ -2285,7 +2286,7 @@ exports[`slots slot with slot scope and t-props 1`] = `
}
return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
return comp1({slots: markRaw({'slotName': {__render: slot1.bind(this), __ctx: ctx1, __scope: \\"info\\"}})}, key + \`__1\`, node, this, null);
}
}"
@@ -2359,7 +2360,8 @@ exports[`slots slots are properly bound to correct component 2`] = `
ctx = Object.create(ctx);
ctx[isBoundary] = 1
setContextValue(ctx, \\"var\\", 1);
let hdlr1 = [()=>this.increment(), ctx];
const v1 = ctx['this'];
let hdlr1 = [()=>v1.increment(), ctx];
let txt1 = ctx['state'].value;
return block1([hdlr1, txt1]);
}
@@ -2387,7 +2389,7 @@ exports[`slots slots are rendered with proper context 1`] = `
return function template(ctx, node, key = \\"\\") {
let txt1 = ctx['state'].val;
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
const b3 = comp1({slots: markRaw({'footer': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__1\`, node, this, null);
return block1([txt1], [b3]);
}
@@ -2431,7 +2433,7 @@ exports[`slots slots are rendered with proper context, part 2 1`] = `
for (let i1 = 0; i1 < l_block2; i1++) {
ctx[\`user\`] = v_block2[i1];
const key1 = ctx['user'].id;
const ctx1 = capture(ctx, this);
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);
c_block2[i1] = withKey(block3([], [b7]), key1);
}
@@ -2480,7 +2482,7 @@ exports[`slots slots are rendered with proper context, part 3 1`] = `
ctx[\`user\`] = v_block2[i1];
const key1 = ctx['user'].id;
setContextValue(ctx, \\"userdescr\\", 'User '+ctx['user'].name);
const ctx1 = capture(ctx, this);
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);
c_block2[i1] = withKey(block3([], [b5]), key1);
}
@@ -2523,7 +2525,7 @@ exports[`slots slots are rendered with proper context, part 4 1`] = `
ctx = Object.create(ctx);
ctx[isBoundary] = 1
setContextValue(ctx, \\"userdescr\\", 'User '+ctx['state'].user.name);
const ctx1 = capture(ctx, this);
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);
return block1([], [b3]);
}
@@ -2565,7 +2567,7 @@ exports[`slots slots in slots, with vars 1`] = `
ctx = Object.create(ctx);
ctx[isBoundary] = 1
setContextValue(ctx, \\"test\\", ctx['state'].name);
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
const b3 = comp1({slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__1\`, node, this, null);
return block1([], [b3]);
}
@@ -2627,7 +2629,7 @@ exports[`slots slots in t-foreach and re-rendering 1`] = `
ctx[\`n\`] = v_block2[i1];
ctx[\`n_index\`] = i1;
const key1 = ctx['n_index'];
const ctx1 = capture(ctx, this);
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);
}
const b2 = list(c_block2);
@@ -2682,7 +2684,7 @@ exports[`slots slots in t-foreach in t-foreach 1`] = `
for (let i2 = 0; i2 < l_block6; i2++) {
ctx[\`node2\`] = v_block6[i2];
const key2 = ctx['node2'].key;
const ctx1 = capture(ctx, this);
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);
}
ctx = ctx.__proto__;
@@ -2734,7 +2736,7 @@ exports[`slots slots in t-foreach with t-set and re-rendering 1`] = `
ctx[\`n_index\`] = i1;
const key1 = ctx['n_index'];
setContextValue(ctx, \\"dummy\\", ctx['n_index']);
const ctx1 = capture(ctx, this);
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);
}
const b2 = list(c_block2);
@@ -2774,7 +2776,7 @@ exports[`slots t-debug on a t-set-slot (defining a slot) 1`] = `
}
return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
const b3 = comp1({slots: markRaw({'content': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__1\`, node, this, null);
return block1([], [b3]);
}
@@ -2813,7 +2815,7 @@ exports[`slots t-set t-value in a slot 1`] = `
}
return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
const b3 = comp1({slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__1\`, node, this, null);
return block1([], [b3]);
}
@@ -2847,7 +2849,7 @@ exports[`slots t-set-slot=default has priority over rest of the content 1`] = `
}
return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
return comp1({slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__1\`, node, this, null);
}
}"
@@ -2905,7 +2907,7 @@ exports[`slots t-slot in recursive templates 1`] = `
}
return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
return comp1({slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__2\`, node, this, null);
}
}"
@@ -3083,7 +3085,7 @@ exports[`slots t-slot within dynamic t-call 1`] = `
}
return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
const b3 = comp1({slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__2\`, node, this, null);
return block1([], [b3]);
}
@@ -363,7 +363,7 @@ exports[`style and class handling error in subcomponent with class 2`] = `
return function template(ctx, node, key = \\"\\") {
let attr1 = ctx['props'].class;
let txt1 = this.will.crash;
let txt1 = ctx['this'].will.crash;
return block1([attr1, txt1]);
}
}"
@@ -109,7 +109,8 @@ exports[`t-call handlers are properly bound through a dynamic t-call 2`] = `
let block1 = createBlock(\`<p block-handler-0=\\"click\\">lucas</p>\`);
return function template(ctx, node, key = \\"\\") {
let hdlr1 = [()=>this.update(), ctx];
const v1 = ctx['this'];
let hdlr1 = [()=>v1.update(), ctx];
return block1([hdlr1]);
}
}"
@@ -168,8 +169,9 @@ exports[`t-call handlers with arguments are properly bound through a t-call 2`]
let block1 = createBlock(\`<p block-handler-0=\\"click\\">lucas</p>\`);
return function template(ctx, node, key = \\"\\") {
const v1 = ctx['a'];
let hdlr1 = [()=>this.update(v1), ctx];
const v1 = ctx['this'];
const v2 = ctx['a'];
let hdlr1 = [()=>v1.update(v2), ctx];
return block1([hdlr1]);
}
}"
@@ -287,7 +289,7 @@ exports[`t-call recursive t-call binding this -- static t-call 2`] = `
ctx[isBoundary] = 1
let b2;
if (ctx['level']<2) {
let hdlr1 = [\\"stop\\", ctx['onClicked'].bind(this), ctx];
let hdlr1 = [\\"stop\\", ctx['onClicked'].bind(ctx['this']), ctx];
let txt1 = ctx['level'];
const b3 = block3([hdlr1, txt1]);
ctx = Object.create(ctx);
@@ -538,7 +540,6 @@ exports[`t-call t-call-context: ComponentNode is not looked up in the context 2`
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>\`);
let block6 = createBlock(\`<div><block-text-0/></div>\`);
function slot1(ctx, node, key = \\"\\") {
const refs = this.__owl__.refs;
@@ -547,20 +548,18 @@ exports[`t-call t-call-context: ComponentNode is not looked up in the context 2`
ctx[isBoundary] = 1
const b4 = block4([ref2]);
setContextValue(ctx, \\"test\\", 3);
let txt1 = this.__owl__.name;
let txt1 = ctx['test'];
const b5 = block5([txt1]);
let txt2 = ctx['test'];
const b6 = block6([txt2]);
return multi([b4, b5, b6]);
return multi([b4, b5]);
}
return function template(ctx, node, key = \\"\\") {
const refs = this.__owl__.refs;
const ref1 = (el) => refs[\`myRef\`] = el;
const b2 = block2([ref1]);
const ctx1 = capture(ctx, this);
const b7 = comp1({prop: bind(this, ctx['method']),slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__1\`, node, this, null);
return multi([b2, b7]);
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]);
}
}"
`;
@@ -576,3 +575,75 @@ exports[`t-call t-call-context: ComponentNode is not looked up in the context 3`
}
}"
`;
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']);
}
}"
`;
@@ -175,7 +175,7 @@ exports[`list of components order is correct when slots are not of same type 1`]
}
return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx, this);
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);
}
}"
@@ -569,6 +569,36 @@ 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`] = `
"function anonymous(app, bdom, helpers
) {
@@ -111,8 +111,9 @@ exports[`t-on t-on method call in t-foreach 1`] = `
const key1 = ctx['val'];
let txt1 = ctx['val_index'];
let txt2 = ctx['val']+'';
const v1 = ctx['val'];
let hdlr1 = [()=>this.addVal(v1), ctx];
const v1 = ctx['this'];
const v2 = ctx['val'];
let hdlr1 = [()=>v1.addVal(v2), ctx];
c_block2[i1] = withKey(block3([txt1, txt2, hdlr1]), key1);
}
const b2 = list(c_block2);
@@ -197,8 +198,9 @@ exports[`t-on t-on on components and t-foreach 1`] = `
for (let i1 = 0; i1 < l_block1; i1++) {
ctx[\`name\`] = v_block1[i1];
const key1 = ctx['name'];
const v1 = ctx['name'];
const hdlr1 = [()=>this.log(v1), ctx];
const v1 = ctx['this'];
const v2 = ctx['name'];
const hdlr1 = [()=>v1.log(v2), ctx];
c_block1[i1] = withKey(catcher1(comp1({value: ctx['name']}, key + \`__1__\${key1}\`, node, this, null), [hdlr1]), key1);
}
return list(c_block1);
@@ -293,8 +295,9 @@ exports[`t-on t-on on components, with a handler update 1`] = `
ctx = Object.create(ctx);
ctx[isBoundary] = 1
setContextValue(ctx, \\"name\\", ctx['state'].name);
const v1 = ctx['name'];
const hdlr1 = [()=>this.log(v1), ctx];
const v1 = ctx['this'];
const v2 = ctx['name'];
const hdlr1 = [()=>v1.log(v2), ctx];
return catcher1(comp1({value: ctx['name']}, key + \`__1\`, node, this, null), [hdlr1]);
}
}"
@@ -393,7 +396,8 @@ exports[`t-on t-on on t-set-slots 1`] = `
function slot1(ctx, node, key = \\"\\") {
const b6 = block6();
const b7 = block7();
const hdlr1 = [()=>this.state.count++, ctx];
const v1 = ctx['this'];
const hdlr1 = [()=>v1.state.count++, ctx];
return catcher1(multi([b6, b7]), [hdlr1]);
}
@@ -401,7 +405,7 @@ exports[`t-on t-on on t-set-slots 1`] = `
const b2 = text(\` [\`);
const b3 = text(ctx['state'].count);
const b4 = text(\`] \`);
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
const b8 = comp1({slots: markRaw({'myslot': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__1\`, node, this, null);
return multi([b2, b3, b4, b8]);
}
@@ -450,7 +454,8 @@ exports[`t-on t-on on t-slots 2`] = `
const b2 = text(\` [\`);
const b3 = text(ctx['state'].count);
const b4 = text(\`] \`);
const hdlr1 = [()=>this.state.count++, ctx];
const v1 = ctx['this'];
const hdlr1 = [()=>v1.state.count++, ctx];
const b5 = catcher1(callSlot(ctx, node, key, 'default', false, {}), [hdlr1]);
return multi([b2, b3, b4, b5]);
}
@@ -21,7 +21,7 @@ exports[`t-set slot setted value (with t-set) not accessible with t-esc 1`] = `
ctx[isBoundary] = 1
setContextValue(ctx, \\"iter\\", 'source');
let txt1 = ctx['iter'];
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
const b2 = comp1({slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__1\`, node, this, null);
let txt2 = ctx['iter'];
return block1([txt1, txt2], [b2]);
@@ -70,7 +70,7 @@ exports[`t-set slots with a t-set with a component in body 1`] = `
}
return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
return comp2({slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__2\`, node, this, null);
}
}"
@@ -127,7 +127,7 @@ exports[`t-set slots with an t-set with a component in body 1`] = `
}
return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
return comp2({slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__2\`, node, this, null);
}
}"
@@ -178,7 +178,7 @@ exports[`t-set slots with an unused t-set with a component in body 1`] = `
}
return function template(ctx, node, key = \\"\\") {
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
return comp2({slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__2\`, node, this, null);
}
}"
+1 -2
View File
@@ -1,6 +1,5 @@
import { App, Component, mount, onMounted, useRef, useState } from "../../src/index";
import { App, Component, mount, onMounted, useRef, useState, xml } from "../../src/index";
import { logStep, makeTestFixture, nextAppError, nextTick, snapshotEverything } from "../helpers";
import { xml } from "../../src/index";
snapshotEverything();
let fixture: HTMLElement;
+44 -3
View File
@@ -316,7 +316,7 @@ describe("t-call", () => {
expect(fixture.innerHTML).toBe("childaaronchildlucas");
});
test.only("t-call-context: ComponentNode is not looked up in the context", async () => {
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"/>`;
@@ -345,16 +345,57 @@ describe("t-call", () => {
<Child prop.bind="method">
<div t-ref="myRef2">I'm the default slot</div>
<t t-set="test" t-value="3"/>
<div t-esc="this.__owl__.name"/>
<div t-esc="test"/>
</Child>
</t>
</templates>`,
});
expect(fixture.innerHTML).toBe(
"<div>outside slot</div><div>I'm the default slot</div><div>Root</div><div>3</div>"
"<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("");
});
});
+31
View File
@@ -644,4 +644,35 @@ describe("t-model directive", () => {
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"]);
});
});
+8 -8
View File
@@ -20,7 +20,7 @@ exports[`Portal Add and remove portals 1`] = `
for (let i1 = 0; i1 < l_block1; i1++) {
ctx[\`portalId\`] = v_block1[i1];
const key1 = ctx['portalId'];
const ctx1 = capture(ctx, this);
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);
}
return list(c_block1);
@@ -49,7 +49,7 @@ exports[`Portal Add and remove portals on div 1`] = `
for (let i1 = 0; i1 < l_block1; i1++) {
ctx[\`portalId\`] = v_block1[i1];
const key1 = ctx['portalId'];
const ctx1 = capture(ctx, this);
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);
}
return list(c_block1);
@@ -80,7 +80,7 @@ exports[`Portal Add and remove portals with t-foreach 1`] = `
ctx[\`portalId\`] = v_block1[i1];
const key1 = ctx['portalId'];
let txt1 = ctx['portalId'];
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
const b6 = comp1({target: '#outside',slots: {'default': {__render: slot1.bind(this), __ctx: ctx1}}}, key + \`__1__\${key1}\`, node, ctx, Portal);
c_block1[i1] = withKey(block2([txt1], [b6]), key1);
}
@@ -112,7 +112,7 @@ exports[`Portal Add and remove portals with t-foreach and destroy 1`] = `
ctx[\`portalId\`] = v_block1[i1];
const key1 = ctx['portalId'];
let txt1 = ctx['portalId'];
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
const b6 = comp1({target: '#outside',slots: {'default': {__render: slot1.bind(this), __ctx: ctx1}}}, key + \`__1__\${key1}\`, node, ctx, Portal);
c_block1[i1] = withKey(block2([txt1], [b6]), key1);
}
@@ -145,7 +145,7 @@ exports[`Portal Add and remove portals with t-foreach inside div 1`] = `
ctx[\`portalId\`] = v_block2[i1];
const key1 = ctx['portalId'];
let txt1 = ctx['portalId'];
const ctx1 = capture(ctx, this);
const ctx1 = capture(ctx);
const b7 = comp1({target: '#outside',slots: {'default': {__render: slot1.bind(this), __ctx: ctx1}}}, key + \`__1__\${key1}\`, node, ctx, Portal);
c_block2[i1] = withKey(block3([txt1], [b7]), key1);
}
@@ -431,7 +431,7 @@ exports[`Portal conditional use of Portal with child and div 2`] = `
for (let i1 = 0; i1 < l_block2; i1++) {
ctx[\`elem\`] = v_block2[i1];
const key1 = ctx['elem'];
const ctx1 = capture(ctx, this);
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);
}
const b2 = list(c_block2);
@@ -481,7 +481,7 @@ exports[`Portal conditional use of Portal with child and div, variation 2`] = `
for (let i1 = 0; i1 < l_block3; i1++) {
ctx[\`elem\`] = v_block3[i1];
const key1 = ctx['elem'];
const ctx1 = capture(ctx, this);
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);
}
const b3 = list(c_block3);
@@ -646,7 +646,7 @@ exports[`Portal portal destroys on crash 2`] = `
let block1 = createBlock(\`<span><block-text-0/></span>\`);
return function template(ctx, node, key = \\"\\") {
let txt1 = ctx['props'].error&&this.will.crash;
let txt1 = ctx['props'].error&&ctx['this'].will.crash;
return block1([txt1]);
}
}"
+27 -1
View File
@@ -9,7 +9,7 @@ import {
markRaw,
toRaw,
} from "../src";
import { reactive } from "../src/runtime/reactivity";
import { reactive, getSubscriptions } from "../src/runtime/reactivity";
import { batched } from "../src/runtime/utils";
import {
makeDeferred,
@@ -1020,6 +1020,32 @@ describe("Reactivity", () => {
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 () => {
// let n = 0;
// const observer = () => n++;
+29 -1
View File
@@ -47,6 +47,18 @@ async function startRelease() {
let file = await ask(`Release notes (${REL_NOTES_FILE}): `);
file = file || REL_NOTES_FILE;
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 {
content = await readFile("./" + file);
} catch (e) {
@@ -104,7 +116,7 @@ async function startRelease() {
// ---------------------------------------------------------------------------
log(`Step 7/${STEPS}: Creating the release...`);
const relaseResult = await execCommand(`gh release create v${next} dist/*.js ${draft} -F ${REL_NOTES_FILE}`);
const relaseResult = await execCommand(`gh release create v${next} dist/*.js ${draft} -F ${file}`);
if (relaseResult !== 0) {
logError("github release failed. Aborting.");
return;
@@ -231,3 +243,19 @@ 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);
}
});
});
}