Compare commits

...

6 Commits

Author SHA1 Message Date
Géry Debongnie b1e79677cb wip 2021-11-20 10:45:30 +01:00
Géry Debongnie 89d6b8b7dd wip 2021-11-20 10:25:37 +01:00
Géry Debongnie 9c2523e3ce wip 2021-11-20 10:25:37 +01:00
Géry Debongnie d8201b8955 add possibility to deep rendering 2021-11-20 10:25:37 +01:00
Géry Debongnie dfead2836e big change: shallow render
With this commit, component only render child
components if they have different props (shallow
equality). Otherwise, we trust the reactivity
system to make sure that all impacted components
are updated
2021-11-20 10:25:37 +01:00
Géry Debongnie 0e0ac5329a [REF] utils: move batched from reactivity to utils 2021-11-20 10:25:36 +01:00
25 changed files with 417 additions and 182 deletions
+2 -2
View File
@@ -948,7 +948,7 @@ export class CodeGenerator {
let propString = propStr;
if (ast.dynamicProps) {
if (!props.length) {
propString = `${compileExpr(ast.dynamicProps)}`;
propString = `Object.assign({}, ${compileExpr(ast.dynamicProps)})`;
} else {
propString = `Object.assign({}, ${compileExpr(ast.dynamicProps)}, ${propStr})`;
}
@@ -1012,7 +1012,7 @@ export class CodeGenerator {
keyArg = `${ctx.tKeyExpr} + ${keyArg}`;
}
const blockArgs = `${expr}, ${propString}, ${keyArg}, node, ctx`;
let blockExpr = `component(${blockArgs})`;
let blockExpr = `component(${blockArgs}${hasSlot ? ", true" : ""})`;
if (Object.keys(extraArgs).length) {
this.shouldDefineAssign = true;
const content = Object.keys(extraArgs).map((k) => `${k}: ${extraArgs[k]}`);
+4 -2
View File
@@ -1,6 +1,8 @@
import type { Env } from "../app/app";
import type { ComponentNode } from "./component_node";
export type Props = { [key: string]: any };
// -----------------------------------------------------------------------------
// Component Class
// -----------------------------------------------------------------------------
@@ -26,7 +28,7 @@ export class Component {
setup() {}
render(): Promise<void> {
return this.__owl__.render();
render(deep: boolean = false): Promise<void> {
return this.__owl__.render(deep);
}
}
+70 -13
View File
@@ -1,6 +1,9 @@
import type { App, Env } from "../app/app";
import { BDom, VNode } from "../blockdom";
import { Component } from "./component";
import { clearReactivesForCallback, Reactive, reactive } from "../reactivity";
import { batched, Callback } from "../utils";
import { Component, Props } from "./component";
import { fibersInError, handleError } from "./error_handling";
import {
Fiber,
makeChildFiber,
@@ -8,20 +11,68 @@ import {
MountFiber,
MountOptions,
RootFiber,
__internal__destroyed,
__internal__destroyed
} from "./fibers";
import { handleError, fibersInError } from "./error_handling";
import { applyDefaultProps } from "./props_validation";
import { STATUS } from "./status";
import { applyStyles } from "./style";
let currentNode: ComponentNode | null = null;
export function getCurrent(): ComponentNode | null {
return currentNode;
}
// -----------------------------------------------------------------------------
// Integration with reactivity system (useState)
// -----------------------------------------------------------------------------
const batchedRenderFunctions = new WeakMap<ComponentNode, Callback>();
/**
* Creates a reactive object that will be observed by the current component.
* Reading data from the returned object (eg during rendering) will cause the
* component to subscribe to that data and be rerendered when it changes.
*
* @param state the state to observe
* @returns a reactive object that will cause the component to re-render on
* relevant changes
* @see reactive
*/
export function useState<T extends object>(state: T): Reactive<T> {
if (!batchedRenderFunctions.has(currentNode!)) {
batchedRenderFunctions.set(
currentNode!,
batched(() => currentNode!.render())
);
}
const render = batchedRenderFunctions.get(currentNode!)!;
const reactiveState = reactive(state, render);
// manual implementation of onWillUnmount to break cyclic dependency
currentNode!.willUnmount.unshift( clearReactivesForCallback.bind(null, render))
return reactiveState;
}
// -----------------------------------------------------------------------------
// component function (used in compiled template code)
// -----------------------------------------------------------------------------
function arePropsDifferent(props1: Props, props2: Props): boolean {
for (let k in props1) {
if (props1[k] !== props2[k]) {
return true;
}
}
return false;
}
export function component(
name: string | typeof Component,
props: any,
key: string,
ctx: ComponentNode,
parent: any
parent: any,
hasSlots: boolean = false
): ComponentNode {
console.warn('asdf')
let node: any = ctx.children[key];
let isDynamic = typeof name !== "string";
@@ -39,7 +90,11 @@ export function component(
const parentFiber = ctx.fiber!;
if (node) {
node.updateAndRender(props, parentFiber);
console.warn('coucou');
if (hasSlots || parentFiber.deep || arePropsDifferent(node.component.props, props)) {
console.warn('coucou3');
node.updateAndRender(props, parentFiber);
}
} else {
// new component
let C;
@@ -61,15 +116,9 @@ export function component(
}
// -----------------------------------------------------------------------------
// Component VNode
// Component VNode class
// -----------------------------------------------------------------------------
let currentNode: ComponentNode | null = null;
export function getCurrent(): ComponentNode | null {
return currentNode;
}
type LifecycleHook = Function;
export class ComponentNode<T extends typeof Component = typeof Component>
@@ -106,6 +155,9 @@ export class ComponentNode<T extends typeof Component = typeof Component>
applyDefaultProps(props, C);
const env = (parent && parent.childEnv) || app.env;
this.childEnv = env;
// if (props) {
// props = useState(props);
// }
this.component = new C(props, env, this) as any;
this.renderFn = app.getTemplate(C.template).bind(this.component, this.component, this);
if (C.style) {
@@ -137,7 +189,7 @@ export class ComponentNode<T extends typeof Component = typeof Component>
}
}
async render() {
async render(deep: boolean = false) {
let fiber = this.fiber;
if (fiber && !fiber.bdom && !fibersInError.has(fiber)) {
return fiber.root.promise;
@@ -147,6 +199,7 @@ export class ComponentNode<T extends typeof Component = typeof Component>
return;
}
fiber = makeRootFiber(this);
fiber.deep = deep;
this.app.scheduler.addFiber(fiber);
await Promise.resolve();
if (this.status === STATUS.DESTROYED) {
@@ -241,6 +294,10 @@ export class ComponentNode<T extends typeof Component = typeof Component>
}
patch() {
if (!this.fiber) {
// component was not rendered => no need to do anything
return;
}
this.bdom!.patch(this!.fiber!.bdom!, false);
this.fiber!.appliedToDom = true;
this.fiber = null;
+1
View File
@@ -67,6 +67,7 @@ export class Fiber {
parent: Fiber | null;
children: Fiber[] = [];
appliedToDom = false;
deep: boolean = false;
constructor(node: ComponentNode, parent: Fiber | null) {
this.node = node;
+2 -1
View File
@@ -34,6 +34,7 @@ import type { AppConfig } from "./app/app";
import { App } from "./app/app";
import { Component } from "./component/component";
import { getCurrent } from "./component/component_node";
export { useState } from "./component/component_node";
export { App, Component };
@@ -55,7 +56,7 @@ export { status } from "./component/status";
export { Portal } from "./portal";
export { Memo } from "./memo";
export { css, xml } from "./tags";
export { useState, reactive } from "./reactivity";
export { reactive } from "./reactivity";
export { useEffect, useEnv, useExternalListener, useRef, useSubEnv } from "./hooks";
export { EventBus, whenReady, loadFile, markup } from "./utils";
+3 -53
View File
@@ -1,5 +1,4 @@
import { onWillUnmount } from "./component/lifecycle_hooks";
import { ComponentNode, getCurrent } from "./component/component_node";
import { Callback } from "./utils";
// Allows to get the target of a Reactive (used for making a new Reactive from the underlying object)
const TARGET = Symbol("Target");
@@ -8,8 +7,7 @@ const KEYCHANGES = Symbol("Key changes");
type ObjectKey = string | number | symbol;
type Target = object;
type Callback = () => void;
type Reactive<T extends Target = Target> = T & {
export type Reactive<T extends Target = Target> = T & {
[TARGET]: any;
};
@@ -81,7 +79,7 @@ const callbacksToTargets = new WeakMap<Callback, Set<Target>>();
*
* @param callback the callback for which the reactives need to be cleared
*/
function clearReactivesForCallback(callback: Callback): void {
export function clearReactivesForCallback(callback: Callback): void {
const targetsToClear = callbacksToTargets.get(callback);
if (!targetsToClear) {
return;
@@ -190,51 +188,3 @@ export function reactive<T extends Target>(target: T, callback: Callback): React
return reactivesForTarget.get(callback) as Reactive<T>;
}
/**
* Creates a batched version of a callback so that all calls to it in the same
* microtick will only call the original callback once.
*
* @param callback the callback to batch
* @returns a batched version of the original callback
*/
export function batched(callback: Callback): Callback {
let called = false;
return async () => {
// This await blocks all calls to the callback here, then releases them sequentially
// in the next microtick. This line decides the granularity of the batch.
await Promise.resolve();
if (!called) {
called = true;
callback();
// wait for all calls in this microtick to fall through before resetting "called"
// so that only the first call to the batched function calls the original callback
await Promise.resolve();
called = false;
}
};
}
const batchedRenderFunctions = new WeakMap<ComponentNode, Callback>();
/**
* Creates a reactive object that will be observed by the current component.
* Reading data from the returned object (eg during rendering) will cause the
* component to subscribe to that data and be rerendered when it changes.
*
* @param state the state to observe
* @returns a reactive object that will cause the component to re-render on
* relevant changes
* @see reactive
*/
export function useState<T extends object>(state: T): Reactive<T> {
const node = getCurrent()!;
if (!batchedRenderFunctions.has(node)) {
batchedRenderFunctions.set(
node,
batched(() => node.render())
);
}
const render = batchedRenderFunctions.get(node)!;
const reactiveState = reactive(state, render);
onWillUnmount(() => clearReactivesForCallback(render));
return reactiveState;
}
+26
View File
@@ -1,3 +1,29 @@
export type Callback = () => void;
/**
* Creates a batched version of a callback so that all calls to it in the same
* microtick will only call the original callback once.
*
* @param callback the callback to batch
* @returns a batched version of the original callback
*/
export function batched(callback: Callback): Callback {
let called = false;
return async () => {
// This await blocks all calls to the callback here, then releases them sequentially
// in the next microtick. This line decides the granularity of the batch.
await Promise.resolve();
if (!called) {
called = true;
callback();
// wait for all calls in this microtick to fall through before resetting "called"
// so that only the first call to the batched function calls the original callback
await Promise.resolve();
called = false;
}
};
}
export class EventBus extends EventTarget {
trigger(name: string, payload?: any) {
this.dispatchEvent(new CustomEvent(name, { detail: payload }));
@@ -1020,7 +1020,7 @@ exports[`basics update props of component without concrete own node 3`] = `
return function template(ctx, node, key = \\"\\") {
const tKey_1 = ctx['childProps'].key;
let b2 = toggler(tKey_1, component(\`Child\`, ctx['childProps'], tKey_1 + key + \`__2\`, node, ctx));
let b2 = toggler(tKey_1, component(\`Child\`, Object.assign({}, ctx['childProps']), tKey_1 + key + \`__2\`, node, ctx));
return block1([], [b2]);
}
}"
@@ -1067,7 +1067,7 @@ exports[`properly behave when destroyed/unmounted while rendering 2`] = `
let block1 = createBlock(\`<div><block-child-0/></div>\`);
return function template(ctx, node, key = \\"\\") {
let b2 = component(\`SubChild\`, {}, key + \`__1\`, node, ctx);
let b2 = component(\`SubChild\`, {val: ctx['props'].val}, key + \`__1\`, node, ctx);
return block1([], [b2]);
}
}"
@@ -126,7 +126,7 @@ exports[`can catch errors can catch an error in a component render function 3`]
}
return function template(ctx, node, key = \\"\\") {
let b3 = assign(component(\`ErrorBoundary\`, {}, key + \`__1\`, node, ctx), {slots: {'default': slot2(ctx)}});
let b3 = assign(component(\`ErrorBoundary\`, {}, key + \`__1\`, node, ctx, true), {slots: {'default': slot2(ctx)}});
return block1([], [b3]);
}
}"
@@ -230,7 +230,7 @@ exports[`can catch errors can catch an error in the constructor call of a compon
}
return function template(ctx, node, key = \\"\\") {
let b5 = assign(component(\`ErrorBoundary\`, {}, key + \`__1\`, node, ctx), {slots: {'default': slot2(ctx)}});
let b5 = assign(component(\`ErrorBoundary\`, {}, key + \`__1\`, node, ctx, true), {slots: {'default': slot2(ctx)}});
return block1([], [b5]);
}
}"
@@ -250,7 +250,7 @@ exports[`can catch errors can catch an error in the constructor call of a compon
}
return function template(ctx, node, key = \\"\\") {
let b3 = assign(component(\`ErrorBoundary\`, {}, key + \`__1\`, node, ctx), {slots: {'default': slot2(ctx)}});
let b3 = assign(component(\`ErrorBoundary\`, {}, key + \`__1\`, node, ctx, true), {slots: {'default': slot2(ctx)}});
return block1([], [b3]);
}
}"
@@ -305,7 +305,7 @@ exports[`can catch errors can catch an error in the initial call of a component
}
return function template(ctx, node, key = \\"\\") {
let b3 = assign(component(\`ErrorBoundary\`, {}, key + \`__1\`, node, ctx), {slots: {'default': slot2(ctx)}});
let b3 = assign(component(\`ErrorBoundary\`, {}, key + \`__1\`, node, ctx, true), {slots: {'default': slot2(ctx)}});
return block1([], [b3]);
}
}"
@@ -362,7 +362,7 @@ exports[`can catch errors can catch an error in the initial call of a component
return function template(ctx, node, key = \\"\\") {
let b3;
if (ctx['state'].flag) {
b3 = assign(component(\`ErrorBoundary\`, {}, key + \`__1\`, node, ctx), {slots: {'default': slot2(ctx)}});
b3 = assign(component(\`ErrorBoundary\`, {}, key + \`__1\`, node, ctx, true), {slots: {'default': slot2(ctx)}});
}
return block1([], [b3]);
}
@@ -417,7 +417,7 @@ exports[`can catch errors can catch an error in the mounted call 3`] = `
}
return function template(ctx, node, key = \\"\\") {
let b3 = assign(component(\`ErrorBoundary\`, {}, key + \`__1\`, node, ctx), {slots: {'default': slot2(ctx)}});
let b3 = assign(component(\`ErrorBoundary\`, {}, key + \`__1\`, node, ctx, true), {slots: {'default': slot2(ctx)}});
return block1([], [b3]);
}
}"
@@ -473,7 +473,7 @@ exports[`can catch errors can catch an error in the willPatch call 3`] = `
return function template(ctx, node, key = \\"\\") {
let d1 = ctx['state'].message;
let b3 = assign(component(\`ErrorBoundary\`, {}, key + \`__1\`, node, ctx), {slots: {'default': slot2(ctx)}});
let b3 = assign(component(\`ErrorBoundary\`, {}, key + \`__1\`, node, ctx, true), {slots: {'default': slot2(ctx)}});
return block1([d1], [b3]);
}
}"
@@ -527,7 +527,7 @@ exports[`can catch errors can catch an error in the willStart call 3`] = `
}
return function template(ctx, node, key = \\"\\") {
let b3 = assign(component(\`ErrorBoundary\`, {}, key + \`__1\`, node, ctx), {slots: {'default': slot2(ctx)}});
let b3 = assign(component(\`ErrorBoundary\`, {}, key + \`__1\`, node, ctx, true), {slots: {'default': slot2(ctx)}});
return block1([], [b3]);
}
}"
@@ -597,7 +597,7 @@ exports[`can catch errors can catch an error origination from a child's willStar
}
return function template(ctx, node, key = \\"\\") {
let b5 = assign(component(\`ErrorBoundary\`, {}, key + \`__1\`, node, ctx), {slots: {'default': slot2(ctx)}});
let b5 = assign(component(\`ErrorBoundary\`, {}, key + \`__1\`, node, ctx, true), {slots: {'default': slot2(ctx)}});
return block1([], [b5]);
}
}"
@@ -555,7 +555,7 @@ exports[`lifecycle hooks onWillRender 2`] = `
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber, safeOutput } = helpers;
return function template(ctx, node, key = \\"\\") {
return component(\`Child\`, {}, key + \`__1\`, node, ctx);
return component(\`Child\`, {someValue: ctx['state'].value}, key + \`__1\`, node, ctx);
}
}"
`;
@@ -53,7 +53,7 @@ exports[`refs refs are properly bound in slots 2`] = `
const refs = ctx.__owl__.refs;
let d1 = ctx['state'].val;
const ctx2 = capture(ctx);
let b3 = assign(component(\`Dialog\`, {}, key + \`__1\`, node, ctx), {slots: {'footer': slot3(ctx2)}});
let b3 = assign(component(\`Dialog\`, {}, key + \`__1\`, node, ctx, true), {slots: {'footer': slot3(ctx2)}});
return block1([d1], [b3]);
}
}"
@@ -0,0 +1,77 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`rendering semantics blabla 1`] = `
"function anonymous(bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, component } = bdom;
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber, safeOutput } = helpers;
return function template(ctx, node, key = \\"\\") {
return text(ctx['props'].a.b);
}
}"
`;
exports[`rendering semantics blabla 2`] = `
"function anonymous(bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, component } = bdom;
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber, safeOutput } = helpers;
return function template(ctx, node, key = \\"\\") {
return component(\`Child\`, {a: ctx['state']}, key + \`__1\`, node, ctx);
}
}"
`;
exports[`rendering semantics can force a render to update sub tree 1`] = `
"function anonymous(bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, component } = bdom;
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber, safeOutput } = helpers;
return function template(ctx, node, key = \\"\\") {
return text(\`child\`);
}
}"
`;
exports[`rendering semantics can force a render to update sub tree 2`] = `
"function anonymous(bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, component } = bdom;
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber, safeOutput } = helpers;
return function template(ctx, node, key = \\"\\") {
let b2 = text(ctx['state'].value);
let b3 = component(\`Child\`, {}, key + \`__1\`, node, ctx);
return multi([b2, b3]);
}
}"
`;
exports[`rendering semantics can render a parent without rendering child 1`] = `
"function anonymous(bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, component } = bdom;
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber, safeOutput } = helpers;
return function template(ctx, node, key = \\"\\") {
return text(\`child\`);
}
}"
`;
exports[`rendering semantics can render a parent without rendering child 2`] = `
"function anonymous(bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, component } = bdom;
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber, safeOutput } = helpers;
return function template(ctx, node, key = \\"\\") {
let b2 = text(ctx['state'].value);
let b3 = component(\`Child\`, {}, key + \`__1\`, node, ctx);
return multi([b2, b3]);
}
}"
`;
@@ -37,7 +37,7 @@ exports[`slots can define and call slots 2`] = `
return function template(ctx, node, key = \\"\\") {
const ctx2 = capture(ctx);
let b4 = assign(component(\`Dialog\`, {}, key + \`__1\`, node, ctx), {slots: {'header': slot3(ctx2), 'footer': slot4(ctx2)}});
let b4 = assign(component(\`Dialog\`, {}, key + \`__1\`, node, ctx, true), {slots: {'header': slot3(ctx2), 'footer': slot4(ctx2)}});
return block1([], [b4]);
}
}"
@@ -74,7 +74,7 @@ exports[`slots can render node with t-ref and Component in same slot 2`] = `
return function template(ctx, node, key = \\"\\") {
const refs = ctx.__owl__.refs;
return assign(component(\`Child\`, {}, key + \`__1\`, node, ctx), {slots: {'default': slot2(ctx)}});
return assign(component(\`Child\`, {}, key + \`__1\`, node, ctx, true), {slots: {'default': slot2(ctx)}});
}
}"
`;
@@ -117,7 +117,7 @@ exports[`slots content is the default slot (variation) 2`] = `
}
return function template(ctx, node, key = \\"\\") {
return assign(component(\`Dialog\`, {}, key + \`__1\`, node, ctx), {slots: {'default': slot2(ctx)}});
return assign(component(\`Dialog\`, {}, key + \`__1\`, node, ctx, true), {slots: {'default': slot2(ctx)}});
}
}"
`;
@@ -152,7 +152,7 @@ exports[`slots content is the default slot 2`] = `
}
return function template(ctx, node, key = \\"\\") {
let b3 = assign(component(\`Dialog\`, {}, key + \`__1\`, node, ctx), {slots: {'default': slot2(ctx)}});
let b3 = assign(component(\`Dialog\`, {}, key + \`__1\`, node, ctx, true), {slots: {'default': slot2(ctx)}});
return block1([], [b3]);
}
}"
@@ -226,7 +226,7 @@ exports[`slots default content is not rendered if named slot is provided 2`] = `
return function template(ctx, node, key = \\"\\") {
const ctx2 = capture(ctx);
let b3 = assign(component(\`Dialog\`, {}, key + \`__1\`, node, ctx), {slots: {'header': slot3(ctx2)}});
let b3 = assign(component(\`Dialog\`, {}, key + \`__1\`, node, ctx, true), {slots: {'header': slot3(ctx2)}});
return block1([], [b3]);
}
}"
@@ -265,7 +265,7 @@ exports[`slots default content is not rendered if slot is provided 2`] = `
}
return function template(ctx, node, key = \\"\\") {
let b3 = assign(component(\`Dialog\`, {}, key + \`__1\`, node, ctx), {slots: {'default': slot2(ctx)}});
let b3 = assign(component(\`Dialog\`, {}, key + \`__1\`, node, ctx, true), {slots: {'default': slot2(ctx)}});
return block1([], [b3]);
}
}"
@@ -310,7 +310,7 @@ exports[`slots default slot next to named slot, with default content 2`] = `
return function template(ctx, node, key = \\"\\") {
const ctx2 = capture(ctx);
let b3 = assign(component(\`Dialog\`, {}, key + \`__1\`, node, ctx), {slots: {'footer': slot3(ctx2)}});
let b3 = assign(component(\`Dialog\`, {}, key + \`__1\`, node, ctx, true), {slots: {'footer': slot3(ctx2)}});
return block1([], [b3]);
}
}"
@@ -340,7 +340,7 @@ exports[`slots default slot work with text nodes (variation) 2`] = `
}
return function template(ctx, node, key = \\"\\") {
return assign(component(\`Dialog\`, {}, key + \`__1\`, node, ctx), {slots: {'default': slot2(ctx)}});
return assign(component(\`Dialog\`, {}, key + \`__1\`, node, ctx, true), {slots: {'default': slot2(ctx)}});
}
}"
`;
@@ -374,7 +374,7 @@ exports[`slots default slot work with text nodes 2`] = `
}
return function template(ctx, node, key = \\"\\") {
let b3 = assign(component(\`Dialog\`, {}, key + \`__1\`, node, ctx), {slots: {'default': slot2(ctx)}});
let b3 = assign(component(\`Dialog\`, {}, key + \`__1\`, node, ctx, true), {slots: {'default': slot2(ctx)}});
return block1([], [b3]);
}
}"
@@ -422,7 +422,7 @@ exports[`slots dynamic t-slot call 2`] = `
return function template(ctx, node, key = \\"\\") {
const ctx2 = capture(ctx);
let b6 = assign(component(\`Toggler\`, {}, key + \`__1\`, node, ctx), {slots: {'slot1': slot3(ctx2), 'slot2': slot4(ctx2)}});
let b6 = assign(component(\`Toggler\`, {}, key + \`__1\`, node, ctx, true), {slots: {'slot1': slot3(ctx2), 'slot2': slot4(ctx2)}});
return block1([], [b6]);
}
}"
@@ -473,7 +473,7 @@ exports[`slots dynamic t-slot call with default 2`] = `
return function template(ctx, node, key = \\"\\") {
const ctx2 = capture(ctx);
let b6 = assign(component(\`Toggler\`, {}, key + \`__1\`, node, ctx), {slots: {'slot1': slot3(ctx2), 'slot2': slot4(ctx2)}});
let b6 = assign(component(\`Toggler\`, {}, key + \`__1\`, node, ctx, true), {slots: {'slot1': slot3(ctx2), 'slot2': slot4(ctx2)}});
return block1([], [b6]);
}
}"
@@ -505,7 +505,7 @@ exports[`slots fun: two calls to the same slot 2`] = `
}
return function template(ctx, node, key = \\"\\") {
return assign(component(\`Child\`, {}, key + \`__1\`, node, ctx), {slots: {'default': slot2(ctx)}});
return assign(component(\`Child\`, {}, key + \`__1\`, node, ctx, true), {slots: {'default': slot2(ctx)}});
}
}"
`;
@@ -574,7 +574,7 @@ exports[`slots multiple roots are allowed in a default slot 2`] = `
}
return function template(ctx, node, key = \\"\\") {
let b5 = assign(component(\`Dialog\`, {}, key + \`__1\`, node, ctx), {slots: {'default': slot2(ctx)}});
let b5 = assign(component(\`Dialog\`, {}, key + \`__1\`, node, ctx, true), {slots: {'default': slot2(ctx)}});
return block1([], [b5]);
}
}"
@@ -614,7 +614,7 @@ exports[`slots multiple roots are allowed in a named slot 2`] = `
return function template(ctx, node, key = \\"\\") {
const ctx2 = capture(ctx);
let b5 = assign(component(\`Dialog\`, {}, key + \`__1\`, node, ctx), {slots: {'content': slot3(ctx2)}});
let b5 = assign(component(\`Dialog\`, {}, key + \`__1\`, node, ctx, true), {slots: {'content': slot3(ctx2)}});
return block1([], [b5]);
}
}"
@@ -668,7 +668,7 @@ exports[`slots multiple slots containing components 3`] = `
return function template(ctx, node, key = \\"\\") {
const ctx2 = capture(ctx);
return assign(component(\`B\`, {}, key + \`__1\`, node, ctx), {slots: {'s1': slot3(ctx2), 's2': slot5(ctx2)}});
return assign(component(\`B\`, {}, key + \`__1\`, node, ctx, true), {slots: {'s1': slot3(ctx2), 's2': slot5(ctx2)}});
}
}"
`;
@@ -707,7 +707,7 @@ exports[`slots named slot inside slot 2`] = `
const slot4 = ctx => (node, key) => {
const ctx6 = capture(ctx);
return assign(component(\`Child\`, {}, key + \`__5\`, node, ctx), {slots: {'brol': slot7(ctx6)}});
return assign(component(\`Child\`, {}, key + \`__5\`, node, ctx, true), {slots: {'brol': slot7(ctx6)}});
}
const slot7 = ctx => (node, key) => {
@@ -717,7 +717,7 @@ exports[`slots named slot inside slot 2`] = `
return function template(ctx, node, key = \\"\\") {
const ctx2 = capture(ctx);
let b5 = assign(component(\`Child\`, {}, key + \`__1\`, node, ctx), {slots: {'brol': slot3(ctx2), 'default': slot4(ctx2)}});
let b5 = assign(component(\`Child\`, {}, key + \`__1\`, node, ctx, true), {slots: {'brol': slot3(ctx2), 'default': slot4(ctx2)}});
return block1([], [b5]);
}
}"
@@ -757,7 +757,7 @@ exports[`slots named slot inside slot, part 3 2`] = `
const slot4 = ctx => (node, key) => {
const ctx6 = capture(ctx);
return assign(component(\`Child\`, {}, key + \`__5\`, node, ctx), {slots: {'brol': slot7(ctx6)}});
return assign(component(\`Child\`, {}, key + \`__5\`, node, ctx, true), {slots: {'brol': slot7(ctx6)}});
}
const slot7 = ctx => (node, key) => {
@@ -767,7 +767,7 @@ exports[`slots named slot inside slot, part 3 2`] = `
return function template(ctx, node, key = \\"\\") {
const ctx2 = capture(ctx);
let b5 = assign(component(\`Child\`, {}, key + \`__1\`, node, ctx), {slots: {'brol': slot3(ctx2), 'default': slot4(ctx2)}});
let b5 = assign(component(\`Child\`, {}, key + \`__1\`, node, ctx, true), {slots: {'brol': slot3(ctx2), 'default': slot4(ctx2)}});
return block1([], [b5]);
}
}"
@@ -850,7 +850,7 @@ exports[`slots named slots inside slot, again 2`] = `
const slot4 = ctx => (node, key) => {
const ctx6 = capture(ctx);
return assign(component(\`Child\`, {}, key + \`__5\`, node, ctx), {slots: {'brol2': slot7(ctx6)}});
return assign(component(\`Child\`, {}, key + \`__5\`, node, ctx, true), {slots: {'brol2': slot7(ctx6)}});
}
const slot7 = ctx => (node, key) => {
@@ -860,7 +860,7 @@ exports[`slots named slots inside slot, again 2`] = `
return function template(ctx, node, key = \\"\\") {
const ctx2 = capture(ctx);
let b5 = assign(component(\`Child\`, {}, key + \`__1\`, node, ctx), {slots: {'brol1': slot3(ctx2), 'default': slot4(ctx2)}});
let b5 = assign(component(\`Child\`, {}, key + \`__1\`, node, ctx, true), {slots: {'brol1': slot3(ctx2), 'default': slot4(ctx2)}});
return block1([], [b5]);
}
}"
@@ -920,7 +920,7 @@ exports[`slots nested slots in same template 4`] = `
let block1 = createBlock(\`<span id=\\"parent\\"><block-child-0/></span>\`);
const slot2 = ctx => (node, key) => {
return assign(component(\`Child2\`, {}, key + \`__3\`, node, ctx), {slots: {'default': slot4(ctx)}});
return assign(component(\`Child2\`, {}, key + \`__3\`, node, ctx, true), {slots: {'default': slot4(ctx)}});
}
const slot4 = ctx => (node, key) => {
@@ -928,7 +928,7 @@ exports[`slots nested slots in same template 4`] = `
}
return function template(ctx, node, key = \\"\\") {
let b4 = assign(component(\`Child\`, {}, key + \`__1\`, node, ctx), {slots: {'default': slot2(ctx)}});
let b4 = assign(component(\`Child\`, {}, key + \`__1\`, node, ctx, true), {slots: {'default': slot2(ctx)}});
return block1([], [b4]);
}
}"
@@ -976,7 +976,7 @@ exports[`slots nested slots: evaluation context and parented relationship 3`] =
}
return function template(ctx, node, key = \\"\\") {
return assign(component(\`GrandChild\`, {}, key + \`__1\`, node, ctx), {slots: {'default': slot2(ctx)}});
return assign(component(\`GrandChild\`, {}, key + \`__1\`, node, ctx, true), {slots: {'default': slot2(ctx)}});
}
}"
`;
@@ -993,7 +993,7 @@ exports[`slots nested slots: evaluation context and parented relationship 4`] =
}
return function template(ctx, node, key = \\"\\") {
return assign(component(\`Child\`, {}, key + \`__1\`, node, ctx), {slots: {'default': slot2(ctx)}});
return assign(component(\`Child\`, {}, key + \`__1\`, node, ctx, true), {slots: {'default': slot2(ctx)}});
}
}"
`;
@@ -1052,7 +1052,7 @@ exports[`slots simple default slot 2`] = `
}
return function template(ctx, node, key = \\"\\") {
return assign(component(\`Child\`, {}, key + \`__1\`, node, ctx), {slots: {'default': slot2(ctx)}});
return assign(component(\`Child\`, {}, key + \`__1\`, node, ctx, true), {slots: {'default': slot2(ctx)}});
}
}"
`;
@@ -1081,7 +1081,7 @@ exports[`slots simple default slot, variation 2`] = `
}
return function template(ctx, node, key = \\"\\") {
return assign(component(\`Child\`, {}, key + \`__1\`, node, ctx), {slots: {'default': slot2(ctx)}});
return assign(component(\`Child\`, {}, key + \`__1\`, node, ctx, true), {slots: {'default': slot2(ctx)}});
}
}"
`;
@@ -1131,7 +1131,7 @@ exports[`slots slot and (inline) t-call 3`] = `
return function template(ctx, node, key = \\"\\") {
const ctx2 = capture(ctx);
let b3 = assign(component(\`Dialog\`, {}, key + \`__1\`, node, ctx), {slots: {'default': slot3(ctx2)}});
let b3 = assign(component(\`Dialog\`, {}, key + \`__1\`, node, ctx, true), {slots: {'default': slot3(ctx2)}});
return block1([], [b3]);
}
}"
@@ -1182,7 +1182,7 @@ exports[`slots slot and t-call 3`] = `
return function template(ctx, node, key = \\"\\") {
const ctx2 = capture(ctx);
let b3 = assign(component(\`Dialog\`, {}, key + \`__1\`, node, ctx), {slots: {'default': slot3(ctx2)}});
let b3 = assign(component(\`Dialog\`, {}, key + \`__1\`, node, ctx, true), {slots: {'default': slot3(ctx2)}});
return block1([], [b3]);
}
}"
@@ -1217,7 +1217,7 @@ exports[`slots slot and t-esc 2`] = `
}
return function template(ctx, node, key = \\"\\") {
let b3 = assign(component(\`Dialog\`, {}, key + \`__1\`, node, ctx), {slots: {'default': slot2(ctx)}});
let b3 = assign(component(\`Dialog\`, {}, key + \`__1\`, node, ctx, true), {slots: {'default': slot2(ctx)}});
return block1([], [b3]);
}
}"
@@ -1270,7 +1270,7 @@ exports[`slots slot are properly rendered if inner props are changed 3`] = `
const v1 = ctx['inc'];
let d1 = [v1, ctx];
let d2 = ctx['state'].val;
let b3 = assign(component(\`GenericComponent\`, {}, key + \`__2\`, node, ctx), {slots: {'default': slot3(ctx)}});
let b3 = assign(component(\`GenericComponent\`, {}, key + \`__2\`, node, ctx, true), {slots: {'default': slot3(ctx)}});
return block1([d1, d2], [b3]);
}
}"
@@ -1307,7 +1307,7 @@ exports[`slots slot content is bound to caller 2`] = `
}
return function template(ctx, node, key = \\"\\") {
return assign(component(\`Child\`, {}, key + \`__1\`, node, ctx), {slots: {'default': slot2(ctx)}});
return assign(component(\`Child\`, {}, key + \`__1\`, node, ctx, true), {slots: {'default': slot2(ctx)}});
}
}"
`;
@@ -1350,7 +1350,7 @@ exports[`slots slot preserves properly parented relationship 3`] = `
}
return function template(ctx, node, key = \\"\\") {
let b3 = assign(component(\`Child\`, {}, key + \`__1\`, node, ctx), {slots: {'default': slot2(ctx)}});
let b3 = assign(component(\`Child\`, {}, key + \`__1\`, node, ctx, true), {slots: {'default': slot2(ctx)}});
return block1([], [b3]);
}
}"
@@ -1408,7 +1408,7 @@ exports[`slots slot preserves properly parented relationship, even through t-cal
return function template(ctx, node, key = \\"\\") {
const ctx2 = capture(ctx);
let b3 = assign(component(\`Child\`, {}, key + \`__1\`, node, ctx), {slots: {'default': slot3(ctx2)}});
let b3 = assign(component(\`Child\`, {}, key + \`__1\`, node, ctx, true), {slots: {'default': slot3(ctx2)}});
return block1([], [b3]);
}
}"
@@ -1441,7 +1441,7 @@ exports[`slots slots and wrapper components 2`] = `
}
return function template(ctx, node, key = \\"\\") {
return assign(component(\`Link\`, {}, key + \`__1\`, node, ctx), {slots: {'default': slot2(ctx)}});
return assign(component(\`Link\`, {}, key + \`__1\`, node, ctx, true), {slots: {'default': slot2(ctx)}});
}
}"
`;
@@ -1480,7 +1480,7 @@ exports[`slots slots are rendered with proper context 2`] = `
return function template(ctx, node, key = \\"\\") {
let d1 = ctx['state'].val;
const ctx2 = capture(ctx);
let b3 = assign(component(\`Dialog\`, {}, key + \`__1\`, node, ctx), {slots: {'footer': slot3(ctx2)}});
let b3 = assign(component(\`Dialog\`, {}, key + \`__1\`, node, ctx, true), {slots: {'footer': slot3(ctx2)}});
return block1([d1], [b3]);
}
}"
@@ -1525,7 +1525,7 @@ exports[`slots slots are rendered with proper context, part 2 2`] = `
ctx[\`user\`] = v_block2[i1];
let key1 = ctx['user'].id;
const ctx2 = capture(ctx);
let b7 = assign(component(\`Link\`, {to: '/user/'+ctx['user'].id}, key + \`__1__\${key1}\`, node, ctx), {slots: {'default': slot3(ctx2)}});
let b7 = assign(component(\`Link\`, {to: '/user/'+ctx['user'].id}, key + \`__1__\${key1}\`, node, ctx, true), {slots: {'default': slot3(ctx2)}});
c_block2[i1] = withKey(block3([], [b7]), key1);
}
let b2 = list(c_block2);
@@ -1574,7 +1574,7 @@ exports[`slots slots are rendered with proper context, part 3 2`] = `
let key1 = ctx['user'].id;
setContextValue(ctx, \\"userdescr\\", 'User '+ctx['user'].name);
const ctx2 = capture(ctx);
let b5 = assign(component(\`Link\`, {to: '/user/'+ctx['user'].id}, key + \`__1__\${key1}\`, node, ctx), {slots: {'default': slot3(ctx2)}});
let b5 = assign(component(\`Link\`, {to: '/user/'+ctx['user'].id}, key + \`__1__\${key1}\`, node, ctx, true), {slots: {'default': slot3(ctx2)}});
c_block2[i1] = withKey(block3([], [b5]), key1);
}
let b2 = list(c_block2);
@@ -1617,7 +1617,7 @@ exports[`slots slots are rendered with proper context, part 4 2`] = `
ctx[isBoundary] = 1
setContextValue(ctx, \\"userdescr\\", 'User '+ctx['state'].user.name);
const ctx2 = capture(ctx);
let b3 = assign(component(\`Link\`, {to: '/user/'+ctx['state'].user.id}, key + \`__1\`, node, ctx), {slots: {'default': slot3(ctx2)}});
let b3 = assign(component(\`Link\`, {to: '/user/'+ctx['state'].user.id}, key + \`__1\`, node, ctx, true), {slots: {'default': slot3(ctx2)}});
return block1([], [b3]);
}
}"
@@ -1652,7 +1652,7 @@ exports[`slots slots in slots, with vars 2`] = `
}
return function template(ctx, node, key = \\"\\") {
let b3 = assign(component(\`B\`, {}, key + \`__1\`, node, ctx), {slots: {'default': slot2(ctx)}});
let b3 = assign(component(\`B\`, {}, key + \`__1\`, node, ctx, true), {slots: {'default': slot2(ctx)}});
return block1([], [b3]);
}
}"
@@ -1678,7 +1678,7 @@ exports[`slots slots in slots, with vars 3`] = `
ctx[isBoundary] = 1
setContextValue(ctx, \\"test\\", ctx['state'].name);
const ctx2 = capture(ctx);
let b3 = assign(component(\`A\`, {}, key + \`__1\`, node, ctx), {slots: {'default': slot3(ctx2)}});
let b3 = assign(component(\`A\`, {}, key + \`__1\`, node, ctx, true), {slots: {'default': slot3(ctx2)}});
return block1([], [b3]);
}
}"
@@ -1721,7 +1721,7 @@ exports[`slots slots in t-foreach and re-rendering 2`] = `
ctx[\`n_index\`] = i1;
let key1 = ctx['n_index'];
const ctx2 = capture(ctx);
c_block2[i1] = withKey(assign(component(\`Child\`, {}, key + \`__1__\${key1}\`, node, ctx), {slots: {'default': slot3(ctx2)}}), key1);
c_block2[i1] = withKey(assign(component(\`Child\`, {}, key + \`__1__\${key1}\`, node, ctx, true), {slots: {'default': slot3(ctx2)}}), key1);
}
let b2 = list(c_block2);
return block1([], [b2]);
@@ -1775,7 +1775,7 @@ exports[`slots slots in t-foreach in t-foreach 2`] = `
ctx[\`node2\`] = v_block6[i2];
let key2 = ctx['node2'].key;
const ctx2 = capture(ctx);
c_block6[i2] = withKey(assign(component(\`Child\`, {}, key + \`__1__\${key1}__\${key2}\`, node, ctx), {slots: {'default': slot3(ctx2)}}), key2);
c_block6[i2] = withKey(assign(component(\`Child\`, {}, key + \`__1__\${key1}__\${key2}\`, node, ctx, true), {slots: {'default': slot3(ctx2)}}), key2);
}
ctx = ctx.__proto__;
let b6 = list(c_block6);
@@ -1828,7 +1828,7 @@ exports[`slots slots in t-foreach with t-set and re-rendering 2`] = `
let key1 = ctx['n_index'];
setContextValue(ctx, \\"dummy\\", ctx['n_index']);
const ctx2 = capture(ctx);
c_block2[i1] = withKey(assign(component(\`Child\`, {}, key + \`__1__\${key1}\`, node, ctx), {slots: {'default': slot3(ctx2)}}), key1);
c_block2[i1] = withKey(assign(component(\`Child\`, {}, key + \`__1__\${key1}\`, node, ctx, true), {slots: {'default': slot3(ctx2)}}), key1);
}
let b2 = list(c_block2);
return block1([], [b2]);
@@ -1867,7 +1867,7 @@ exports[`slots t-debug on a t-set-slot (defining a slot) 2`] = `
return function template(ctx, node, key = \\"\\") {
const ctx2 = capture(ctx);
let b3 = assign(component(\`Dialog\`, {}, key + \`__1\`, node, ctx), {slots: {'content': slot3(ctx2)}});
let b3 = assign(component(\`Dialog\`, {}, key + \`__1\`, node, ctx, true), {slots: {'content': slot3(ctx2)}});
return block1([], [b3]);
}
}"
@@ -1906,7 +1906,7 @@ exports[`slots t-set t-value in a slot 2`] = `
ctx = Object.create(ctx);
ctx[isBoundary] = 1
const ctx2 = capture(ctx);
let b3 = assign(component(\`Dialog\`, {}, key + \`__1\`, node, ctx), {slots: {'default': slot3(ctx2)}});
let b3 = assign(component(\`Dialog\`, {}, key + \`__1\`, node, ctx, true), {slots: {'default': slot3(ctx2)}});
return block1([], [b3]);
}
}"
@@ -1968,7 +1968,7 @@ exports[`slots t-slot in recursive templates 2`] = `
ctx = Object.create(ctx);
ctx[isBoundary] = 1
const ctx2 = capture(ctx);
return assign(component(\`Wrapper\`, {}, key + \`__1\`, node, ctx), {slots: {'default': slot3(ctx2)}});
return assign(component(\`Wrapper\`, {}, key + \`__1\`, node, ctx, true), {slots: {'default': slot3(ctx2)}});
}
}"
`;
@@ -2027,7 +2027,7 @@ exports[`slots t-slot nested within another slot 4`] = `
let block1 = createBlock(\`<span id=\\"c2\\"><block-child-0/></span>\`);
const slot2 = ctx => (node, key) => {
return assign(component(\`Portal\`, {}, key + \`__3\`, node, ctx), {slots: {'default': slot4(ctx)}});
return assign(component(\`Portal\`, {}, key + \`__3\`, node, ctx, true), {slots: {'default': slot4(ctx)}});
}
const slot4 = ctx => (node, key) => {
@@ -2035,7 +2035,7 @@ exports[`slots t-slot nested within another slot 4`] = `
}
return function template(ctx, node, key = \\"\\") {
let b4 = assign(component(\`Modal\`, {}, key + \`__1\`, node, ctx), {slots: {'default': slot2(ctx)}});
let b4 = assign(component(\`Modal\`, {}, key + \`__1\`, node, ctx, true), {slots: {'default': slot2(ctx)}});
return block1([], [b4]);
}
}"
@@ -2055,7 +2055,7 @@ exports[`slots t-slot nested within another slot 5`] = `
}
return function template(ctx, node, key = \\"\\") {
let b3 = assign(component(\`Dialog\`, {}, key + \`__1\`, node, ctx), {slots: {'default': slot2(ctx)}});
let b3 = assign(component(\`Dialog\`, {}, key + \`__1\`, node, ctx, true), {slots: {'default': slot2(ctx)}});
return block1([], [b3]);
}
}"
@@ -2090,7 +2090,7 @@ exports[`slots t-slot scope context 2`] = `
}
return function template(ctx, node, key = \\"\\") {
return assign(component(\`Wrapper\`, {}, key + \`__1\`, node, ctx), {slots: {'default': slot2(ctx)}});
return assign(component(\`Wrapper\`, {}, key + \`__1\`, node, ctx, true), {slots: {'default': slot2(ctx)}});
}
}"
`;
@@ -2109,7 +2109,7 @@ exports[`slots t-slot scope context 3`] = `
}
return function template(ctx, node, key = \\"\\") {
return assign(component(\`Dialog\`, {}, key + \`__1\`, node, ctx), {slots: {'default': slot2(ctx)}});
return assign(component(\`Dialog\`, {}, key + \`__1\`, node, ctx, true), {slots: {'default': slot2(ctx)}});
}
}"
`;
@@ -2174,7 +2174,7 @@ exports[`slots t-slot within dynamic t-call 4`] = `
return function template(ctx, node, key = \\"\\") {
const ctx2 = capture(ctx);
let b3 = assign(component(\`Slotted\`, {}, key + \`__1\`, node, ctx), {slots: {'default': slot3(ctx2)}});
let b3 = assign(component(\`Slotted\`, {}, key + \`__1\`, node, ctx, true), {slots: {'default': slot3(ctx2)}});
return block1([], [b3]);
}
}"
@@ -2221,7 +2221,7 @@ exports[`slots template can just return a slot 3`] = `
}
return function template(ctx, node, key = \\"\\") {
let b3 = assign(component(\`SlotComponent\`, {}, key + \`__1\`, node, ctx), {slots: {'default': slot2(ctx)}});
let b3 = assign(component(\`SlotComponent\`, {}, key + \`__1\`, node, ctx, true), {slots: {'default': slot2(ctx)}});
return block1([], [b3]);
}
}"
@@ -24,7 +24,7 @@ exports[`t-props basic use 2`] = `
let block1 = createBlock(\`<div><block-child-0/></div>\`);
return function template(ctx, node, key = \\"\\") {
let b2 = component(\`Child\`, ctx['some'].obj, key + \`__1\`, node, ctx);
let b2 = component(\`Child\`, Object.assign({}, ctx['some'].obj), key + \`__1\`, node, ctx);
return block1([], [b2]);
}
}"
@@ -85,7 +85,7 @@ exports[`t-props t-props only 2`] = `
let block1 = createBlock(\`<div><div><block-child-0/></div></div>\`);
return function template(ctx, node, key = \\"\\") {
let b2 = component(\`Comp\`, ctx['state'], key + \`__1\`, node, ctx);
let b2 = component(\`Comp\`, Object.assign({}, ctx['state']), key + \`__1\`, node, ctx);
return block1([], [b2]);
}
}"
@@ -38,7 +38,7 @@ exports[`t-set slot setted value (with t-set) not accessible with t-esc 2`] = `
setContextValue(ctx, \\"iter\\", 'source');
let d1 = ctx['iter'];
const ctx2 = capture(ctx);
let b2 = assign(component(\`Childcomp\`, {}, key + \`__1\`, node, ctx), {slots: {'default': slot3(ctx2)}});
let b2 = assign(component(\`Childcomp\`, {}, key + \`__1\`, node, ctx, true), {slots: {'default': slot3(ctx2)}});
let d2 = ctx['iter'];
return block1([d1, d2], [b2]);
}
+1 -1
View File
@@ -419,7 +419,7 @@ describe("basics", () => {
expect(fixture.innerHTML).toBe("<div><span></span></div>");
});
test("child can be updated", async () => {
test.only("child can be updated", async () => {
class Child extends Component {
static template = xml`<t t-esc="props.value"/>`;
}
+1 -6
View File
@@ -543,7 +543,7 @@ test("properly behave when destroyed/unmounted while rendering ", async () => {
}
class Child extends Component {
static template = xml`<div><SubChild /></div>`;
static template = xml`<div><SubChild val="props.val"/></div>`;
static components = { SubChild };
setup() {
useLogLifecycle(steps);
@@ -1900,18 +1900,13 @@ test("concurrent renderings scenario 13", async () => {
"Child:willPatch",
"Child:patched",
"Parent:willRender",
"Child:willUpdateProps",
"Child:setup",
"Child:willStart",
"Parent:rendered",
"Child:willRender",
"Child:rendered",
"Child:willRender",
"Child:rendered",
"Parent:willPatch",
"Child:willPatch",
"Child:mounted",
"Child:patched",
"Parent:patched",
"Child:willRender",
"Child:rendered",
+4 -7
View File
@@ -899,8 +899,9 @@ describe("lifecycle hooks", () => {
class Parent extends Component {
static template = xml`
<Child />`;
<Child someValue="state.value" />`;
static components = { Child };
state = useState({ value: 1 });
setup() {
useLogLifecycle(steps);
}
@@ -910,7 +911,7 @@ describe("lifecycle hooks", () => {
expect(fixture.innerHTML).toBe("<button>1</button>");
parent.render(); // to block child render
parent.state.value++; // to block child render
await nextTick();
fixture.querySelector("button")!.click();
@@ -1063,22 +1064,18 @@ describe("lifecycle hooks", () => {
steps.splice(0);
c!.state.flag = false;
await nextTick();
expect(fixture.innerHTML).toBe(`<div>A<div>B</div><div>C<div>D</div><div>F</div></div></div>`);
expect(steps).toEqual([
"C:willRender",
"D:willUpdateProps",
"F:setup",
"F:willStart",
"C:rendered",
"D:willRender",
"D:rendered",
"F:willRender",
"F:rendered",
"C:willPatch",
"D:willPatch",
"E:willUnmount",
"E:destroyed",
"F:mounted",
"D:patched",
"C:patched",
]);
});
+110
View File
@@ -0,0 +1,110 @@
import { Component, mount, onRendered, useState } from "../../src";
import { xml } from "../../src/tags";
import { makeTestFixture, snapshotEverything, nextTick } from "../helpers";
let fixture: HTMLElement;
snapshotEverything();
beforeEach(() => {
fixture = makeTestFixture();
});
describe("rendering semantics", () => {
test("can render a parent without rendering child", async () => {
let childN = 0;
let parentN = 0;
class Child extends Component {
static template = xml`child`;
setup() {
onRendered(() => childN++);
}
}
class Parent extends Component {
static template = xml`
<t t-esc="state.value"/>
<Child/>
`;
static components = { Child };
state = useState({ value: "A" });
setup() {
onRendered(() => parentN++);
}
}
const parent = await mount(Parent, fixture);
expect(fixture.innerHTML).toBe("Achild");
expect(parentN).toBe(1);
expect(childN).toBe(1);
parent.state.value = "B";
await nextTick();
expect(fixture.innerHTML).toBe("Bchild");
expect(parentN).toBe(2);
expect(childN).toBe(1);
});
test("can force a render to update sub tree", async () => {
let childN = 0;
let parentN = 0;
class Child extends Component {
static template = xml`child`;
setup() {
onRendered(() => childN++);
}
}
class Parent extends Component {
static template = xml`
<t t-esc="state.value"/>
<Child/>
`;
static components = { Child };
state = { value: "A" };
setup() {
onRendered(() => parentN++);
}
}
const parent = await mount(Parent, fixture);
expect(fixture.innerHTML).toBe("Achild");
expect(parentN).toBe(1);
expect(childN).toBe(1);
parent.state.value = "B";
parent.render(true);
await nextTick();
expect(fixture.innerHTML).toBe("Bchild");
expect(parentN).toBe(2);
expect(childN).toBe(2);
});
test("blabla", async () => {
class Child extends Component {
static template = xml`<t t-esc="props.a.b"/>`;
}
class Parent extends Component {
static template = xml`
<Child a="state"/>
`;
static components = { Child };
state = useState({ b: 1 });
}
const parent = await mount(Parent, fixture);
expect(fixture.innerHTML).toBe("1");
parent.state.b = 3;
await nextTick();
expect(fixture.innerHTML).toBe("3");
});
});
+1 -1
View File
@@ -65,7 +65,7 @@ describe("t-props", () => {
`;
setup() {
expect(this.props).toEqual({ a: 1, b: 2 });
expect(this.props).toBe(props);
expect(this.props).not.toBe(props);
}
}
class Parent extends Component {
+3 -3
View File
@@ -30,7 +30,7 @@ exports[`Memo if no prop change, prevent renderings from above 2`] = `
let b2 = text(ctx['state'].a);
let b3 = text(ctx['state'].b);
let b4 = text(ctx['state'].c);
let b9 = assign(component(\`Memo\`, {a: ctx['state'].a,b: ctx['state'].b}, key + \`__1\`, node, ctx), {slots: {'default': slot2(ctx)}});
let b9 = assign(component(\`Memo\`, {a: ctx['state'].a,b: ctx['state'].b}, key + \`__1\`, node, ctx, true), {slots: {'default': slot2(ctx)}});
return multi([b2, b3, b4, b9]);
}
}"
@@ -61,7 +61,7 @@ exports[`Memo if no props, prevent renderings from above 2`] = `
return function template(ctx, node, key = \\"\\") {
let b2 = component(\`Child\`, {value: ctx['state'].value}, key + \`__1\`, node, ctx);
let b4 = assign(component(\`Memo\`, {}, key + \`__2\`, node, ctx), {slots: {'default': slot3(ctx)}});
let b4 = assign(component(\`Memo\`, {}, key + \`__2\`, node, ctx, true), {slots: {'default': slot3(ctx)}});
return multi([b2, b4]);
}
}"
@@ -80,7 +80,7 @@ exports[`Memo if no props, prevent renderings from above (work with simple html)
return function template(ctx, node, key = \\"\\") {
let b2 = text(ctx['state'].value);
let b4 = assign(component(\`Memo\`, {}, key + \`__1\`, node, ctx), {slots: {'default': slot2(ctx)}});
let b4 = assign(component(\`Memo\`, {}, key + \`__1\`, node, ctx, true), {slots: {'default': slot2(ctx)}});
return multi([b2, b4]);
}
}"
+20 -20
View File
@@ -28,7 +28,7 @@ exports[`Portal Portal composed with t-slot 2`] = `
}
return function template(ctx, node, key = \\"\\") {
return assign(component(\`Portal\`, {target: '#outside'}, key + \`__1\`, node, ctx), {slots: {'default': slot2(ctx)}});
return assign(component(\`Portal\`, {target: '#outside'}, key + \`__1\`, node, ctx, true), {slots: {'default': slot2(ctx)}});
}
}"
`;
@@ -47,7 +47,7 @@ exports[`Portal Portal composed with t-slot 3`] = `
}
return function template(ctx, node, key = \\"\\") {
let b3 = assign(component(\`Child\`, {}, key + \`__1\`, node, ctx), {slots: {'default': slot2(ctx)}});
let b3 = assign(component(\`Child\`, {}, key + \`__1\`, node, ctx, true), {slots: {'default': slot2(ctx)}});
return block1([], [b3]);
}
}"
@@ -68,7 +68,7 @@ exports[`Portal basic use of portal 1`] = `
}
return function template(ctx, node, key = \\"\\") {
let b3 = assign(component(\`Portal\`, {target: '#outside'}, key + \`__1\`, node, ctx), {slots: {'default': slot2(ctx)}});
let b3 = assign(component(\`Portal\`, {target: '#outside'}, key + \`__1\`, node, ctx, true), {slots: {'default': slot2(ctx)}});
return block1([], [b3]);
}
}"
@@ -106,7 +106,7 @@ exports[`Portal conditional use of Portal (with sub Component) 2`] = `
let b2,b4;
b2 = block2();
if (ctx['state'].hasPortal) {
b4 = assign(component(\`Portal\`, {target: '#outside'}, key + \`__1\`, node, ctx), {slots: {'default': slot2(ctx)}});
b4 = assign(component(\`Portal\`, {target: '#outside'}, key + \`__1\`, node, ctx, true), {slots: {'default': slot2(ctx)}});
}
return multi([b2, b4]);
}
@@ -131,7 +131,7 @@ exports[`Portal conditional use of Portal 1`] = `
let b2,b4;
b2 = block2();
if (ctx['state'].hasPortal) {
b4 = assign(component(\`Portal\`, {target: '#outside'}, key + \`__1\`, node, ctx), {slots: {'default': slot2(ctx)}});
b4 = assign(component(\`Portal\`, {target: '#outside'}, key + \`__1\`, node, ctx, true), {slots: {'default': slot2(ctx)}});
}
return multi([b2, b4]);
}
@@ -169,7 +169,7 @@ exports[`Portal lifecycle hooks of portal sub component are properly called 2`]
return function template(ctx, node, key = \\"\\") {
let b3;
if (ctx['state'].hasChild) {
b3 = assign(component(\`Portal\`, {target: '#outside'}, key + \`__1\`, node, ctx), {slots: {'default': slot2(ctx)}});
b3 = assign(component(\`Portal\`, {target: '#outside'}, key + \`__1\`, node, ctx, true), {slots: {'default': slot2(ctx)}});
}
return block1([], [b3]);
}
@@ -196,7 +196,7 @@ exports[`Portal portal could have dynamically no content 1`] = `
}
return function template(ctx, node, key = \\"\\") {
let b4 = assign(component(\`Portal\`, {target: '#outside'}, key + \`__1\`, node, ctx), {slots: {'default': slot2(ctx)}});
let b4 = assign(component(\`Portal\`, {target: '#outside'}, key + \`__1\`, node, ctx, true), {slots: {'default': slot2(ctx)}});
return block1([], [b4]);
}
}"
@@ -231,7 +231,7 @@ exports[`Portal portal destroys on crash 2`] = `
}
return function template(ctx, node, key = \\"\\") {
let b3 = assign(component(\`Portal\`, {target: '#outside'}, key + \`__1\`, node, ctx), {slots: {'default': slot2(ctx)}});
let b3 = assign(component(\`Portal\`, {target: '#outside'}, key + \`__1\`, node, ctx, true), {slots: {'default': slot2(ctx)}});
return block1([], [b3]);
}
}"
@@ -266,7 +266,7 @@ exports[`Portal portal with child and props 2`] = `
}
return function template(ctx, node, key = \\"\\") {
let b3 = assign(component(\`Portal\`, {target: '#outside'}, key + \`__1\`, node, ctx), {slots: {'default': slot2(ctx)}});
let b3 = assign(component(\`Portal\`, {target: '#outside'}, key + \`__1\`, node, ctx, true), {slots: {'default': slot2(ctx)}});
return block1([], [b3]);
}
}"
@@ -295,7 +295,7 @@ exports[`Portal portal with dynamic body 1`] = `
}
return function template(ctx, node, key = \\"\\") {
let b5 = assign(component(\`Portal\`, {target: '#outside'}, key + \`__1\`, node, ctx), {slots: {'default': slot2(ctx)}});
let b5 = assign(component(\`Portal\`, {target: '#outside'}, key + \`__1\`, node, ctx, true), {slots: {'default': slot2(ctx)}});
return block1([], [b5]);
}
}"
@@ -319,7 +319,7 @@ exports[`Portal portal with many children 1`] = `
}
return function template(ctx, node, key = \\"\\") {
let b5 = assign(component(\`Portal\`, {target: '#outside'}, key + \`__1\`, node, ctx), {slots: {'default': slot2(ctx)}});
let b5 = assign(component(\`Portal\`, {target: '#outside'}, key + \`__1\`, node, ctx, true), {slots: {'default': slot2(ctx)}});
return block1([], [b5]);
}
}"
@@ -343,7 +343,7 @@ exports[`Portal portal with no content 1`] = `
}
return function template(ctx, node, key = \\"\\") {
let b4 = assign(component(\`Portal\`, {target: '#outside'}, key + \`__1\`, node, ctx), {slots: {'default': slot2(ctx)}});
let b4 = assign(component(\`Portal\`, {target: '#outside'}, key + \`__1\`, node, ctx, true), {slots: {'default': slot2(ctx)}});
return block1([], [b4]);
}
}"
@@ -363,7 +363,7 @@ exports[`Portal portal with only text as content 1`] = `
}
return function template(ctx, node, key = \\"\\") {
let b3 = assign(component(\`Portal\`, {target: '#outside'}, key + \`__1\`, node, ctx), {slots: {'default': slot2(ctx)}});
let b3 = assign(component(\`Portal\`, {target: '#outside'}, key + \`__1\`, node, ctx, true), {slots: {'default': slot2(ctx)}});
return block1([], [b3]);
}
}"
@@ -384,7 +384,7 @@ exports[`Portal portal with target not in dom 1`] = `
}
return function template(ctx, node, key = \\"\\") {
let b3 = assign(component(\`Portal\`, {target: '#does-not-exist'}, key + \`__1\`, node, ctx), {slots: {'default': slot2(ctx)}});
let b3 = assign(component(\`Portal\`, {target: '#does-not-exist'}, key + \`__1\`, node, ctx, true), {slots: {'default': slot2(ctx)}});
return block1([], [b3]);
}
}"
@@ -418,7 +418,7 @@ exports[`Portal portal's parent's env is not polluted 2`] = `
}
return function template(ctx, node, key = \\"\\") {
let b3 = assign(component(\`Portal\`, {target: '#outside'}, key + \`__1\`, node, ctx), {slots: {'default': slot2(ctx)}});
let b3 = assign(component(\`Portal\`, {target: '#outside'}, key + \`__1\`, node, ctx, true), {slots: {'default': slot2(ctx)}});
return block1([], [b3]);
}
}"
@@ -439,7 +439,7 @@ exports[`Portal with target in template (after portal) 1`] = `
}
return function template(ctx, node, key = \\"\\") {
let b3 = assign(component(\`Portal\`, {target: '#local-target'}, key + \`__1\`, node, ctx), {slots: {'default': slot2(ctx)}});
let b3 = assign(component(\`Portal\`, {target: '#local-target'}, key + \`__1\`, node, ctx, true), {slots: {'default': slot2(ctx)}});
return block1([], [b3]);
}
}"
@@ -460,7 +460,7 @@ exports[`Portal with target in template (before portal) 1`] = `
}
return function template(ctx, node, key = \\"\\") {
let b3 = assign(component(\`Portal\`, {target: '#local-target'}, key + \`__1\`, node, ctx), {slots: {'default': slot2(ctx)}});
let b3 = assign(component(\`Portal\`, {target: '#local-target'}, key + \`__1\`, node, ctx, true), {slots: {'default': slot2(ctx)}});
return block1([], [b3]);
}
}"
@@ -481,7 +481,7 @@ exports[`Portal: Props validation target is mandatory 1`] = `
}
return function template(ctx, node, key = \\"\\") {
let b3 = assign(component(\`Portal\`, {}, key + \`__1\`, node, ctx), {slots: {'default': slot2(ctx)}});
let b3 = assign(component(\`Portal\`, {}, key + \`__1\`, node, ctx, true), {slots: {'default': slot2(ctx)}});
return block1([], [b3]);
}
}"
@@ -502,7 +502,7 @@ exports[`Portal: Props validation target is not list 1`] = `
}
return function template(ctx, node, key = \\"\\") {
let b3 = assign(component(\`Portal\`, {target: ['body']}, key + \`__1\`, node, ctx), {slots: {'default': slot2(ctx)}});
let b3 = assign(component(\`Portal\`, {target: ['body']}, key + \`__1\`, node, ctx, true), {slots: {'default': slot2(ctx)}});
return block1([], [b3]);
}
}"
@@ -537,7 +537,7 @@ exports[`Portal: UI/UX focus is kept across re-renders 2`] = `
}
return function template(ctx, node, key = \\"\\") {
let b3 = assign(component(\`Portal\`, {target: '#outside'}, key + \`__1\`, node, ctx), {slots: {'default': slot2(ctx)}});
let b3 = assign(component(\`Portal\`, {target: '#outside'}, key + \`__1\`, node, ctx, true), {slots: {'default': slot2(ctx)}});
return block1([], [b3]);
}
}"
+3 -2
View File
@@ -1,6 +1,7 @@
import { Component, mount, onWillRender, onWillStart, onWillUpdateProps, useState } from "../src";
import { batched, reactive } from "../src/reactivity";
import { reactive } from "../src/reactivity";
import { xml } from "../src/tags";
import { batched } from "../src/utils";
import {
makeDeferred,
makeTestFixture,
@@ -1509,7 +1510,7 @@ describe("Reactivity: useState", () => {
expect([...steps]).toEqual(["list"]);
await nextTick();
expect(fixture.innerHTML).toBe("<div><div>3</div> Total: 3 Count: 1</div>");
expect([...steps]).toEqual(["list", "quantity1"]);
expect([...steps]).toEqual(["list"]);
steps.clear();
secondQuantity.quantity = 2;
+19 -1
View File
@@ -1,4 +1,5 @@
import { EventBus } from "../src/utils";
import { batched, EventBus } from "../src/utils";
import { nextMicroTick } from "./helpers";
describe("event bus behaviour", () => {
test("can subscribe and be notified", () => {
@@ -33,3 +34,20 @@ describe("event bus behaviour", () => {
bus.trigger("event", "hello world");
});
});
describe("batched", () => {
test("callback is called only once after operations", async () => {
let n = 0;
let fn = batched(() => n++);
expect(n).toBe(0);
fn();
fn();
expect(n).toBe(0);
await nextMicroTick();
expect(n).toBe(1);
await nextMicroTick();
expect(n).toBe(1);
});
});