Compare commits

...

7 Commits

Author SHA1 Message Date
Géry Debongnie ab72cdddde [REL] v2.0.0-beta-21
# v2.0.0-beta-21

- fix: prevent side effects at template compilation
- fix: props validation: does not crash with t-call-context
- fix: make t-portal work in all cases
- fix: make props validation work through slots
2022-09-26 15:44:11 +02:00
Géry Debongnie 17fb33475c [FIX] props validation: make it work through slots
A recent commit fixes the props validation code to make it work
regardless of the rendering context (important with the recent
t-call-context directive). Unfortunately, it then breaks props
validation through slots, because it assumed that the parent node in the
virtual node was the parent of the component, but it is not necessarily
true.

To fix this, we can use a simple property of the template functions:
they are bound to the current instance of the component, so we can
simply use "this"
2022-09-26 15:17:58 +02:00
Géry Debongnie ab29b896eb [FIX] portal: make it work in all cases
Before this commit, the portal wouldn't work when its target is created
after the portal content, since it wouldn't be able to mount the dom at
the correct location.

With this commit, we work around the issue by mounting the portal
content at the portal location, then when the Portal component is
mounted, moving it to its correct location.

The big downside with that approach is that the portal content is
(sometimes) rendered and mounted at a location, THEN mounted in another
location. I think that it is most of the time not an issue, but one
could argue that it is inconsistent: some specific code could work at
one point, then fail in a different very similar situation (for example,
iframes don't support very well being moved around).  On the flip side,
having the portal work as expected is very useful, and may be worth the
tradeoff.

closes #1250
2022-09-26 12:01:12 +02:00
Géry Debongnie d5ed25cd19 [FIX] props validation: does not crash with t-call-context
The code for props validation assumed that the rendering context was a
component.  This was actually true when it was written, but is no longer
true since t-call-context was introduced.

Because of that, it would crash when trying to access the internals of
the component, such as the static components object.

The fix is simple: instead of passing the context to the props
validation code, which can now be anything, we pass the component node,
which is guaranteed to give a reference to the component (and also to
the app).  This also make the code slightly simpler.

closes #1261
2022-09-24 08:34:22 +02:00
Géry Debongnie c4f0f17b9b [FIX] blockdom: prevent side effects at block compilation
When creating the template node for a block, we create htmlelements and
set their (static) attributes.  But this can have side effects. For
example, setting the src attribute for an img element will trigger a
request to fetch the image.

We avoid that issue by simply setting the html element template node
inside a <template/> element.

Note that I don't really see how to test this fix in jest: we don't have
a real browser, and no real way to check for this side effect.

closes #1257
2022-09-21 13:59:13 +02:00
Florent Dardenne - dafl@odoo d27455e9f2 [IMP] doc: explicit useEffect first parameter
The `useEffect` has two parameters:
* The `effect` function
* The `computeDependencies` function

The `effect` function always take as parameters the result
 of the `computeDependencies` function.

Expliciting this allows to better understand the `useEffect`
behaviour and the following example in the doc:

```
useEffect(
    (el) => el && el.focus(),
    () => [ref.el]
  );
```
2022-09-09 20:24:56 +02:00
Géry Debongnie 6ef38676c4 [DOC] doc: fix broken link and update roadmap 2022-09-09 09:45:05 +02:00
18 changed files with 384 additions and 139 deletions
+1 -1
View File
@@ -124,5 +124,5 @@ npm install @odoo/owl
If you want to use a simple `<script>` tag, the last release can be downloaded here:
- [owl-1.4.10](https://github.com/odoo/owl/releases/tag/v1.4.10)
- [owl](https://github.com/odoo/owl/releases/latest)
+2 -1
View File
@@ -234,7 +234,8 @@ are defined by a function instead of just the dependencies.
The `useEffect` hook takes two function: the effect function and the dependency
function. The effect function perform some task and return (optionally) a cleanup
function. The dependency function returns a list of dependencies. If any of these
function. The dependency function returns a list of dependencies, these dependencies
are passed as parameters in the effect function . If any of these
dependencies changes, then the current effect will be cleaned up and reexecuted.
Here is an example without any dependencies:
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@odoo/owl",
"version": "2.0.0-beta-20",
"version": "2.0.0-beta-21",
"description": "Odoo Web Library (OWL)",
"main": "dist/owl.cjs.js",
"module": "dist/owl.es.js",
+4 -23
View File
@@ -1,28 +1,9 @@
# 🦉 OWL Roadmap 🦉
- Current version: 1.4.10
- Current version: 2.X
- Status: stable
This roadmap is only an attempt at predicting Owl's future. Everything may
change!
### 1.x
- add chrome and firefox devtools,
- fix every bugs,
- improve documentation,
- small backward compatible improvements.
### 2.x (2020? 2021? 2022?)
- stop support for `t-set` directive to define the content of a slot
Maybe:
- reimplement vdom to use *block* system, like Vue 3, which should make Owl
much faster
- refactor `QWeb` to use an intermediate representation (some kind of AST) to
allow additional optimisations.
Owl is currently stable. No (large) improvements is expected in the near future.
Note that we intend to keep maintaining owl, and as such, improvements and/or
breaking changes may require a version bump in the future.
+1 -1
View File
@@ -1188,7 +1188,7 @@ export class CodeGenerator {
}
if (this.dev) {
this.addLine(`helpers.validateProps(${expr}, ${propVar!}, ctx);`);
this.addLine(`helpers.validateProps(${expr}, ${propVar!}, this);`);
}
if (block && (ctx.forceNewBlock === false || ctx.tKeyExpr)) {
+9
View File
@@ -157,6 +157,15 @@ function buildTree(
: document.createElement(tagName);
}
if (el instanceof Element) {
if (!domParentTree) {
// some html elements may have side effects when setting their attributes.
// For example, setting the src attribute of an <img/> will trigger a
// request to get the corresponding image. This is something that we
// don't want at compile time. We avoid that by putting the content of
// the block in a <template/> element
const fragment = document.createElement("template").content;
fragment.appendChild(el);
}
for (let i = 0; i < attrs.length; i++) {
const attrName = attrs[i].name;
const attrValue = attrs[i].value;
+37 -32
View File
@@ -1,4 +1,4 @@
import { onWillUnmount } from "./lifecycle_hooks";
import { onMounted, onWillUnmount } from "./lifecycle_hooks";
import { BDom, text, VNode } from "./blockdom";
import { Component } from "./component";
import { OwlError } from "./error_handling";
@@ -6,60 +6,55 @@ import { OwlError } from "./error_handling";
const VText: any = text("").constructor;
class VPortal extends VText implements Partial<VNode<VPortal>> {
// selector: string;
realBDom: BDom | null;
content: BDom | null;
selector: string;
target: HTMLElement | null = null;
constructor(selector: string, realBDom: BDom) {
constructor(selector: string, content: BDom) {
super("");
this.selector = selector;
this.realBDom = realBDom;
this.content = content;
}
mount(parent: HTMLElement, anchor: ChildNode) {
super.mount(parent, anchor);
this.target = document.querySelector(this.selector) as any;
if (!this.target) {
let el: any = this.el;
while (el && el.parentElement instanceof HTMLElement) {
el = el.parentElement;
}
this.target = el && el.querySelector(this.selector);
if (!this.target) {
throw new OwlError("invalid portal target");
}
if (this.target) {
this.content!.mount(this.target!, null);
} else {
this.content!.mount(parent, anchor);
}
this.realBDom!.mount(this.target!, null);
}
beforeRemove() {
this.realBDom!.beforeRemove();
this.content!.beforeRemove();
}
remove() {
if (this.realBDom) {
if (this.content) {
super.remove();
this.realBDom!.remove();
this.realBDom = null;
this.content!.remove();
this.content = null;
}
}
patch(other: VPortal) {
super.patch(other);
if (this.realBDom) {
this.realBDom.patch(other.realBDom!, true);
if (this.content) {
this.content.patch(other.content!, true);
} else {
this.realBDom = other.realBDom;
this.realBDom!.mount(this.target!, null);
this.content = other.content;
this.content!.mount(this.target!, null);
}
}
}
/**
* <t t-slot="default"/>
* kind of similar to <t t-slot="default"/>, but it wraps it around a VPortal
*/
export function portalTemplate(app: any, bdom: any, helpers: any) {
let { callSlot } = helpers;
return function template(ctx: any, node: any, key = "") {
return callSlot(ctx, node, key, "default", false, null);
return function template(ctx: any, node: any, key = ""): any {
return new VPortal(ctx.props.target, callSlot(ctx, node, key, "default", false, null));
};
}
@@ -73,13 +68,23 @@ export class Portal extends Component {
};
setup() {
const node = this.__owl__;
const renderFn = node.renderFn;
node.renderFn = () => new VPortal(this.props.target, renderFn());
onWillUnmount(() => {
if (node.bdom) {
node.bdom.remove();
const node: any = this.__owl__;
onMounted(() => {
const portal: VPortal = node.bdom;
if (!portal.target) {
const target: HTMLElement = document.querySelector(this.props.target);
if (target) {
portal.content!.moveBefore(target, null);
} else {
throw new OwlError("invalid portal target");
}
}
});
onWillUnmount(() => {
const portal: VPortal = node.bdom;
portal.remove();
});
}
}
+3 -3
View File
@@ -207,11 +207,11 @@ function multiRefSetter(refs: RefMap, name: string): RefSetter {
* visit recursively the props and all the children to check if they are valid.
* This is why it is only done in 'dev' mode.
*/
export function validateProps<P>(name: string | ComponentConstructor<P>, props: P, parent?: any) {
export function validateProps<P>(name: string | ComponentConstructor<P>, props: P, comp?: any) {
const ComponentClass =
typeof name !== "string"
? name
: (parent.constructor.components[name] as ComponentConstructor<P> | undefined);
: (comp.constructor.components[name] as ComponentConstructor<P> | undefined);
if (!ComponentClass) {
// this is an error, wrong component. We silently return here instead so the
@@ -221,7 +221,7 @@ export function validateProps<P>(name: string | ComponentConstructor<P>, props:
const schema = ComponentClass.props;
if (!schema) {
if (parent.__owl__.app.warnIfNoStaticProps) {
if (comp.__owl__.app.warnIfNoStaticProps) {
console.warn(`Component '${ComponentClass.name}' does not have a static props description`);
}
return;
@@ -20,7 +20,7 @@ exports[`basics display a nice error if it cannot find component (in dev mode) 1
return function template(ctx, node, key = \\"\\") {
const props1 = {};
helpers.validateProps(\`SomeMispelledComponent\`, props1, ctx);
helpers.validateProps(\`SomeMispelledComponent\`, props1, this);
return comp1(props1, key + \`__1\`, node, this, null);
}
}"
@@ -704,7 +704,7 @@ exports[`lifecycle hooks timeout in onWillUpdateProps emits a warning 1`] = `
return function template(ctx, node, key = \\"\\") {
const props1 = {prop: ctx['state'].prop};
helpers.validateProps(\`Child\`, props1, ctx);
helpers.validateProps(\`Child\`, props1, this);
return comp1(props1, key + \`__1\`, node, this, null);
}
}"
@@ -8,7 +8,7 @@ exports[`default props a default prop cannot be defined on a mandatory prop 1`]
return function template(ctx, node, key = \\"\\") {
const props1 = {};
helpers.validateProps(\`Child\`, props1, ctx);
helpers.validateProps(\`Child\`, props1, this);
return comp1(props1, key + \`__1\`, node, this, null);
}
}"
@@ -24,7 +24,7 @@ exports[`default props can set default boolean values 1`] = `
return function template(ctx, node, key = \\"\\") {
const props1 = {};
helpers.validateProps(\`SubComp\`, props1, ctx);
helpers.validateProps(\`SubComp\`, props1, this);
const b2 = comp1(props1, key + \`__1\`, node, this, null);
return block1([], [b2]);
}
@@ -61,7 +61,7 @@ exports[`default props can set default values 1`] = `
return function template(ctx, node, key = \\"\\") {
const props1 = {};
helpers.validateProps(\`SubComp\`, props1, ctx);
helpers.validateProps(\`SubComp\`, props1, this);
const b2 = comp1(props1, key + \`__1\`, node, this, null);
return block1([], [b2]);
}
@@ -92,7 +92,7 @@ exports[`default props default values are also set whenever component is updated
return function template(ctx, node, key = \\"\\") {
const props1 = {p: ctx['state'].p};
helpers.validateProps(\`SubComp\`, props1, ctx);
helpers.validateProps(\`SubComp\`, props1, this);
const b2 = comp1(props1, key + \`__1\`, node, this, null);
return block1([], [b2]);
}
@@ -121,7 +121,7 @@ exports[`props validation can specify that additional props are allowed (array)
return function template(ctx, node, key = \\"\\") {
const props1 = {message: 'm',otherProp: 'o'};
helpers.validateProps(\`Child\`, props1, ctx);
helpers.validateProps(\`Child\`, props1, this);
return comp1(props1, key + \`__1\`, node, this, null);
}
}"
@@ -148,7 +148,7 @@ exports[`props validation can specify that additional props are allowed (object)
return function template(ctx, node, key = \\"\\") {
const props1 = {message: 'm',otherProp: 'o'};
helpers.validateProps(\`Child\`, props1, ctx);
helpers.validateProps(\`Child\`, props1, this);
return comp1(props1, key + \`__1\`, node, this, null);
}
}"
@@ -177,7 +177,7 @@ exports[`props validation can validate a prop with multiple types 1`] = `
return function template(ctx, node, key = \\"\\") {
const props1 = {p: ctx['p']};
helpers.validateProps(\`SubComp\`, props1, ctx);
helpers.validateProps(\`SubComp\`, props1, this);
const b2 = comp1(props1, key + \`__1\`, node, this, null);
return block1([], [b2]);
}
@@ -207,7 +207,7 @@ exports[`props validation can validate a prop with multiple types 3`] = `
return function template(ctx, node, key = \\"\\") {
const props1 = {p: ctx['p']};
helpers.validateProps(\`SubComp\`, props1, ctx);
helpers.validateProps(\`SubComp\`, props1, this);
const b2 = comp1(props1, key + \`__1\`, node, this, null);
return block1([], [b2]);
}
@@ -237,7 +237,7 @@ exports[`props validation can validate a prop with multiple types 5`] = `
return function template(ctx, node, key = \\"\\") {
const props1 = {p: ctx['p']};
helpers.validateProps(\`SubComp\`, props1, ctx);
helpers.validateProps(\`SubComp\`, props1, this);
const b2 = comp1(props1, key + \`__1\`, node, this, null);
return block1([], [b2]);
}
@@ -254,7 +254,7 @@ exports[`props validation can validate an array with given primitive type 1`] =
return function template(ctx, node, key = \\"\\") {
const props1 = {p: ctx['p']};
helpers.validateProps(\`SubComp\`, props1, ctx);
helpers.validateProps(\`SubComp\`, props1, this);
const b2 = comp1(props1, key + \`__1\`, node, this, null);
return block1([], [b2]);
}
@@ -284,7 +284,7 @@ exports[`props validation can validate an array with given primitive type 3`] =
return function template(ctx, node, key = \\"\\") {
const props1 = {p: ctx['p']};
helpers.validateProps(\`SubComp\`, props1, ctx);
helpers.validateProps(\`SubComp\`, props1, this);
const b2 = comp1(props1, key + \`__1\`, node, this, null);
return block1([], [b2]);
}
@@ -314,7 +314,7 @@ exports[`props validation can validate an array with given primitive type 5`] =
return function template(ctx, node, key = \\"\\") {
const props1 = {p: ctx['p']};
helpers.validateProps(\`SubComp\`, props1, ctx);
helpers.validateProps(\`SubComp\`, props1, this);
const b2 = comp1(props1, key + \`__1\`, node, this, null);
return block1([], [b2]);
}
@@ -331,7 +331,7 @@ exports[`props validation can validate an array with given primitive type 6`] =
return function template(ctx, node, key = \\"\\") {
const props1 = {p: ctx['p']};
helpers.validateProps(\`SubComp\`, props1, ctx);
helpers.validateProps(\`SubComp\`, props1, this);
const b2 = comp1(props1, key + \`__1\`, node, this, null);
return block1([], [b2]);
}
@@ -348,7 +348,7 @@ exports[`props validation can validate an array with multiple sub element types
return function template(ctx, node, key = \\"\\") {
const props1 = {p: ctx['p']};
helpers.validateProps(\`SubComp\`, props1, ctx);
helpers.validateProps(\`SubComp\`, props1, this);
const b2 = comp1(props1, key + \`__1\`, node, this, null);
return block1([], [b2]);
}
@@ -378,7 +378,7 @@ exports[`props validation can validate an array with multiple sub element types
return function template(ctx, node, key = \\"\\") {
const props1 = {p: ctx['p']};
helpers.validateProps(\`SubComp\`, props1, ctx);
helpers.validateProps(\`SubComp\`, props1, this);
const b2 = comp1(props1, key + \`__1\`, node, this, null);
return block1([], [b2]);
}
@@ -408,7 +408,7 @@ exports[`props validation can validate an array with multiple sub element types
return function template(ctx, node, key = \\"\\") {
const props1 = {p: ctx['p']};
helpers.validateProps(\`SubComp\`, props1, ctx);
helpers.validateProps(\`SubComp\`, props1, this);
const b2 = comp1(props1, key + \`__1\`, node, this, null);
return block1([], [b2]);
}
@@ -438,7 +438,7 @@ exports[`props validation can validate an array with multiple sub element types
return function template(ctx, node, key = \\"\\") {
const props1 = {p: ctx['p']};
helpers.validateProps(\`SubComp\`, props1, ctx);
helpers.validateProps(\`SubComp\`, props1, this);
const b2 = comp1(props1, key + \`__1\`, node, this, null);
return block1([], [b2]);
}
@@ -455,7 +455,7 @@ exports[`props validation can validate an object with simple shape 1`] = `
return function template(ctx, node, key = \\"\\") {
const props1 = {p: ctx['p']};
helpers.validateProps(\`SubComp\`, props1, ctx);
helpers.validateProps(\`SubComp\`, props1, this);
const b2 = comp1(props1, key + \`__1\`, node, this, null);
return block1([], [b2]);
}
@@ -485,7 +485,7 @@ exports[`props validation can validate an object with simple shape 3`] = `
return function template(ctx, node, key = \\"\\") {
const props1 = {p: ctx['p']};
helpers.validateProps(\`SubComp\`, props1, ctx);
helpers.validateProps(\`SubComp\`, props1, this);
const b2 = comp1(props1, key + \`__1\`, node, this, null);
return block1([], [b2]);
}
@@ -502,7 +502,7 @@ exports[`props validation can validate an object with simple shape 4`] = `
return function template(ctx, node, key = \\"\\") {
const props1 = {p: ctx['p']};
helpers.validateProps(\`SubComp\`, props1, ctx);
helpers.validateProps(\`SubComp\`, props1, this);
const b2 = comp1(props1, key + \`__1\`, node, this, null);
return block1([], [b2]);
}
@@ -519,7 +519,7 @@ exports[`props validation can validate an object with simple shape 5`] = `
return function template(ctx, node, key = \\"\\") {
const props1 = {p: ctx['p']};
helpers.validateProps(\`SubComp\`, props1, ctx);
helpers.validateProps(\`SubComp\`, props1, this);
const b2 = comp1(props1, key + \`__1\`, node, this, null);
return block1([], [b2]);
}
@@ -536,7 +536,7 @@ exports[`props validation can validate an optional props 1`] = `
return function template(ctx, node, key = \\"\\") {
const props1 = {p: ctx['p']};
helpers.validateProps(\`SubComp\`, props1, ctx);
helpers.validateProps(\`SubComp\`, props1, this);
const b2 = comp1(props1, key + \`__1\`, node, this, null);
return block1([], [b2]);
}
@@ -566,7 +566,7 @@ exports[`props validation can validate an optional props 3`] = `
return function template(ctx, node, key = \\"\\") {
const props1 = {p: ctx['p']};
helpers.validateProps(\`SubComp\`, props1, ctx);
helpers.validateProps(\`SubComp\`, props1, this);
const b2 = comp1(props1, key + \`__1\`, node, this, null);
return block1([], [b2]);
}
@@ -596,7 +596,7 @@ exports[`props validation can validate an optional props 5`] = `
return function template(ctx, node, key = \\"\\") {
const props1 = {p: ctx['p']};
helpers.validateProps(\`SubComp\`, props1, ctx);
helpers.validateProps(\`SubComp\`, props1, this);
const b2 = comp1(props1, key + \`__1\`, node, this, null);
return block1([], [b2]);
}
@@ -613,7 +613,7 @@ exports[`props validation can validate recursively complicated prop def 1`] = `
return function template(ctx, node, key = \\"\\") {
const props1 = {p: ctx['p']};
helpers.validateProps(\`SubComp\`, props1, ctx);
helpers.validateProps(\`SubComp\`, props1, this);
const b2 = comp1(props1, key + \`__1\`, node, this, null);
return block1([], [b2]);
}
@@ -643,7 +643,7 @@ exports[`props validation can validate recursively complicated prop def 3`] = `
return function template(ctx, node, key = \\"\\") {
const props1 = {p: ctx['p']};
helpers.validateProps(\`SubComp\`, props1, ctx);
helpers.validateProps(\`SubComp\`, props1, this);
const b2 = comp1(props1, key + \`__1\`, node, this, null);
return block1([], [b2]);
}
@@ -673,13 +673,47 @@ exports[`props validation can validate recursively complicated prop def 5`] = `
return function template(ctx, node, key = \\"\\") {
const props1 = {p: ctx['p']};
helpers.validateProps(\`SubComp\`, props1, ctx);
helpers.validateProps(\`SubComp\`, props1, this);
const b2 = comp1(props1, key + \`__1\`, node, this, null);
return block1([], [b2]);
}
}"
`;
exports[`props validation can validate through slots 1`] = `
"function anonymous(app, bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { markRaw } = helpers;
const comp1 = app.createComponent(\`Child\`, true, false, false, true);
const comp2 = app.createComponent(\`Wrapper\`, true, true, false, true);
function slot1(ctx, node, key = \\"\\") {
const props1 = {};
helpers.validateProps(\`Child\`, props1, this);
return comp1(props1, key + \`__1\`, node, this, null);
}
return function template(ctx, node, key = \\"\\") {
const props2 = {slots: markRaw({'default': {__render: slot1, __ctx: ctx}})};
helpers.validateProps(\`Wrapper\`, props2, this);
return comp2(props2, key + \`__2\`, node, this, null);
}
}"
`;
exports[`props validation can validate through slots 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, 'default', false, {});
}
}"
`;
exports[`props validation default values are applied before validating props at update 1`] = `
"function anonymous(app, bdom, helpers
) {
@@ -690,7 +724,7 @@ exports[`props validation default values are applied before validating props at
return function template(ctx, node, key = \\"\\") {
const props1 = {p: ctx['state'].p};
helpers.validateProps(\`SubComp\`, props1, ctx);
helpers.validateProps(\`SubComp\`, props1, this);
const b2 = comp1(props1, key + \`__1\`, node, this, null);
return block1([], [b2]);
}
@@ -721,7 +755,7 @@ exports[`props validation missing required boolean prop causes an error 1`] = `
return function template(ctx, node, key = \\"\\") {
const props1 = {};
helpers.validateProps(\`SubComp\`, props1, ctx);
helpers.validateProps(\`SubComp\`, props1, this);
const b2 = comp1(props1, key + \`__1\`, node, this, null);
return block1([], [b2]);
}
@@ -738,7 +772,7 @@ exports[`props validation mix of optional and mandatory 1`] = `
return function template(ctx, node, key = \\"\\") {
const props1 = {};
helpers.validateProps(\`Child\`, props1, ctx);
helpers.validateProps(\`Child\`, props1, this);
const b2 = comp1(props1, key + \`__1\`, node, this, null);
return block1([], [b2]);
}
@@ -755,7 +789,7 @@ exports[`props validation props are validated in dev mode (code snapshot) 1`] =
return function template(ctx, node, key = \\"\\") {
const props1 = {message: 1};
helpers.validateProps(\`Child\`, props1, ctx);
helpers.validateProps(\`Child\`, props1, this);
const b2 = comp1(props1, key + \`__1\`, node, this, null);
return block1([], [b2]);
}
@@ -786,7 +820,7 @@ exports[`props validation props are validated whenever component is updated 1`]
return function template(ctx, node, key = \\"\\") {
const props1 = {p: ctx['state'].p};
helpers.validateProps(\`SubComp\`, props1, ctx);
helpers.validateProps(\`SubComp\`, props1, this);
const b2 = comp1(props1, key + \`__1\`, node, this, null);
return block1([], [b2]);
}
@@ -817,7 +851,7 @@ exports[`props validation props: list of strings 1`] = `
return function template(ctx, node, key = \\"\\") {
const props1 = {};
helpers.validateProps(\`SubComp\`, props1, ctx);
helpers.validateProps(\`SubComp\`, props1, this);
const b2 = comp1(props1, key + \`__1\`, node, this, null);
return block1([], [b2]);
}
@@ -834,7 +868,7 @@ exports[`props validation validate simple types 1`] = `
return function template(ctx, node, key = \\"\\") {
const props1 = {p: ctx['p']};
helpers.validateProps(\`SubComp\`, props1, ctx);
helpers.validateProps(\`SubComp\`, props1, this);
const b2 = comp1(props1, key + \`__1\`, node, this, null);
return block1([], [b2]);
}
@@ -851,7 +885,7 @@ exports[`props validation validate simple types 2`] = `
return function template(ctx, node, key = \\"\\") {
const props1 = {p: ctx['p']};
helpers.validateProps(\`SubComp\`, props1, ctx);
helpers.validateProps(\`SubComp\`, props1, this);
const b2 = comp1(props1, key + \`__1\`, node, this, null);
return block1([], [b2]);
}
@@ -881,7 +915,7 @@ exports[`props validation validate simple types 4`] = `
return function template(ctx, node, key = \\"\\") {
const props1 = {p: ctx['p']};
helpers.validateProps(\`SubComp\`, props1, ctx);
helpers.validateProps(\`SubComp\`, props1, this);
const b2 = comp1(props1, key + \`__1\`, node, this, null);
return block1([], [b2]);
}
@@ -898,7 +932,7 @@ exports[`props validation validate simple types 5`] = `
return function template(ctx, node, key = \\"\\") {
const props1 = {p: ctx['p']};
helpers.validateProps(\`SubComp\`, props1, ctx);
helpers.validateProps(\`SubComp\`, props1, this);
const b2 = comp1(props1, key + \`__1\`, node, this, null);
return block1([], [b2]);
}
@@ -915,7 +949,7 @@ exports[`props validation validate simple types 6`] = `
return function template(ctx, node, key = \\"\\") {
const props1 = {p: ctx['p']};
helpers.validateProps(\`SubComp\`, props1, ctx);
helpers.validateProps(\`SubComp\`, props1, this);
const b2 = comp1(props1, key + \`__1\`, node, this, null);
return block1([], [b2]);
}
@@ -945,7 +979,7 @@ exports[`props validation validate simple types 8`] = `
return function template(ctx, node, key = \\"\\") {
const props1 = {p: ctx['p']};
helpers.validateProps(\`SubComp\`, props1, ctx);
helpers.validateProps(\`SubComp\`, props1, this);
const b2 = comp1(props1, key + \`__1\`, node, this, null);
return block1([], [b2]);
}
@@ -962,7 +996,7 @@ exports[`props validation validate simple types 9`] = `
return function template(ctx, node, key = \\"\\") {
const props1 = {p: ctx['p']};
helpers.validateProps(\`SubComp\`, props1, ctx);
helpers.validateProps(\`SubComp\`, props1, this);
const b2 = comp1(props1, key + \`__1\`, node, this, null);
return block1([], [b2]);
}
@@ -979,7 +1013,7 @@ exports[`props validation validate simple types 10`] = `
return function template(ctx, node, key = \\"\\") {
const props1 = {p: ctx['p']};
helpers.validateProps(\`SubComp\`, props1, ctx);
helpers.validateProps(\`SubComp\`, props1, this);
const b2 = comp1(props1, key + \`__1\`, node, this, null);
return block1([], [b2]);
}
@@ -1009,7 +1043,7 @@ exports[`props validation validate simple types 12`] = `
return function template(ctx, node, key = \\"\\") {
const props1 = {p: ctx['p']};
helpers.validateProps(\`SubComp\`, props1, ctx);
helpers.validateProps(\`SubComp\`, props1, this);
const b2 = comp1(props1, key + \`__1\`, node, this, null);
return block1([], [b2]);
}
@@ -1026,7 +1060,7 @@ exports[`props validation validate simple types 13`] = `
return function template(ctx, node, key = \\"\\") {
const props1 = {p: ctx['p']};
helpers.validateProps(\`SubComp\`, props1, ctx);
helpers.validateProps(\`SubComp\`, props1, this);
const b2 = comp1(props1, key + \`__1\`, node, this, null);
return block1([], [b2]);
}
@@ -1043,7 +1077,7 @@ exports[`props validation validate simple types 14`] = `
return function template(ctx, node, key = \\"\\") {
const props1 = {p: ctx['p']};
helpers.validateProps(\`SubComp\`, props1, ctx);
helpers.validateProps(\`SubComp\`, props1, this);
const b2 = comp1(props1, key + \`__1\`, node, this, null);
return block1([], [b2]);
}
@@ -1073,7 +1107,7 @@ exports[`props validation validate simple types 16`] = `
return function template(ctx, node, key = \\"\\") {
const props1 = {p: ctx['p']};
helpers.validateProps(\`SubComp\`, props1, ctx);
helpers.validateProps(\`SubComp\`, props1, this);
const b2 = comp1(props1, key + \`__1\`, node, this, null);
return block1([], [b2]);
}
@@ -1090,7 +1124,7 @@ exports[`props validation validate simple types 17`] = `
return function template(ctx, node, key = \\"\\") {
const props1 = {p: ctx['p']};
helpers.validateProps(\`SubComp\`, props1, ctx);
helpers.validateProps(\`SubComp\`, props1, this);
const b2 = comp1(props1, key + \`__1\`, node, this, null);
return block1([], [b2]);
}
@@ -1107,7 +1141,7 @@ exports[`props validation validate simple types 18`] = `
return function template(ctx, node, key = \\"\\") {
const props1 = {p: ctx['p']};
helpers.validateProps(\`SubComp\`, props1, ctx);
helpers.validateProps(\`SubComp\`, props1, this);
const b2 = comp1(props1, key + \`__1\`, node, this, null);
return block1([], [b2]);
}
@@ -1137,7 +1171,7 @@ exports[`props validation validate simple types 20`] = `
return function template(ctx, node, key = \\"\\") {
const props1 = {p: ctx['p']};
helpers.validateProps(\`SubComp\`, props1, ctx);
helpers.validateProps(\`SubComp\`, props1, this);
const b2 = comp1(props1, key + \`__1\`, node, this, null);
return block1([], [b2]);
}
@@ -1154,7 +1188,7 @@ exports[`props validation validate simple types 21`] = `
return function template(ctx, node, key = \\"\\") {
const props1 = {p: ctx['p']};
helpers.validateProps(\`SubComp\`, props1, ctx);
helpers.validateProps(\`SubComp\`, props1, this);
const b2 = comp1(props1, key + \`__1\`, node, this, null);
return block1([], [b2]);
}
@@ -1171,7 +1205,7 @@ exports[`props validation validate simple types 22`] = `
return function template(ctx, node, key = \\"\\") {
const props1 = {p: ctx['p']};
helpers.validateProps(\`SubComp\`, props1, ctx);
helpers.validateProps(\`SubComp\`, props1, this);
const b2 = comp1(props1, key + \`__1\`, node, this, null);
return block1([], [b2]);
}
@@ -1201,7 +1235,7 @@ exports[`props validation validate simple types 24`] = `
return function template(ctx, node, key = \\"\\") {
const props1 = {p: ctx['p']};
helpers.validateProps(\`SubComp\`, props1, ctx);
helpers.validateProps(\`SubComp\`, props1, this);
const b2 = comp1(props1, key + \`__1\`, node, this, null);
return block1([], [b2]);
}
@@ -1218,7 +1252,7 @@ exports[`props validation validate simple types, alternate form 1`] = `
return function template(ctx, node, key = \\"\\") {
const props1 = {p: ctx['p']};
helpers.validateProps(\`SubComp\`, props1, ctx);
helpers.validateProps(\`SubComp\`, props1, this);
const b2 = comp1(props1, key + \`__1\`, node, this, null);
return block1([], [b2]);
}
@@ -1235,7 +1269,7 @@ exports[`props validation validate simple types, alternate form 2`] = `
return function template(ctx, node, key = \\"\\") {
const props1 = {p: ctx['p']};
helpers.validateProps(\`SubComp\`, props1, ctx);
helpers.validateProps(\`SubComp\`, props1, this);
const b2 = comp1(props1, key + \`__1\`, node, this, null);
return block1([], [b2]);
}
@@ -1265,7 +1299,7 @@ exports[`props validation validate simple types, alternate form 4`] = `
return function template(ctx, node, key = \\"\\") {
const props1 = {p: ctx['p']};
helpers.validateProps(\`SubComp\`, props1, ctx);
helpers.validateProps(\`SubComp\`, props1, this);
const b2 = comp1(props1, key + \`__1\`, node, this, null);
return block1([], [b2]);
}
@@ -1282,7 +1316,7 @@ exports[`props validation validate simple types, alternate form 5`] = `
return function template(ctx, node, key = \\"\\") {
const props1 = {p: ctx['p']};
helpers.validateProps(\`SubComp\`, props1, ctx);
helpers.validateProps(\`SubComp\`, props1, this);
const b2 = comp1(props1, key + \`__1\`, node, this, null);
return block1([], [b2]);
}
@@ -1299,7 +1333,7 @@ exports[`props validation validate simple types, alternate form 6`] = `
return function template(ctx, node, key = \\"\\") {
const props1 = {p: ctx['p']};
helpers.validateProps(\`SubComp\`, props1, ctx);
helpers.validateProps(\`SubComp\`, props1, this);
const b2 = comp1(props1, key + \`__1\`, node, this, null);
return block1([], [b2]);
}
@@ -1329,7 +1363,7 @@ exports[`props validation validate simple types, alternate form 8`] = `
return function template(ctx, node, key = \\"\\") {
const props1 = {p: ctx['p']};
helpers.validateProps(\`SubComp\`, props1, ctx);
helpers.validateProps(\`SubComp\`, props1, this);
const b2 = comp1(props1, key + \`__1\`, node, this, null);
return block1([], [b2]);
}
@@ -1346,7 +1380,7 @@ exports[`props validation validate simple types, alternate form 9`] = `
return function template(ctx, node, key = \\"\\") {
const props1 = {p: ctx['p']};
helpers.validateProps(\`SubComp\`, props1, ctx);
helpers.validateProps(\`SubComp\`, props1, this);
const b2 = comp1(props1, key + \`__1\`, node, this, null);
return block1([], [b2]);
}
@@ -1363,7 +1397,7 @@ exports[`props validation validate simple types, alternate form 10`] = `
return function template(ctx, node, key = \\"\\") {
const props1 = {p: ctx['p']};
helpers.validateProps(\`SubComp\`, props1, ctx);
helpers.validateProps(\`SubComp\`, props1, this);
const b2 = comp1(props1, key + \`__1\`, node, this, null);
return block1([], [b2]);
}
@@ -1393,7 +1427,7 @@ exports[`props validation validate simple types, alternate form 12`] = `
return function template(ctx, node, key = \\"\\") {
const props1 = {p: ctx['p']};
helpers.validateProps(\`SubComp\`, props1, ctx);
helpers.validateProps(\`SubComp\`, props1, this);
const b2 = comp1(props1, key + \`__1\`, node, this, null);
return block1([], [b2]);
}
@@ -1410,7 +1444,7 @@ exports[`props validation validate simple types, alternate form 13`] = `
return function template(ctx, node, key = \\"\\") {
const props1 = {p: ctx['p']};
helpers.validateProps(\`SubComp\`, props1, ctx);
helpers.validateProps(\`SubComp\`, props1, this);
const b2 = comp1(props1, key + \`__1\`, node, this, null);
return block1([], [b2]);
}
@@ -1427,7 +1461,7 @@ exports[`props validation validate simple types, alternate form 14`] = `
return function template(ctx, node, key = \\"\\") {
const props1 = {p: ctx['p']};
helpers.validateProps(\`SubComp\`, props1, ctx);
helpers.validateProps(\`SubComp\`, props1, this);
const b2 = comp1(props1, key + \`__1\`, node, this, null);
return block1([], [b2]);
}
@@ -1457,7 +1491,7 @@ exports[`props validation validate simple types, alternate form 16`] = `
return function template(ctx, node, key = \\"\\") {
const props1 = {p: ctx['p']};
helpers.validateProps(\`SubComp\`, props1, ctx);
helpers.validateProps(\`SubComp\`, props1, this);
const b2 = comp1(props1, key + \`__1\`, node, this, null);
return block1([], [b2]);
}
@@ -1474,7 +1508,7 @@ exports[`props validation validate simple types, alternate form 17`] = `
return function template(ctx, node, key = \\"\\") {
const props1 = {p: ctx['p']};
helpers.validateProps(\`SubComp\`, props1, ctx);
helpers.validateProps(\`SubComp\`, props1, this);
const b2 = comp1(props1, key + \`__1\`, node, this, null);
return block1([], [b2]);
}
@@ -1491,7 +1525,7 @@ exports[`props validation validate simple types, alternate form 18`] = `
return function template(ctx, node, key = \\"\\") {
const props1 = {p: ctx['p']};
helpers.validateProps(\`SubComp\`, props1, ctx);
helpers.validateProps(\`SubComp\`, props1, this);
const b2 = comp1(props1, key + \`__1\`, node, this, null);
return block1([], [b2]);
}
@@ -1521,7 +1555,7 @@ exports[`props validation validate simple types, alternate form 20`] = `
return function template(ctx, node, key = \\"\\") {
const props1 = {p: ctx['p']};
helpers.validateProps(\`SubComp\`, props1, ctx);
helpers.validateProps(\`SubComp\`, props1, this);
const b2 = comp1(props1, key + \`__1\`, node, this, null);
return block1([], [b2]);
}
@@ -1538,7 +1572,7 @@ exports[`props validation validate simple types, alternate form 21`] = `
return function template(ctx, node, key = \\"\\") {
const props1 = {p: ctx['p']};
helpers.validateProps(\`SubComp\`, props1, ctx);
helpers.validateProps(\`SubComp\`, props1, this);
const b2 = comp1(props1, key + \`__1\`, node, this, null);
return block1([], [b2]);
}
@@ -1555,7 +1589,7 @@ exports[`props validation validate simple types, alternate form 22`] = `
return function template(ctx, node, key = \\"\\") {
const props1 = {p: ctx['p']};
helpers.validateProps(\`SubComp\`, props1, ctx);
helpers.validateProps(\`SubComp\`, props1, this);
const b2 = comp1(props1, key + \`__1\`, node, this, null);
return block1([], [b2]);
}
@@ -1585,7 +1619,7 @@ exports[`props validation validate simple types, alternate form 24`] = `
return function template(ctx, node, key = \\"\\") {
const props1 = {p: ctx['p']};
helpers.validateProps(\`SubComp\`, props1, ctx);
helpers.validateProps(\`SubComp\`, props1, this);
const b2 = comp1(props1, key + \`__1\`, node, this, null);
return block1([], [b2]);
}
@@ -1602,7 +1636,7 @@ exports[`props validation validation is only done in dev mode 1`] = `
return function template(ctx, node, key = \\"\\") {
const props1 = {};
helpers.validateProps(\`SubComp\`, props1, ctx);
helpers.validateProps(\`SubComp\`, props1, this);
const b2 = comp1(props1, key + \`__1\`, node, this, null);
return block1([], [b2]);
}
@@ -444,6 +444,51 @@ exports[`t-call t-call with t-call-context and subcomponent 3`] = `
}"
`;
exports[`t-call t-call with t-call-context and subcomponent, in dev mode 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 = ctx['subctx'];
return callTemplate_1.call(this, ctx1, node, key + \`__1\`);
}
}"
`;
exports[`t-call t-call with t-call-context and subcomponent, in dev mode 2`] = `
"function anonymous(app, bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
const comp1 = app.createComponent(\`Child\`, true, false, false, false);
const comp2 = app.createComponent(\`Child\`, true, false, false, false);
return function template(ctx, node, key = \\"\\") {
const props1 = {name: ctx['aab']};
helpers.validateProps(\`Child\`, props1, this);
const b2 = comp1(props1, key + \`__1\`, node, this, null);
const props2 = {name: ctx['lpe']};
helpers.validateProps(\`Child\`, props2, this);
const b3 = comp2(props2, key + \`__2\`, node, this, null);
return multi([b2, b3]);
}
}"
`;
exports[`t-call t-call with t-call-context and subcomponent, in dev mode 3`] = `
"function anonymous(app, bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
return function template(ctx, node, key = \\"\\") {
const b2 = text(\`child\`);
const b3 = text(ctx['props'].name);
return multi([b2, b3]);
}
}"
`;
exports[`t-call t-call with t-call-context, simple use 1`] = `
"function anonymous(app, bdom, helpers
) {
@@ -56,7 +56,7 @@ exports[`list of components crash on duplicate key in dev mode 1`] = `
if (keys1.has(key1)) { throw new OwlError(\`Got duplicate key in t-foreach: \${key1}\`)}
keys1.add(key1);
const props1 = {};
helpers.validateProps(\`Child\`, props1, ctx);
helpers.validateProps(\`Child\`, props1, this);
c_block1[i1] = withKey(comp1(props1, key + \`__1__\${key1}\`, node, this, null), key1);
}
return list(c_block1);
+26
View File
@@ -780,6 +780,32 @@ describe("props validation", () => {
// we just check that it doesn't throw
await expect(mount(Parent, fixture, { dev: true })).resolves.toEqual(expect.anything());
});
test("can validate through slots", async () => {
class Child extends Component {
static props = ["message"];
static template = xml`<div>hey</div>`;
}
class Wrapper extends Component {
static template = xml`<t t-slot="default"/>`;
}
class Parent extends Component {
static components = { Child, Wrapper };
static template = xml`<Wrapper><Child /></Wrapper>`;
}
const app = new App(Parent, { test: true });
let error: OwlError | undefined;
const mountProm = app.mount(fixture).catch((e: Error) => (error = e));
await expect(nextAppError(app)).resolves.toThrow(
"Invalid props for component 'Child': 'message' is missing"
);
await mountProm;
expect(error!).toBeDefined();
expect(error!.message).toBe("Invalid props for component 'Child': 'message' is missing");
});
});
//------------------------------------------------------------------------------
+28
View File
@@ -287,4 +287,32 @@ describe("t-call", () => {
});
expect(fixture.innerHTML).toBe("childaaronchildlucas");
});
test("t-call with t-call-context and subcomponent, in dev mode", async () => {
class Child extends Component {
static template = xml`child<t t-esc="props.name"/>`;
static props = ["name"];
}
class Root extends Component {
static template = xml`
<t t-call="someTemplate" t-call-context="subctx"/>`;
static components = { Child };
subctx = { aab: "aaron", lpe: "lucas" };
}
await mount(Root, fixture, {
dev: true,
templates: `
<templates>
<t t-name="someTemplate">
<Child name="aab"/>
<Child name="lpe"/>
</t>
</templates>`,
});
expect(fixture.innerHTML).toBe("childaaronchildlucas");
});
});
@@ -155,6 +155,44 @@ exports[`Portal Add and remove portals with t-foreach inside div 1`] = `
}"
`;
exports[`Portal Child and Portal 1`] = `
"function anonymous(app, bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
const comp1 = app.createComponent(\`Child\`, true, false, false, true);
let block3 = createBlock(\`<div class=\\"portal\\"/>\`);
return function template(ctx, node, key = \\"\\") {
const b2 = comp1({}, key + \`__1\`, node, this, null);
const b3 = block3();
return multi([b2, b3]);
}
}"
`;
exports[`Portal Child and Portal 2`] = `
"function anonymous(app, bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
const Portal = app.Portal;
const comp1 = app.createComponent(null, false, true, false, false);
let block2 = createBlock(\`<span>child</span>\`);
let block3 = createBlock(\`<span>portal</span>\`);
function slot1(ctx, node, key = \\"\\") {
return block3();
}
return function template(ctx, node, key = \\"\\") {
const b2 = block2();
const b4 = comp1({target: '.portal',slots: {'default': {__render: slot1, __ctx: ctx}}}, key + \`__1\`, node, ctx, Portal);
return multi([b2, b4]);
}
}"
`;
exports[`Portal Portal composed with t-slot 1`] = `
"function anonymous(app, bdom, helpers
) {
@@ -515,6 +553,44 @@ exports[`Portal lifecycle hooks of portal sub component are properly called 2`]
}"
`;
exports[`Portal portal and Child 1`] = `
"function anonymous(app, bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
const comp1 = app.createComponent(\`Child\`, true, false, false, true);
let block2 = createBlock(\`<div class=\\"portal\\"/>\`);
return function template(ctx, node, key = \\"\\") {
const b2 = block2();
const b3 = comp1({}, key + \`__1\`, node, this, null);
return multi([b2, b3]);
}
}"
`;
exports[`Portal portal and Child 2`] = `
"function anonymous(app, bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
const Portal = app.Portal;
const comp1 = app.createComponent(null, false, true, false, false);
let block2 = createBlock(\`<span>child</span>\`);
let block3 = createBlock(\`<span>portal</span>\`);
function slot1(ctx, node, key = \\"\\") {
return block3();
}
return function template(ctx, node, key = \\"\\") {
const b2 = block2();
const b4 = comp1({target: '.portal',slots: {'default': {__render: slot1, __ctx: ctx}}}, key + \`__1\`, node, ctx, Portal);
return multi([b2, b4]);
}
}"
`;
exports[`Portal portal could have dynamically no content 1`] = `
"function anonymous(app, bdom, helpers
) {
+43 -1
View File
@@ -276,7 +276,7 @@ describe("Portal", () => {
expect(error!).toBeDefined();
expect(error!.message).toBe("invalid portal target");
expect(fixture.innerHTML).toBe(`<div></div>`);
expect(fixture.innerHTML).toBe(``);
expect(mockConsoleWarn).toBeCalledTimes(1);
});
@@ -874,6 +874,48 @@ describe("Portal", () => {
await nextTick();
expect(fixture.innerHTML).toBe('<div id="outside"></div><div></div>');
});
test("Child and Portal", async () => {
class Child extends Component {
static template = xml`
<span>child</span>
<t t-portal="'.portal'"><span>portal</span></t>`;
}
class Parent extends Component {
static template = xml`
<t>
<Child/>
<div class="portal"></div>
</t>`;
static components = { Child };
}
await mount(Parent, fixture);
expect(fixture.innerHTML).toBe(
'<span>child</span><div class="portal"></div><span>portal</span>'
);
});
test("portal and Child", async () => {
class Child extends Component {
static template = xml`
<span>child</span>
<t t-portal="'.portal'"><span>portal</span></t>`;
}
class Parent extends Component {
static template = xml`
<t>
<div class="portal"></div>
<Child/>
</t>`;
static components = { Child };
}
await mount(Parent, fixture);
expect(fixture.innerHTML).toBe(
'<div class="portal"><span>portal</span></div><span>child</span>'
);
});
});
describe("Portal: UI/UX", () => {
+1 -3
View File
@@ -72,10 +72,8 @@ async function startRelease() {
}
// ---------------------------------------------------------------------------
log(`Step 3/${STEPS}: updating package.json, readme.md and roadmap.md...`);
log(`Step 3/${STEPS}: updating package.json...`);
await replaceInFile("./package.json", current, next);
await replaceInFile("./README.md", current, next);
await replaceInFile("./roadmap.md", current, next);
// ---------------------------------------------------------------------------
log(`Step 4/${STEPS}: creating git commit...`);