mirror of
https://github.com/odoo/owl.git
synced 2025-10-06 19:59:41 +07:00
Compare commits
241 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| aef6923c23 | |||
| 0dbd2bd463 | |||
| 8ec7a6f9bf | |||
| 12b8ce963e | |||
| bb9d65e95b | |||
| 73c339fff1 | |||
| 406be446a5 | |||
| bd2aa8a72f | |||
| 3536f41f00 | |||
| 804ad3c35e | |||
| 4a922ed82d | |||
| a6f0985d43 | |||
| 6aee1355c8 | |||
| 921ced7c90 | |||
| 1da930cb25 | |||
| cc8e11c9c9 | |||
| 3196b585fd | |||
| ea2ccc5a03 | |||
| 960808aeb2 | |||
| 1fb1d37e32 | |||
| 24ce8613c5 | |||
| 2c1226d737 | |||
| 140818b5f9 | |||
| 83de53d283 | |||
| 50aac42bdc | |||
| bd98d4d0d0 | |||
| 4d68dac24d | |||
| 722abd6d5f | |||
| a7305a5cdb | |||
| ff734c706c | |||
| add5fdd737 | |||
| a221411938 | |||
| bb6479f44f | |||
| aa95149997 | |||
| e4b4ee471f | |||
| 3af5e57825 | |||
| 979712f84e | |||
| 93f2c1d766 | |||
| 0728c8333d | |||
| 6639d361c3 | |||
| 72962f1dd1 | |||
| a4d9aae9a7 | |||
| 1e8576ad40 | |||
| 753d82149e | |||
| 700030cc7d | |||
| d828f39a2d | |||
| 0a73154985 | |||
| d88eb34d4f | |||
| a8d88d4009 | |||
| 6d9ed0d62f | |||
| b33471e819 | |||
| 3fb65b3a89 | |||
| 09d192999a | |||
| 466cf50b73 | |||
| 96620d3e8e | |||
| 176c89b278 | |||
| 38941bc26f | |||
| ce8ddd1cbf | |||
| 81f44ee5d3 | |||
| 99b5e9ec55 | |||
| 4f35f03986 | |||
| eab0caa6cb | |||
| 5a2c769eab | |||
| 89d63ff29a | |||
| a2e8abc243 | |||
| dfd0dcedb8 | |||
| ad743c205c | |||
| 7c78442e43 | |||
| 0d13c362d3 | |||
| 374dbb2fd9 | |||
| 2a5f37cf4b | |||
| cfb6b9f958 | |||
| 82f6923a21 | |||
| cccb379377 | |||
| 82c7c24438 | |||
| 41ad5db2e3 | |||
| 42a140a8e3 | |||
| 7711733a23 | |||
| 281b32965e | |||
| cec451fd15 | |||
| 6a7703ea82 | |||
| 4a03a60084 | |||
| a6bdca082a | |||
| 90167c5436 | |||
| c221721d7f | |||
| 38f39b6755 | |||
| 1775467434 | |||
| 52d0526ddd | |||
| 4b170b9b45 | |||
| 3e1fe07ba7 | |||
| 5bf47500d5 | |||
| 75ad0835e9 | |||
| 3d6a5eb828 | |||
| aceaeef8cc | |||
| 06fc3a2c77 | |||
| 92cc4375f8 | |||
| 9f2e2bcc66 | |||
| c7af885f43 | |||
| bd5637c0a3 | |||
| 3c98ef8cb1 | |||
| aad6b806ba | |||
| 772c275bd4 | |||
| 416deeb865 | |||
| 7e40fa300a | |||
| cc1eea0945 | |||
| bf9cceb56f | |||
| 7eaecac0b5 | |||
| ddc358f48a | |||
| e2819323ee | |||
| 983b9f996d | |||
| fd295b3be3 | |||
| e675f7ff5b | |||
| 702fb3b253 | |||
| 63fbcf99fd | |||
| 14a6289f60 | |||
| 894deed13b | |||
| 6f435c36d8 | |||
| 5dddf8f9a3 | |||
| 463eb4bb86 | |||
| b66d5231d3 | |||
| 3c12519277 | |||
| c1a973a4d8 | |||
| e91e50a812 | |||
| 2e176f135d | |||
| 49c7585998 | |||
| f32b1deb2c | |||
| 1da3ecdbee | |||
| a1c619f094 | |||
| eceb3e6280 | |||
| 8a1ac13975 | |||
| 779003e715 | |||
| 8c600fa539 | |||
| bcc4fe2a27 | |||
| bb4948f3dc | |||
| cc4480e001 | |||
| 7143c2e39b | |||
| a8d8310b8e | |||
| 0bbea351a6 | |||
| 2601a176c4 | |||
| 05a57d6da5 | |||
| e6e6c31632 | |||
| 3f66d9fe6c | |||
| 5d8141a67c | |||
| 6459d8d289 | |||
| 2943ca3921 | |||
| 4866ed8e8a | |||
| 93b88cad8d | |||
| b90180a9e0 | |||
| 8239a5d2cd | |||
| a073568667 | |||
| 7143dd3ff5 | |||
| c0cf2c9e3d | |||
| db9658c140 | |||
| eb2c41aa91 | |||
| a45ca98dac | |||
| ced777f0be | |||
| 7df0a4e93f | |||
| f3555cfae0 | |||
| 629b379ea9 | |||
| a400fc5e69 | |||
| 5d4a38ad0f | |||
| 093218a067 | |||
| ed3e6dcbb6 | |||
| cb107cef7d | |||
| aecc320c29 | |||
| e580ec00fe | |||
| ee5f6c7569 | |||
| c627b0add8 | |||
| b902edc1be | |||
| 1761af9c24 | |||
| db93ef08ff | |||
| 03787cfb39 | |||
| 10745c52d0 | |||
| 2b90e3a688 | |||
| b2ea241270 | |||
| 81e5b24f2f | |||
| 717fd3b6ab | |||
| 3fa1bb62f6 | |||
| 201f06c187 | |||
| ae30d9db7d | |||
| af80cefa76 | |||
| 1f6e84d141 | |||
| 1658d15b87 | |||
| c1439814bf | |||
| d5fbaff9f7 | |||
| 8c16790471 | |||
| 9b8c582b32 | |||
| 1700a6fba3 | |||
| 7513b1e507 | |||
| e0c0306acd | |||
| bca6afeb90 | |||
| 756d32daa0 | |||
| 8169f05edc | |||
| 153f4379f4 | |||
| 7ffb9afbd9 | |||
| c03042b44d | |||
| df2d6b6a0e | |||
| 7c04cc425e | |||
| 16f1e2c237 | |||
| 8a84b5be56 | |||
| 219923d752 | |||
| 348b505e5f | |||
| d3745e4e5f | |||
| 80cb6b7a91 | |||
| 4cceb239dd | |||
| 2ae0149adb | |||
| 3eb63452e7 | |||
| 1296964ae2 | |||
| c71db28bc6 | |||
| a0b2551e4a | |||
| ebd2e4324f | |||
| aa3148eddf | |||
| 42811344da | |||
| a0e1af83ac | |||
| c16d8ed6de | |||
| 15e4c856da | |||
| ced5d0f69f | |||
| b6eb4d009e | |||
| 8c71d99e5f | |||
| d7f3f4defe | |||
| 0f2192604c | |||
| 10df0b5f4a | |||
| d569ea1c28 | |||
| 52fa81c510 | |||
| d6668e3439 | |||
| ee1ef20ce1 | |||
| 9d5ffe11c7 | |||
| 900a3ee501 | |||
| 0a544bd7e8 | |||
| 4415cc8932 | |||
| efa147fdab | |||
| e746574a1d | |||
| c06049076a | |||
| bc04f727ac | |||
| 0bc9573a8a | |||
| 73f94fba3f | |||
| 7a16449724 | |||
| 718c765e3b | |||
| 150d620b8e | |||
| 307b936d01 | |||
| 6950f8e628 |
@@ -5,7 +5,7 @@ name: Node.js CI
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: [ master, owl-next ]
|
branches: [ master ]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
|
|||||||
+50
-14
@@ -30,19 +30,32 @@ All changes are documented here in no particular order.
|
|||||||
- breaking: `catchError` method is replaced by `onError` hook ([details](#36-catcherror-method-is-replaced-by-onerror-hook))
|
- breaking: `catchError` method is replaced by `onError` hook ([details](#36-catcherror-method-is-replaced-by-onerror-hook))
|
||||||
- breaking: Support for inline css (`css` tag and static `style`) has been removed ([details](#37-support-for-inline-css-css-tag-and-static-style-has-been-removed))
|
- breaking: Support for inline css (`css` tag and static `style`) has been removed ([details](#37-support-for-inline-css-css-tag-and-static-style-has-been-removed))
|
||||||
- new: prop validation system can now describe that additional props are allowed (with `*`) ([doc](doc/reference/props.md#props-validation))
|
- new: prop validation system can now describe that additional props are allowed (with `*`) ([doc](doc/reference/props.md#props-validation))
|
||||||
|
- breaking: prop validation system does not allow default prop on a mandatory (not optional) prop ([doc](doc/reference/props.md#props-validation))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
**Templates**
|
**Templates**
|
||||||
|
|
||||||
- breaking: `t-foreach` should always have a corresponding `t-key` ([details](#20-t-foreach-should-always-have-a-corresponding-t-key))
|
- breaking: `t-foreach` should always have a corresponding `t-key` ([details](#20-t-foreach-should-always-have-a-corresponding-t-key))
|
||||||
- breaking: `t-ref` does not work on components ([details](#29-t-ref-does-not-work-on-component))
|
- breaking: `t-ref` does not work on components ([details](#29-t-ref-does-not-work-on-component))
|
||||||
- breaking: `t-on` does not accept expressions, only functions ([details](#30-t-on-does-not-accept-expressions-only-functions))
|
|
||||||
- breaking: `t-raw` directive has been removed (replaced by `t-out`) ([details](#38-t-raw-directive-has-been-removed-replaced-by-t-out))
|
- breaking: `t-raw` directive has been removed (replaced by `t-out`) ([details](#38-t-raw-directive-has-been-removed-replaced-by-t-out))
|
||||||
- new: add support for synthetic events ([doc](doc/reference/event_handling.md#synthetic-events))
|
- new: add support for synthetic events ([doc](doc/reference/event_handling.md#synthetic-events))
|
||||||
- breaking: style/class on components are now regular props ([details](#10-styleclass-on-components-are-now-regular-props))
|
- breaking: style/class on components are now regular props ([details](#10-styleclass-on-components-are-now-regular-props))
|
||||||
- new: components can use the `.bind` suffix to bind function props ([doc](doc/reference/props.md#binding-function-props))
|
- new: components can use the `.bind` suffix to bind function props ([doc](doc/reference/props.md#binding-function-props))
|
||||||
|
- breaking: `t-on` does not accept expressions, only functions ([details](#30-t-on-does-not-accept-expressions-only-functions))
|
||||||
- breaking: `t-on` does not work on components any more ([details](#12-t-on-does-not-work-on-components-any-more))
|
- breaking: `t-on` does not work on components any more ([details](#12-t-on-does-not-work-on-components-any-more))
|
||||||
|
- new: an error is thrown if an handler defined in a `t-on-` directive is not a function (failed silently previously in some cases)
|
||||||
- breaking: `t-component` no longer accepts strings ([details](#17-t-component-no-longer-accepts-strings))
|
- breaking: `t-component` no longer accepts strings ([details](#17-t-component-no-longer-accepts-strings))
|
||||||
|
- new: the `this` variable in template expressions is now bound to the component
|
||||||
|
|
||||||
|
|
||||||
|
**Reactivity**
|
||||||
|
|
||||||
|
- finer grained reactivity: owl 2 tracks change per key/component
|
||||||
|
- finer grained reactivity: sub components can reobserve state ([doc](doc/reference/reactivity.md))
|
||||||
|
- new: `reactive` function: create reactive state (without being linked to a component) ([doc](doc/reference/reactivity.md#reactive))
|
||||||
|
- new: `markRaw` function: mark an object or array so that it is ignored by the reactivity system ([doc](doc/reference/reactivity.md#markraw))
|
||||||
|
- new: `toRaw` function: given a reactive objet, return the raw (non reactive) underlying object ([doc](doc/reference/reactivity.md#toraw))
|
||||||
|
|
||||||
|
|
||||||
**Slots**
|
**Slots**
|
||||||
@@ -66,13 +79,13 @@ All changes are documented here in no particular order.
|
|||||||
|
|
||||||
- improved performance
|
- improved performance
|
||||||
- much simpler code
|
- much simpler code
|
||||||
- finer grained reactivity: owl 2 tracks change per key/component
|
|
||||||
- finer grained reactivity: sub components can reobserve state
|
|
||||||
- new App class to encapsulate a root Owl component (with the config for that application) ([doc](doc/reference/app.md))
|
- new App class to encapsulate a root Owl component (with the config for that application) ([doc](doc/reference/app.md))
|
||||||
- new `Memo` component
|
- new `Memo` component
|
||||||
- new `useEffect` hook ([doc](doc/reference/hooks.md#useeffect))
|
- new `useEffect` hook ([doc](doc/reference/hooks.md#useeffect))
|
||||||
- breaking: `Context` is removed ([details](#15-context-is-removed))
|
- breaking: `Context` is removed ([details](#15-context-is-removed))
|
||||||
- breaking: `env` is now totally empty ([details](#16-env-is-now-totally-empty))
|
- breaking: `env` is now totally empty ([details](#16-env-is-now-totally-empty))
|
||||||
|
- breaking: `env` is now frozen ([details](#28-env-is-now-frozen))
|
||||||
|
- new hook: `useChildSubEnv` (only applies to child components) ([details](#27-usechildsubenv-only-applies-to-child-components))
|
||||||
- breaking: most exports are exported at top level ([details](#18-most-exports-are-exported-at-top-level))
|
- breaking: most exports are exported at top level ([details](#18-most-exports-are-exported-at-top-level))
|
||||||
- breaking: properties are no longer set as attributes ([details](#19-properties-are-no-longer-set-as-attributes))
|
- breaking: properties are no longer set as attributes ([details](#19-properties-are-no-longer-set-as-attributes))
|
||||||
- breaking: `EventBus` api changed: it is now an `EventTarget` ([details](#21-eventbus-api-changed-it-is-now-an-eventtarget))
|
- breaking: `EventBus` api changed: it is now an `EventTarget` ([details](#21-eventbus-api-changed-it-is-now-an-eventtarget))
|
||||||
@@ -81,8 +94,6 @@ All changes are documented here in no particular order.
|
|||||||
- breaking: transition system is removed ([details](#24-transition-system-is-removed))
|
- breaking: transition system is removed ([details](#24-transition-system-is-removed))
|
||||||
- breaking: no more global components or templates ([details](#25-no-more-global-components-or-templates))
|
- breaking: no more global components or templates ([details](#25-no-more-global-components-or-templates))
|
||||||
- breaking: `AsyncRoot` utility component is removed ([details](#26-asyncroot-utility-component-is-removed))
|
- breaking: `AsyncRoot` utility component is removed ([details](#26-asyncroot-utility-component-is-removed))
|
||||||
- breaking: `useSubEnv` only applies to child components ([details](#27-usesubenv-only-applies-to-child-components))
|
|
||||||
- breaking: `env` is now frozen ([details](#28-env-is-now-frozen))
|
|
||||||
- breaking: `renderToString` function on qweb has been removed ([details](#32-rendertostring-on-qweb-has-been-removed))
|
- breaking: `renderToString` function on qweb has been removed ([details](#32-rendertostring-on-qweb-has-been-removed))
|
||||||
- breaking: `debounce` utility function has been removed ([details](#34-debounce-utility-function-has-been-removed))
|
- breaking: `debounce` utility function has been removed ([details](#34-debounce-utility-function-has-been-removed))
|
||||||
- breaking: `browser` object has been removed ([details](#39-browser-object-has-been-removed))
|
- breaking: `browser` object has been removed ([details](#39-browser-object-has-been-removed))
|
||||||
@@ -565,15 +576,12 @@ either a fallback when the data is not ready, or the actual component with data
|
|||||||
as props. If there is no escape, and `AsyncRoot` is needed, please reach out to
|
as props. If there is no escape, and `AsyncRoot` is needed, please reach out to
|
||||||
us so we can study this usecase.
|
us so we can study this usecase.
|
||||||
|
|
||||||
### 27. `useSubEnv` only applies to child components
|
### 27. `useChildSubEnv` (only applies to child components)
|
||||||
|
|
||||||
In Owl 1, a call to `useSubEnv` would define a new environment for the children
|
In Owl, a call to `useSubEnv` would define a new environment for the children
|
||||||
AND the component. It now only defines an environment for the children.
|
AND the component. It is very useful, but in some cases, one only need to update
|
||||||
|
the children component environment. This can now be done with a new hook:
|
||||||
Rationale: This was a subtle cause for bugs: some code had to be rrun
|
[`useChildSubEnv`](doc/reference/hooks.md#usesubenv-and-usechildsubenv)
|
||||||
before the call to `useSubEnv`, otherwise it could interfere with the sub environment.
|
|
||||||
|
|
||||||
Documentation: [Hooks](doc/reference/hooks.md#usesubenv)
|
|
||||||
|
|
||||||
### 28. `env` is now frozen
|
### 28. `env` is now frozen
|
||||||
|
|
||||||
@@ -659,10 +667,14 @@ Also, this can easily be done in userspace, by mounting a component in a div. F
|
|||||||
export async function renderToString(template, context) {
|
export async function renderToString(template, context) {
|
||||||
class C extends Component {
|
class C extends Component {
|
||||||
static template = template;
|
static template = template;
|
||||||
|
setup () {
|
||||||
|
Object.assign(this, context);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
const div = document.createElement('div');
|
const div = document.createElement('div');
|
||||||
document.body.appendChild(div);
|
document.body.appendChild(div);
|
||||||
const component = await mount(C, div);
|
const app = new App(C);
|
||||||
|
await app.mount(div);
|
||||||
const result = div.innerHTML;
|
const result = div.innerHTML;
|
||||||
app.destroy();
|
app.destroy();
|
||||||
div.remove();
|
div.remove();
|
||||||
@@ -670,6 +682,30 @@ export async function renderToString(template, context) {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
The function above works for most cases, but is asynchronous. An alternative
|
||||||
|
function could look like this:
|
||||||
|
|
||||||
|
```js
|
||||||
|
const { App, blockDom } = owl;
|
||||||
|
const app = new App(Component); // act as a template repository
|
||||||
|
|
||||||
|
function renderToString(template, context = {}) {
|
||||||
|
app.addTemplate(template, template, { allowDuplicate: true });
|
||||||
|
const templateFn = app.getTemplate(template);
|
||||||
|
const bdom = templateFn(context, {});
|
||||||
|
const div = document.createElement('div')
|
||||||
|
blockDom.mount(bdom, div);
|
||||||
|
return div.innerHTML;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This is a synchronous function, so it will not work with components, but it should
|
||||||
|
be useful for most simple templates.
|
||||||
|
|
||||||
|
Also note that these two examples do not translate their templates. To do that,
|
||||||
|
they need to be modified to pass the proper translate function to the `App`
|
||||||
|
configuration.
|
||||||
|
|
||||||
### 33. Portal are now defined with `t-portal`
|
### 33. Portal are now defined with `t-portal`
|
||||||
|
|
||||||
Before Owl 2, one could use the `Portal` component by importing it and using it.
|
Before Owl 2, one could use the `Portal` component by importing it and using it.
|
||||||
|
|||||||
@@ -121,5 +121,5 @@ npm install @odoo/owl
|
|||||||
|
|
||||||
If you want to use a simple `<script>` tag, the last release can be downloaded here:
|
If you want to use a simple `<script>` tag, the last release can be downloaded here:
|
||||||
|
|
||||||
- [owl-1.4.7](https://github.com/odoo/owl/releases/tag/v1.4.7)
|
- [owl-1.4.10](https://github.com/odoo/owl/releases/tag/v1.4.10)
|
||||||
|
|
||||||
|
|||||||
+8
-2
@@ -7,9 +7,14 @@ Main entities:
|
|||||||
- [`App`](reference/app.md): represent an Owl application (mainly a root component,a set of templates, and a config)
|
- [`App`](reference/app.md): represent an Owl application (mainly a root component,a set of templates, and a config)
|
||||||
- [`Component`](reference/component.md): the main class to define a concrete Owl component
|
- [`Component`](reference/component.md): the main class to define a concrete Owl component
|
||||||
- [`mount`](reference/app.md#mount-helper): main entry point for most application: mount a component to a target
|
- [`mount`](reference/app.md#mount-helper): main entry point for most application: mount a component to a target
|
||||||
|
- [`xml`](reference/templates.md#inline-templates): helper to define an inline template
|
||||||
|
|
||||||
|
Reactivity
|
||||||
|
|
||||||
- [`useState`](reference/reactivity.md#usestate): create a reactive object (hook, linked to a specific component)
|
- [`useState`](reference/reactivity.md#usestate): create a reactive object (hook, linked to a specific component)
|
||||||
- [`reactive`](reference/reactivity.md#reactive): create a reactive object (not linked to any component)
|
- [`reactive`](reference/reactivity.md#reactive): create a reactive object (not linked to any component)
|
||||||
- [`xml`](reference/templates.md#inline-templates): helper to define an inline template
|
- [`markRaw`](reference/reactivity.md#markraw): mark an object or array so that it is ignored by the reactivity system
|
||||||
|
- [`toRaw`](reference/reactivity.md#toraw): given a reactive objet, return the raw (non reactive) underlying object
|
||||||
|
|
||||||
Lifecycle hooks:
|
Lifecycle hooks:
|
||||||
|
|
||||||
@@ -31,7 +36,8 @@ Other hooks:
|
|||||||
- [`useEnv`](reference/hooks.md#useenv): return a reference to the current env
|
- [`useEnv`](reference/hooks.md#useenv): return a reference to the current env
|
||||||
- [`useExternalListener`](reference/hooks.md#useexternallistener): add a listener outside of a component DOM
|
- [`useExternalListener`](reference/hooks.md#useexternallistener): add a listener outside of a component DOM
|
||||||
- [`useRef`](reference/hooks.md#useref): get an object representing a reference (`t-ref`)
|
- [`useRef`](reference/hooks.md#useref): get an object representing a reference (`t-ref`)
|
||||||
- [`useSubEnv`](reference/hooks.md#usesubenv): extend the current env with additional information for child components
|
- [`useChildSubEnv`](reference/hooks.md#usesubenv-and-usechildsubenv): extend the current env with additional information (for child components)
|
||||||
|
- [`useSubEnv`](reference/hooks.md#usesubenv-and-usechildsubenv): extend the current env with additional information (for current component and child components)
|
||||||
|
|
||||||
Utility/helpers:
|
Utility/helpers:
|
||||||
|
|
||||||
|
|||||||
+21
-16
@@ -3,6 +3,7 @@
|
|||||||
## Content
|
## Content
|
||||||
|
|
||||||
- [Overview](#overview)
|
- [Overview](#overview)
|
||||||
|
- [API](#api)
|
||||||
- [Configuration](#configuration)
|
- [Configuration](#configuration)
|
||||||
- [`mount` helper](#mount-helper)
|
- [`mount` helper](#mount-helper)
|
||||||
- [Loading templates](#loading-templates)
|
- [Loading templates](#loading-templates)
|
||||||
@@ -26,24 +27,10 @@ The basic workflow is: create an `App` instance configured with the root
|
|||||||
component, the templates, and possibly other settings. Then, we mount that
|
component, the templates, and possibly other settings. Then, we mount that
|
||||||
instance somewhere in the DOM.
|
instance somewhere in the DOM.
|
||||||
|
|
||||||
## Configuration
|
## API
|
||||||
|
|
||||||
- **`constructor(Root[, config])`**: first argument should be a component class (not
|
- **`constructor(Root[, config])`**: first argument should be a component class (not
|
||||||
an instance), and the optional second argument is a configuration object.
|
an instance), and the optional second argument is a configuration object (see below).
|
||||||
|
|
||||||
The `config` object is an object with some of the following keys:
|
|
||||||
|
|
||||||
- **`env (object)`**: if given, this will be the shared `env` given to each component
|
|
||||||
- **`props (object)`**: the props given to the root component
|
|
||||||
- **`dev (boolean, default=false)`**: if `true`, the application is rendered in `dev`
|
|
||||||
mode, which activates some additional checks (in particular, the props validation
|
|
||||||
code is only performed in dev mode)
|
|
||||||
- **`translatableAttributes (string[])`**: a list of additional attributes that should
|
|
||||||
be translated (see [translations](translations.md))
|
|
||||||
- **`translateFn (function)`**: a function that will be called by owl to translate
|
|
||||||
templates (see [translations](translations.md))
|
|
||||||
- **`templates (string | xml document)`**: all the templates that will be used by
|
|
||||||
the components created by the application.
|
|
||||||
|
|
||||||
- **`mount(target, options)`**: first argument is an html element, and the optional
|
- **`mount(target, options)`**: first argument is an html element, and the optional
|
||||||
second argument is an object with mounting options (see below). Mount the app
|
second argument is an object with mounting options (see below). Mount the app
|
||||||
@@ -58,6 +45,24 @@ instance somewhere in the DOM.
|
|||||||
|
|
||||||
- **`destroy()`**: destroys the application
|
- **`destroy()`**: destroys the application
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
The `config` object is an object with some of the following keys:
|
||||||
|
|
||||||
|
- **`env (object)`**: if given, this will be the shared `env` given to each component
|
||||||
|
- **`props (object)`**: the props given to the root component
|
||||||
|
- **`dev (boolean, default=false)`**: if `true`, the application is rendered in `dev`
|
||||||
|
mode, which activates some additional checks (in particular, the props validation
|
||||||
|
code is only performed in dev mode)
|
||||||
|
- **`test (boolean, default=false)`**: `test` mode is the same as `dev` mode, except
|
||||||
|
that Owll will not log a message to warn that Owl is in `dev` mode.
|
||||||
|
- **`translatableAttributes (string[])`**: a list of additional attributes that should
|
||||||
|
be translated (see [translations](translations.md))
|
||||||
|
- **`translateFn (function)`**: a function that will be called by owl to translate
|
||||||
|
templates (see [translations](translations.md))
|
||||||
|
- **`templates (string | xml document)`**: all the templates that will be used by
|
||||||
|
the components created by the application.
|
||||||
|
|
||||||
## `mount` helper
|
## `mount` helper
|
||||||
|
|
||||||
Note that there is a `mount` helper to do that in just a line:
|
Note that there is a `mount` helper to do that in just a line:
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ It is sometimes useful to add one (or more) specific keys to the environment,
|
|||||||
from the perspective of a specific component and its children. In that case, the
|
from the perspective of a specific component and its children. In that case, the
|
||||||
solution presented above will not work, since it sets the global environment.
|
solution presented above will not work, since it sets the global environment.
|
||||||
|
|
||||||
There is a hook for this situation: [`useSubEnv`](hooks.md#usesubenv).
|
There are two hooks for this situation: [`useSubEnv` and `useChildSubEnv`](hooks.md#usesubenv-and-usechildsubenv).
|
||||||
|
|
||||||
```js
|
```js
|
||||||
class SomeComponent extends Component {
|
class SomeComponent extends Component {
|
||||||
|
|||||||
@@ -1,25 +0,0 @@
|
|||||||
# 🦉 Event Bus 🦉
|
|
||||||
|
|
||||||
It is sometimes useful to use a `Bus` to communicate informations between various
|
|
||||||
parts of the code. Owl has a very simple bus class, which manages subscriptions,
|
|
||||||
triggering events, and callbacks.
|
|
||||||
|
|
||||||
```js
|
|
||||||
const bus = new owl.core.EventBus();
|
|
||||||
|
|
||||||
bus.on("some-event", null, function (...args) {
|
|
||||||
console.log(...args);
|
|
||||||
});
|
|
||||||
|
|
||||||
bus.trigger("some-event", 1, 2, 3);
|
|
||||||
// [1,2,3] will be logged to the console
|
|
||||||
```
|
|
||||||
|
|
||||||
Its API is:
|
|
||||||
|
|
||||||
| Method | Description |
|
|
||||||
| -------------------------------- | --------------------------------- |
|
|
||||||
| `on(eventType, owner, callback)` | add a listener |
|
|
||||||
| `off(eventType, owner)` | remove all listeners for an owner |
|
|
||||||
| `trigger(eventType, ...args)` | trigger an event |
|
|
||||||
| `clear` | remove all subscriptions |
|
|
||||||
+18
-7
@@ -8,7 +8,7 @@
|
|||||||
- [Other hooks](#other-hooks)
|
- [Other hooks](#other-hooks)
|
||||||
- [`useState`](#usestate)
|
- [`useState`](#usestate)
|
||||||
- [`useRef`](#useref)
|
- [`useRef`](#useref)
|
||||||
- [`useSubEnv`](#usesubenv)
|
- [`useSubEnv` and `useChildSubEnv`](#usesubenv-and-usechildsubenv)
|
||||||
- [`useExternalListener`](#useexternallistener)
|
- [`useExternalListener`](#useexternallistener)
|
||||||
- [`useComponent`](#usecomponent)
|
- [`useComponent`](#usecomponent)
|
||||||
- [`useEnv`](#useenv)
|
- [`useEnv`](#useenv)
|
||||||
@@ -152,14 +152,14 @@ this.ref2 = useRef("component_2");
|
|||||||
References are only guaranteed to be active while the parent component is mounted.
|
References are only guaranteed to be active while the parent component is mounted.
|
||||||
If this is not the case, accessing `el` or `comp` on it will return `null`.
|
If this is not the case, accessing `el` or `comp` on it will return `null`.
|
||||||
|
|
||||||
### `useSubEnv`
|
### `useSubEnv` and `useChildSubEnv`
|
||||||
|
|
||||||
The environment is sometimes useful to share some common information between
|
The environment is sometimes useful to share some common information between
|
||||||
all components. But sometimes, we want to _scope_ that knowledge to a subtree.
|
all components. But sometimes, we want to _scope_ that knowledge to a subtree.
|
||||||
|
|
||||||
For example, if we have a form view component, maybe we would like to make some
|
For example, if we have a form view component, maybe we would like to make some
|
||||||
`model` object available to all sub components, but not to the whole application.
|
`model` object available to all sub components, but not to the whole application.
|
||||||
This is where the `useSubEnv` hook may be useful: it lets a component add some
|
This is where the `useChildSubEnv` hook may be useful: it lets a component add some
|
||||||
information to the environment in a way that only its children
|
information to the environment in a way that only its children
|
||||||
can access it:
|
can access it:
|
||||||
|
|
||||||
@@ -167,15 +167,26 @@ can access it:
|
|||||||
class FormComponent extends Component {
|
class FormComponent extends Component {
|
||||||
setup() {
|
setup() {
|
||||||
const model = makeModel();
|
const model = makeModel();
|
||||||
|
// model will be available on this.env for this component and all children
|
||||||
useSubEnv({ model });
|
useSubEnv({ model });
|
||||||
|
// someKey will be available on this.env for all children
|
||||||
|
useChildSubEnv({ someKey: "value" });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
The `useSubEnv` takes one argument: an object which contains some key/value that
|
The `useSubEnv` and `useChildSubEnv` hooks take one argument: an object which
|
||||||
will be added to the parent environment. Note that it will extend, not replace
|
contains some key/value that will be added to the current environment. These hooks
|
||||||
the parent environment. And of course, the parent environment will not be
|
will create a new env object with the new information:
|
||||||
affected.
|
|
||||||
|
- `useSubEnv` will assign this new `env` to itself and to all children components
|
||||||
|
- `useChildSubEnv` will only assign this new `env` to all children components.
|
||||||
|
|
||||||
|
As usual in Owl, [environments](environment.md) created with these two hooks are
|
||||||
|
frozen, to prevent unwanted modifications.
|
||||||
|
|
||||||
|
Note that both these hooks can be called an arbitrary number of times. The `env`
|
||||||
|
will then be updated accordingly.
|
||||||
|
|
||||||
### `useExternalListener`
|
### `useExternalListener`
|
||||||
|
|
||||||
|
|||||||
@@ -168,11 +168,15 @@ For each key, a `prop` definition is either a boolean, a constructor, a list of
|
|||||||
- `shape`: if the type was `Object`, then the `shape` key describes the interface of the object. If it is not set, then we only validate the object, not its elements,
|
- `shape`: if the type was `Object`, then the `shape` key describes the interface of the object. If it is not set, then we only validate the object, not its elements,
|
||||||
- `validate`: this is a function which should return a boolean to determine if
|
- `validate`: this is a function which should return a boolean to determine if
|
||||||
the value is valid or not. Useful for custom validation logic.
|
the value is valid or not. Useful for custom validation logic.
|
||||||
|
- `optional`: if true, the prop is not mandatory
|
||||||
|
|
||||||
There is a special `*` prop that means that additional prop are allowed. This is
|
There is a special `*` prop that means that additional prop are allowed. This is
|
||||||
sometimes useful for generic components that will propagate some or all their
|
sometimes useful for generic components that will propagate some or all their
|
||||||
props to their child components.
|
props to their child components.
|
||||||
|
|
||||||
|
Note that default values cannot be defined for a mandatory props. Doing so will
|
||||||
|
result in a prop validation error.
|
||||||
|
|
||||||
Examples:
|
Examples:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
@@ -190,7 +194,8 @@ class ComponentB extends owl.Component {
|
|||||||
element: {type: Object, shape: {id: Boolean, text: String }
|
element: {type: Object, shape: {id: Boolean, text: String }
|
||||||
},
|
},
|
||||||
date: Date,
|
date: Date,
|
||||||
combinedVal: [Number, Boolean]
|
combinedVal: [Number, Boolean],
|
||||||
|
optionalProp: { type: Number, optional: true }
|
||||||
};
|
};
|
||||||
|
|
||||||
...
|
...
|
||||||
|
|||||||
@@ -5,6 +5,8 @@
|
|||||||
- [Overview](#overview)
|
- [Overview](#overview)
|
||||||
- [`useState`](#usestate)
|
- [`useState`](#usestate)
|
||||||
- [`reactive`](#reactive)
|
- [`reactive`](#reactive)
|
||||||
|
- [`markRaw`](#markraw)
|
||||||
|
- [`toRaw`](#toraw)
|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
@@ -78,3 +80,47 @@ obj2.b = 3; // log 'observer1' and 'observer2'
|
|||||||
|
|
||||||
Obviously, one can use `reactive` on the result of a `useState` if wanted, this
|
Obviously, one can use `reactive` on the result of a `useState` if wanted, this
|
||||||
is the proper way to watch for some state changes.
|
is the proper way to watch for some state changes.
|
||||||
|
|
||||||
|
## `markRaw`
|
||||||
|
|
||||||
|
Marks an object so that it is ignored by the reactivity system. This function returns its argument.
|
||||||
|
|
||||||
|
```js
|
||||||
|
const someObject = markRaw(...);
|
||||||
|
const state = useState({
|
||||||
|
a: 1,
|
||||||
|
obj: someObject
|
||||||
|
});
|
||||||
|
// here, state.obj === someObject
|
||||||
|
```
|
||||||
|
|
||||||
|
This is useful in some rare cases. For example, some complex and large object such
|
||||||
|
that going through the reactivity system may cause a non trivial performance slowdown.
|
||||||
|
|
||||||
|
However, use this function with caution: this is an escape hatch from the reactivity
|
||||||
|
system, and as such, using it may cause subtle and unintended issues!
|
||||||
|
|
||||||
|
## `toRaw`
|
||||||
|
|
||||||
|
Given a reactive object, this function returns the underlying, non-reactive,
|
||||||
|
corresponding object.
|
||||||
|
|
||||||
|
```js
|
||||||
|
// in setup
|
||||||
|
const state = useState({ value: 1 });
|
||||||
|
|
||||||
|
// later:
|
||||||
|
const rawState = toRaw(this.state);
|
||||||
|
rawState.value = 3; // will NOT be picked up by the reactivity system!!!
|
||||||
|
```
|
||||||
|
|
||||||
|
Here again, this is useful in some situations where we want to explicitely bypass
|
||||||
|
Owl, but using this function means that the responsability of coordinating
|
||||||
|
state update is given to the user code, instead of Owl. Subtle bugs may arise!
|
||||||
|
|
||||||
|
Also, normal (non-reactive objects) will be directly returned by `toRaw`:
|
||||||
|
|
||||||
|
```js
|
||||||
|
const obj = { a: 1 };
|
||||||
|
console.log(toRaw(obj) === obj); // true
|
||||||
|
```
|
||||||
|
|||||||
+28
-12
@@ -87,8 +87,8 @@ grandparent of the slot content).
|
|||||||
|
|
||||||
## Default Slot
|
## Default Slot
|
||||||
|
|
||||||
The first element inside the component which is not a named slot will
|
All elements inside the component which are not a named slot will be treated as
|
||||||
be considered the `default` slot. For example:
|
part of the content of the `default` slot. For example:
|
||||||
|
|
||||||
```xml
|
```xml
|
||||||
<div t-name="Parent">
|
<div t-name="Parent">
|
||||||
@@ -110,6 +110,7 @@ One can mix default slot and named slots:
|
|||||||
default content
|
default content
|
||||||
<t t-set-slot="footer">
|
<t t-set-slot="footer">
|
||||||
content for footer slot here
|
content for footer slot here
|
||||||
|
</t>
|
||||||
</Child>
|
</Child>
|
||||||
</div>
|
</div>
|
||||||
```
|
```
|
||||||
@@ -209,30 +210,45 @@ use this `Notebook` component:
|
|||||||
</Notebook>
|
</Notebook>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Slot params works like normal props, so one can use the `.bind` suffix to
|
||||||
|
bind a function if needed.
|
||||||
|
|
||||||
## Slot scopes
|
## Slot scopes
|
||||||
|
|
||||||
For other kind of advanced use cases, the content of a slot may depends on some
|
For other kinds of advanced use cases, the content of a slot may depends on some
|
||||||
specific information specific to the generic component. This is the opposite
|
information specific to the generic component. This is the opposite of the slot
|
||||||
of the slot params.
|
params.
|
||||||
|
|
||||||
To solve this kind of problems, one can use the `t-set-scope` directive along
|
To solve this kind of problems, one can use the `t-slot-scope` directive along
|
||||||
with the `t-set-slot`. This defines the name of a variable that can access
|
with the `t-set-slot`. This defines the name of a variable that can access
|
||||||
everything given by the child component:
|
everything given by the child component:
|
||||||
|
|
||||||
```xml
|
```xml
|
||||||
<div>
|
<MyComponent>
|
||||||
<t t-set-slot="foo" t-set-scope="scope">
|
<t t-set-slot="foo" t-slot-scope="scope">
|
||||||
content
|
content
|
||||||
<t t-esc="scope.bool"/>
|
<t t-esc="scope.bool"/>
|
||||||
<t t-esc="scope.num"/>
|
<t t-esc="scope.num"/>
|
||||||
</t>
|
</t>
|
||||||
</div>
|
</MyComponent>
|
||||||
```
|
```
|
||||||
|
|
||||||
And the child component that includes the slot can provide values like this:
|
And the child component that includes the slot can provide values like this:
|
||||||
|
|
||||||
```xml
|
```xml
|
||||||
<div>
|
<t t-slot="foo" bool="other_var" num="5">
|
||||||
<t t-slot="foo" bool="other_var" num="5">
|
|
||||||
</div>
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
In the case of the default slot, you may declare the slot scope directly on the
|
||||||
|
component itself:
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<MyComponent t-slot-scope="scope">
|
||||||
|
content
|
||||||
|
<t t-esc="scope.bool"/>
|
||||||
|
<t t-esc="scope.num"/>
|
||||||
|
</MyComponent>
|
||||||
|
```
|
||||||
|
|
||||||
|
Slot values works like normal props, so one can use the `.bind` suffix to
|
||||||
|
bind a function if needed.
|
||||||
|
|||||||
@@ -21,6 +21,7 @@
|
|||||||
- [Fragments](#fragments)
|
- [Fragments](#fragments)
|
||||||
- [Inline templates](#inline-templates)
|
- [Inline templates](#inline-templates)
|
||||||
- [Rendering svg](#rendering-svg)
|
- [Rendering svg](#rendering-svg)
|
||||||
|
- [Restrictions](#restrictions)
|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
@@ -674,7 +675,19 @@ This `RootNode` component will then display a live SVG representation of the
|
|||||||
graph described by the `graph` property. Note that there is a recursive structure
|
graph described by the `graph` property. Note that there is a recursive structure
|
||||||
here: the `Node` component uses itself as a subcomponent.
|
here: the `Node` component uses itself as a subcomponent.
|
||||||
|
|
||||||
Note that since SVG needs to be handled in a specific way (its namespace needs
|
**Important note:** Owl needs to properly set the namespace for each svg elements.
|
||||||
to be properly set), there is a small constraint for Owl components: if an owl
|
Since Owl compile each template separately, it is not able to determine easily
|
||||||
component is supposed to be a part of an svg graph, then its root node needs to
|
if a template is supposed to be included in a svg namespace or not. Therefore,
|
||||||
be a `g` tag, so Owl can properly set the namespace.
|
Owl depends on a heuristic: if a tag is either `svg`, `g` or `path`, then it will
|
||||||
|
be considered as svg. In practice, this means that each component or each sub
|
||||||
|
templates (included with `t-call`) should have one of these tag as root tag.
|
||||||
|
|
||||||
|
## Restrictions
|
||||||
|
|
||||||
|
Note that Owl templates forbid the use of tag and or attributes starting with
|
||||||
|
the `block-` string. This restriction prevents name collision with the internal
|
||||||
|
code of Owl.
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<div><block-1>this will not be accepted by Owl</block-1></div>
|
||||||
|
```
|
||||||
|
|||||||
@@ -54,6 +54,17 @@ For example, one may want to also translate `data-title` attributes. To do that,
|
|||||||
we can define additional attributes with the `translatableAttributes` option:
|
we can define additional attributes with the `translatableAttributes` option:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
const app = new App(Root, { templates, tranaslateFn, translatalbeAttributes: ["data-title"] });
|
const app = new App(Root, { templates, tranaslateFn, translatableAttributes: ["data-title"] });
|
||||||
// ...
|
// ...
|
||||||
```
|
```
|
||||||
|
|
||||||
|
It is also possible to remove an attribute from the default list by prefixing it with `-`:
|
||||||
|
|
||||||
|
```js
|
||||||
|
const app = new App(Root, {
|
||||||
|
templates,
|
||||||
|
tranaslateFn,
|
||||||
|
translatableAttributes: ["data-title", "-title"],
|
||||||
|
});
|
||||||
|
// data-title attribute will be translated, but not title attribute...
|
||||||
|
```
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@odoo/owl",
|
"name": "@odoo/owl",
|
||||||
"version": "2.0.0-alpha1",
|
"version": "2.0.0-alpha.2",
|
||||||
"description": "Odoo Web Library (OWL)",
|
"description": "Odoo Web Library (OWL)",
|
||||||
"main": "dist/owl.cjs.js",
|
"main": "dist/owl.cjs.js",
|
||||||
"browser": "dist/owl.iife.js",
|
"browser": "dist/owl.iife.js",
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
# 🦉 OWL Roadmap 🦉
|
# 🦉 OWL Roadmap 🦉
|
||||||
|
|
||||||
- Current version: 1.4.7
|
- Current version: 1.4.10
|
||||||
- Status: stable
|
- Status: stable
|
||||||
|
|
||||||
This roadmap is only an attempt at predicting Owl's future. Everything may
|
This roadmap is only an attempt at predicting Owl's future. Everything may
|
||||||
|
|||||||
+38
-32
@@ -1,9 +1,10 @@
|
|||||||
import { Component } from "../component/component";
|
import { Component, ComponentConstructor } from "../component/component";
|
||||||
import { ComponentNode } from "../component/component_node";
|
import { ComponentNode } from "../component/component_node";
|
||||||
import { MountOptions } from "../component/fibers";
|
import { MountOptions } from "../component/fibers";
|
||||||
import { Scheduler } from "../component/scheduler";
|
import { Scheduler } from "../component/scheduler";
|
||||||
import { TemplateSet, TemplateSetConfig } from "./template_set";
|
import { TemplateSet, TemplateSetConfig } from "./template_set";
|
||||||
import { nodeErrorHandlers } from "../component/error_handling";
|
import { nodeErrorHandlers } from "../component/error_handling";
|
||||||
|
import { validateTarget } from "../utils";
|
||||||
|
|
||||||
// reimplement dev mode stuff see last change in 0f7a8289a6fb8387c3c1af41c6664b2a8448758f
|
// reimplement dev mode stuff see last change in 0f7a8289a6fb8387c3c1af41c6664b2a8448758f
|
||||||
|
|
||||||
@@ -11,52 +12,57 @@ export interface Env {
|
|||||||
[key: string]: any;
|
[key: string]: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AppConfig extends TemplateSetConfig {
|
export interface AppConfig<P, E> extends TemplateSetConfig {
|
||||||
env?: Env;
|
props?: P;
|
||||||
props?: any;
|
env?: E;
|
||||||
|
test?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DEV_MSG = `Owl is running in 'dev' mode.
|
export const DEV_MSG = () => {
|
||||||
|
const hash = (window as any).owl ? (window as any).owl.__info__.hash : "master";
|
||||||
|
|
||||||
|
return `Owl is running in 'dev' mode.
|
||||||
|
|
||||||
This is not suitable for production use.
|
This is not suitable for production use.
|
||||||
See https://github.com/odoo/owl/blob/master/doc/reference/config.md#mode for more information.`;
|
See https://github.com/odoo/owl/blob/${hash}/doc/reference/app.md#configuration for more information.`;
|
||||||
|
};
|
||||||
|
|
||||||
export class App<T extends typeof Component = any> extends TemplateSet {
|
export class App<
|
||||||
Root: T;
|
T extends abstract new (...args: any) => any = any,
|
||||||
props: any;
|
P = any,
|
||||||
env: Env;
|
E = any
|
||||||
|
> extends TemplateSet {
|
||||||
|
static validateTarget = validateTarget;
|
||||||
|
|
||||||
|
Root: ComponentConstructor<P, E>;
|
||||||
|
props: P;
|
||||||
|
env: E;
|
||||||
scheduler = new Scheduler();
|
scheduler = new Scheduler();
|
||||||
root: ComponentNode | null = null;
|
root: ComponentNode<P, E> | null = null;
|
||||||
|
|
||||||
constructor(Root: T, config: AppConfig = {}) {
|
constructor(Root: ComponentConstructor<P, E>, config: AppConfig<P, E> = {}) {
|
||||||
super(config);
|
super(config);
|
||||||
this.Root = Root;
|
this.Root = Root;
|
||||||
if (config.dev) {
|
if (config.test) {
|
||||||
console.info(DEV_MSG);
|
this.dev = true;
|
||||||
|
}
|
||||||
|
if (this.dev && !config.test) {
|
||||||
|
console.info(DEV_MSG());
|
||||||
}
|
}
|
||||||
const descrs = Object.getOwnPropertyDescriptors(config.env || {});
|
const descrs = Object.getOwnPropertyDescriptors(config.env || {});
|
||||||
this.env = Object.freeze(Object.defineProperties({}, descrs));
|
this.env = Object.freeze(Object.defineProperties({}, descrs)) as E;
|
||||||
this.props = config.props || {};
|
this.props = config.props || ({} as P);
|
||||||
}
|
}
|
||||||
|
|
||||||
mount(target: HTMLElement, options?: MountOptions): Promise<InstanceType<T>> {
|
mount(target: HTMLElement, options?: MountOptions): Promise<Component<P, E> & InstanceType<T>> {
|
||||||
this.checkTarget(target);
|
App.validateTarget(target);
|
||||||
const node = this.makeNode(this.Root, this.props);
|
const node = this.makeNode(this.Root, this.props);
|
||||||
const prom = this.mountNode(node, target, options);
|
const prom = this.mountNode(node, target, options);
|
||||||
this.root = node;
|
this.root = node;
|
||||||
return prom;
|
return prom;
|
||||||
}
|
}
|
||||||
|
|
||||||
checkTarget(target: HTMLElement) {
|
makeNode(Component: ComponentConstructor, props: any): ComponentNode {
|
||||||
if (!(target instanceof HTMLElement)) {
|
|
||||||
throw new Error("Cannot mount component: the target is not a valid DOM element");
|
|
||||||
}
|
|
||||||
if (!document.body.contains(target)) {
|
|
||||||
throw new Error("Cannot mount a component on a detached dom node");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
makeNode(Component: T, props: any): ComponentNode {
|
|
||||||
return new ComponentNode(Component, props, this);
|
return new ComponentNode(Component, props, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -96,10 +102,10 @@ export class App<T extends typeof Component = any> extends TemplateSet {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function mount<T extends typeof Component>(
|
export async function mount<T extends abstract new (...args: any) => any = any, P = any, E = any>(
|
||||||
C: T,
|
C: T & ComponentConstructor<P, E>,
|
||||||
target: HTMLElement,
|
target: HTMLElement,
|
||||||
config: AppConfig & MountOptions = {}
|
config: AppConfig<P, E> & MountOptions = {}
|
||||||
): Promise<InstanceType<T>> {
|
): Promise<Component<P, E> & InstanceType<T>> {
|
||||||
return new App(C, config).mount(target, config);
|
return new App(C, config).mount(target, config);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -135,7 +135,7 @@ export function safeOutput(value: any): ReturnType<typeof toggler> {
|
|||||||
} else if (value instanceof LazyValue) {
|
} else if (value instanceof LazyValue) {
|
||||||
safeKey = `lazy_value`;
|
safeKey = `lazy_value`;
|
||||||
block = value.evaluate();
|
block = value.evaluate();
|
||||||
} else if (typeof value === "string") {
|
} else if (value instanceof String || typeof value === "string") {
|
||||||
safeKey = "string_unsafe";
|
safeKey = "string_unsafe";
|
||||||
block = text(value);
|
block = text(value);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
+17
-13
@@ -1,11 +1,11 @@
|
|||||||
import { createBlock, html, list, multi, text, toggler, comment } from "../blockdom";
|
import { createBlock, html, list, multi, text, toggler, comment } from "../blockdom";
|
||||||
import { compile, Template } from "../compiler";
|
import { compile, Template } from "../compiler";
|
||||||
import { component } from "../component/component_node";
|
import { component, getCurrent } from "../component/component_node";
|
||||||
import { UTILS } from "./template_helpers";
|
import { UTILS } from "./template_helpers";
|
||||||
|
|
||||||
const bdom = { text, createBlock, list, multi, html, toggler, component, comment };
|
const bdom = { text, createBlock, list, multi, html, toggler, component, comment };
|
||||||
|
|
||||||
export const globalTemplates: { [key: string]: string | Node } = {};
|
export const globalTemplates: { [key: string]: string | Element } = {};
|
||||||
|
|
||||||
function parseXML(xml: string): Document {
|
function parseXML(xml: string): Document {
|
||||||
const parser = new DOMParser();
|
const parser = new DOMParser();
|
||||||
@@ -55,7 +55,7 @@ export class TemplateSet {
|
|||||||
const template = this.getTemplate(subTemplate);
|
const template = this.getTemplate(subTemplate);
|
||||||
return toggler(subTemplate, template.call(owner, ctx, parent, key));
|
return toggler(subTemplate, template.call(owner, ctx, parent, key));
|
||||||
},
|
},
|
||||||
getTemplate: (name: string, nameSpace?: string) => this.getTemplate(name, nameSpace),
|
getTemplate: (name: string) => this.getTemplate(name),
|
||||||
});
|
});
|
||||||
|
|
||||||
constructor(config: TemplateSetConfig = {}) {
|
constructor(config: TemplateSetConfig = {}) {
|
||||||
@@ -67,7 +67,11 @@ export class TemplateSet {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
addTemplate(name: string, template: string | Node, options: { allowDuplicate?: boolean } = {}) {
|
addTemplate(
|
||||||
|
name: string,
|
||||||
|
template: string | Element,
|
||||||
|
options: { allowDuplicate?: boolean } = {}
|
||||||
|
) {
|
||||||
if (name in this.rawTemplates && !options.allowDuplicate) {
|
if (name in this.rawTemplates && !options.allowDuplicate) {
|
||||||
throw new Error(`Template ${name} already defined`);
|
throw new Error(`Template ${name} already defined`);
|
||||||
}
|
}
|
||||||
@@ -82,20 +86,22 @@ export class TemplateSet {
|
|||||||
xml = xml instanceof Document ? xml : parseXML(xml);
|
xml = xml instanceof Document ? xml : parseXML(xml);
|
||||||
for (const template of xml.querySelectorAll("[t-name]")) {
|
for (const template of xml.querySelectorAll("[t-name]")) {
|
||||||
const name = template.getAttribute("t-name")!;
|
const name = template.getAttribute("t-name")!;
|
||||||
template.removeAttribute("t-name");
|
|
||||||
this.addTemplate(name, template, options);
|
this.addTemplate(name, template, options);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getTemplate(name: string, nameSpace?: string): Template {
|
getTemplate(name: string): Template {
|
||||||
|
|
||||||
if (!(name in this.templates)) {
|
if (!(name in this.templates)) {
|
||||||
const rawTemplate = this.rawTemplates[name];
|
const rawTemplate = this.rawTemplates[name];
|
||||||
if (rawTemplate === undefined) {
|
if (rawTemplate === undefined) {
|
||||||
throw new Error(`Missing template: "${name}"`);
|
let extraInfo = "";
|
||||||
|
try {
|
||||||
|
const componentName = getCurrent().component.constructor.name;
|
||||||
|
extraInfo = ` (for component "${componentName}")`;
|
||||||
|
} catch {}
|
||||||
|
throw new Error(`Missing template: "${name}"${extraInfo}`);
|
||||||
}
|
}
|
||||||
console.log("getTemplate", nameSpace);
|
const templateFn = this._compileTemplate(name, rawTemplate);
|
||||||
const templateFn = this._compileTemplate(name, rawTemplate, nameSpace);
|
|
||||||
// first add a function to lazily get the template, in case there is a
|
// first add a function to lazily get the template, in case there is a
|
||||||
// recursive call to the template name
|
// recursive call to the template name
|
||||||
const templates = this.templates;
|
const templates = this.templates;
|
||||||
@@ -108,14 +114,12 @@ export class TemplateSet {
|
|||||||
return this.templates[name];
|
return this.templates[name];
|
||||||
}
|
}
|
||||||
|
|
||||||
_compileTemplate(name: string, template: string | Node, nameSpace?: string) {
|
_compileTemplate(name: string, template: string | Element) {
|
||||||
console.log("CompileTpm", nameSpace)
|
|
||||||
return compile(template, {
|
return compile(template, {
|
||||||
name,
|
name,
|
||||||
dev: this.dev,
|
dev: this.dev,
|
||||||
translateFn: this.translateFn,
|
translateFn: this.translateFn,
|
||||||
translatableAttributes: this.translatableAttributes,
|
translatableAttributes: this.translatableAttributes,
|
||||||
nameSpace: nameSpace,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -139,7 +139,7 @@ export function updateClass(this: HTMLElement, val: any, oldVal: any) {
|
|||||||
|
|
||||||
export function makePropSetter(name: string): Setter<HTMLElement> {
|
export function makePropSetter(name: string): Setter<HTMLElement> {
|
||||||
return function setProp(this: HTMLElement, value: any) {
|
return function setProp(this: HTMLElement, value: any) {
|
||||||
(this as any)[name] = value;
|
(this as any)[name] = value || "";
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -299,12 +299,13 @@ interface BlockCtx {
|
|||||||
locations: IndexedLocation[];
|
locations: IndexedLocation[];
|
||||||
children: Child[];
|
children: Child[];
|
||||||
cbRefs: number[];
|
cbRefs: number[];
|
||||||
|
refList: (() => void)[][];
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildContext(tree: IntermediateTree, ctx?: BlockCtx, fromIdx?: number): BlockCtx {
|
function buildContext(tree: IntermediateTree, ctx?: BlockCtx, fromIdx?: number): BlockCtx {
|
||||||
if (!ctx) {
|
if (!ctx) {
|
||||||
const children = new Array(tree.info.filter((v) => v.type === "child").length);
|
const children = new Array(tree.info.filter((v) => v.type === "child").length);
|
||||||
ctx = { collectors: [], locations: [], children, cbRefs: [], refN: tree.refN };
|
ctx = { collectors: [], locations: [], children, cbRefs: [], refN: tree.refN, refList: [] };
|
||||||
fromIdx = 0;
|
fromIdx = 0;
|
||||||
}
|
}
|
||||||
if (tree.refN) {
|
if (tree.refN) {
|
||||||
@@ -408,11 +409,11 @@ function updateCtx(ctx: BlockCtx, tree: IntermediateTree) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "ref":
|
case "ref":
|
||||||
ctx.cbRefs.push(info.idx);
|
const index = ctx.cbRefs.push(info.idx) - 1;
|
||||||
ctx.locations.push({
|
ctx.locations.push({
|
||||||
idx: info.idx,
|
idx: info.idx,
|
||||||
refIdx: info.refIdx!,
|
refIdx: info.refIdx!,
|
||||||
setData: setRef,
|
setData: makeRefSetter(index, ctx.refList),
|
||||||
updateData: NO_OP,
|
updateData: NO_OP,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -426,12 +427,21 @@ function buildBlock(template: HTMLElement, ctx: BlockCtx): BlockType {
|
|||||||
let B = createBlockClass(template, ctx);
|
let B = createBlockClass(template, ctx);
|
||||||
|
|
||||||
if (ctx.cbRefs.length) {
|
if (ctx.cbRefs.length) {
|
||||||
const refs = ctx.cbRefs;
|
const cbRefs = ctx.cbRefs;
|
||||||
|
const refList = ctx.refList;
|
||||||
|
let cbRefsNumber = cbRefs.length;
|
||||||
B = class extends B {
|
B = class extends B {
|
||||||
|
mount(parent: HTMLElement, afterNode: Node | null) {
|
||||||
|
refList.push(new Array(cbRefsNumber));
|
||||||
|
super.mount(parent, afterNode);
|
||||||
|
for (let cbRef of refList.pop()!) {
|
||||||
|
cbRef();
|
||||||
|
}
|
||||||
|
}
|
||||||
remove() {
|
remove() {
|
||||||
super.remove();
|
super.remove();
|
||||||
for (let ref of refs) {
|
for (let cbRef of cbRefs) {
|
||||||
let fn = (this as any).data[ref];
|
let fn = (this as any).data[cbRef];
|
||||||
fn(null);
|
fn(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -476,12 +486,12 @@ function createBlockClass(template: HTMLElement, ctx: BlockCtx): BlockClass {
|
|||||||
const nodeInsertBefore = nodeProto.insertBefore;
|
const nodeInsertBefore = nodeProto.insertBefore;
|
||||||
const elementRemove = elementProto.remove;
|
const elementRemove = elementProto.remove;
|
||||||
|
|
||||||
return class Block {
|
class Block {
|
||||||
el: HTMLElement | undefined;
|
el: HTMLElement | undefined;
|
||||||
refs: Node[] | undefined;
|
|
||||||
data: any[] | undefined;
|
|
||||||
parentEl?: HTMLElement | undefined;
|
parentEl?: HTMLElement | undefined;
|
||||||
|
data: any[] | undefined;
|
||||||
children?: (VNode | undefined)[];
|
children?: (VNode | undefined)[];
|
||||||
|
refs: Node[] | undefined;
|
||||||
|
|
||||||
constructor(data?: any[]) {
|
constructor(data?: any[]) {
|
||||||
this.data = data;
|
this.data = data;
|
||||||
@@ -502,46 +512,62 @@ function createBlockClass(template: HTMLElement, ctx: BlockCtx): BlockClass {
|
|||||||
nodeInsertBefore.call(this.parentEl, this.el!, target);
|
nodeInsertBefore.call(this.parentEl, this.el!, target);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
toString() {
|
||||||
|
const div = document.createElement("div");
|
||||||
|
this.mount(div, null);
|
||||||
|
return div.innerHTML;
|
||||||
|
}
|
||||||
|
|
||||||
mount(parent: HTMLElement, afterNode: Node | null) {
|
mount(parent: HTMLElement, afterNode: Node | null) {
|
||||||
const el = nodeCloneNode.call(template, true);
|
const el = nodeCloneNode.call(template, true) as HTMLElement;
|
||||||
nodeInsertBefore.call(parent, el, afterNode);
|
nodeInsertBefore.call(parent, el, afterNode);
|
||||||
if (isDynamic) {
|
this.el = el;
|
||||||
// collecting references
|
this.parentEl = parent;
|
||||||
const refs: Node[] = new Array(refN);
|
}
|
||||||
this.refs = refs;
|
patch(other: Block, withBeforeRemove: boolean) {}
|
||||||
refs[0] = el;
|
}
|
||||||
for (let i = 0; i < colN; i++) {
|
|
||||||
const w = collectors[i];
|
|
||||||
refs[w.idx] = w.getVal.call(refs[w.prevIdx]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// applying data to all update points
|
if (isDynamic) {
|
||||||
if (locN) {
|
Block.prototype.mount = function mount(parent: HTMLElement, afterNode: Node | null) {
|
||||||
const data = this.data!;
|
const el = nodeCloneNode.call(template, true);
|
||||||
for (let i = 0; i < locN; i++) {
|
// collecting references
|
||||||
const loc = locations[i];
|
const refs: Node[] = new Array(refN);
|
||||||
loc.setData.call(refs[loc.refIdx], data[i]);
|
this.refs = refs;
|
||||||
}
|
refs[0] = el;
|
||||||
}
|
for (let i = 0; i < colN; i++) {
|
||||||
|
const w = collectors[i];
|
||||||
|
refs[w.idx] = w.getVal.call(refs[w.prevIdx]);
|
||||||
|
}
|
||||||
|
|
||||||
// preparing all children
|
// applying data to all update points
|
||||||
if (childN) {
|
if (locN) {
|
||||||
const children = this.children;
|
const data = this.data!;
|
||||||
for (let i = 0; i < childN; i++) {
|
for (let i = 0; i < locN; i++) {
|
||||||
const child = children![i];
|
const loc = locations[i];
|
||||||
if (child) {
|
loc.setData.call(refs[loc.refIdx], data[i]);
|
||||||
const loc = childrenLocs[i];
|
}
|
||||||
const afterNode = loc.afterRefIdx ? refs[loc.afterRefIdx] : null;
|
}
|
||||||
child.isOnlyChild = loc.isOnlyChild;
|
|
||||||
child.mount(refs[loc.parentRefIdx] as any, afterNode);
|
nodeInsertBefore.call(parent, el, afterNode);
|
||||||
}
|
|
||||||
|
// preparing all children
|
||||||
|
if (childN) {
|
||||||
|
const children = this.children;
|
||||||
|
for (let i = 0; i < childN; i++) {
|
||||||
|
const child = children![i];
|
||||||
|
if (child) {
|
||||||
|
const loc = childrenLocs[i];
|
||||||
|
const afterNode = loc.afterRefIdx ? refs[loc.afterRefIdx] : null;
|
||||||
|
child.isOnlyChild = loc.isOnlyChild;
|
||||||
|
child.mount(refs[loc.parentRefIdx] as any, afterNode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.el = el as HTMLElement;
|
this.el = el as HTMLElement;
|
||||||
this.parentEl = parent;
|
this.parentEl = parent;
|
||||||
}
|
};
|
||||||
patch(other: Block, withBeforeRemove: boolean) {
|
|
||||||
|
Block.prototype.patch = function patch(other: Block, withBeforeRemove: boolean) {
|
||||||
if (this === other) {
|
if (this === other) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -586,19 +612,17 @@ function createBlockClass(template: HTMLElement, ctx: BlockCtx): BlockClass {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
toString() {
|
}
|
||||||
const div = document.createElement("div");
|
return Block;
|
||||||
this.mount(div, null);
|
|
||||||
return div.innerHTML;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function setText(this: Text, value: any) {
|
function setText(this: Text, value: any) {
|
||||||
characterDataSetData.call(this, toText(value));
|
characterDataSetData.call(this, toText(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
function setRef(this: HTMLElement, fn: any) {
|
function makeRefSetter(index: number, refs: (() => void)[][]): Setter<HTMLElement> {
|
||||||
fn(this);
|
return function setRef(this: HTMLElement, fn: any) {
|
||||||
|
refs[refs.length - 1][index] = () => fn(this);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,6 +61,7 @@ class VHtml {
|
|||||||
// remove current content
|
// remove current content
|
||||||
this.remove();
|
this.remove();
|
||||||
this.content = content;
|
this.content = content;
|
||||||
|
this.html = other.html;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,11 +9,11 @@ const characterDataSetData = getDescriptor(characterDataProto, "data").set!;
|
|||||||
const nodeRemoveChild = nodeProto.removeChild;
|
const nodeRemoveChild = nodeProto.removeChild;
|
||||||
|
|
||||||
abstract class VSimpleNode {
|
abstract class VSimpleNode {
|
||||||
text: string;
|
text: string | String;
|
||||||
parentEl?: HTMLElement | undefined;
|
parentEl?: HTMLElement | undefined;
|
||||||
el?: any;
|
el?: any;
|
||||||
|
|
||||||
constructor(text: string) {
|
constructor(text: string | String) {
|
||||||
this.text = text;
|
this.text = text;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,7 +65,7 @@ class VComment extends VSimpleNode {
|
|||||||
patch() {}
|
patch() {}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function text(str: string): VNode<VText> {
|
export function text(str: string | String): VNode<VText> {
|
||||||
return new VText(str);
|
return new VText(str);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -33,7 +33,6 @@ export interface Config {
|
|||||||
export interface CodeGenOptions extends Config {
|
export interface CodeGenOptions extends Config {
|
||||||
hasSafeContext?: boolean;
|
hasSafeContext?: boolean;
|
||||||
name?: string;
|
name?: string;
|
||||||
nameSpace?: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// using a non-html document so that <inner/outer>HTML serializes as XML instead
|
// using a non-html document so that <inner/outer>HTML serializes as XML instead
|
||||||
@@ -129,6 +128,7 @@ interface Context {
|
|||||||
translate: boolean;
|
translate: boolean;
|
||||||
tKeyExpr: string | null;
|
tKeyExpr: string | null;
|
||||||
nameSpace?: string;
|
nameSpace?: string;
|
||||||
|
tModelSelectedExpr?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
function createContext(parentCtx: Context, params?: Partial<Context>) {
|
function createContext(parentCtx: Context, params?: Partial<Context>) {
|
||||||
@@ -140,6 +140,7 @@ function createContext(parentCtx: Context, params?: Partial<Context>) {
|
|||||||
translate: parentCtx.translate,
|
translate: parentCtx.translate,
|
||||||
tKeyExpr: null,
|
tKeyExpr: null,
|
||||||
nameSpace: parentCtx.nameSpace,
|
nameSpace: parentCtx.nameSpace,
|
||||||
|
tModelSelectedExpr: parentCtx.tModelSelectedExpr,
|
||||||
},
|
},
|
||||||
params
|
params
|
||||||
);
|
);
|
||||||
@@ -213,21 +214,28 @@ export class CodeGenerator {
|
|||||||
templateName?: string;
|
templateName?: string;
|
||||||
dev: boolean;
|
dev: boolean;
|
||||||
translateFn: (s: string) => string;
|
translateFn: (s: string) => string;
|
||||||
translatableAttributes: string[];
|
translatableAttributes: string[] = TRANSLATABLE_ATTRS;
|
||||||
ast: AST;
|
ast: AST;
|
||||||
staticCalls: { id: string; template: string, nameSpace?: string }[] = [];
|
staticCalls: { id: string; template: string }[] = [];
|
||||||
helpers: Set<string> = new Set();
|
helpers: Set<string> = new Set();
|
||||||
nameSpace?: string;
|
|
||||||
|
|
||||||
constructor(ast: AST, options: CodeGenOptions) {
|
constructor(ast: AST, options: CodeGenOptions) {
|
||||||
this.translateFn = options.translateFn || ((s: string) => s);
|
this.translateFn = options.translateFn || ((s: string) => s);
|
||||||
this.translatableAttributes = options.translatableAttributes || TRANSLATABLE_ATTRS;
|
if (options.translatableAttributes) {
|
||||||
|
const attrs = new Set(TRANSLATABLE_ATTRS);
|
||||||
|
for (let attr of options.translatableAttributes) {
|
||||||
|
if (attr.startsWith("-")) {
|
||||||
|
attrs.delete(attr.slice(1));
|
||||||
|
} else {
|
||||||
|
attrs.add(attr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.translatableAttributes = [...attrs];
|
||||||
|
}
|
||||||
this.hasSafeContext = options.hasSafeContext || false;
|
this.hasSafeContext = options.hasSafeContext || false;
|
||||||
this.dev = options.dev || false;
|
this.dev = options.dev || false;
|
||||||
this.ast = ast;
|
this.ast = ast;
|
||||||
this.templateName = options.name;
|
this.templateName = options.name;
|
||||||
this.nameSpace = options.nameSpace;
|
|
||||||
console.log('ThisNS', this.nameSpace)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
generateCode(): string {
|
generateCode(): string {
|
||||||
@@ -242,7 +250,6 @@ export class CodeGenerator {
|
|||||||
isLast: true,
|
isLast: true,
|
||||||
translate: true,
|
translate: true,
|
||||||
tKeyExpr: null,
|
tKeyExpr: null,
|
||||||
nameSpace: this.nameSpace,
|
|
||||||
});
|
});
|
||||||
// define blocks and utility functions
|
// define blocks and utility functions
|
||||||
let mainCode = [
|
let mainCode = [
|
||||||
@@ -255,12 +262,8 @@ export class CodeGenerator {
|
|||||||
mainCode.push(`// Template name: "${this.templateName}"`);
|
mainCode.push(`// Template name: "${this.templateName}"`);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let { id, template, nameSpace } of this.staticCalls) {
|
for (let { id, template } of this.staticCalls) {
|
||||||
let ns;
|
mainCode.push(`const ${id} = getTemplate(${template});`);
|
||||||
if (nameSpace) {
|
|
||||||
ns = `, "${nameSpace}"`;
|
|
||||||
}
|
|
||||||
mainCode.push(`const ${id} = getTemplate(${template}${ns});`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// define all blocks
|
// define all blocks
|
||||||
@@ -531,10 +534,10 @@ export class CodeGenerator {
|
|||||||
|
|
||||||
compileTDomNode(ast: ASTDomNode, ctx: Context) {
|
compileTDomNode(ast: ASTDomNode, ctx: Context) {
|
||||||
let { block, forceNewBlock } = ctx;
|
let { block, forceNewBlock } = ctx;
|
||||||
const isNewBlock = !block || forceNewBlock || ast.dynamicTag !== null;
|
const isNewBlock = !block || forceNewBlock || ast.dynamicTag !== null || ast.ns;
|
||||||
let codeIdx = this.target.code.length;
|
let codeIdx = this.target.code.length;
|
||||||
if (isNewBlock) {
|
if (isNewBlock) {
|
||||||
if ((ast.dynamicTag || ctx.tKeyExpr) && ctx.block) {
|
if ((ast.dynamicTag || ctx.tKeyExpr || ast.ns) && ctx.block) {
|
||||||
this.insertAnchor(ctx.block!);
|
this.insertAnchor(ctx.block!);
|
||||||
}
|
}
|
||||||
block = this.createBlock(block, "block", ctx);
|
block = this.createBlock(block, "block", ctx);
|
||||||
@@ -548,29 +551,40 @@ export class CodeGenerator {
|
|||||||
// attributes
|
// attributes
|
||||||
const attrs: { [key: string]: string } = {};
|
const attrs: { [key: string]: string } = {};
|
||||||
const nameSpace = ast.ns || ctx.nameSpace;
|
const nameSpace = ast.ns || ctx.nameSpace;
|
||||||
console.log(nameSpace)
|
|
||||||
if (nameSpace && isNewBlock) {
|
if (nameSpace && isNewBlock) {
|
||||||
// specific namespace uri
|
// specific namespace uri
|
||||||
attrs["block-ns"] = nameSpace;
|
attrs["block-ns"] = nameSpace;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let key in ast.attrs) {
|
for (let key in ast.attrs) {
|
||||||
|
let expr, attrName;
|
||||||
if (key.startsWith("t-attf")) {
|
if (key.startsWith("t-attf")) {
|
||||||
let expr = interpolate(ast.attrs[key]);
|
expr = interpolate(ast.attrs[key]);
|
||||||
const idx = block!.insertData(expr, "attr");
|
const idx = block!.insertData(expr, "attr");
|
||||||
attrs["block-attribute-" + idx] = key.slice(7);
|
|
||||||
|
attrName = key.slice(7);
|
||||||
|
attrs["block-attribute-" + idx] = attrName;
|
||||||
} else if (key.startsWith("t-att")) {
|
} else if (key.startsWith("t-att")) {
|
||||||
let expr = compileExpr(ast.attrs[key]);
|
expr = compileExpr(ast.attrs[key]);
|
||||||
const idx = block!.insertData(expr, "attr");
|
const idx = block!.insertData(expr, "attr");
|
||||||
if (key === "t-att") {
|
if (key === "t-att") {
|
||||||
attrs[`block-attributes`] = String(idx);
|
attrs[`block-attributes`] = String(idx);
|
||||||
} else {
|
} else {
|
||||||
attrs[`block-attribute-${idx}`] = key.slice(6);
|
attrName = key.slice(6);
|
||||||
|
attrs[`block-attribute-${idx}`] = attrName;
|
||||||
}
|
}
|
||||||
} else if (this.translatableAttributes.includes(key)) {
|
} else if (this.translatableAttributes.includes(key)) {
|
||||||
attrs[key] = this.translateFn(ast.attrs[key]);
|
attrs[key] = this.translateFn(ast.attrs[key]);
|
||||||
} else {
|
} else {
|
||||||
|
expr = `"${ast.attrs[key]}"`;
|
||||||
|
attrName = key;
|
||||||
attrs[key] = ast.attrs[key];
|
attrs[key] = ast.attrs[key];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (attrName === "value" && ctx.tModelSelectedExpr) {
|
||||||
|
let selectedId = block!.insertData(`${ctx.tModelSelectedExpr} === ${expr}`, "attr");
|
||||||
|
attrs[`block-attribute-${selectedId}`] = "selected";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// event handlers
|
// event handlers
|
||||||
@@ -610,8 +624,10 @@ export class CodeGenerator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// t-model
|
// t-model
|
||||||
|
let tModelSelectedExpr;
|
||||||
if (ast.model) {
|
if (ast.model) {
|
||||||
const {
|
const {
|
||||||
|
hasDynamicChildren,
|
||||||
baseExpr,
|
baseExpr,
|
||||||
expr,
|
expr,
|
||||||
eventType,
|
eventType,
|
||||||
@@ -622,20 +638,25 @@ export class CodeGenerator {
|
|||||||
} = ast.model;
|
} = ast.model;
|
||||||
|
|
||||||
const baseExpression = compileExpr(baseExpr);
|
const baseExpression = compileExpr(baseExpr);
|
||||||
const id = this.generateId();
|
const bExprId = this.generateId("bExpr");
|
||||||
this.addLine(`const bExpr${id} = ${baseExpression};`);
|
this.addLine(`const ${bExprId} = ${baseExpression};`);
|
||||||
|
|
||||||
const expression = compileExpr(expr);
|
const expression = compileExpr(expr);
|
||||||
|
const exprId = this.generateId("expr");
|
||||||
|
this.addLine(`const ${exprId} = ${expression};`);
|
||||||
|
|
||||||
|
const fullExpression = `${bExprId}[${exprId}]`;
|
||||||
|
|
||||||
let idx: number;
|
let idx: number;
|
||||||
if (specialInitTargetAttr) {
|
if (specialInitTargetAttr) {
|
||||||
idx = block!.insertData(
|
idx = block!.insertData(`${fullExpression} === '${attrs[targetAttr]}'`, "attr");
|
||||||
`${baseExpression}[${expression}] === '${attrs[targetAttr]}'`,
|
|
||||||
"attr"
|
|
||||||
);
|
|
||||||
attrs[`block-attribute-${idx}`] = specialInitTargetAttr;
|
attrs[`block-attribute-${idx}`] = specialInitTargetAttr;
|
||||||
|
} else if (hasDynamicChildren) {
|
||||||
|
const bValueId = this.generateId("bValue");
|
||||||
|
tModelSelectedExpr = `${bValueId}`;
|
||||||
|
this.addLine(`let ${tModelSelectedExpr} = ${fullExpression}`);
|
||||||
} else {
|
} else {
|
||||||
idx = block!.insertData(`${baseExpression}[${expression}]`, "attr");
|
idx = block!.insertData(`${fullExpression}`, "attr");
|
||||||
attrs[`block-attribute-${idx}`] = targetAttr;
|
attrs[`block-attribute-${idx}`] = targetAttr;
|
||||||
}
|
}
|
||||||
this.helpers.add("toNumber");
|
this.helpers.add("toNumber");
|
||||||
@@ -643,7 +664,7 @@ export class CodeGenerator {
|
|||||||
valueCode = shouldTrim ? `${valueCode}.trim()` : valueCode;
|
valueCode = shouldTrim ? `${valueCode}.trim()` : valueCode;
|
||||||
valueCode = shouldNumberize ? `toNumber(${valueCode})` : valueCode;
|
valueCode = shouldNumberize ? `toNumber(${valueCode})` : valueCode;
|
||||||
|
|
||||||
const handler = `[(ev) => { bExpr${id}[${expression}] = ${valueCode}; }]`;
|
const handler = `[(ev) => { ${fullExpression} = ${valueCode}; }]`;
|
||||||
idx = block!.insertData(handler, "hdlr");
|
idx = block!.insertData(handler, "hdlr");
|
||||||
attrs[`block-handler-${idx}`] = eventType;
|
attrs[`block-handler-${idx}`] = eventType;
|
||||||
}
|
}
|
||||||
@@ -668,6 +689,7 @@ export class CodeGenerator {
|
|||||||
isLast: ctx.isLast && i === children.length - 1,
|
isLast: ctx.isLast && i === children.length - 1,
|
||||||
tKeyExpr: ctx.tKeyExpr,
|
tKeyExpr: ctx.tKeyExpr,
|
||||||
nameSpace,
|
nameSpace,
|
||||||
|
tModelSelectedExpr,
|
||||||
});
|
});
|
||||||
this.compileAST(child, subCtx);
|
this.compileAST(child, subCtx);
|
||||||
}
|
}
|
||||||
@@ -970,7 +992,7 @@ export class CodeGenerator {
|
|||||||
} else {
|
} else {
|
||||||
const id = this.generateId(`callTemplate_`);
|
const id = this.generateId(`callTemplate_`);
|
||||||
this.helpers.add("getTemplate");
|
this.helpers.add("getTemplate");
|
||||||
this.staticCalls.push({ id, template: subTemplate, nameSpace: ctx.nameSpace });
|
this.staticCalls.push({ id, template: subTemplate });
|
||||||
block = this.createBlock(block, "multi", ctx);
|
block = this.createBlock(block, "multi", ctx);
|
||||||
this.insertBlock(`${id}.call(this, ctx, node, ${key})`, block!, {
|
this.insertBlock(`${id}.call(this, ctx, node, ${key})`, block!, {
|
||||||
...ctx,
|
...ctx,
|
||||||
@@ -1028,29 +1050,50 @@ export class CodeGenerator {
|
|||||||
return parts.join("__");
|
return parts.join("__");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formats a prop name and value into a string suitable to be inserted in the
|
||||||
|
* generated code. For example:
|
||||||
|
*
|
||||||
|
* Name Value Result
|
||||||
|
* ---------------------------------------------------------
|
||||||
|
* "number" "state" "number: ctx['state']"
|
||||||
|
* "something" "" "something: undefined"
|
||||||
|
* "some-prop" "state" "'some-prop': ctx['state']"
|
||||||
|
* "onClick.bind" "onClick" "onClick: bind(ctx, ctx['onClick'])"
|
||||||
|
*/
|
||||||
|
formatProp(name: string, value: string): string {
|
||||||
|
value = this.captureExpression(value);
|
||||||
|
if (name.includes(".")) {
|
||||||
|
let [_name, suffix] = name.split(".");
|
||||||
|
if (suffix === "bind") {
|
||||||
|
this.helpers.add("bind");
|
||||||
|
name = _name;
|
||||||
|
value = `bind(ctx, ${value || undefined})`;
|
||||||
|
} else {
|
||||||
|
throw new Error("Invalid prop suffix");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
name = /^[a-z_]+$/i.test(name) ? name : `'${name}'`;
|
||||||
|
return `${name}: ${value || undefined}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
formatPropObject(obj: { [prop: string]: any }): string {
|
||||||
|
const params = [];
|
||||||
|
for (const [n, v] of Object.entries(obj)) {
|
||||||
|
params.push(this.formatProp(n, v));
|
||||||
|
}
|
||||||
|
return params.join(", ");
|
||||||
|
}
|
||||||
|
|
||||||
compileComponent(ast: ASTComponent, ctx: Context) {
|
compileComponent(ast: ASTComponent, ctx: Context) {
|
||||||
let { block } = ctx;
|
let { block } = ctx;
|
||||||
|
|
||||||
// props
|
// props
|
||||||
|
const hasSlotsProp = "slots" in ast.props;
|
||||||
const props: string[] = [];
|
const props: string[] = [];
|
||||||
let hasSlotsProp = false;
|
const propExpr = this.formatPropObject(ast.props);
|
||||||
for (let propName in ast.props) {
|
if (propExpr) {
|
||||||
let propValue = this.captureExpression(ast.props[propName]) || undefined;
|
props.push(propExpr);
|
||||||
if (propName.includes(".")) {
|
|
||||||
let [name, suffix] = propName.split(".");
|
|
||||||
if (suffix === "bind") {
|
|
||||||
this.helpers.add("bind");
|
|
||||||
propName = name;
|
|
||||||
propValue = `bind(ctx, ${propValue})`;
|
|
||||||
} else {
|
|
||||||
throw new Error("Invalid prop suffix");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
propName = /^[a-z_]+$/i.test(propName) ? propName : `'${propName}'`;
|
|
||||||
props.push(`${propName}: ${propValue}`);
|
|
||||||
if (propName === "slots") {
|
|
||||||
hasSlotsProp = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// slots
|
// slots
|
||||||
@@ -1073,9 +1116,7 @@ export class CodeGenerator {
|
|||||||
params.push(`__scope: "${scope}"`);
|
params.push(`__scope: "${scope}"`);
|
||||||
}
|
}
|
||||||
if (ast.slots[slotName].attrs) {
|
if (ast.slots[slotName].attrs) {
|
||||||
for (const [n, v] of Object.entries(ast.slots[slotName].attrs!)) {
|
params.push(this.formatPropObject(ast.slots[slotName].attrs!));
|
||||||
params.push(`${n}: ${compileExpr(v) || undefined}`);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
const slotInfo = `{${params.join(", ")}}`;
|
const slotInfo = `{${params.join(", ")}}`;
|
||||||
slotStr.push(`'${slotName}': ${slotInfo}`);
|
slotStr.push(`'${slotName}': ${slotInfo}`);
|
||||||
@@ -1092,7 +1133,7 @@ export class CodeGenerator {
|
|||||||
let propString = propStr;
|
let propString = propStr;
|
||||||
if (ast.dynamicProps) {
|
if (ast.dynamicProps) {
|
||||||
if (!props.length) {
|
if (!props.length) {
|
||||||
propString = `${compileExpr(ast.dynamicProps)}`;
|
propString = `Object.assign({}, ${compileExpr(ast.dynamicProps)})`;
|
||||||
} else {
|
} else {
|
||||||
propString = `Object.assign({}, ${compileExpr(ast.dynamicProps)}, ${propStr})`;
|
propString = `Object.assign({}, ${compileExpr(ast.dynamicProps)}, ${propStr})`;
|
||||||
}
|
}
|
||||||
@@ -1101,7 +1142,7 @@ export class CodeGenerator {
|
|||||||
let propVar: string;
|
let propVar: string;
|
||||||
if ((slotDef && (ast.dynamicProps || hasSlotsProp)) || this.dev) {
|
if ((slotDef && (ast.dynamicProps || hasSlotsProp)) || this.dev) {
|
||||||
propVar = this.generateId("props");
|
propVar = this.generateId("props");
|
||||||
this.addLine(`const ${propVar!} = ${propString}`);
|
this.addLine(`const ${propVar!} = ${propString};`);
|
||||||
propString = propVar!;
|
propString = propVar!;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1120,7 +1161,7 @@ export class CodeGenerator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (this.dev) {
|
if (this.dev) {
|
||||||
this.addLine(`helpers.validateProps(${expr}, ${propVar!}, ctx)`);
|
this.addLine(`helpers.validateProps(${expr}, ${propVar!}, ctx);`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (block && (ctx.forceNewBlock === false || ctx.tKeyExpr)) {
|
if (block && (ctx.forceNewBlock === false || ctx.tKeyExpr)) {
|
||||||
@@ -1154,15 +1195,7 @@ export class CodeGenerator {
|
|||||||
slotName = "'" + ast.name + "'";
|
slotName = "'" + ast.name + "'";
|
||||||
}
|
}
|
||||||
|
|
||||||
let scope = null;
|
const scope = ast.attrs ? `{${this.formatPropObject(ast.attrs)}}` : null;
|
||||||
if (ast.attrs) {
|
|
||||||
const params = [];
|
|
||||||
for (const [n, v] of Object.entries(ast.attrs!)) {
|
|
||||||
params.push(`${n}: ${compileExpr(v) || undefined}`);
|
|
||||||
}
|
|
||||||
scope = `{${params.join(", ")}}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ast.defaultContent) {
|
if (ast.defaultContent) {
|
||||||
const name = this.compileInNewTarget("defaultContent", ast.defaultContent, ctx);
|
const name = this.compileInNewTarget("defaultContent", ast.defaultContent, ctx);
|
||||||
blockString = `callSlot(ctx, node, key, ${slotName}, ${dynamic}, ${scope}, ${name})`;
|
blockString = `callSlot(ctx, node, key, ${slotName}, ${dynamic}, ${scope}, ${name})`;
|
||||||
|
|||||||
@@ -8,9 +8,11 @@ export type TemplateFunction = (blocks: any, utils: any) => Template;
|
|||||||
|
|
||||||
interface CompileOptions extends Config {
|
interface CompileOptions extends Config {
|
||||||
name?: string;
|
name?: string;
|
||||||
nameSpace?: string,
|
|
||||||
}
|
}
|
||||||
export function compile(template: string | Node, options: CompileOptions = {}): TemplateFunction {
|
export function compile(
|
||||||
|
template: string | Element,
|
||||||
|
options: CompileOptions = {}
|
||||||
|
): TemplateFunction {
|
||||||
// parsing
|
// parsing
|
||||||
const ast = parse(template);
|
const ast = parse(template);
|
||||||
|
|
||||||
@@ -21,10 +23,8 @@ export function compile(template: string | Node, options: CompileOptions = {}):
|
|||||||
: !template.includes("t-set") && !template.includes("t-call");
|
: !template.includes("t-set") && !template.includes("t-call");
|
||||||
|
|
||||||
// code generation
|
// code generation
|
||||||
console.log("compile", options)
|
|
||||||
const codeGenerator = new CodeGenerator(ast, { ...options, hasSafeContext });
|
const codeGenerator = new CodeGenerator(ast, { ...options, hasSafeContext });
|
||||||
const code = codeGenerator.generateCode();
|
const code = codeGenerator.generateCode();
|
||||||
console.log(code)
|
|
||||||
// template function
|
// template function
|
||||||
return new Function("bdom, helpers", code) as TemplateFunction;
|
return new Function("bdom, helpers", code) as TemplateFunction;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -329,7 +329,9 @@ export function compileExprToArray(expr: string): Token[] {
|
|||||||
// Mark all variables that have been used locally.
|
// Mark all variables that have been used locally.
|
||||||
// This assumes the expression has only one scope (incorrect but "good enough for now")
|
// This assumes the expression has only one scope (incorrect but "good enough for now")
|
||||||
for (const token of tokens) {
|
for (const token of tokens) {
|
||||||
if (token.type === "SYMBOL" && localVars.has(token.value)) {
|
if (token.type === "SYMBOL" && token.varName && localVars.has(token.value)) {
|
||||||
|
token.originalValue = token.value;
|
||||||
|
token.value = `_${token.value}`;
|
||||||
token.isLocal = true;
|
token.isLocal = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+65
-28
@@ -33,6 +33,17 @@ export interface ASTComment {
|
|||||||
value: string;
|
value: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface TModelInfo {
|
||||||
|
hasDynamicChildren?: boolean;
|
||||||
|
baseExpr: string;
|
||||||
|
expr: string;
|
||||||
|
targetAttr: string;
|
||||||
|
specialInitTargetAttr: string | null;
|
||||||
|
eventType: "change" | "click" | "input";
|
||||||
|
shouldTrim: boolean;
|
||||||
|
shouldNumberize: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
export interface ASTDomNode {
|
export interface ASTDomNode {
|
||||||
type: ASTType.DomNode;
|
type: ASTType.DomNode;
|
||||||
tag: string;
|
tag: string;
|
||||||
@@ -41,15 +52,7 @@ export interface ASTDomNode {
|
|||||||
content: AST[];
|
content: AST[];
|
||||||
ref: string | null;
|
ref: string | null;
|
||||||
on: { [key: string]: string };
|
on: { [key: string]: string };
|
||||||
model: {
|
model?: TModelInfo | null;
|
||||||
baseExpr: string;
|
|
||||||
expr: string;
|
|
||||||
targetAttr: string;
|
|
||||||
specialInitTargetAttr: string | null;
|
|
||||||
eventType: "change" | "click" | "input";
|
|
||||||
shouldTrim: boolean;
|
|
||||||
shouldNumberize: boolean;
|
|
||||||
} | null;
|
|
||||||
ns: string | null;
|
ns: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -177,23 +180,35 @@ export type AST =
|
|||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// Parser
|
// Parser
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
interface ParsingContext {
|
const cache: WeakMap<Element, AST> = new WeakMap();
|
||||||
inPreTag: boolean;
|
|
||||||
inSVG: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function parse(xml: string | Node): AST {
|
export function parse(xml: string | Element): AST {
|
||||||
const node = xml instanceof Element ? xml : (parseXML(`<t>${xml}</t>`).firstChild! as Element);
|
if (typeof xml === "string") {
|
||||||
normalizeXML(node);
|
const elem = parseXML(`<t>${xml}</t>`).firstChild as Element;
|
||||||
const ctx = { inPreTag: false, inSVG: false };
|
return _parse(elem);
|
||||||
const ast = parseNode(node, ctx);
|
}
|
||||||
|
let ast = cache.get(xml);
|
||||||
if (!ast) {
|
if (!ast) {
|
||||||
return { type: ASTType.Text, value: "" };
|
// we clone here the xml to prevent modifying it in place
|
||||||
|
ast = _parse(xml.cloneNode(true) as Element);
|
||||||
|
cache.set(xml, ast);
|
||||||
}
|
}
|
||||||
return ast;
|
return ast;
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseNode(node: ChildNode, ctx: ParsingContext): AST | null {
|
function _parse(xml: Element): AST {
|
||||||
|
normalizeXML(xml);
|
||||||
|
const ctx = { inPreTag: false, inSVG: false };
|
||||||
|
return parseNode(xml, ctx) || { type: ASTType.Text, value: "" };
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ParsingContext {
|
||||||
|
tModelInfo?: TModelInfo | null;
|
||||||
|
inPreTag: boolean;
|
||||||
|
inSVG: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseNode(node: Node, ctx: ParsingContext): AST | null {
|
||||||
if (!(node instanceof Element)) {
|
if (!(node instanceof Element)) {
|
||||||
return parseTextCommentNode(node, ctx);
|
return parseTextCommentNode(node, ctx);
|
||||||
}
|
}
|
||||||
@@ -233,7 +248,7 @@ function parseTNode(node: Element, ctx: ParsingContext): AST | null {
|
|||||||
const lineBreakRE = /[\r\n]/;
|
const lineBreakRE = /[\r\n]/;
|
||||||
const whitespaceRE = /\s+/g;
|
const whitespaceRE = /\s+/g;
|
||||||
|
|
||||||
function parseTextCommentNode(node: ChildNode, ctx: ParsingContext): AST | null {
|
function parseTextCommentNode(node: Node, ctx: ParsingContext): AST | null {
|
||||||
if (node.nodeType === Node.TEXT_NODE) {
|
if (node.nodeType === Node.TEXT_NODE) {
|
||||||
let value = node.textContent || "";
|
let value = node.textContent || "";
|
||||||
if (!ctx.inPreTag) {
|
if (!ctx.inPreTag) {
|
||||||
@@ -281,6 +296,8 @@ function parseTDebugLog(node: Element, ctx: ParsingContext): AST | null {
|
|||||||
const hasDotAtTheEnd = /\.[\w_]+\s*$/;
|
const hasDotAtTheEnd = /\.[\w_]+\s*$/;
|
||||||
const hasBracketsAtTheEnd = /\[[^\[]+\]\s*$/;
|
const hasBracketsAtTheEnd = /\[[^\[]+\]\s*$/;
|
||||||
|
|
||||||
|
const ROOT_SVG_TAGS = new Set(["svg", "g", "path"]);
|
||||||
|
|
||||||
function parseDOMNode(node: Element, ctx: ParsingContext): AST | null {
|
function parseDOMNode(node: Element, ctx: ParsingContext): AST | null {
|
||||||
const { tagName } = node;
|
const { tagName } = node;
|
||||||
const dynamicTag = node.getAttribute("t-tag");
|
const dynamicTag = node.getAttribute("t-tag");
|
||||||
@@ -288,22 +305,23 @@ function parseDOMNode(node: Element, ctx: ParsingContext): AST | null {
|
|||||||
if (tagName === "t" && !dynamicTag) {
|
if (tagName === "t" && !dynamicTag) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
if (tagName.startsWith("block-")) {
|
||||||
|
throw new Error(`Invalid tag name: '${tagName}'`);
|
||||||
|
}
|
||||||
ctx = Object.assign({}, ctx);
|
ctx = Object.assign({}, ctx);
|
||||||
if (tagName === "pre") {
|
if (tagName === "pre") {
|
||||||
ctx.inPreTag = true;
|
ctx.inPreTag = true;
|
||||||
}
|
}
|
||||||
const shouldAddSVGNS = tagName === "svg" || (tagName === "g" && !ctx.inSVG);
|
const shouldAddSVGNS = ROOT_SVG_TAGS.has(tagName) && !ctx.inSVG;
|
||||||
ctx.inSVG = ctx.inSVG || shouldAddSVGNS;
|
ctx.inSVG = ctx.inSVG || shouldAddSVGNS;
|
||||||
const ns = shouldAddSVGNS ? "http://www.w3.org/2000/svg" : null;
|
const ns = shouldAddSVGNS ? "http://www.w3.org/2000/svg" : null;
|
||||||
const ref = node.getAttribute("t-ref");
|
const ref = node.getAttribute("t-ref");
|
||||||
node.removeAttribute("t-ref");
|
node.removeAttribute("t-ref");
|
||||||
|
|
||||||
const children = parseChildren(node, ctx);
|
|
||||||
|
|
||||||
const nodeAttrsNames = node.getAttributeNames();
|
const nodeAttrsNames = node.getAttributeNames();
|
||||||
const attrs: ASTDomNode["attrs"] = {};
|
const attrs: ASTDomNode["attrs"] = {};
|
||||||
const on: ASTDomNode["on"] = {};
|
const on: ASTDomNode["on"] = {};
|
||||||
let model: ASTDomNode["model"] = null;
|
let model: TModelInfo | null = null;
|
||||||
|
|
||||||
for (let attr of nodeAttrsNames) {
|
for (let attr of nodeAttrsNames) {
|
||||||
const value = node.getAttribute(attr)!;
|
const value = node.getAttribute(attr)!;
|
||||||
@@ -351,13 +369,26 @@ function parseDOMNode(node: Element, ctx: ParsingContext): AST | null {
|
|||||||
shouldTrim: hasTrimMod && (isOtherInput || isTextarea),
|
shouldTrim: hasTrimMod && (isOtherInput || isTextarea),
|
||||||
shouldNumberize: hasNumberMod && (isOtherInput || isTextarea),
|
shouldNumberize: hasNumberMod && (isOtherInput || isTextarea),
|
||||||
};
|
};
|
||||||
} else {
|
if (isSelect) {
|
||||||
|
// don't pollute the original ctx
|
||||||
|
ctx = Object.assign({}, ctx);
|
||||||
|
ctx.tModelInfo = model;
|
||||||
|
}
|
||||||
|
} else if (attr.startsWith("block-")) {
|
||||||
|
throw new Error(`Invalid attribute: '${attr}'`);
|
||||||
|
} else if (attr !== "t-name") {
|
||||||
if (attr.startsWith("t-") && !attr.startsWith("t-att")) {
|
if (attr.startsWith("t-") && !attr.startsWith("t-att")) {
|
||||||
throw new Error(`Unknown QWeb directive: '${attr}'`);
|
throw new Error(`Unknown QWeb directive: '${attr}'`);
|
||||||
}
|
}
|
||||||
|
const tModel = ctx.tModelInfo;
|
||||||
|
if (tModel && ["t-att-value", "t-attf-value"].includes(attr)) {
|
||||||
|
tModel.hasDynamicChildren = true;
|
||||||
|
}
|
||||||
attrs[attr] = value;
|
attrs[attr] = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const children = parseChildren(node, ctx);
|
||||||
return {
|
return {
|
||||||
type: ASTType.DomNode,
|
type: ASTType.DomNode,
|
||||||
tag: tagName,
|
tag: tagName,
|
||||||
@@ -651,6 +682,9 @@ function parseComponent(node: Element, ctx: ParsingContext): AST | null {
|
|||||||
const dynamicProps = node.getAttribute("t-props");
|
const dynamicProps = node.getAttribute("t-props");
|
||||||
node.removeAttribute("t-props");
|
node.removeAttribute("t-props");
|
||||||
|
|
||||||
|
const defaultSlotScope = node.getAttribute("t-slot-scope");
|
||||||
|
node.removeAttribute("t-slot-scope");
|
||||||
|
|
||||||
const props: ASTComponent["props"] = {};
|
const props: ASTComponent["props"] = {};
|
||||||
for (let name of node.getAttributeNames()) {
|
for (let name of node.getAttributeNames()) {
|
||||||
const value = node.getAttribute(name)!;
|
const value = node.getAttribute(name)!;
|
||||||
@@ -716,6 +750,9 @@ function parseComponent(node: Element, ctx: ParsingContext): AST | null {
|
|||||||
const defaultContent = parseChildNodes(clone, ctx);
|
const defaultContent = parseChildNodes(clone, ctx);
|
||||||
if (defaultContent) {
|
if (defaultContent) {
|
||||||
slots.default = { content: defaultContent };
|
slots.default = { content: defaultContent };
|
||||||
|
if (defaultSlotScope) {
|
||||||
|
slots.default.scope = defaultSlotScope;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return { type: ASTType.TComponent, name, isDynamic, dynamicProps, props, slots };
|
return { type: ASTType.TComponent, name, isDynamic, dynamicProps, props, slots };
|
||||||
@@ -786,7 +823,7 @@ function parseTPortal(node: Element, ctx: ParsingContext): AST | null {
|
|||||||
/**
|
/**
|
||||||
* Parse all the child nodes of a given node and return a list of ast elements
|
* Parse all the child nodes of a given node and return a list of ast elements
|
||||||
*/
|
*/
|
||||||
function parseChildren(node: Node, ctx: ParsingContext): AST[] {
|
function parseChildren(node: Element, ctx: ParsingContext): AST[] {
|
||||||
const children: AST[] = [];
|
const children: AST[] = [];
|
||||||
for (let child of node.childNodes) {
|
for (let child of node.childNodes) {
|
||||||
const childAst = parseNode(child, ctx);
|
const childAst = parseNode(child, ctx);
|
||||||
@@ -805,7 +842,7 @@ function parseChildren(node: Node, ctx: ParsingContext): AST[] {
|
|||||||
* Parse all the child nodes of a given node and return an ast if possible.
|
* Parse all the child nodes of a given node and return an ast if possible.
|
||||||
* In the case there are multiple children, they are wrapped in a astmulti.
|
* In the case there are multiple children, they are wrapped in a astmulti.
|
||||||
*/
|
*/
|
||||||
function parseChildNodes(node: Node, ctx: ParsingContext): AST | null {
|
function parseChildNodes(node: Element, ctx: ParsingContext): AST | null {
|
||||||
const children = parseChildren(node, ctx);
|
const children = parseChildren(node, ctx);
|
||||||
switch (children.length) {
|
switch (children.length) {
|
||||||
case 0:
|
case 0:
|
||||||
|
|||||||
@@ -4,9 +4,25 @@ import type { ComponentNode } from "./component_node";
|
|||||||
// Component Class
|
// Component Class
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
type Props = { [key: string]: any };
|
||||||
|
|
||||||
|
interface StaticComponentProperties {
|
||||||
|
template: string;
|
||||||
|
defaultProps?: any;
|
||||||
|
props?: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ComponentConstructor<P extends Props = any, E = any> = (new (
|
||||||
|
props: P,
|
||||||
|
env: E,
|
||||||
|
node: ComponentNode
|
||||||
|
) => Component<P, E>) &
|
||||||
|
StaticComponentProperties;
|
||||||
|
|
||||||
export class Component<Props = any, Env = any> {
|
export class Component<Props = any, Env = any> {
|
||||||
static template: string = "";
|
static template: string = "";
|
||||||
static props?: any;
|
static props?: any;
|
||||||
|
static defaultProps?: any;
|
||||||
|
|
||||||
props: Props;
|
props: Props;
|
||||||
env: Env;
|
env: Env;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import type { App, Env } from "../app/app";
|
import type { App, Env } from "../app/app";
|
||||||
import { BDom, VNode } from "../blockdom";
|
import { BDom, VNode } from "../blockdom";
|
||||||
import { Component } from "./component";
|
import { Component, ComponentConstructor } from "./component";
|
||||||
import {
|
import {
|
||||||
Fiber,
|
Fiber,
|
||||||
makeChildFiber,
|
makeChildFiber,
|
||||||
@@ -15,7 +15,10 @@ import { STATUS } from "./status";
|
|||||||
|
|
||||||
let currentNode: ComponentNode | null = null;
|
let currentNode: ComponentNode | null = null;
|
||||||
|
|
||||||
export function getCurrent(): ComponentNode | null {
|
export function getCurrent(): ComponentNode {
|
||||||
|
if (!currentNode) {
|
||||||
|
throw new Error("No active component (a hook function should only be called in 'setup')");
|
||||||
|
}
|
||||||
return currentNode;
|
return currentNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -74,13 +77,11 @@ export function component(
|
|||||||
|
|
||||||
type LifecycleHook = Function;
|
type LifecycleHook = Function;
|
||||||
|
|
||||||
export class ComponentNode<T extends typeof Component = typeof Component>
|
export class ComponentNode<P = any, E = any> implements VNode<ComponentNode<P, E>> {
|
||||||
implements VNode<ComponentNode>
|
|
||||||
{
|
|
||||||
el?: HTMLElement | Text | undefined;
|
el?: HTMLElement | Text | undefined;
|
||||||
app: App;
|
app: App;
|
||||||
fiber: Fiber | null = null;
|
fiber: Fiber | null = null;
|
||||||
component: InstanceType<T>;
|
component: Component<P, E>;
|
||||||
bdom: BDom | null = null;
|
bdom: BDom | null = null;
|
||||||
status: STATUS = STATUS.NEW;
|
status: STATUS = STATUS.NEW;
|
||||||
|
|
||||||
@@ -99,7 +100,7 @@ export class ComponentNode<T extends typeof Component = typeof Component>
|
|||||||
patched: LifecycleHook[] = [];
|
patched: LifecycleHook[] = [];
|
||||||
willDestroy: LifecycleHook[] = [];
|
willDestroy: LifecycleHook[] = [];
|
||||||
|
|
||||||
constructor(C: T, props: any, app: App, parent?: ComponentNode) {
|
constructor(C: ComponentConstructor<P, E>, props: P, app: App, parent?: ComponentNode) {
|
||||||
currentNode = this;
|
currentNode = this;
|
||||||
this.app = app;
|
this.app = app;
|
||||||
this.parent = parent || null;
|
this.parent = parent || null;
|
||||||
@@ -110,6 +111,7 @@ export class ComponentNode<T extends typeof Component = typeof Component>
|
|||||||
this.component = new C(props, env, this) as any;
|
this.component = new C(props, env, this) as any;
|
||||||
this.renderFn = app.getTemplate(C.template).bind(this.component, this.component, this);
|
this.renderFn = app.getTemplate(C.template).bind(this.component, this.component, this);
|
||||||
this.component.setup();
|
this.component.setup();
|
||||||
|
currentNode = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
mountComponent(target: any, options?: MountOptions) {
|
mountComponent(target: any, options?: MountOptions) {
|
||||||
@@ -118,20 +120,30 @@ export class ComponentNode<T extends typeof Component = typeof Component>
|
|||||||
this.initiateRender(fiber);
|
this.initiateRender(fiber);
|
||||||
}
|
}
|
||||||
|
|
||||||
async initiateRender(fiber: Fiber | MountFiber) {
|
initiateRender(fiber: Fiber | MountFiber) {
|
||||||
this.fiber = fiber;
|
this.fiber = fiber;
|
||||||
if (this.mounted.length) {
|
if (this.mounted.length) {
|
||||||
fiber.root!.mounted.push(fiber);
|
fiber.root!.mounted.push(fiber);
|
||||||
}
|
}
|
||||||
const component = this.component;
|
const component = this.component;
|
||||||
|
const finish = () => {
|
||||||
|
if (this.status === STATUS.NEW && this.fiber === fiber) {
|
||||||
|
this._render(fiber);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let willStartResults;
|
||||||
try {
|
try {
|
||||||
await Promise.all(this.willStart.map((f) => f.call(component)));
|
willStartResults = this.willStart.map((f) => f.call(component));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
handleError({ node: this, error: e });
|
handleError({ node: this, error: e });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (this.status === STATUS.NEW && this.fiber === fiber) {
|
if (willStartResults.some(result => result instanceof Promise)) {
|
||||||
this._render(fiber);
|
Promise.all(willStartResults)
|
||||||
|
.then(finish)
|
||||||
|
.catch(e => handleError({ node: this, error: e }));
|
||||||
|
} else {
|
||||||
|
finish();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -151,25 +163,27 @@ export class ComponentNode<T extends typeof Component = typeof Component>
|
|||||||
|
|
||||||
const fiber = makeRootFiber(this);
|
const fiber = makeRootFiber(this);
|
||||||
this.fiber = fiber;
|
this.fiber = fiber;
|
||||||
|
debugger;
|
||||||
|
window.requestAnimationFrame(() => {
|
||||||
|
if (this.status === STATUS.DESTROYED) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// We only want to actually render the component if the following two
|
||||||
|
// conditions are true:
|
||||||
|
// * this.fiber: it could be null, in which case the render has been cancelled
|
||||||
|
// * (current || !fiber.parent): if current is not null, this means that the
|
||||||
|
// render function was called when a render was already occurring. In this
|
||||||
|
// case, the pending rendering was cancelled, and the fiber needs to be
|
||||||
|
// rendered to complete the work. If current is null, we check that the
|
||||||
|
// fiber has no parent. If that is the case, the fiber was downgraded from
|
||||||
|
// a root fiber to a child fiber in the previous microtick, because it was
|
||||||
|
// embedded in a rendering coming from above, so the fiber will be rendered
|
||||||
|
// in the next microtick anyway, so we should not render it again.
|
||||||
|
if (this.fiber === fiber && (current || !fiber.parent)) {
|
||||||
|
this._render(fiber);
|
||||||
|
}
|
||||||
|
});
|
||||||
this.app.scheduler.addFiber(fiber);
|
this.app.scheduler.addFiber(fiber);
|
||||||
await Promise.resolve();
|
|
||||||
if (this.status === STATUS.DESTROYED) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// We only want to actually render the component if the following two
|
|
||||||
// conditions are true:
|
|
||||||
// * this.fiber: it could be null, in which case the render has been cancelled
|
|
||||||
// * (current || !fiber.parent): if current is not null, this means that the
|
|
||||||
// render function was called when a render was already occurring. In this
|
|
||||||
// case, the pending rendering was cancelled, and the fiber needs to be
|
|
||||||
// rendered to complete the work. If current is null, we check that the
|
|
||||||
// fiber has no parent. If that is the case, the fiber was downgraded from
|
|
||||||
// a root fiber to a child fiber in the previous microtick, because it was
|
|
||||||
// embedded in a rendering coming from above, so the fiber will be rendered
|
|
||||||
// in the next microtick anyway, so we should not render it again.
|
|
||||||
if (this.fiber === fiber && (current || !fiber.parent)) {
|
|
||||||
this._render(fiber);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_render(fiber: Fiber | RootFiber) {
|
_render(fiber: Fiber | RootFiber) {
|
||||||
@@ -205,25 +219,32 @@ export class ComponentNode<T extends typeof Component = typeof Component>
|
|||||||
this.status = STATUS.DESTROYED;
|
this.status = STATUS.DESTROYED;
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateAndRender(props: any, parentFiber: Fiber) {
|
updateAndRender(props: any, parentFiber: Fiber) {
|
||||||
// update
|
// update
|
||||||
const fiber = makeChildFiber(this, parentFiber);
|
const fiber = makeChildFiber(this, parentFiber);
|
||||||
this.fiber = fiber;
|
this.fiber = fiber;
|
||||||
const component = this.component;
|
const component = this.component;
|
||||||
applyDefaultProps(props, component.constructor as any);
|
applyDefaultProps(props, component.constructor as any);
|
||||||
const prom = Promise.all(this.willUpdateProps.map((f) => f.call(component, props)));
|
|
||||||
await prom;
|
const finish = () => {
|
||||||
if (fiber !== this.fiber) {
|
if (fiber !== this.fiber) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
component.props = props;
|
||||||
|
this._render(fiber);
|
||||||
|
const parentRoot = parentFiber.root!;
|
||||||
|
if (this.willPatch.length) {
|
||||||
|
parentRoot.willPatch.push(fiber);
|
||||||
|
}
|
||||||
|
if (this.patched.length) {
|
||||||
|
parentRoot.patched.push(fiber);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
component.props = props;
|
const willUpdatePropsResults = this.willUpdateProps.map((f) => f.call(component, props));
|
||||||
this._render(fiber);
|
if (willUpdatePropsResults.some(res => res instanceof Promise)) {
|
||||||
const parentRoot = parentFiber.root!;
|
Promise.all(willUpdatePropsResults).then(finish);
|
||||||
if (this.willPatch.length) {
|
} else {
|
||||||
parentRoot.willPatch.push(fiber);
|
finish();
|
||||||
}
|
|
||||||
if (this.patched.length) {
|
|
||||||
parentRoot.patched.push(fiber);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,8 +6,7 @@ import { STATUS } from "./status";
|
|||||||
export function makeChildFiber(node: ComponentNode, parent: Fiber): Fiber {
|
export function makeChildFiber(node: ComponentNode, parent: Fiber): Fiber {
|
||||||
let current = node.fiber;
|
let current = node.fiber;
|
||||||
if (current) {
|
if (current) {
|
||||||
let root = parent.root;
|
cancelFibers(current.children);
|
||||||
cancelFibers(root, current.children);
|
|
||||||
current.root = null;
|
current.root = null;
|
||||||
}
|
}
|
||||||
return new Fiber(node, parent);
|
return new Fiber(node, parent);
|
||||||
@@ -17,7 +16,7 @@ export function makeRootFiber(node: ComponentNode): Fiber {
|
|||||||
let current = node.fiber;
|
let current = node.fiber;
|
||||||
if (current) {
|
if (current) {
|
||||||
let root = current.root!;
|
let root = current.root!;
|
||||||
root.counter -= cancelFibers(root, current.children);
|
root.counter -= cancelFibers(current.children);
|
||||||
current.children = [];
|
current.children = [];
|
||||||
root.counter++;
|
root.counter++;
|
||||||
current.bdom = null;
|
current.bdom = null;
|
||||||
@@ -42,15 +41,14 @@ export function makeRootFiber(node: ComponentNode): Fiber {
|
|||||||
/**
|
/**
|
||||||
* @returns number of not-yet rendered fibers cancelled
|
* @returns number of not-yet rendered fibers cancelled
|
||||||
*/
|
*/
|
||||||
function cancelFibers(root: any, fibers: Fiber[]): number {
|
function cancelFibers(fibers: Fiber[]): number {
|
||||||
let result = 0;
|
let result = 0;
|
||||||
for (let fiber of fibers) {
|
for (let fiber of fibers) {
|
||||||
fiber.node.fiber = null;
|
fiber.node.fiber = null;
|
||||||
fiber.root = root;
|
|
||||||
if (!fiber.bdom) {
|
if (!fiber.bdom) {
|
||||||
result++;
|
result++;
|
||||||
}
|
}
|
||||||
result += cancelFibers(root, fiber.children);
|
result += cancelFibers(fiber.children);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -159,6 +157,7 @@ export class MountFiber extends RootFiber {
|
|||||||
let current: Fiber | undefined = this;
|
let current: Fiber | undefined = this;
|
||||||
try {
|
try {
|
||||||
const node = this.node;
|
const node = this.node;
|
||||||
|
(node.app.constructor as any).validateTarget(this.target);
|
||||||
if (node.bdom) {
|
if (node.bdom) {
|
||||||
// this is a complicated situation: if we mount a fiber with an existing
|
// this is a complicated situation: if we mount a fiber with an existing
|
||||||
// bdom, this means that this same fiber was already completed, mounted,
|
// bdom, this means that this same fiber was already completed, mounted,
|
||||||
|
|||||||
@@ -1,67 +1,102 @@
|
|||||||
import { getCurrent } from "./component_node";
|
import { getCurrent } from "./component_node";
|
||||||
import { nodeErrorHandlers } from "./error_handling";
|
import { nodeErrorHandlers } from "./error_handling";
|
||||||
|
|
||||||
|
function wrapError(fn: (...args: any[]) => any, hookName: string) {
|
||||||
|
const error = new Error(`The following error occurred in ${hookName}: `) as Error & {
|
||||||
|
cause: any;
|
||||||
|
};
|
||||||
|
return (...args: any[]) => {
|
||||||
|
try {
|
||||||
|
const result = fn(...args);
|
||||||
|
if (result instanceof Promise) {
|
||||||
|
return result.catch((cause) => {
|
||||||
|
error.cause = cause;
|
||||||
|
if (cause instanceof Error) {
|
||||||
|
error.message += `"${cause.message}"`;
|
||||||
|
}
|
||||||
|
throw error;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
} catch (cause) {
|
||||||
|
if (cause instanceof Error) {
|
||||||
|
error.message += `"${cause.message}"`;
|
||||||
|
}
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// hooks
|
// hooks
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
export function onWillStart(fn: () => Promise<void> | void | any) {
|
export function onWillStart(fn: () => Promise<void> | void | any) {
|
||||||
const node = getCurrent()!;
|
const node = getCurrent();
|
||||||
node.willStart.push(fn.bind(node.component));
|
const decorate = node.app.dev ? wrapError : (fn: any) => fn;
|
||||||
|
node.willStart.push(decorate(fn.bind(node.component), "onWillStart"));
|
||||||
}
|
}
|
||||||
|
|
||||||
export function onWillUpdateProps(fn: (nextProps: any) => Promise<void> | void | any) {
|
export function onWillUpdateProps(fn: (nextProps: any) => Promise<void> | void | any) {
|
||||||
const node = getCurrent()!;
|
const node = getCurrent();
|
||||||
node.willUpdateProps.push(fn.bind(node.component));
|
const decorate = node.app.dev ? wrapError : (fn: any) => fn;
|
||||||
|
node.willUpdateProps.push(decorate(fn.bind(node.component), "onWillUpdateProps"));
|
||||||
}
|
}
|
||||||
|
|
||||||
export function onMounted(fn: () => void | any) {
|
export function onMounted(fn: () => void | any) {
|
||||||
const node = getCurrent()!;
|
const node = getCurrent();
|
||||||
node.mounted.push(fn.bind(node.component));
|
const decorate = node.app.dev ? wrapError : (fn: any) => fn;
|
||||||
|
node.mounted.push(decorate(fn.bind(node.component), "onMounted"));
|
||||||
}
|
}
|
||||||
|
|
||||||
export function onWillPatch(fn: () => Promise<void> | any | void) {
|
export function onWillPatch(fn: () => Promise<void> | any | void) {
|
||||||
const node = getCurrent()!;
|
const node = getCurrent();
|
||||||
node.willPatch.unshift(fn.bind(node.component));
|
const decorate = node.app.dev ? wrapError : (fn: any) => fn;
|
||||||
|
node.willPatch.unshift(decorate(fn.bind(node.component), "onWillPatch"));
|
||||||
}
|
}
|
||||||
|
|
||||||
export function onPatched(fn: () => void | any) {
|
export function onPatched(fn: () => void | any) {
|
||||||
const node = getCurrent()!;
|
const node = getCurrent();
|
||||||
node.patched.push(fn.bind(node.component));
|
const decorate = node.app.dev ? wrapError : (fn: any) => fn;
|
||||||
|
node.patched.push(decorate(fn.bind(node.component), "onPatched"));
|
||||||
}
|
}
|
||||||
|
|
||||||
export function onWillUnmount(fn: () => Promise<void> | void | any) {
|
export function onWillUnmount(fn: () => Promise<void> | void | any) {
|
||||||
const node = getCurrent()!;
|
const node = getCurrent();
|
||||||
node.willUnmount.unshift(fn.bind(node.component));
|
const decorate = node.app.dev ? wrapError : (fn: any) => fn;
|
||||||
|
node.willUnmount.unshift(decorate(fn.bind(node.component), "onWillUnmount"));
|
||||||
}
|
}
|
||||||
|
|
||||||
export function onWillDestroy(fn: () => Promise<void> | void | any) {
|
export function onWillDestroy(fn: () => Promise<void> | void | any) {
|
||||||
const node = getCurrent()!;
|
const node = getCurrent();
|
||||||
node.willDestroy.push(fn.bind(node.component));
|
const decorate = node.app.dev ? wrapError : (fn: any) => fn;
|
||||||
|
node.willDestroy.push(decorate(fn.bind(node.component), "onWillDestroy"));
|
||||||
}
|
}
|
||||||
|
|
||||||
export function onWillRender(fn: () => void | any) {
|
export function onWillRender(fn: () => void | any) {
|
||||||
const node = getCurrent()!;
|
const node = getCurrent();
|
||||||
const renderFn = node.renderFn;
|
const renderFn = node.renderFn;
|
||||||
node.renderFn = () => {
|
const decorate = node.app.dev ? wrapError : (fn: any) => fn;
|
||||||
|
node.renderFn = decorate(() => {
|
||||||
fn.call(node.component);
|
fn.call(node.component);
|
||||||
return renderFn();
|
return renderFn();
|
||||||
};
|
}, "onWillRender");
|
||||||
}
|
}
|
||||||
|
|
||||||
export function onRendered(fn: () => void | any) {
|
export function onRendered(fn: () => void | any) {
|
||||||
const node = getCurrent()!;
|
const node = getCurrent();
|
||||||
const renderFn = node.renderFn;
|
const renderFn = node.renderFn;
|
||||||
node.renderFn = () => {
|
const decorate = node.app.dev ? wrapError : (fn: any) => fn;
|
||||||
|
node.renderFn = decorate(() => {
|
||||||
const result = renderFn();
|
const result = renderFn();
|
||||||
fn.call(node.component);
|
fn.call(node.component);
|
||||||
return result;
|
return result;
|
||||||
};
|
}, "onRendered");
|
||||||
}
|
}
|
||||||
|
|
||||||
type OnErrorCallback = (error: any) => void | any;
|
type OnErrorCallback = (error: any) => void | any;
|
||||||
export function onError(callback: OnErrorCallback) {
|
export function onError(callback: OnErrorCallback) {
|
||||||
const node = getCurrent()!;
|
const node = getCurrent();
|
||||||
let handlers = nodeErrorHandlers.get(node);
|
let handlers = nodeErrorHandlers.get(node);
|
||||||
if (!handlers) {
|
if (!handlers) {
|
||||||
handlers = [];
|
handlers = [];
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
import { Component } from "./component";
|
import { ComponentConstructor } from "./component";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Apply default props (only top level).
|
* Apply default props (only top level).
|
||||||
*
|
*
|
||||||
* Note that this method does modify in place the props
|
* Note that this method does modify in place the props
|
||||||
*/
|
*/
|
||||||
export function applyDefaultProps(props: { [key: string]: any }, ComponentClass: typeof Component) {
|
export function applyDefaultProps<P>(props: P, ComponentClass: ComponentConstructor<P>) {
|
||||||
const defaultProps = (ComponentClass as any).defaultProps;
|
const defaultProps = ComponentClass.defaultProps;
|
||||||
if (defaultProps) {
|
if (defaultProps) {
|
||||||
for (let propName in defaultProps) {
|
for (let propName in defaultProps) {
|
||||||
if (props![propName] === undefined) {
|
if ((props as any)[propName] === undefined) {
|
||||||
props![propName] = defaultProps[propName];
|
(props as any)[propName] = defaultProps[propName];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -34,13 +34,20 @@ function getPropDescription(staticProps: any) {
|
|||||||
* visit recursively the props and all the children to check if they are valid.
|
* visit recursively the props and all the children to check if they are valid.
|
||||||
* This is why it is only done in 'dev' mode.
|
* This is why it is only done in 'dev' mode.
|
||||||
*/
|
*/
|
||||||
export const validateProps = function (name: string | typeof Component, props: any, parent?: any) {
|
export function validateProps<P>(name: string | ComponentConstructor<P>, props: P, parent?: any) {
|
||||||
const ComponentClass = (
|
const ComponentClass =
|
||||||
typeof name !== "string" ? name : parent.constructor.components[name]
|
typeof name !== "string"
|
||||||
) as typeof Component;
|
? name
|
||||||
|
: (parent.constructor.components[name] as ComponentConstructor<P> | undefined);
|
||||||
|
|
||||||
|
if (!ComponentClass) {
|
||||||
|
// this is an error, wrong component. We silently return here instead so the
|
||||||
|
// error is triggered by the usual path ('component' function)
|
||||||
|
return;
|
||||||
|
}
|
||||||
applyDefaultProps(props, ComponentClass);
|
applyDefaultProps(props, ComponentClass);
|
||||||
|
|
||||||
|
const defaultProps = ComponentClass.defaultProps || {};
|
||||||
let propsDef = getPropDescription(ComponentClass.props);
|
let propsDef = getPropDescription(ComponentClass.props);
|
||||||
const allowAdditionalProps = "*" in propsDef;
|
const allowAdditionalProps = "*" in propsDef;
|
||||||
|
|
||||||
@@ -48,8 +55,18 @@ export const validateProps = function (name: string | typeof Component, props: a
|
|||||||
if (propName === "*") {
|
if (propName === "*") {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (props[propName] === undefined) {
|
const propDef = propsDef[propName];
|
||||||
if (propsDef[propName] && !propsDef[propName].optional) {
|
let isMandatory = !!propDef;
|
||||||
|
if (typeof propDef === "object" && "optional" in propDef) {
|
||||||
|
isMandatory = !propDef.optional;
|
||||||
|
}
|
||||||
|
if (isMandatory && propName in defaultProps) {
|
||||||
|
throw new Error(
|
||||||
|
`A default value cannot be defined for a mandatory prop (name: '${propName}', component: ${ComponentClass.name})`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if ((props as any)[propName] === undefined) {
|
||||||
|
if (isMandatory) {
|
||||||
throw new Error(`Missing props '${propName}' (component '${ComponentClass.name}')`);
|
throw new Error(`Missing props '${propName}' (component '${ComponentClass.name}')`);
|
||||||
} else {
|
} else {
|
||||||
continue;
|
continue;
|
||||||
@@ -57,7 +74,7 @@ export const validateProps = function (name: string | typeof Component, props: a
|
|||||||
}
|
}
|
||||||
let isValid;
|
let isValid;
|
||||||
try {
|
try {
|
||||||
isValid = isValidProp(props[propName], propsDef[propName]);
|
isValid = isValidProp((props as any)[propName], propDef);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
(e as Error).message = `Invalid prop '${propName}' in component ${ComponentClass.name} (${
|
(e as Error).message = `Invalid prop '${propName}' in component ${ComponentClass.name} (${
|
||||||
(e as Error).message
|
(e as Error).message
|
||||||
@@ -75,7 +92,7 @@ export const validateProps = function (name: string | typeof Component, props: a
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if an invidual prop value matches its (static) prop definition
|
* Check if an invidual prop value matches its (static) prop definition
|
||||||
|
|||||||
+16
-7
@@ -11,7 +11,7 @@ import { onMounted, onPatched, onWillUnmount } from "./component/lifecycle_hooks
|
|||||||
* html node or component.
|
* html node or component.
|
||||||
*/
|
*/
|
||||||
export function useRef<T extends HTMLElement = HTMLElement>(name: string): { el: T | null } {
|
export function useRef<T extends HTMLElement = HTMLElement>(name: string): { el: T | null } {
|
||||||
const node = getCurrent()!;
|
const node = getCurrent();
|
||||||
const refs = node.refs;
|
const refs = node.refs;
|
||||||
return {
|
return {
|
||||||
get el(): T | null {
|
get el(): T | null {
|
||||||
@@ -29,7 +29,13 @@ export function useRef<T extends HTMLElement = HTMLElement>(name: string): { el:
|
|||||||
* need a reference to the env of the component calling them.
|
* need a reference to the env of the component calling them.
|
||||||
*/
|
*/
|
||||||
export function useEnv<E extends Env>(): E {
|
export function useEnv<E extends Env>(): E {
|
||||||
return getCurrent()!.component.env as any;
|
return getCurrent().component.env as any;
|
||||||
|
}
|
||||||
|
|
||||||
|
function extendEnv(currentEnv: Object, extension: Object): Object {
|
||||||
|
const env = Object.create(currentEnv);
|
||||||
|
const descrs = Object.getOwnPropertyDescriptors(extension);
|
||||||
|
return Object.freeze(Object.defineProperties(env, descrs));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -38,12 +44,15 @@ export function useEnv<E extends Env>(): E {
|
|||||||
* constructor method.
|
* constructor method.
|
||||||
*/
|
*/
|
||||||
export function useSubEnv(envExtension: Env) {
|
export function useSubEnv(envExtension: Env) {
|
||||||
const node = getCurrent()!;
|
const node = getCurrent();
|
||||||
const env = Object.create(node.childEnv);
|
node.component.env = extendEnv(node.component.env as any, envExtension);
|
||||||
const descrs = Object.getOwnPropertyDescriptors(envExtension);
|
useChildSubEnv(envExtension);
|
||||||
node.childEnv = Object.freeze(Object.defineProperties(env, descrs));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function useChildSubEnv(envExtension: Env) {
|
||||||
|
const node = getCurrent();
|
||||||
|
node.childEnv = extendEnv(node.childEnv, envExtension);
|
||||||
|
}
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// useEffect
|
// useEffect
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
@@ -112,7 +121,7 @@ export function useExternalListener(
|
|||||||
handler: EventListener,
|
handler: EventListener,
|
||||||
eventParams?: AddEventListenerOptions
|
eventParams?: AddEventListenerOptions
|
||||||
) {
|
) {
|
||||||
const node = getCurrent()!;
|
const node = getCurrent();
|
||||||
const boundHandler = handler.bind(node.component);
|
const boundHandler = handler.bind(node.component);
|
||||||
onMounted(() => target.addEventListener(eventName, boundHandler, eventParams));
|
onMounted(() => target.addEventListener(eventName, boundHandler, eventParams));
|
||||||
onWillUnmount(() => target.removeEventListener(eventName, boundHandler, eventParams));
|
onWillUnmount(() => target.removeEventListener(eventName, boundHandler, eventParams));
|
||||||
|
|||||||
+2
-2
@@ -42,8 +42,8 @@ export { useComponent } from "./component/component_node";
|
|||||||
export { status } from "./component/status";
|
export { status } from "./component/status";
|
||||||
export { Memo } from "./memo";
|
export { Memo } from "./memo";
|
||||||
export { xml } from "./app/template_set";
|
export { xml } from "./app/template_set";
|
||||||
export { useState, reactive } from "./reactivity";
|
export { useState, reactive, markRaw, toRaw } from "./reactivity";
|
||||||
export { useEffect, useEnv, useExternalListener, useRef, useSubEnv } from "./hooks";
|
export { useEffect, useEnv, useExternalListener, useRef, useChildSubEnv, useSubEnv } from "./hooks";
|
||||||
export { EventBus, whenReady, loadFile, markup } from "./utils";
|
export { EventBus, whenReady, loadFile, markup } from "./utils";
|
||||||
export {
|
export {
|
||||||
onWillStart,
|
onWillStart,
|
||||||
|
|||||||
+49
-13
@@ -1,18 +1,27 @@
|
|||||||
import { onWillUnmount } from "./component/lifecycle_hooks";
|
import { onWillDestroy } from "./component/lifecycle_hooks";
|
||||||
import { ComponentNode, getCurrent } from "./component/component_node";
|
import { ComponentNode, getCurrent } from "./component/component_node";
|
||||||
import { batched, Callback } from "./utils";
|
import { batched, Callback } from "./utils";
|
||||||
|
|
||||||
// Allows to get the target of a Reactive (used for making a new Reactive from the underlying object)
|
// Allows to get the target of a Reactive (used for making a new Reactive from the underlying object)
|
||||||
const TARGET = Symbol("Target");
|
const TARGET = Symbol("Target");
|
||||||
|
// Escape hatch to prevent reactivity system to turn something into a reactive
|
||||||
|
const SKIP = Symbol("Skip");
|
||||||
// Special key to subscribe to, to be notified of key creation/deletion
|
// Special key to subscribe to, to be notified of key creation/deletion
|
||||||
const KEYCHANGES = Symbol("Key changes");
|
const KEYCHANGES = Symbol("Key changes");
|
||||||
|
|
||||||
type ObjectKey = string | number | symbol;
|
type ObjectKey = string | number | symbol;
|
||||||
|
|
||||||
type Target = object;
|
type Target = object;
|
||||||
|
|
||||||
export type Reactive<T extends Target = Target> = T & {
|
export type Reactive<T extends Target = Target> = T & {
|
||||||
[TARGET]: any;
|
[TARGET]: any;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type NonReactive<T extends Target = Target> = T & {
|
||||||
|
[SKIP]: any;
|
||||||
|
};
|
||||||
|
const objectToString = Object.prototype.toString;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks whether a given value can be made into a reactive object.
|
* Checks whether a given value can be made into a reactive object.
|
||||||
*
|
*
|
||||||
@@ -20,14 +29,35 @@ export type Reactive<T extends Target = Target> = T & {
|
|||||||
* @returns whether the value can be made reactive
|
* @returns whether the value can be made reactive
|
||||||
*/
|
*/
|
||||||
function canBeMadeReactive(value: any): boolean {
|
function canBeMadeReactive(value: any): boolean {
|
||||||
return (
|
if (typeof value !== "object") {
|
||||||
typeof value === "object" &&
|
return false;
|
||||||
value !== null &&
|
}
|
||||||
!(value instanceof Date) &&
|
// extract "RawType" from strings like "[object RawType]" => this lets us
|
||||||
!(value instanceof Promise) &&
|
// ignore many native objects such as Promise (whose toString is [object Promise])
|
||||||
!(value instanceof String) &&
|
// or Date ([object Date]).
|
||||||
!(value instanceof Number)
|
const rawType = objectToString.call(value).slice(8, -1);
|
||||||
);
|
return rawType === "Object" || rawType === "Array";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mark an object or array so that it is ignored by the reactivity system
|
||||||
|
*
|
||||||
|
* @param value the value to mark
|
||||||
|
* @returns the object itself
|
||||||
|
*/
|
||||||
|
export function markRaw<T extends Target>(value: T): NonReactive<T> {
|
||||||
|
(value as any)[SKIP] = true;
|
||||||
|
return value as NonReactive<T>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given a reactive objet, return the raw (non reactive) underlying object
|
||||||
|
*
|
||||||
|
* @param value a reactive value
|
||||||
|
* @returns the underlying value
|
||||||
|
*/
|
||||||
|
export function toRaw<T extends object>(value: Reactive<T>): T {
|
||||||
|
return value[TARGET] || value;
|
||||||
}
|
}
|
||||||
|
|
||||||
const targetToKeysToCallbacks = new WeakMap<Target, Map<ObjectKey, Set<Callback>>>();
|
const targetToKeysToCallbacks = new WeakMap<Target, Map<ObjectKey, Set<Callback>>>();
|
||||||
@@ -130,10 +160,16 @@ const reactiveCache = new WeakMap<Target, WeakMap<Callback, Reactive>>();
|
|||||||
* reactive has changed
|
* reactive has changed
|
||||||
* @returns a proxy that tracks changes to it
|
* @returns a proxy that tracks changes to it
|
||||||
*/
|
*/
|
||||||
export function reactive<T extends Target>(target: T, callback: Callback = () => {}): Reactive<T> {
|
export function reactive<T extends Target>(
|
||||||
|
target: T,
|
||||||
|
callback: Callback = () => {}
|
||||||
|
): Reactive<T> | NonReactive<T> {
|
||||||
if (!canBeMadeReactive(target)) {
|
if (!canBeMadeReactive(target)) {
|
||||||
throw new Error(`Cannot make the given value reactive`);
|
throw new Error(`Cannot make the given value reactive`);
|
||||||
}
|
}
|
||||||
|
if (SKIP in target) {
|
||||||
|
return target as NonReactive<T>;
|
||||||
|
}
|
||||||
const originalTarget = (target as Reactive)[TARGET];
|
const originalTarget = (target as Reactive)[TARGET];
|
||||||
if (originalTarget) {
|
if (originalTarget) {
|
||||||
return reactive(originalTarget, callback);
|
return reactive(originalTarget, callback);
|
||||||
@@ -202,14 +238,14 @@ const batchedRenderFunctions = new WeakMap<ComponentNode, Callback>();
|
|||||||
* relevant changes
|
* relevant changes
|
||||||
* @see reactive
|
* @see reactive
|
||||||
*/
|
*/
|
||||||
export function useState<T extends object>(state: T): Reactive<T> {
|
export function useState<T extends object>(state: T): Reactive<T> | NonReactive<T> {
|
||||||
const node = getCurrent()!;
|
const node = getCurrent();
|
||||||
if (!batchedRenderFunctions.has(node)) {
|
if (!batchedRenderFunctions.has(node)) {
|
||||||
batchedRenderFunctions.set(
|
batchedRenderFunctions.set(
|
||||||
node,
|
node,
|
||||||
batched(() => node.render())
|
batched(() => node.render())
|
||||||
);
|
);
|
||||||
onWillUnmount(() => clearReactivesForCallback(render));
|
onWillDestroy(() => clearReactivesForCallback(render));
|
||||||
}
|
}
|
||||||
const render = batchedRenderFunctions.get(node)!;
|
const render = batchedRenderFunctions.get(node)!;
|
||||||
const reactiveState = reactive(state, render);
|
const reactiveState = reactive(state, render);
|
||||||
|
|||||||
@@ -24,6 +24,15 @@ export function batched(callback: Callback): Callback {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function validateTarget(target: HTMLElement) {
|
||||||
|
if (!(target instanceof HTMLElement)) {
|
||||||
|
throw new Error("Cannot mount component: the target is not a valid DOM element");
|
||||||
|
}
|
||||||
|
if (!document.body.contains(target)) {
|
||||||
|
throw new Error("Cannot mount a component on a detached dom node");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export class EventBus extends EventTarget {
|
export class EventBus extends EventTarget {
|
||||||
trigger(name: string, payload?: any) {
|
trigger(name: string, payload?: any) {
|
||||||
this.dispatchEvent(new CustomEvent(name, { detail: payload }));
|
this.dispatchEvent(new CustomEvent(name, { detail: payload }));
|
||||||
|
|||||||
@@ -169,6 +169,15 @@ describe("properties", () => {
|
|||||||
expect(input.value).toBe("potato");
|
expect(input.value).toBe("potato");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("input with value attribute, and undefined given", () => {
|
||||||
|
const block = createBlock(`<input block-attribute-0="value"/>`);
|
||||||
|
|
||||||
|
const tree = block([undefined]);
|
||||||
|
mount(tree, fixture);
|
||||||
|
const input = fixture.querySelector("input")!;
|
||||||
|
expect(input.value).toBe("");
|
||||||
|
});
|
||||||
|
|
||||||
test("input type=checkbox with checked attribute", () => {
|
test("input type=checkbox with checked attribute", () => {
|
||||||
// render input with initial value
|
// render input with initial value
|
||||||
const block = createBlock(`<input type="checkbox" block-attribute-0="checked"/>`);
|
const block = createBlock(`<input type="checkbox" block-attribute-0="checked"/>`);
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { createBlock, mount, patch, remove } from "../../src/blockdom";
|
import { createBlock, mount, patch, remove } from "../../src/blockdom";
|
||||||
|
import { logStep } from "../helpers";
|
||||||
import { makeTestFixture } from "./helpers";
|
import { makeTestFixture } from "./helpers";
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
@@ -55,3 +56,15 @@ test("is in dom when callback is called", async () => {
|
|||||||
|
|
||||||
mount(tree, fixture);
|
mount(tree, fixture);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("callback ref in callback ref with same block", async () => {
|
||||||
|
const block = createBlock('<p block-ref="0"><block-text-1/><block-child-0/></p>');
|
||||||
|
let refFn = (el: HTMLParagraphElement) => logStep(el.outerHTML);
|
||||||
|
|
||||||
|
const child = block([refFn, "child"], []);
|
||||||
|
const parent = block([refFn, "parent"], [child]);
|
||||||
|
mount(parent, fixture);
|
||||||
|
|
||||||
|
expect(fixture.innerHTML).toBe("<p>parent<p>child</p></p>");
|
||||||
|
expect(["<p>child</p>", "<p>parent<p>child</p></p>"]).toBeLogged();
|
||||||
|
});
|
||||||
|
|||||||
@@ -353,7 +353,7 @@ exports[`t-on t-on modifiers (native listener) t-on with prevent modifier in t-f
|
|||||||
let key1 = ctx['project'];
|
let key1 = ctx['project'];
|
||||||
const v1 = ctx['onEdit'];
|
const v1 = ctx['onEdit'];
|
||||||
const v2 = ctx['project'];
|
const v2 = ctx['project'];
|
||||||
let hdlr1 = [\\"prevent\\", ev=>v1(v2.id,ev), ctx];
|
let hdlr1 = [\\"prevent\\", _ev=>v1(v2.id,_ev), ctx];
|
||||||
let txt1 = ctx['project'].name;
|
let txt1 = ctx['project'].name;
|
||||||
c_block2[i1] = withKey(block3([hdlr1, txt1]), key1);
|
c_block2[i1] = withKey(block3([hdlr1, txt1]), key1);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,11 +30,11 @@ exports[`misc complex template 1`] = `
|
|||||||
b3 = block3();
|
b3 = block3();
|
||||||
}
|
}
|
||||||
ctx = Object.create(ctx);
|
ctx = Object.create(ctx);
|
||||||
const [k_block4, v_block4, l_block4, c_block4] = prepareList(ctx['batch'].slot_ids.filter(slot=>slot.build_id.id&&!slot.trigger_id.manual&&(ctx['options'].trigger_display[slot.trigger_id.id])));
|
const [k_block4, v_block4, l_block4, c_block4] = prepareList(ctx['batch'].slot_ids.filter(_slot=>_slot.build_id.id&&!_slot.trigger_id.manual&&(ctx['options'].trigger_display[_slot.trigger_id.id])));
|
||||||
for (let i1 = 0; i1 < l_block4; i1++) {
|
for (let i1 = 0; i1 < l_block4; i1++) {
|
||||||
ctx[\`slot\`] = v_block4[i1];
|
ctx[\`slot\`] = v_block4[i1];
|
||||||
let key1 = ctx['slot'].id;
|
let key1 = ctx['slot'].id;
|
||||||
c_block4[i1] = withKey(component(\`SlotButton\`, {class: ctx['slot_container'],slot: ctx['slot']}, key + \`__1__\${key1}\`, node, ctx), key1);
|
c_block4[i1] = withKey(component(\`SlotButton\`, {class: ctx['slot_container'], slot: ctx['slot']}, key + \`__1__\${key1}\`, node, ctx), key1);
|
||||||
}
|
}
|
||||||
ctx = ctx.__proto__;
|
ctx = ctx.__proto__;
|
||||||
b4 = list(c_block4);
|
b4 = list(c_block4);
|
||||||
@@ -316,8 +316,8 @@ exports[`misc other complex template 1`] = `
|
|||||||
if (!ctx['project']) {
|
if (!ctx['project']) {
|
||||||
b24 = block24();
|
b24 = block24();
|
||||||
} else {
|
} else {
|
||||||
let b26 = component(\`BundlesList\`, {bundles: ctx['bundles'].sticky,category_custom_views: ctx['category_custom_views'],search: ctx['search']}, key + \`__2\`, node, ctx);
|
let b26 = component(\`BundlesList\`, {bundles: ctx['bundles'].sticky, category_custom_views: ctx['category_custom_views'], search: ctx['search']}, key + \`__2\`, node, ctx);
|
||||||
let b27 = component(\`BundlesList\`, {bundles: ctx['bundles'].dev,search: ctx['search']}, key + \`__3\`, node, ctx);
|
let b27 = component(\`BundlesList\`, {bundles: ctx['bundles'].dev, search: ctx['search']}, key + \`__3\`, node, ctx);
|
||||||
b25 = block25([], [b26, b27]);
|
b25 = block25([], [b26, b27]);
|
||||||
}
|
}
|
||||||
return block1([attr1, txt1, hdlr2, hdlr3, attr8, hdlr4, hdlr5, ref1, hdlr6, ref2], [b2, b4, b14, b17, b22, b23, b24, b25]);
|
return block1([attr1, txt1, hdlr2, hdlr3, attr8, hdlr4, hdlr5, ref1, hdlr6, ref2], [b2, b4, b14, b17, b22, b23, b24, b25]);
|
||||||
|
|||||||
@@ -52,12 +52,47 @@ exports[`properly support svg namespace to svg tags added even if already in svg
|
|||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`properly support svg svg namespace added to sub-blocks (t-call) 1`] = `
|
exports[`properly support svg svg creates new block if it is within html -- 2 1`] = `
|
||||||
|
"function anonymous(bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
|
||||||
|
|
||||||
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
let block2 = createBlock(\`<svg block-ns=\\"http://www.w3.org/2000/svg\\"><polygon fill=\\"#000000\\" points=\\"0 0 4 4 8 0\\" transform=\\"translate(5 7)\\"/><block-child-0/></svg>\`);
|
||||||
|
let block3 = createBlock(\`<path block-ns=\\"http://www.w3.org/2000/svg\\"/>\`);
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
let b3;
|
||||||
|
if (ctx['hasPath']) {
|
||||||
|
b3 = block3();
|
||||||
|
}
|
||||||
|
let b2 = block2([], [b3]);
|
||||||
|
return block1([], [b2]);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`properly support svg svg creates new block if it is within html 1`] = `
|
||||||
|
"function anonymous(bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
|
||||||
|
|
||||||
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
let block2 = createBlock(\`<svg block-ns=\\"http://www.w3.org/2000/svg\\"><polygon fill=\\"#000000\\" points=\\"0 0 4 4 8 0\\" transform=\\"translate(5 7)\\"/></svg>\`);
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
let b2 = block2();
|
||||||
|
return block1([], [b2]);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`properly support svg svg namespace added to sub templates if root tag is path 1`] = `
|
||||||
"function anonymous(bdom, helpers
|
"function anonymous(bdom, helpers
|
||||||
) {
|
) {
|
||||||
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
|
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
|
||||||
let { getTemplate } = helpers;
|
let { getTemplate } = helpers;
|
||||||
const callTemplate_1 = getTemplate(\`path\`, \\"http://www.w3.org/2000/svg\\");
|
const callTemplate_1 = getTemplate(\`path\`);
|
||||||
|
|
||||||
let block1 = createBlock(\`<svg block-ns=\\"http://www.w3.org/2000/svg\\"><block-child-0/></svg>\`);
|
let block1 = createBlock(\`<svg block-ns=\\"http://www.w3.org/2000/svg\\"><block-child-0/></svg>\`);
|
||||||
|
|
||||||
@@ -68,12 +103,12 @@ exports[`properly support svg svg namespace added to sub-blocks (t-call) 1`] = `
|
|||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`properly support svg svg namespace added to sub-blocks (t-call) 2`] = `
|
exports[`properly support svg svg namespace added to sub templates if root tag is path 2`] = `
|
||||||
"function anonymous(bdom, helpers
|
"function anonymous(bdom, helpers
|
||||||
) {
|
) {
|
||||||
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
|
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
|
||||||
|
|
||||||
let block1 = createBlock(\`<path/>\`);
|
let block1 = createBlock(\`<path block-ns=\\"http://www.w3.org/2000/svg\\"/>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
return block1();
|
return block1();
|
||||||
|
|||||||
@@ -407,6 +407,36 @@ exports[`t-out variable 1`] = `
|
|||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`t-out with a String class 1`] = `
|
||||||
|
"function anonymous(bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
|
||||||
|
let { safeOutput } = helpers;
|
||||||
|
|
||||||
|
let block1 = createBlock(\`<span><block-child-0/></span>\`);
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
let b2 = safeOutput(ctx['var']);
|
||||||
|
return block1([], [b2]);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`t-out with an extended String class 1`] = `
|
||||||
|
"function anonymous(bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
|
||||||
|
let { safeOutput } = helpers;
|
||||||
|
|
||||||
|
let block1 = createBlock(\`<span><block-child-0/></span>\`);
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
let b2 = safeOutput(ctx['var']);
|
||||||
|
return block1([], [b2]);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`t-raw is deprecated should warn 1`] = `
|
exports[`t-raw is deprecated should warn 1`] = `
|
||||||
"function anonymous(bdom, helpers
|
"function anonymous(bdom, helpers
|
||||||
) {
|
) {
|
||||||
|
|||||||
@@ -1,5 +1,19 @@
|
|||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`loading templates addTemplates does not modify its xml document in place 1`] = `
|
||||||
|
"function anonymous(bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
|
||||||
|
|
||||||
|
let block1 = createBlock(\`<div><block-text-0/></div>\`);
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
let txt1 = ctx['value'];
|
||||||
|
return block1([txt1]);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`loading templates can initialize qweb with a string 1`] = `
|
exports[`loading templates can initialize qweb with a string 1`] = `
|
||||||
"function anonymous(bdom, helpers
|
"function anonymous(bdom, helpers
|
||||||
) {
|
) {
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
exports[`translation support can set translatable attributes 1`] = `
|
exports[`translation support can set and remove translatable attributes 1`] = `
|
||||||
"function anonymous(bdom, helpers
|
"function anonymous(bdom, helpers
|
||||||
) {
|
) {
|
||||||
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
|
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
|
||||||
|
|
||||||
let block1 = createBlock(\`<div tomato=\\"word\\" potato=\\"mot\\" title=\\"word\\">text</div>\`);
|
let block1 = createBlock(\`<div tomato=\\"word\\" potato=\\"mot\\" title=\\"mot\\" label=\\"word\\">text</div>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
return block1();
|
return block1();
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import { renderToString } from "../helpers";
|
||||||
|
|
||||||
|
describe("blacklisted tags and attributes", () => {
|
||||||
|
test("template with block-text tag", () => {
|
||||||
|
const template = `<div><block-text-0/>hello</div>`;
|
||||||
|
expect(() => renderToString(template)).toThrow("Invalid tag name: 'block-text-0'");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("template with block-handler tag", () => {
|
||||||
|
const template = `<div block-handler-0="click">hello</div>`;
|
||||||
|
expect(() => renderToString(template)).toThrow("Invalid attribute: 'block-handler-0'");
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -163,17 +163,21 @@ describe("expression evaluation", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test("arrow functions", () => {
|
test("arrow functions", () => {
|
||||||
expect(compileExpr("list.map(e => e.val)")).toBe("ctx['list'].map(e=>e.val)");
|
expect(compileExpr("list.map(e => e.val)")).toBe("ctx['list'].map(_e=>_e.val)");
|
||||||
expect(compileExpr("list.map(e => a + e)")).toBe("ctx['list'].map(e=>ctx['a']+e)");
|
expect(compileExpr("list.map(e => a + e)")).toBe("ctx['list'].map(_e=>ctx['a']+_e)");
|
||||||
expect(compileExpr("list.map((e) => e)")).toBe("ctx['list'].map((e)=>e)");
|
expect(compileExpr("list.map((e) => e)")).toBe("ctx['list'].map((_e)=>_e)");
|
||||||
expect(compileExpr("list.map((elem, index) => elem + index)")).toBe(
|
expect(compileExpr("list.map((elem, index) => elem + index)")).toBe(
|
||||||
"ctx['list'].map((elem,index)=>elem+index)"
|
"ctx['list'].map((_elem,_index)=>_elem+_index)"
|
||||||
|
);
|
||||||
|
expect(compileExpr("(ev => ev)(e)")).toBe("(_ev=>_ev)(ctx['e'])");
|
||||||
|
expect(compileExpr("(v1) => myFunc(v1)")).toBe("(_v1)=>ctx['myFunc'](_v1)");
|
||||||
|
expect(compileExpr("list.data.map((data) => data)")).toBe(
|
||||||
|
"ctx['list'].data.map((_data)=>_data)"
|
||||||
);
|
);
|
||||||
expect(compileExpr("(ev => ev)(e)")).toBe("(ev=>ev)(ctx['e'])");
|
|
||||||
});
|
});
|
||||||
test.skip("arrow functions: not yet supported", () => {
|
test.skip("arrow functions: not yet supported", () => {
|
||||||
// e is added to localvars in inline_expression but not removed after the arrow func body
|
// e is added to localvars in inline_expression but not removed after the arrow func body
|
||||||
expect(compileExpr("(e => e)(e)")).toBe("(e=>e)(ctx['e'])");
|
expect(compileExpr("(e => e)(e)")).toBe("(_e=>_e)(ctx['e'])");
|
||||||
});
|
});
|
||||||
|
|
||||||
test("assignation", () => {
|
test("assignation", () => {
|
||||||
|
|||||||
@@ -1132,6 +1132,81 @@ describe("qweb parser", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// t-model
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
test("t-model select", async () => {
|
||||||
|
expect(parse(`<select t-model="state.model"><option value="1" /></select>`)).toEqual({
|
||||||
|
type: 2,
|
||||||
|
tag: "select",
|
||||||
|
dynamicTag: null,
|
||||||
|
attrs: {},
|
||||||
|
on: {},
|
||||||
|
ref: null,
|
||||||
|
content: [
|
||||||
|
{
|
||||||
|
type: 2,
|
||||||
|
tag: "option",
|
||||||
|
dynamicTag: null,
|
||||||
|
attrs: { value: "1" },
|
||||||
|
on: {},
|
||||||
|
ref: null,
|
||||||
|
content: [],
|
||||||
|
model: null,
|
||||||
|
ns: null,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
model: {
|
||||||
|
baseExpr: "state",
|
||||||
|
expr: "'model'",
|
||||||
|
targetAttr: "value",
|
||||||
|
specialInitTargetAttr: null,
|
||||||
|
eventType: "change",
|
||||||
|
shouldTrim: false,
|
||||||
|
shouldNumberize: false,
|
||||||
|
},
|
||||||
|
ns: null,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test("t-model select dynamic options", async () => {
|
||||||
|
expect(
|
||||||
|
parse(`<select t-model="state.model"><option t-att-value="valueVar" /></select>`)
|
||||||
|
).toEqual({
|
||||||
|
type: 2,
|
||||||
|
tag: "select",
|
||||||
|
dynamicTag: null,
|
||||||
|
attrs: {},
|
||||||
|
on: {},
|
||||||
|
ref: null,
|
||||||
|
content: [
|
||||||
|
{
|
||||||
|
type: 2,
|
||||||
|
tag: "option",
|
||||||
|
dynamicTag: null,
|
||||||
|
attrs: { "t-att-value": "valueVar" },
|
||||||
|
on: {},
|
||||||
|
ref: null,
|
||||||
|
content: [],
|
||||||
|
model: null,
|
||||||
|
ns: null,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
model: {
|
||||||
|
baseExpr: "state",
|
||||||
|
expr: "'model'",
|
||||||
|
targetAttr: "value",
|
||||||
|
specialInitTargetAttr: null,
|
||||||
|
eventType: "change",
|
||||||
|
shouldTrim: false,
|
||||||
|
shouldNumberize: false,
|
||||||
|
hasDynamicChildren: true,
|
||||||
|
},
|
||||||
|
ns: null,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// t-component
|
// t-component
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { renderToString, renderToBdom, snapshotEverything, makeTestFixture } from "../helpers";
|
import { renderToString, renderToBdom, snapshotEverything, makeTestFixture } from "../helpers";
|
||||||
import { mount } from "../../src/blockdom";
|
import { mount } from "../../src/blockdom";
|
||||||
import { mount as mountComponent, Component } from "../../src/index"
|
import { mount as mountComponent, Component, xml } from "../../src/index";
|
||||||
|
|
||||||
// NB: check the snapshots to see where the SVG namespaces are added
|
// NB: check the snapshots to see where the SVG namespaces are added
|
||||||
snapshotEverything();
|
snapshotEverything();
|
||||||
@@ -55,23 +55,63 @@ describe("properly support svg", () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
test.only("svg namespace added to sub-blocks (t-call)", async () => {
|
test("svg namespace added to sub templates if root tag is path", async () => {
|
||||||
const templates = `<t>
|
const templates = `<t>
|
||||||
<t t-name="svg"><svg><t t-call="path" /></svg></t>
|
<t t-name="svg"><svg><t t-call="path" /></svg></t>
|
||||||
<t t-name="path"><path /></t>
|
<t t-name="path"><path /></t>
|
||||||
</t>
|
</t>
|
||||||
`
|
`;
|
||||||
const fixture = makeTestFixture();
|
const fixture = makeTestFixture();
|
||||||
|
|
||||||
class Svg extends Component {
|
class Svg extends Component {
|
||||||
static template = "svg";
|
static template = "svg";
|
||||||
}
|
}
|
||||||
|
|
||||||
await mountComponent(Svg, fixture, {templates})
|
await mountComponent(Svg, fixture, { templates });
|
||||||
const elems = fixture.querySelectorAll("svg, path");
|
const elems = fixture.querySelectorAll("svg, path");
|
||||||
expect(elems.length).toEqual(2);
|
expect(elems.length).toEqual(2);
|
||||||
for (const el of elems) {
|
for (const el of elems) {
|
||||||
expect(el.namespaceURI).toBe("http://www.w3.org/2000/svg");
|
expect(el.namespaceURI).toBe("http://www.w3.org/2000/svg");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("svg creates new block if it is within html", async () => {
|
||||||
|
class Test extends Component {
|
||||||
|
static template = xml`
|
||||||
|
<div>
|
||||||
|
<svg>
|
||||||
|
<polygon fill="#000000" points="0 0 4 4 8 0" transform="translate(5 7)"/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
const fixture = makeTestFixture();
|
||||||
|
await mountComponent(Test, fixture);
|
||||||
|
const elems = fixture.querySelectorAll("svg, polygon");
|
||||||
|
expect(elems.length).toEqual(2);
|
||||||
|
for (const el of elems) {
|
||||||
|
expect(el.namespaceURI).toBe("http://www.w3.org/2000/svg");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test("svg creates new block if it is within html -- 2", async () => {
|
||||||
|
class Test extends Component {
|
||||||
|
static template = xml`
|
||||||
|
<div>
|
||||||
|
<svg>
|
||||||
|
<polygon fill="#000000" points="0 0 4 4 8 0" transform="translate(5 7)"/>
|
||||||
|
<path t-if="hasPath" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
hasPath = true;
|
||||||
|
}
|
||||||
|
const fixture = makeTestFixture();
|
||||||
|
await mountComponent(Test, fixture);
|
||||||
|
const elems = fixture.querySelectorAll("svg, polygon, path");
|
||||||
|
expect(elems.length).toEqual(3);
|
||||||
|
for (const el of elems) {
|
||||||
|
expect(el.namespaceURI).toBe("http://www.w3.org/2000/svg");
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -37,6 +37,26 @@ describe("t-out", () => {
|
|||||||
expect(renderToString(template, { var: "ok" })).toBe("<span>ok</span>");
|
expect(renderToString(template, { var: "ok" })).toBe("<span>ok</span>");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("with a String class", () => {
|
||||||
|
const template = `<span><t t-out="var"/></span>`;
|
||||||
|
expect(renderToString(template, { var: new String("ok") })).toBe("<span>ok</span>");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("with an extended String class", () => {
|
||||||
|
class LoveString extends String {
|
||||||
|
valueOf(): string {
|
||||||
|
return `<3 ${super.valueOf()} <3`;
|
||||||
|
}
|
||||||
|
toString(): string {
|
||||||
|
return this.valueOf();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const template = `<span><t t-out="var"/></span>`;
|
||||||
|
expect(renderToString(template, { var: new LoveString("ok") })).toBe(
|
||||||
|
"<span><3 ok <3</span>"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
test("not escaping", () => {
|
test("not escaping", () => {
|
||||||
const template = `<div><t t-out="var"/></div>`;
|
const template = `<div><t t-out="var"/></div>`;
|
||||||
expect(renderToString(template, { var: markup("<ok></ok>") })).toBe("<div><ok></ok></div>");
|
expect(renderToString(template, { var: markup("<ok></ok>") })).toBe("<div><ok></ok></div>");
|
||||||
|
|||||||
@@ -24,6 +24,17 @@ describe("loading templates", () => {
|
|||||||
expect(context.renderToString("hey")).toBe("<div>jupiler</div>");
|
expect(context.renderToString("hey")).toBe("<div>jupiler</div>");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("addTemplates does not modify its xml document in place", () => {
|
||||||
|
const data = `<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<templates id="template" xml:space="preserve"><div t-name="hey"><t t-esc="value"/></div></templates>`;
|
||||||
|
const xml = new DOMParser().parseFromString(data, "text/xml");
|
||||||
|
const context = new TestContext();
|
||||||
|
expect(xml.firstElementChild!.innerHTML).toBe(`<div t-name="hey"><t t-esc="value"/></div>`);
|
||||||
|
context.addTemplates(xml);
|
||||||
|
expect(context.renderToString("hey", { value: 123 })).toBe("<div>123</div>");
|
||||||
|
expect(xml.firstElementChild!.innerHTML).toBe(`<div t-name="hey"><t t-esc="value"/></div>`);
|
||||||
|
});
|
||||||
|
|
||||||
test("can load a few templates from a xml string", () => {
|
test("can load a few templates from a xml string", () => {
|
||||||
const data = `<?xml version="1.0" encoding="UTF-8"?>
|
const data = `<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<templates id="template" xml:space="preserve">
|
<templates id="template" xml:space="preserve">
|
||||||
|
|||||||
@@ -59,18 +59,20 @@ describe("translation support", () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("can set translatable attributes", async () => {
|
test("can set and remove translatable attributes", async () => {
|
||||||
class SomeComponent extends Component {
|
class SomeComponent extends Component {
|
||||||
static template = xml`
|
static template = xml`
|
||||||
<div tomato="word" potato="word" title="word">text</div>
|
<div tomato="word" potato="word" title="word" label="word">text</div>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
await mount(SomeComponent, fixture, {
|
await mount(SomeComponent, fixture, {
|
||||||
translateFn: (expr: string) => (expr === "word" ? "mot" : expr),
|
translateFn: (expr: string) => (expr === "word" ? "mot" : expr),
|
||||||
translatableAttributes: ["potato"],
|
translatableAttributes: ["potato", "-label"],
|
||||||
});
|
});
|
||||||
expect(fixture.innerHTML).toBe('<div tomato="word" potato="mot" title="word">text</div>');
|
expect(fixture.innerHTML).toBe(
|
||||||
|
'<div tomato="word" potato="mot" title="mot" label="word">text</div>'
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("translation is done on the trimmed text, with extra spaces readded after", async () => {
|
test("translation is done on the trimmed text, with extra spaces readded after", async () => {
|
||||||
|
|||||||
@@ -81,6 +81,19 @@ exports[`basics a class component inside a class component, no external dom 2`]
|
|||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`basics a component cannot be mounted in a detached node (even if node is detached later) 1`] = `
|
||||||
|
"function anonymous(bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
|
||||||
|
|
||||||
|
let block1 = createBlock(\`<div/>\`);
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
return block1();
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`basics a component inside a component 1`] = `
|
exports[`basics a component inside a component 1`] = `
|
||||||
"function anonymous(bdom, helpers
|
"function anonymous(bdom, helpers
|
||||||
) {
|
) {
|
||||||
@@ -968,7 +981,7 @@ exports[`basics update props of component without concrete own node 1`] = `
|
|||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const tKey_1 = ctx['childProps'].key;
|
const tKey_1 = ctx['childProps'].key;
|
||||||
let b2 = toggler(tKey_1, component(\`Child\`, ctx['childProps'], tKey_1 + key + \`__1\`, node, ctx));
|
let b2 = toggler(tKey_1, component(\`Child\`, Object.assign({}, ctx['childProps']), tKey_1 + key + \`__1\`, node, ctx));
|
||||||
return block1([], [b2]);
|
return block1([], [b2]);
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
@@ -981,7 +994,7 @@ exports[`basics update props of component without concrete own node 2`] = `
|
|||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const tKey_1 = ctx['props'].subKey;
|
const tKey_1 = ctx['props'].subKey;
|
||||||
return toggler(tKey_1, component(\`Custom\`, {key: ctx['props'].key,subKey: ctx['props'].subKey}, tKey_1 + key + \`__1\`, node, ctx));
|
return toggler(tKey_1, component(\`Custom\`, {key: ctx['props'].key, subKey: ctx['props'].subKey}, tKey_1 + key + \`__1\`, node, ctx));
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
@@ -1217,6 +1230,20 @@ exports[`t-out in components can render list of t-out 1`] = `
|
|||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`t-out in components can switch the contents of two t-out repeatedly 1`] = `
|
||||||
|
"function anonymous(bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
|
||||||
|
let { safeOutput } = helpers;
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
let b2 = safeOutput(ctx['state'].a);
|
||||||
|
let b3 = safeOutput(ctx['state'].b);
|
||||||
|
return multi([b2, b3]);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`t-out in components update properly on state changes 1`] = `
|
exports[`t-out in components update properly on state changes 1`] = `
|
||||||
"function anonymous(bdom, helpers
|
"function anonymous(bdom, helpers
|
||||||
) {
|
) {
|
||||||
|
|||||||
@@ -183,7 +183,7 @@ exports[`concurrent renderings scenario 1 2`] = `
|
|||||||
let block1 = createBlock(\`<p><block-child-0/></p>\`);
|
let block1 = createBlock(\`<p><block-child-0/></p>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
let b2 = component(\`ComponentC\`, {fromA: ctx['props'].fromA,fromB: ctx['state'].fromB}, key + \`__1\`, node, ctx);
|
let b2 = component(\`ComponentC\`, {fromA: ctx['props'].fromA, fromB: ctx['state'].fromB}, key + \`__1\`, node, ctx);
|
||||||
return block1([], [b2]);
|
return block1([], [b2]);
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
@@ -227,7 +227,7 @@ exports[`concurrent renderings scenario 2 2`] = `
|
|||||||
let block1 = createBlock(\`<p><block-child-0/></p>\`);
|
let block1 = createBlock(\`<p><block-child-0/></p>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
let b2 = component(\`ComponentC\`, {fromA: ctx['props'].fromA,fromB: ctx['state'].fromB}, key + \`__1\`, node, ctx);
|
let b2 = component(\`ComponentC\`, {fromA: ctx['props'].fromA, fromB: ctx['state'].fromB}, key + \`__1\`, node, ctx);
|
||||||
return block1([], [b2]);
|
return block1([], [b2]);
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
@@ -270,7 +270,7 @@ exports[`concurrent renderings scenario 2bis 2`] = `
|
|||||||
let block1 = createBlock(\`<p><block-child-0/></p>\`);
|
let block1 = createBlock(\`<p><block-child-0/></p>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
let b2 = component(\`ComponentC\`, {fromA: ctx['props'].fromA,fromB: ctx['state'].fromB}, key + \`__1\`, node, ctx);
|
let b2 = component(\`ComponentC\`, {fromA: ctx['props'].fromA, fromB: ctx['state'].fromB}, key + \`__1\`, node, ctx);
|
||||||
return block1([], [b2]);
|
return block1([], [b2]);
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
@@ -327,7 +327,7 @@ exports[`concurrent renderings scenario 3 3`] = `
|
|||||||
let block1 = createBlock(\`<span><block-child-0/></span>\`);
|
let block1 = createBlock(\`<span><block-child-0/></span>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
let b2 = component(\`ComponentD\`, {fromA: ctx['props'].fromA,fromC: ctx['state'].fromC}, key + \`__1\`, node, ctx);
|
let b2 = component(\`ComponentD\`, {fromA: ctx['props'].fromA, fromC: ctx['state'].fromC}, key + \`__1\`, node, ctx);
|
||||||
return block1([], [b2]);
|
return block1([], [b2]);
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
@@ -384,7 +384,7 @@ exports[`concurrent renderings scenario 4 3`] = `
|
|||||||
let block1 = createBlock(\`<span><block-child-0/></span>\`);
|
let block1 = createBlock(\`<span><block-child-0/></span>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
let b2 = component(\`ComponentD\`, {fromA: ctx['props'].fromA,fromC: ctx['state'].fromC}, key + \`__1\`, node, ctx);
|
let b2 = component(\`ComponentD\`, {fromA: ctx['props'].fromA, fromC: ctx['state'].fromC}, key + \`__1\`, node, ctx);
|
||||||
return block1([], [b2]);
|
return block1([], [b2]);
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
@@ -557,7 +557,7 @@ exports[`concurrent renderings scenario 9 3`] = `
|
|||||||
let block1 = createBlock(\`<p><block-child-0/></p>\`);
|
let block1 = createBlock(\`<p><block-child-0/></p>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
let b2 = component(\`ComponentD\`, {fromA: ctx['props'].fromA,fromC: ctx['state'].fromC}, key + \`__1\`, node, ctx);
|
let b2 = component(\`ComponentD\`, {fromA: ctx['props'].fromA, fromC: ctx['state'].fromC}, key + \`__1\`, node, ctx);
|
||||||
return block1([], [b2]);
|
return block1([], [b2]);
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
@@ -734,7 +734,7 @@ exports[`concurrent renderings scenario 14 2`] = `
|
|||||||
let block1 = createBlock(\`<p><block-child-0/></p>\`);
|
let block1 = createBlock(\`<p><block-child-0/></p>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
let b2 = component(\`C\`, {fromB: ctx['state'].fromB,fromA: ctx['props'].fromA}, key + \`__1\`, node, ctx);
|
let b2 = component(\`C\`, {fromB: ctx['state'].fromB, fromA: ctx['props'].fromA}, key + \`__1\`, node, ctx);
|
||||||
return block1([], [b2]);
|
return block1([], [b2]);
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
@@ -778,7 +778,7 @@ exports[`concurrent renderings scenario 15 2`] = `
|
|||||||
let block1 = createBlock(\`<p><block-child-0/></p>\`);
|
let block1 = createBlock(\`<p><block-child-0/></p>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
let b2 = component(\`C\`, {fromB: ctx['state'].fromB,fromA: ctx['props'].fromA}, key + \`__1\`, node, ctx);
|
let b2 = component(\`C\`, {fromB: ctx['state'].fromB, fromA: ctx['props'].fromA}, key + \`__1\`, node, ctx);
|
||||||
return block1([], [b2]);
|
return block1([], [b2]);
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
@@ -817,7 +817,7 @@ exports[`concurrent renderings scenario 16 2`] = `
|
|||||||
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
|
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
return component(\`C\`, {fromB: ctx['state'].fromB,fromA: ctx['props'].fromA}, key + \`__1\`, node, ctx);
|
return component(\`C\`, {fromB: ctx['state'].fromB, fromA: ctx['props'].fromA}, key + \`__1\`, node, ctx);
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|||||||
@@ -1,5 +1,18 @@
|
|||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`basics display a nice error if it cannot find component (in dev mode) 1`] = `
|
||||||
|
"function anonymous(bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
const props1 = {};
|
||||||
|
helpers.validateProps(\`SomeMispelledComponent\`, props1, ctx);
|
||||||
|
return component(\`SomeMispelledComponent\`, props1, key + \`__1\`, node, ctx);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`basics display a nice error if it cannot find component 1`] = `
|
exports[`basics display a nice error if it cannot find component 1`] = `
|
||||||
"function anonymous(bdom, helpers
|
"function anonymous(bdom, helpers
|
||||||
) {
|
) {
|
||||||
@@ -72,6 +85,17 @@ exports[`basics simple catchError 2`] = `
|
|||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`can catch errors calling a hook outside setup should crash 1`] = `
|
||||||
|
"function anonymous(bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
return text(ctx['state'].value);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`can catch errors can catch an error in a component render function 1`] = `
|
exports[`can catch errors can catch an error in a component render function 1`] = `
|
||||||
"function anonymous(bdom, helpers
|
"function anonymous(bdom, helpers
|
||||||
) {
|
) {
|
||||||
@@ -799,7 +823,7 @@ exports[`can catch errors catching in child makes parent render 1`] = `
|
|||||||
let key1 = ctx['elem'][0];
|
let key1 = ctx['elem'][0];
|
||||||
const v1 = ctx['elem'];
|
const v1 = ctx['elem'];
|
||||||
const ctx1 = capture(ctx);
|
const ctx1 = capture(ctx);
|
||||||
c_block1[i1] = withKey(component(\`Catch\`, {onError: (error)=>this.onError(v1[0],error),slots: {'default': {__render: slot1, __ctx: ctx1}}}, key + \`__2__\${key1}\`, node, ctx), key1);
|
c_block1[i1] = withKey(component(\`Catch\`, {onError: (_error)=>this.onError(v1[0],_error),slots: {'default': {__render: slot1, __ctx: ctx1}}}, key + \`__2__\${key1}\`, node, ctx), key1);
|
||||||
}
|
}
|
||||||
return list(c_block1);
|
return list(c_block1);
|
||||||
}
|
}
|
||||||
@@ -1068,6 +1092,19 @@ exports[`errors and promises an error in mounted call will reject the mount prom
|
|||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`errors and promises an error in onMounted callback will have the component's setup in its stack trace 1`] = `
|
||||||
|
"function anonymous(bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
|
||||||
|
|
||||||
|
let block1 = createBlock(\`<div>abc</div>\`);
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
return block1();
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`errors and promises an error in patched call will reject the render promise 1`] = `
|
exports[`errors and promises an error in patched call will reject the render promise 1`] = `
|
||||||
"function anonymous(bdom, helpers
|
"function anonymous(bdom, helpers
|
||||||
) {
|
) {
|
||||||
|
|||||||
@@ -105,7 +105,7 @@ exports[`event handling objects from scope are properly captured by t-on 1`] = `
|
|||||||
let key1 = ctx['item'];
|
let key1 = ctx['item'];
|
||||||
const v1 = ctx['onClick'];
|
const v1 = ctx['onClick'];
|
||||||
const v2 = ctx['item'];
|
const v2 = ctx['item'];
|
||||||
let hdlr1 = [ev=>v1(v2.val,ev), ctx];
|
let hdlr1 = [_ev=>v1(v2.val,_ev), ctx];
|
||||||
c_block2[i1] = withKey(block3([hdlr1]), key1);
|
c_block2[i1] = withKey(block3([hdlr1]), key1);
|
||||||
}
|
}
|
||||||
let b2 = list(c_block2);
|
let b2 = list(c_block2);
|
||||||
@@ -146,7 +146,7 @@ exports[`event handling t-on with handler bound to dynamic argument on a t-forea
|
|||||||
let key1 = ctx['item'];
|
let key1 = ctx['item'];
|
||||||
const v1 = ctx['onClick'];
|
const v1 = ctx['onClick'];
|
||||||
const v2 = ctx['item'];
|
const v2 = ctx['item'];
|
||||||
let hdlr1 = [ev=>v1(v2,ev), ctx];
|
let hdlr1 = [_ev=>v1(v2,_ev), ctx];
|
||||||
c_block2[i1] = withKey(block3([hdlr1]), key1);
|
c_block2[i1] = withKey(block3([hdlr1]), key1);
|
||||||
}
|
}
|
||||||
let b2 = list(c_block2);
|
let b2 = list(c_block2);
|
||||||
|
|||||||
@@ -103,7 +103,7 @@ exports[`hooks mounted callbacks should be called in reverse order from willUnmo
|
|||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`hooks parent and child env 1`] = `
|
exports[`hooks parent and child env (with useChildSubEnv then useSubEnv) 1`] = `
|
||||||
"function anonymous(bdom, helpers
|
"function anonymous(bdom, helpers
|
||||||
) {
|
) {
|
||||||
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
|
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
|
||||||
@@ -116,7 +116,65 @@ exports[`hooks parent and child env 1`] = `
|
|||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`hooks parent and child env 2`] = `
|
exports[`hooks parent and child env (with useChildSubEnv then useSubEnv) 2`] = `
|
||||||
|
"function anonymous(bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
|
||||||
|
|
||||||
|
let block2 = createBlock(\`<div><block-text-0/></div>\`);
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
let b2;
|
||||||
|
if (ctx['env'].hasParent) {
|
||||||
|
let txt1 = ctx['env'].val;
|
||||||
|
b2 = block2([txt1]);
|
||||||
|
}
|
||||||
|
return multi([b2]);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`hooks parent and child env (with useChildSubEnv) 1`] = `
|
||||||
|
"function anonymous(bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
let b2 = text(ctx['env'].val);
|
||||||
|
let b3 = component(\`Child\`, {}, key + \`__1\`, node, ctx);
|
||||||
|
return multi([b2, b3]);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`hooks parent and child env (with useChildSubEnv) 2`] = `
|
||||||
|
"function anonymous(bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
|
||||||
|
|
||||||
|
let block1 = createBlock(\`<div><block-text-0/></div>\`);
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
let txt1 = ctx['env'].val;
|
||||||
|
return block1([txt1]);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`hooks parent and child env (with useSubEnv) 1`] = `
|
||||||
|
"function anonymous(bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
let b2 = text(ctx['env'].val);
|
||||||
|
let b3 = component(\`Child\`, {}, key + \`__1\`, node, ctx);
|
||||||
|
return multi([b2, b3]);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`hooks parent and child env (with useSubEnv) 2`] = `
|
||||||
"function anonymous(bdom, helpers
|
"function anonymous(bdom, helpers
|
||||||
) {
|
) {
|
||||||
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
|
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
|
||||||
@@ -144,7 +202,7 @@ exports[`hooks two different call to willPatch/patched should work 1`] = `
|
|||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`hooks use sub env does not pollute user env 1`] = `
|
exports[`hooks useChildSubEnv does not pollute user env 1`] = `
|
||||||
"function anonymous(bdom, helpers
|
"function anonymous(bdom, helpers
|
||||||
) {
|
) {
|
||||||
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
|
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
|
||||||
@@ -158,7 +216,7 @@ exports[`hooks use sub env does not pollute user env 1`] = `
|
|||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`hooks use sub env supports arbitrary descriptor 1`] = `
|
exports[`hooks useChildSubEnv supports arbitrary descriptor 1`] = `
|
||||||
"function anonymous(bdom, helpers
|
"function anonymous(bdom, helpers
|
||||||
) {
|
) {
|
||||||
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
|
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
|
||||||
@@ -169,7 +227,7 @@ exports[`hooks use sub env supports arbitrary descriptor 1`] = `
|
|||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`hooks use sub env supports arbitrary descriptor 2`] = `
|
exports[`hooks useChildSubEnv supports arbitrary descriptor 2`] = `
|
||||||
"function anonymous(bdom, helpers
|
"function anonymous(bdom, helpers
|
||||||
) {
|
) {
|
||||||
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
|
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
|
||||||
@@ -287,3 +345,43 @@ exports[`hooks useRef hook: basic use 1`] = `
|
|||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`hooks useSubEnv modifies user env 1`] = `
|
||||||
|
"function anonymous(bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
|
||||||
|
|
||||||
|
let block1 = createBlock(\`<div><block-text-0/></div>\`);
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
let txt1 = ctx['env'].val;
|
||||||
|
return block1([txt1]);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`hooks useSubEnv supports arbitrary descriptor 1`] = `
|
||||||
|
"function anonymous(bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
return component(\`Child\`, {}, key + \`__1\`, node, ctx);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`hooks useSubEnv supports arbitrary descriptor 2`] = `
|
||||||
|
"function anonymous(bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
|
||||||
|
|
||||||
|
let block1 = createBlock(\`<div><block-text-0/> <block-text-1/></div>\`);
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
let txt1 = ctx['env'].someVal;
|
||||||
|
let txt2 = ctx['env'].someVal2;
|
||||||
|
return block1([txt1, txt2]);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|||||||
@@ -160,6 +160,34 @@ exports[`lifecycle hooks components are unmounted destroyed if no longer in DOM
|
|||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`lifecycle hooks destroy new children before being mountged 1`] = `
|
||||||
|
"function anonymous(bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
let b2,b3,b4;
|
||||||
|
b2 = text(\`before\`);
|
||||||
|
if (ctx['state'].flag) {
|
||||||
|
b3 = component(\`Child\`, {}, key + \`__1\`, node, ctx);
|
||||||
|
}
|
||||||
|
b4 = text(\`after\`);
|
||||||
|
return multi([b2, b3, b4]);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`lifecycle hooks destroy new children before being mountged 2`] = `
|
||||||
|
"function anonymous(bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
return text(\`child\`);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`lifecycle hooks hooks are called in proper order in widget creation/destruction 1`] = `
|
exports[`lifecycle hooks hooks are called in proper order in widget creation/destruction 1`] = `
|
||||||
"function anonymous(bdom, helpers
|
"function anonymous(bdom, helpers
|
||||||
) {
|
) {
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ exports[`basics arrow functions as prop correctly capture their scope 1`] = `
|
|||||||
let key1 = ctx['item'].val;
|
let key1 = ctx['item'].val;
|
||||||
const v1 = ctx['onClick'];
|
const v1 = ctx['onClick'];
|
||||||
const v2 = ctx['item'];
|
const v2 = ctx['item'];
|
||||||
c_block1[i1] = withKey(component(\`Child\`, {onClick: ev=>v1(v2.val,ev)}, key + \`__1__\${key1}\`, node, ctx), key1);
|
c_block1[i1] = withKey(component(\`Child\`, {onClick: _ev=>v1(v2.val,_ev)}, key + \`__1__\${key1}\`, node, ctx), key1);
|
||||||
}
|
}
|
||||||
return list(c_block1);
|
return list(c_block1);
|
||||||
}
|
}
|
||||||
@@ -273,7 +273,7 @@ exports[`bound functions is referentially equal after update 1`] = `
|
|||||||
let { bind } = helpers;
|
let { bind } = helpers;
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
return component(\`Child\`, {val: ctx['state'].val,fn: bind(ctx, ctx['someFunction'])}, key + \`__1\`, node, ctx);
|
return component(\`Child\`, {val: ctx['state'].val, fn: bind(ctx, ctx['someFunction'])}, key + \`__1\`, node, ctx);
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|||||||
@@ -1,6 +1,19 @@
|
|||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
exports[`default props can set default required boolean values 1`] = `
|
exports[`default props a default prop cannot be defined on a mandatory prop 1`] = `
|
||||||
|
"function anonymous(bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
const props1 = {};
|
||||||
|
helpers.validateProps(\`Child\`, props1, ctx);
|
||||||
|
return component(\`Child\`, props1, key + \`__1\`, node, ctx);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`default props can set default boolean values 1`] = `
|
||||||
"function anonymous(bdom, helpers
|
"function anonymous(bdom, helpers
|
||||||
) {
|
) {
|
||||||
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
|
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
|
||||||
@@ -8,15 +21,15 @@ exports[`default props can set default required boolean values 1`] = `
|
|||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const props1 = {}
|
const props1 = {};
|
||||||
helpers.validateProps(\`SubComp\`, props1, ctx)
|
helpers.validateProps(\`SubComp\`, props1, ctx);
|
||||||
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
||||||
return block1([], [b2]);
|
return block1([], [b2]);
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`default props can set default required boolean values 2`] = `
|
exports[`default props can set default boolean values 2`] = `
|
||||||
"function anonymous(bdom, helpers
|
"function anonymous(bdom, helpers
|
||||||
) {
|
) {
|
||||||
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
|
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
|
||||||
@@ -44,8 +57,8 @@ exports[`default props can set default values 1`] = `
|
|||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const props1 = {}
|
const props1 = {};
|
||||||
helpers.validateProps(\`SubComp\`, props1, ctx)
|
helpers.validateProps(\`SubComp\`, props1, ctx);
|
||||||
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
||||||
return block1([], [b2]);
|
return block1([], [b2]);
|
||||||
}
|
}
|
||||||
@@ -74,8 +87,8 @@ exports[`default props default values are also set whenever component is updated
|
|||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const props1 = {p: ctx['state'].p}
|
const props1 = {p: ctx['state'].p};
|
||||||
helpers.validateProps(\`SubComp\`, props1, ctx)
|
helpers.validateProps(\`SubComp\`, props1, ctx);
|
||||||
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
||||||
return block1([], [b2]);
|
return block1([], [b2]);
|
||||||
}
|
}
|
||||||
@@ -102,8 +115,8 @@ exports[`props validation can specify that additional props are allowed (array)
|
|||||||
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
|
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const props1 = {message: 'm',otherProp: 'o'}
|
const props1 = {message: 'm', otherProp: 'o'};
|
||||||
helpers.validateProps(\`Child\`, props1, ctx)
|
helpers.validateProps(\`Child\`, props1, ctx);
|
||||||
return component(\`Child\`, props1, key + \`__1\`, node, ctx);
|
return component(\`Child\`, props1, key + \`__1\`, node, ctx);
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
@@ -128,8 +141,8 @@ exports[`props validation can specify that additional props are allowed (object)
|
|||||||
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
|
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const props1 = {message: 'm',otherProp: 'o'}
|
const props1 = {message: 'm', otherProp: 'o'};
|
||||||
helpers.validateProps(\`Child\`, props1, ctx)
|
helpers.validateProps(\`Child\`, props1, ctx);
|
||||||
return component(\`Child\`, props1, key + \`__1\`, node, ctx);
|
return component(\`Child\`, props1, key + \`__1\`, node, ctx);
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
@@ -156,8 +169,8 @@ exports[`props validation can validate a prop with multiple types 1`] = `
|
|||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const props1 = {p: ctx['p']}
|
const props1 = {p: ctx['p']};
|
||||||
helpers.validateProps(\`SubComp\`, props1, ctx)
|
helpers.validateProps(\`SubComp\`, props1, ctx);
|
||||||
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
||||||
return block1([], [b2]);
|
return block1([], [b2]);
|
||||||
}
|
}
|
||||||
@@ -185,8 +198,8 @@ exports[`props validation can validate a prop with multiple types 3`] = `
|
|||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const props1 = {p: ctx['p']}
|
const props1 = {p: ctx['p']};
|
||||||
helpers.validateProps(\`SubComp\`, props1, ctx)
|
helpers.validateProps(\`SubComp\`, props1, ctx);
|
||||||
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
||||||
return block1([], [b2]);
|
return block1([], [b2]);
|
||||||
}
|
}
|
||||||
@@ -214,8 +227,8 @@ exports[`props validation can validate a prop with multiple types 5`] = `
|
|||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const props1 = {p: ctx['p']}
|
const props1 = {p: ctx['p']};
|
||||||
helpers.validateProps(\`SubComp\`, props1, ctx)
|
helpers.validateProps(\`SubComp\`, props1, ctx);
|
||||||
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
||||||
return block1([], [b2]);
|
return block1([], [b2]);
|
||||||
}
|
}
|
||||||
@@ -230,8 +243,8 @@ exports[`props validation can validate an array with given primitive type 1`] =
|
|||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const props1 = {p: ctx['p']}
|
const props1 = {p: ctx['p']};
|
||||||
helpers.validateProps(\`SubComp\`, props1, ctx)
|
helpers.validateProps(\`SubComp\`, props1, ctx);
|
||||||
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
||||||
return block1([], [b2]);
|
return block1([], [b2]);
|
||||||
}
|
}
|
||||||
@@ -259,8 +272,8 @@ exports[`props validation can validate an array with given primitive type 3`] =
|
|||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const props1 = {p: ctx['p']}
|
const props1 = {p: ctx['p']};
|
||||||
helpers.validateProps(\`SubComp\`, props1, ctx)
|
helpers.validateProps(\`SubComp\`, props1, ctx);
|
||||||
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
||||||
return block1([], [b2]);
|
return block1([], [b2]);
|
||||||
}
|
}
|
||||||
@@ -288,8 +301,8 @@ exports[`props validation can validate an array with given primitive type 5`] =
|
|||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const props1 = {p: ctx['p']}
|
const props1 = {p: ctx['p']};
|
||||||
helpers.validateProps(\`SubComp\`, props1, ctx)
|
helpers.validateProps(\`SubComp\`, props1, ctx);
|
||||||
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
||||||
return block1([], [b2]);
|
return block1([], [b2]);
|
||||||
}
|
}
|
||||||
@@ -304,8 +317,8 @@ exports[`props validation can validate an array with given primitive type 6`] =
|
|||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const props1 = {p: ctx['p']}
|
const props1 = {p: ctx['p']};
|
||||||
helpers.validateProps(\`SubComp\`, props1, ctx)
|
helpers.validateProps(\`SubComp\`, props1, ctx);
|
||||||
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
||||||
return block1([], [b2]);
|
return block1([], [b2]);
|
||||||
}
|
}
|
||||||
@@ -320,8 +333,8 @@ exports[`props validation can validate an array with multiple sub element types
|
|||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const props1 = {p: ctx['p']}
|
const props1 = {p: ctx['p']};
|
||||||
helpers.validateProps(\`SubComp\`, props1, ctx)
|
helpers.validateProps(\`SubComp\`, props1, ctx);
|
||||||
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
||||||
return block1([], [b2]);
|
return block1([], [b2]);
|
||||||
}
|
}
|
||||||
@@ -349,8 +362,8 @@ exports[`props validation can validate an array with multiple sub element types
|
|||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const props1 = {p: ctx['p']}
|
const props1 = {p: ctx['p']};
|
||||||
helpers.validateProps(\`SubComp\`, props1, ctx)
|
helpers.validateProps(\`SubComp\`, props1, ctx);
|
||||||
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
||||||
return block1([], [b2]);
|
return block1([], [b2]);
|
||||||
}
|
}
|
||||||
@@ -378,8 +391,8 @@ exports[`props validation can validate an array with multiple sub element types
|
|||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const props1 = {p: ctx['p']}
|
const props1 = {p: ctx['p']};
|
||||||
helpers.validateProps(\`SubComp\`, props1, ctx)
|
helpers.validateProps(\`SubComp\`, props1, ctx);
|
||||||
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
||||||
return block1([], [b2]);
|
return block1([], [b2]);
|
||||||
}
|
}
|
||||||
@@ -407,8 +420,8 @@ exports[`props validation can validate an array with multiple sub element types
|
|||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const props1 = {p: ctx['p']}
|
const props1 = {p: ctx['p']};
|
||||||
helpers.validateProps(\`SubComp\`, props1, ctx)
|
helpers.validateProps(\`SubComp\`, props1, ctx);
|
||||||
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
||||||
return block1([], [b2]);
|
return block1([], [b2]);
|
||||||
}
|
}
|
||||||
@@ -423,8 +436,8 @@ exports[`props validation can validate an object with simple shape 1`] = `
|
|||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const props1 = {p: ctx['p']}
|
const props1 = {p: ctx['p']};
|
||||||
helpers.validateProps(\`SubComp\`, props1, ctx)
|
helpers.validateProps(\`SubComp\`, props1, ctx);
|
||||||
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
||||||
return block1([], [b2]);
|
return block1([], [b2]);
|
||||||
}
|
}
|
||||||
@@ -452,8 +465,8 @@ exports[`props validation can validate an object with simple shape 3`] = `
|
|||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const props1 = {p: ctx['p']}
|
const props1 = {p: ctx['p']};
|
||||||
helpers.validateProps(\`SubComp\`, props1, ctx)
|
helpers.validateProps(\`SubComp\`, props1, ctx);
|
||||||
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
||||||
return block1([], [b2]);
|
return block1([], [b2]);
|
||||||
}
|
}
|
||||||
@@ -468,8 +481,8 @@ exports[`props validation can validate an object with simple shape 4`] = `
|
|||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const props1 = {p: ctx['p']}
|
const props1 = {p: ctx['p']};
|
||||||
helpers.validateProps(\`SubComp\`, props1, ctx)
|
helpers.validateProps(\`SubComp\`, props1, ctx);
|
||||||
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
||||||
return block1([], [b2]);
|
return block1([], [b2]);
|
||||||
}
|
}
|
||||||
@@ -484,8 +497,8 @@ exports[`props validation can validate an object with simple shape 5`] = `
|
|||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const props1 = {p: ctx['p']}
|
const props1 = {p: ctx['p']};
|
||||||
helpers.validateProps(\`SubComp\`, props1, ctx)
|
helpers.validateProps(\`SubComp\`, props1, ctx);
|
||||||
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
||||||
return block1([], [b2]);
|
return block1([], [b2]);
|
||||||
}
|
}
|
||||||
@@ -500,8 +513,8 @@ exports[`props validation can validate an optional props 1`] = `
|
|||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const props1 = {p: ctx['p']}
|
const props1 = {p: ctx['p']};
|
||||||
helpers.validateProps(\`SubComp\`, props1, ctx)
|
helpers.validateProps(\`SubComp\`, props1, ctx);
|
||||||
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
||||||
return block1([], [b2]);
|
return block1([], [b2]);
|
||||||
}
|
}
|
||||||
@@ -529,8 +542,8 @@ exports[`props validation can validate an optional props 3`] = `
|
|||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const props1 = {p: ctx['p']}
|
const props1 = {p: ctx['p']};
|
||||||
helpers.validateProps(\`SubComp\`, props1, ctx)
|
helpers.validateProps(\`SubComp\`, props1, ctx);
|
||||||
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
||||||
return block1([], [b2]);
|
return block1([], [b2]);
|
||||||
}
|
}
|
||||||
@@ -558,8 +571,8 @@ exports[`props validation can validate an optional props 5`] = `
|
|||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const props1 = {p: ctx['p']}
|
const props1 = {p: ctx['p']};
|
||||||
helpers.validateProps(\`SubComp\`, props1, ctx)
|
helpers.validateProps(\`SubComp\`, props1, ctx);
|
||||||
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
||||||
return block1([], [b2]);
|
return block1([], [b2]);
|
||||||
}
|
}
|
||||||
@@ -574,8 +587,8 @@ exports[`props validation can validate recursively complicated prop def 1`] = `
|
|||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const props1 = {p: ctx['p']}
|
const props1 = {p: ctx['p']};
|
||||||
helpers.validateProps(\`SubComp\`, props1, ctx)
|
helpers.validateProps(\`SubComp\`, props1, ctx);
|
||||||
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
||||||
return block1([], [b2]);
|
return block1([], [b2]);
|
||||||
}
|
}
|
||||||
@@ -603,8 +616,8 @@ exports[`props validation can validate recursively complicated prop def 3`] = `
|
|||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const props1 = {p: ctx['p']}
|
const props1 = {p: ctx['p']};
|
||||||
helpers.validateProps(\`SubComp\`, props1, ctx)
|
helpers.validateProps(\`SubComp\`, props1, ctx);
|
||||||
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
||||||
return block1([], [b2]);
|
return block1([], [b2]);
|
||||||
}
|
}
|
||||||
@@ -632,8 +645,8 @@ exports[`props validation can validate recursively complicated prop def 5`] = `
|
|||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const props1 = {p: ctx['p']}
|
const props1 = {p: ctx['p']};
|
||||||
helpers.validateProps(\`SubComp\`, props1, ctx)
|
helpers.validateProps(\`SubComp\`, props1, ctx);
|
||||||
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
||||||
return block1([], [b2]);
|
return block1([], [b2]);
|
||||||
}
|
}
|
||||||
@@ -648,8 +661,8 @@ exports[`props validation default values are applied before validating props at
|
|||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const props1 = {p: ctx['state'].p}
|
const props1 = {p: ctx['state'].p};
|
||||||
helpers.validateProps(\`SubComp\`, props1, ctx)
|
helpers.validateProps(\`SubComp\`, props1, ctx);
|
||||||
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
||||||
return block1([], [b2]);
|
return block1([], [b2]);
|
||||||
}
|
}
|
||||||
@@ -678,8 +691,8 @@ exports[`props validation missing required boolean prop causes an error 1`] = `
|
|||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const props1 = {}
|
const props1 = {};
|
||||||
helpers.validateProps(\`SubComp\`, props1, ctx)
|
helpers.validateProps(\`SubComp\`, props1, ctx);
|
||||||
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
||||||
return block1([], [b2]);
|
return block1([], [b2]);
|
||||||
}
|
}
|
||||||
@@ -694,8 +707,8 @@ exports[`props validation mix of optional and mandatory 1`] = `
|
|||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const props1 = {}
|
const props1 = {};
|
||||||
helpers.validateProps(\`Child\`, props1, ctx)
|
helpers.validateProps(\`Child\`, props1, ctx);
|
||||||
let b2 = component(\`Child\`, props1, key + \`__1\`, node, ctx);
|
let b2 = component(\`Child\`, props1, key + \`__1\`, node, ctx);
|
||||||
return block1([], [b2]);
|
return block1([], [b2]);
|
||||||
}
|
}
|
||||||
@@ -710,8 +723,8 @@ exports[`props validation props are validated in dev mode (code snapshot) 1`] =
|
|||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const props1 = {message: 1}
|
const props1 = {message: 1};
|
||||||
helpers.validateProps(\`Child\`, props1, ctx)
|
helpers.validateProps(\`Child\`, props1, ctx);
|
||||||
let b2 = component(\`Child\`, props1, key + \`__1\`, node, ctx);
|
let b2 = component(\`Child\`, props1, key + \`__1\`, node, ctx);
|
||||||
return block1([], [b2]);
|
return block1([], [b2]);
|
||||||
}
|
}
|
||||||
@@ -740,8 +753,8 @@ exports[`props validation props are validated whenever component is updated 1`]
|
|||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const props1 = {p: ctx['state'].p}
|
const props1 = {p: ctx['state'].p};
|
||||||
helpers.validateProps(\`SubComp\`, props1, ctx)
|
helpers.validateProps(\`SubComp\`, props1, ctx);
|
||||||
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
||||||
return block1([], [b2]);
|
return block1([], [b2]);
|
||||||
}
|
}
|
||||||
@@ -770,8 +783,8 @@ exports[`props validation props: list of strings 1`] = `
|
|||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const props1 = {}
|
const props1 = {};
|
||||||
helpers.validateProps(\`SubComp\`, props1, ctx)
|
helpers.validateProps(\`SubComp\`, props1, ctx);
|
||||||
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
||||||
return block1([], [b2]);
|
return block1([], [b2]);
|
||||||
}
|
}
|
||||||
@@ -786,8 +799,8 @@ exports[`props validation validate simple types 1`] = `
|
|||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const props1 = {p: ctx['p']}
|
const props1 = {p: ctx['p']};
|
||||||
helpers.validateProps(\`SubComp\`, props1, ctx)
|
helpers.validateProps(\`SubComp\`, props1, ctx);
|
||||||
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
||||||
return block1([], [b2]);
|
return block1([], [b2]);
|
||||||
}
|
}
|
||||||
@@ -802,8 +815,8 @@ exports[`props validation validate simple types 2`] = `
|
|||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const props1 = {p: ctx['p']}
|
const props1 = {p: ctx['p']};
|
||||||
helpers.validateProps(\`SubComp\`, props1, ctx)
|
helpers.validateProps(\`SubComp\`, props1, ctx);
|
||||||
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
||||||
return block1([], [b2]);
|
return block1([], [b2]);
|
||||||
}
|
}
|
||||||
@@ -831,8 +844,8 @@ exports[`props validation validate simple types 4`] = `
|
|||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const props1 = {p: ctx['p']}
|
const props1 = {p: ctx['p']};
|
||||||
helpers.validateProps(\`SubComp\`, props1, ctx)
|
helpers.validateProps(\`SubComp\`, props1, ctx);
|
||||||
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
||||||
return block1([], [b2]);
|
return block1([], [b2]);
|
||||||
}
|
}
|
||||||
@@ -847,8 +860,8 @@ exports[`props validation validate simple types 5`] = `
|
|||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const props1 = {p: ctx['p']}
|
const props1 = {p: ctx['p']};
|
||||||
helpers.validateProps(\`SubComp\`, props1, ctx)
|
helpers.validateProps(\`SubComp\`, props1, ctx);
|
||||||
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
||||||
return block1([], [b2]);
|
return block1([], [b2]);
|
||||||
}
|
}
|
||||||
@@ -863,8 +876,8 @@ exports[`props validation validate simple types 6`] = `
|
|||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const props1 = {p: ctx['p']}
|
const props1 = {p: ctx['p']};
|
||||||
helpers.validateProps(\`SubComp\`, props1, ctx)
|
helpers.validateProps(\`SubComp\`, props1, ctx);
|
||||||
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
||||||
return block1([], [b2]);
|
return block1([], [b2]);
|
||||||
}
|
}
|
||||||
@@ -892,8 +905,8 @@ exports[`props validation validate simple types 8`] = `
|
|||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const props1 = {p: ctx['p']}
|
const props1 = {p: ctx['p']};
|
||||||
helpers.validateProps(\`SubComp\`, props1, ctx)
|
helpers.validateProps(\`SubComp\`, props1, ctx);
|
||||||
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
||||||
return block1([], [b2]);
|
return block1([], [b2]);
|
||||||
}
|
}
|
||||||
@@ -908,8 +921,8 @@ exports[`props validation validate simple types 9`] = `
|
|||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const props1 = {p: ctx['p']}
|
const props1 = {p: ctx['p']};
|
||||||
helpers.validateProps(\`SubComp\`, props1, ctx)
|
helpers.validateProps(\`SubComp\`, props1, ctx);
|
||||||
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
||||||
return block1([], [b2]);
|
return block1([], [b2]);
|
||||||
}
|
}
|
||||||
@@ -924,8 +937,8 @@ exports[`props validation validate simple types 10`] = `
|
|||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const props1 = {p: ctx['p']}
|
const props1 = {p: ctx['p']};
|
||||||
helpers.validateProps(\`SubComp\`, props1, ctx)
|
helpers.validateProps(\`SubComp\`, props1, ctx);
|
||||||
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
||||||
return block1([], [b2]);
|
return block1([], [b2]);
|
||||||
}
|
}
|
||||||
@@ -953,8 +966,8 @@ exports[`props validation validate simple types 12`] = `
|
|||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const props1 = {p: ctx['p']}
|
const props1 = {p: ctx['p']};
|
||||||
helpers.validateProps(\`SubComp\`, props1, ctx)
|
helpers.validateProps(\`SubComp\`, props1, ctx);
|
||||||
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
||||||
return block1([], [b2]);
|
return block1([], [b2]);
|
||||||
}
|
}
|
||||||
@@ -969,8 +982,8 @@ exports[`props validation validate simple types 13`] = `
|
|||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const props1 = {p: ctx['p']}
|
const props1 = {p: ctx['p']};
|
||||||
helpers.validateProps(\`SubComp\`, props1, ctx)
|
helpers.validateProps(\`SubComp\`, props1, ctx);
|
||||||
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
||||||
return block1([], [b2]);
|
return block1([], [b2]);
|
||||||
}
|
}
|
||||||
@@ -985,8 +998,8 @@ exports[`props validation validate simple types 14`] = `
|
|||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const props1 = {p: ctx['p']}
|
const props1 = {p: ctx['p']};
|
||||||
helpers.validateProps(\`SubComp\`, props1, ctx)
|
helpers.validateProps(\`SubComp\`, props1, ctx);
|
||||||
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
||||||
return block1([], [b2]);
|
return block1([], [b2]);
|
||||||
}
|
}
|
||||||
@@ -1014,8 +1027,8 @@ exports[`props validation validate simple types 16`] = `
|
|||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const props1 = {p: ctx['p']}
|
const props1 = {p: ctx['p']};
|
||||||
helpers.validateProps(\`SubComp\`, props1, ctx)
|
helpers.validateProps(\`SubComp\`, props1, ctx);
|
||||||
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
||||||
return block1([], [b2]);
|
return block1([], [b2]);
|
||||||
}
|
}
|
||||||
@@ -1030,8 +1043,8 @@ exports[`props validation validate simple types 17`] = `
|
|||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const props1 = {p: ctx['p']}
|
const props1 = {p: ctx['p']};
|
||||||
helpers.validateProps(\`SubComp\`, props1, ctx)
|
helpers.validateProps(\`SubComp\`, props1, ctx);
|
||||||
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
||||||
return block1([], [b2]);
|
return block1([], [b2]);
|
||||||
}
|
}
|
||||||
@@ -1046,8 +1059,8 @@ exports[`props validation validate simple types 18`] = `
|
|||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const props1 = {p: ctx['p']}
|
const props1 = {p: ctx['p']};
|
||||||
helpers.validateProps(\`SubComp\`, props1, ctx)
|
helpers.validateProps(\`SubComp\`, props1, ctx);
|
||||||
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
||||||
return block1([], [b2]);
|
return block1([], [b2]);
|
||||||
}
|
}
|
||||||
@@ -1075,8 +1088,8 @@ exports[`props validation validate simple types 20`] = `
|
|||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const props1 = {p: ctx['p']}
|
const props1 = {p: ctx['p']};
|
||||||
helpers.validateProps(\`SubComp\`, props1, ctx)
|
helpers.validateProps(\`SubComp\`, props1, ctx);
|
||||||
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
||||||
return block1([], [b2]);
|
return block1([], [b2]);
|
||||||
}
|
}
|
||||||
@@ -1091,8 +1104,8 @@ exports[`props validation validate simple types 21`] = `
|
|||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const props1 = {p: ctx['p']}
|
const props1 = {p: ctx['p']};
|
||||||
helpers.validateProps(\`SubComp\`, props1, ctx)
|
helpers.validateProps(\`SubComp\`, props1, ctx);
|
||||||
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
||||||
return block1([], [b2]);
|
return block1([], [b2]);
|
||||||
}
|
}
|
||||||
@@ -1107,8 +1120,8 @@ exports[`props validation validate simple types 22`] = `
|
|||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const props1 = {p: ctx['p']}
|
const props1 = {p: ctx['p']};
|
||||||
helpers.validateProps(\`SubComp\`, props1, ctx)
|
helpers.validateProps(\`SubComp\`, props1, ctx);
|
||||||
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
||||||
return block1([], [b2]);
|
return block1([], [b2]);
|
||||||
}
|
}
|
||||||
@@ -1136,8 +1149,8 @@ exports[`props validation validate simple types 24`] = `
|
|||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const props1 = {p: ctx['p']}
|
const props1 = {p: ctx['p']};
|
||||||
helpers.validateProps(\`SubComp\`, props1, ctx)
|
helpers.validateProps(\`SubComp\`, props1, ctx);
|
||||||
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
||||||
return block1([], [b2]);
|
return block1([], [b2]);
|
||||||
}
|
}
|
||||||
@@ -1152,8 +1165,8 @@ exports[`props validation validate simple types, alternate form 1`] = `
|
|||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const props1 = {p: ctx['p']}
|
const props1 = {p: ctx['p']};
|
||||||
helpers.validateProps(\`SubComp\`, props1, ctx)
|
helpers.validateProps(\`SubComp\`, props1, ctx);
|
||||||
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
||||||
return block1([], [b2]);
|
return block1([], [b2]);
|
||||||
}
|
}
|
||||||
@@ -1168,8 +1181,8 @@ exports[`props validation validate simple types, alternate form 2`] = `
|
|||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const props1 = {p: ctx['p']}
|
const props1 = {p: ctx['p']};
|
||||||
helpers.validateProps(\`SubComp\`, props1, ctx)
|
helpers.validateProps(\`SubComp\`, props1, ctx);
|
||||||
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
||||||
return block1([], [b2]);
|
return block1([], [b2]);
|
||||||
}
|
}
|
||||||
@@ -1197,8 +1210,8 @@ exports[`props validation validate simple types, alternate form 4`] = `
|
|||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const props1 = {p: ctx['p']}
|
const props1 = {p: ctx['p']};
|
||||||
helpers.validateProps(\`SubComp\`, props1, ctx)
|
helpers.validateProps(\`SubComp\`, props1, ctx);
|
||||||
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
||||||
return block1([], [b2]);
|
return block1([], [b2]);
|
||||||
}
|
}
|
||||||
@@ -1213,8 +1226,8 @@ exports[`props validation validate simple types, alternate form 5`] = `
|
|||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const props1 = {p: ctx['p']}
|
const props1 = {p: ctx['p']};
|
||||||
helpers.validateProps(\`SubComp\`, props1, ctx)
|
helpers.validateProps(\`SubComp\`, props1, ctx);
|
||||||
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
||||||
return block1([], [b2]);
|
return block1([], [b2]);
|
||||||
}
|
}
|
||||||
@@ -1229,8 +1242,8 @@ exports[`props validation validate simple types, alternate form 6`] = `
|
|||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const props1 = {p: ctx['p']}
|
const props1 = {p: ctx['p']};
|
||||||
helpers.validateProps(\`SubComp\`, props1, ctx)
|
helpers.validateProps(\`SubComp\`, props1, ctx);
|
||||||
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
||||||
return block1([], [b2]);
|
return block1([], [b2]);
|
||||||
}
|
}
|
||||||
@@ -1258,8 +1271,8 @@ exports[`props validation validate simple types, alternate form 8`] = `
|
|||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const props1 = {p: ctx['p']}
|
const props1 = {p: ctx['p']};
|
||||||
helpers.validateProps(\`SubComp\`, props1, ctx)
|
helpers.validateProps(\`SubComp\`, props1, ctx);
|
||||||
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
||||||
return block1([], [b2]);
|
return block1([], [b2]);
|
||||||
}
|
}
|
||||||
@@ -1274,8 +1287,8 @@ exports[`props validation validate simple types, alternate form 9`] = `
|
|||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const props1 = {p: ctx['p']}
|
const props1 = {p: ctx['p']};
|
||||||
helpers.validateProps(\`SubComp\`, props1, ctx)
|
helpers.validateProps(\`SubComp\`, props1, ctx);
|
||||||
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
||||||
return block1([], [b2]);
|
return block1([], [b2]);
|
||||||
}
|
}
|
||||||
@@ -1290,8 +1303,8 @@ exports[`props validation validate simple types, alternate form 10`] = `
|
|||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const props1 = {p: ctx['p']}
|
const props1 = {p: ctx['p']};
|
||||||
helpers.validateProps(\`SubComp\`, props1, ctx)
|
helpers.validateProps(\`SubComp\`, props1, ctx);
|
||||||
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
||||||
return block1([], [b2]);
|
return block1([], [b2]);
|
||||||
}
|
}
|
||||||
@@ -1319,8 +1332,8 @@ exports[`props validation validate simple types, alternate form 12`] = `
|
|||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const props1 = {p: ctx['p']}
|
const props1 = {p: ctx['p']};
|
||||||
helpers.validateProps(\`SubComp\`, props1, ctx)
|
helpers.validateProps(\`SubComp\`, props1, ctx);
|
||||||
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
||||||
return block1([], [b2]);
|
return block1([], [b2]);
|
||||||
}
|
}
|
||||||
@@ -1335,8 +1348,8 @@ exports[`props validation validate simple types, alternate form 13`] = `
|
|||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const props1 = {p: ctx['p']}
|
const props1 = {p: ctx['p']};
|
||||||
helpers.validateProps(\`SubComp\`, props1, ctx)
|
helpers.validateProps(\`SubComp\`, props1, ctx);
|
||||||
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
||||||
return block1([], [b2]);
|
return block1([], [b2]);
|
||||||
}
|
}
|
||||||
@@ -1351,8 +1364,8 @@ exports[`props validation validate simple types, alternate form 14`] = `
|
|||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const props1 = {p: ctx['p']}
|
const props1 = {p: ctx['p']};
|
||||||
helpers.validateProps(\`SubComp\`, props1, ctx)
|
helpers.validateProps(\`SubComp\`, props1, ctx);
|
||||||
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
||||||
return block1([], [b2]);
|
return block1([], [b2]);
|
||||||
}
|
}
|
||||||
@@ -1380,8 +1393,8 @@ exports[`props validation validate simple types, alternate form 16`] = `
|
|||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const props1 = {p: ctx['p']}
|
const props1 = {p: ctx['p']};
|
||||||
helpers.validateProps(\`SubComp\`, props1, ctx)
|
helpers.validateProps(\`SubComp\`, props1, ctx);
|
||||||
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
||||||
return block1([], [b2]);
|
return block1([], [b2]);
|
||||||
}
|
}
|
||||||
@@ -1396,8 +1409,8 @@ exports[`props validation validate simple types, alternate form 17`] = `
|
|||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const props1 = {p: ctx['p']}
|
const props1 = {p: ctx['p']};
|
||||||
helpers.validateProps(\`SubComp\`, props1, ctx)
|
helpers.validateProps(\`SubComp\`, props1, ctx);
|
||||||
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
||||||
return block1([], [b2]);
|
return block1([], [b2]);
|
||||||
}
|
}
|
||||||
@@ -1412,8 +1425,8 @@ exports[`props validation validate simple types, alternate form 18`] = `
|
|||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const props1 = {p: ctx['p']}
|
const props1 = {p: ctx['p']};
|
||||||
helpers.validateProps(\`SubComp\`, props1, ctx)
|
helpers.validateProps(\`SubComp\`, props1, ctx);
|
||||||
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
||||||
return block1([], [b2]);
|
return block1([], [b2]);
|
||||||
}
|
}
|
||||||
@@ -1441,8 +1454,8 @@ exports[`props validation validate simple types, alternate form 20`] = `
|
|||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const props1 = {p: ctx['p']}
|
const props1 = {p: ctx['p']};
|
||||||
helpers.validateProps(\`SubComp\`, props1, ctx)
|
helpers.validateProps(\`SubComp\`, props1, ctx);
|
||||||
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
||||||
return block1([], [b2]);
|
return block1([], [b2]);
|
||||||
}
|
}
|
||||||
@@ -1457,8 +1470,8 @@ exports[`props validation validate simple types, alternate form 21`] = `
|
|||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const props1 = {p: ctx['p']}
|
const props1 = {p: ctx['p']};
|
||||||
helpers.validateProps(\`SubComp\`, props1, ctx)
|
helpers.validateProps(\`SubComp\`, props1, ctx);
|
||||||
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
||||||
return block1([], [b2]);
|
return block1([], [b2]);
|
||||||
}
|
}
|
||||||
@@ -1473,8 +1486,8 @@ exports[`props validation validate simple types, alternate form 22`] = `
|
|||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const props1 = {p: ctx['p']}
|
const props1 = {p: ctx['p']};
|
||||||
helpers.validateProps(\`SubComp\`, props1, ctx)
|
helpers.validateProps(\`SubComp\`, props1, ctx);
|
||||||
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
||||||
return block1([], [b2]);
|
return block1([], [b2]);
|
||||||
}
|
}
|
||||||
@@ -1502,8 +1515,8 @@ exports[`props validation validate simple types, alternate form 24`] = `
|
|||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const props1 = {p: ctx['p']}
|
const props1 = {p: ctx['p']};
|
||||||
helpers.validateProps(\`SubComp\`, props1, ctx)
|
helpers.validateProps(\`SubComp\`, props1, ctx);
|
||||||
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
||||||
return block1([], [b2]);
|
return block1([], [b2]);
|
||||||
}
|
}
|
||||||
@@ -1518,8 +1531,8 @@ exports[`props validation validation is only done in dev mode 1`] = `
|
|||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const props1 = {}
|
const props1 = {};
|
||||||
helpers.validateProps(\`SubComp\`, props1, ctx)
|
helpers.validateProps(\`SubComp\`, props1, ctx);
|
||||||
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
let b2 = component(\`SubComp\`, props1, key + \`__1\`, node, ctx);
|
||||||
return block1([], [b2]);
|
return block1([], [b2]);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,6 +38,26 @@ exports[`refs can use 2 refs with same name in a t-if/t-else situation 1`] = `
|
|||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`refs refs and recursive templates 1`] = `
|
||||||
|
"function anonymous(bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
|
||||||
|
|
||||||
|
let block1 = createBlock(\`<p block-ref=\\"0\\"><block-text-1/><block-child-0/></p>\`);
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
const refs = ctx.__owl__.refs;
|
||||||
|
const ref1 = (el) => refs[\`root\`] = el;
|
||||||
|
let b2;
|
||||||
|
let txt1 = ctx['props'].tree.value;
|
||||||
|
if (ctx['props'].tree.child) {
|
||||||
|
b2 = component(\`Test\`, {tree: ctx['props'].tree.child}, key + \`__1\`, node, ctx);
|
||||||
|
}
|
||||||
|
return block1([ref1, txt1], [b2]);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`refs refs are properly bound in slots 1`] = `
|
exports[`refs refs are properly bound in slots 1`] = `
|
||||||
"function anonymous(bdom, helpers
|
"function anonymous(bdom, helpers
|
||||||
) {
|
) {
|
||||||
|
|||||||
@@ -75,6 +75,37 @@ exports[`slots can define and call slots 2`] = `
|
|||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`slots can define and call slots with bound params 1`] = `
|
||||||
|
"function anonymous(bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
|
||||||
|
let { capture, bind } = helpers;
|
||||||
|
|
||||||
|
function slot1(ctx, node, key = \\"\\") {
|
||||||
|
return text(\`abc\`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
const ctx1 = capture(ctx);
|
||||||
|
return component(\`Child\`, {slots: {'abc': {__render: slot1, __ctx: ctx1, getValue: bind(ctx, ctx['getValue'])}}}, key + \`__1\`, node, ctx);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`slots can define and call slots with bound params 2`] = `
|
||||||
|
"function anonymous(bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
|
||||||
|
let { callSlot } = helpers;
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
let b2 = callSlot(ctx, node, key, 'abc', false, {});
|
||||||
|
let b3 = text(ctx['props'].slots['abc'].getValue());
|
||||||
|
return multi([b2, b3]);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`slots can define and call slots with params 1`] = `
|
exports[`slots can define and call slots with params 1`] = `
|
||||||
"function anonymous(bdom, helpers
|
"function anonymous(bdom, helpers
|
||||||
) {
|
) {
|
||||||
@@ -424,6 +455,69 @@ exports[`slots default slot next to named slot, with default content 2`] = `
|
|||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`slots default slot with params with - in it 1`] = `
|
||||||
|
"function anonymous(bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
|
||||||
|
|
||||||
|
function slot1(ctx, node, key = \\"\\") {
|
||||||
|
return text(ctx['slotScope']['some-value']);
|
||||||
|
}
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
return component(\`Child\`, {slots: {'default': {__render: slot1, __ctx: ctx, __scope: \\"slotScope\\"}}}, key + \`__1\`, node, ctx);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`slots default slot with params with - in it 2`] = `
|
||||||
|
"function anonymous(bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
|
||||||
|
let { callSlot } = helpers;
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
return callSlot(ctx, node, key, 'default', false, {'some-value': ctx['state'].value});
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`slots default slot with slot scope: shorthand syntax 1`] = `
|
||||||
|
"function anonymous(bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
|
||||||
|
|
||||||
|
function slot1(ctx, node, key = \\"\\") {
|
||||||
|
let b2,b3;
|
||||||
|
if (ctx['slotScope'].bool) {
|
||||||
|
b2 = text(\`some text\`);
|
||||||
|
} else {
|
||||||
|
b3 = text(\`other text\`);
|
||||||
|
}
|
||||||
|
return multi([b2, b3]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
return component(\`Child\`, {slots: {'default': {__render: slot1, __ctx: ctx, __scope: \\"slotScope\\"}}}, key + \`__1\`, node, ctx);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`slots default slot with slot scope: shorthand syntax 2`] = `
|
||||||
|
"function anonymous(bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
|
||||||
|
let { callSlot } = helpers;
|
||||||
|
|
||||||
|
let block1 = createBlock(\`<span><block-child-0/></span>\`);
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
let b2 = callSlot(ctx, node, key, 'default', false, {bool: ctx['state'].bool});
|
||||||
|
return block1([], [b2]);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`slots default slot work with text nodes (variation) 1`] = `
|
exports[`slots default slot work with text nodes (variation) 1`] = `
|
||||||
"function anonymous(bdom, helpers
|
"function anonymous(bdom, helpers
|
||||||
) {
|
) {
|
||||||
@@ -1139,7 +1233,6 @@ exports[`slots simple default slot with params 1`] = `
|
|||||||
"function anonymous(bdom, helpers
|
"function anonymous(bdom, helpers
|
||||||
) {
|
) {
|
||||||
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
|
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
|
||||||
let { capture } = helpers;
|
|
||||||
|
|
||||||
function slot1(ctx, node, key = \\"\\") {
|
function slot1(ctx, node, key = \\"\\") {
|
||||||
let b2,b3;
|
let b2,b3;
|
||||||
@@ -1152,8 +1245,7 @@ exports[`slots simple default slot with params 1`] = `
|
|||||||
}
|
}
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const ctx1 = capture(ctx);
|
return component(\`Child\`, {slots: {'default': {__render: slot1, __ctx: ctx}}}, key + \`__1\`, node, ctx);
|
||||||
return component(\`Child\`, {slots: {'default': {__render: slot1, __ctx: ctx1, __scope: \\"slotScope\\"}}}, key + \`__1\`, node, ctx);
|
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
@@ -1173,38 +1265,29 @@ exports[`slots simple default slot with params 2`] = `
|
|||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`slots simple default slot with params 3`] = `
|
exports[`slots simple default slot with params and bound function 1`] = `
|
||||||
"function anonymous(bdom, helpers
|
"function anonymous(bdom, helpers
|
||||||
) {
|
) {
|
||||||
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
|
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
|
||||||
|
|
||||||
function slot1(ctx, node, key = \\"\\") {
|
function slot1(ctx, node, key = \\"\\") {
|
||||||
let b2,b3;
|
return text(ctx['slotScope'].fn());
|
||||||
if (ctx['slotScope'].bool) {
|
|
||||||
b2 = text(\`some text\`);
|
|
||||||
} else {
|
|
||||||
b3 = text(\`other text\`);
|
|
||||||
}
|
|
||||||
return multi([b2, b3]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
return component(\`Child\`, {slots: {'default': {__render: slot1, __ctx: ctx}}}, key + \`__1\`, node, ctx);
|
return component(\`Child\`, {slots: {'default': {__render: slot1, __ctx: ctx, __scope: \\"slotScope\\"}}}, key + \`__1\`, node, ctx);
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`slots simple default slot with params 4`] = `
|
exports[`slots simple default slot with params and bound function 2`] = `
|
||||||
"function anonymous(bdom, helpers
|
"function anonymous(bdom, helpers
|
||||||
) {
|
) {
|
||||||
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
|
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
|
||||||
let { callSlot } = helpers;
|
let { callSlot, bind } = helpers;
|
||||||
|
|
||||||
let block1 = createBlock(\`<span><block-child-0/></span>\`);
|
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
let b2 = callSlot(ctx, node, key, 'default', false, {bool: ctx['state'].bool});
|
return callSlot(ctx, node, key, 'default', false, {fn: bind(ctx, ctx['getValue'])});
|
||||||
return block1([], [b2]);
|
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
@@ -1236,6 +1319,44 @@ exports[`slots simple default slot, variation 2`] = `
|
|||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`slots simple slot with slot scope 1`] = `
|
||||||
|
"function anonymous(bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
|
||||||
|
let { capture } = helpers;
|
||||||
|
|
||||||
|
function slot1(ctx, node, key = \\"\\") {
|
||||||
|
let b2,b3;
|
||||||
|
if (ctx['slotScope'].bool) {
|
||||||
|
b2 = text(\`some text\`);
|
||||||
|
} else {
|
||||||
|
b3 = text(\`other text\`);
|
||||||
|
}
|
||||||
|
return multi([b2, b3]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
const ctx1 = capture(ctx);
|
||||||
|
return component(\`Child\`, {slots: {'slotName': {__render: slot1, __ctx: ctx1, __scope: \\"slotScope\\"}}}, key + \`__1\`, node, ctx);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`slots simple slot with slot scope 2`] = `
|
||||||
|
"function anonymous(bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
|
||||||
|
let { callSlot } = helpers;
|
||||||
|
|
||||||
|
let block1 = createBlock(\`<span><block-child-0/></span>\`);
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
let b2 = callSlot(ctx, node, key, 'slotName', false, {bool: ctx['state'].bool});
|
||||||
|
return block1([], [b2]);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`slots slot and (inline) t-call 1`] = `
|
exports[`slots slot and (inline) t-call 1`] = `
|
||||||
"function anonymous(bdom, helpers
|
"function anonymous(bdom, helpers
|
||||||
) {
|
) {
|
||||||
|
|||||||
@@ -147,7 +147,7 @@ exports[`style and class handling class on sub component, which is switched to a
|
|||||||
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
|
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
return component(\`Child\`, {class: 'someclass',child: ctx['state'].child}, key + \`__1\`, node, ctx);
|
return component(\`Child\`, {class: 'someclass', child: ctx['state'].child}, key + \`__1\`, node, ctx);
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|||||||
@@ -53,8 +53,8 @@ exports[`list of components crash on duplicate key in dev mode 1`] = `
|
|||||||
let key1 = 'child';
|
let key1 = 'child';
|
||||||
if (keys1.has(key1)) { throw new Error(\`Got duplicate key in t-foreach: \${key1}\`)}
|
if (keys1.has(key1)) { throw new Error(\`Got duplicate key in t-foreach: \${key1}\`)}
|
||||||
keys1.add(key1);
|
keys1.add(key1);
|
||||||
const props1 = {}
|
const props1 = {};
|
||||||
helpers.validateProps(\`Child\`, props1, ctx)
|
helpers.validateProps(\`Child\`, props1, ctx);
|
||||||
c_block1[i1] = withKey(component(\`Child\`, props1, key + \`__1__\${key1}\`, node, ctx), key1);
|
c_block1[i1] = withKey(component(\`Child\`, props1, key + \`__1__\${key1}\`, node, ctx), key1);
|
||||||
}
|
}
|
||||||
return list(c_block1);
|
return list(c_block1);
|
||||||
@@ -177,7 +177,7 @@ exports[`list of components reconciliation alg works for t-foreach in t-foreach,
|
|||||||
for (let i2 = 0; i2 < l_block4; i2++) {
|
for (let i2 = 0; i2 < l_block4; i2++) {
|
||||||
ctx[\`col\`] = v_block4[i2];
|
ctx[\`col\`] = v_block4[i2];
|
||||||
let key2 = ctx['col'];
|
let key2 = ctx['col'];
|
||||||
let b6 = component(\`Child\`, {row: ctx['row'],col: ctx['col']}, key + \`__1__\${key1}__\${key2}\`, node, ctx);
|
let b6 = component(\`Child\`, {row: ctx['row'], col: ctx['col']}, key + \`__1__\${key1}__\${key2}\`, node, ctx);
|
||||||
c_block4[i2] = withKey(block5([], [b6]), key2);
|
c_block4[i2] = withKey(block5([], [b6]), key2);
|
||||||
}
|
}
|
||||||
ctx = ctx.__proto__;
|
ctx = ctx.__proto__;
|
||||||
|
|||||||
@@ -10,8 +10,9 @@ exports[`t-model directive .lazy modifier 1`] = `
|
|||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const bExpr1 = ctx['state'];
|
const bExpr1 = ctx['state'];
|
||||||
let attr1 = ctx['state']['text'];
|
const expr1 = 'text';
|
||||||
let hdlr1 = [(ev) => { bExpr1['text'] = ev.target.value; }];
|
let attr1 = bExpr1[expr1];
|
||||||
|
let hdlr1 = [(ev) => { bExpr1[expr1] = ev.target.value; }];
|
||||||
let txt1 = ctx['state'].text;
|
let txt1 = ctx['state'].text;
|
||||||
return block1([attr1, hdlr1, txt1]);
|
return block1([attr1, hdlr1, txt1]);
|
||||||
}
|
}
|
||||||
@@ -28,8 +29,9 @@ exports[`t-model directive .number modifier 1`] = `
|
|||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const bExpr1 = ctx['state'];
|
const bExpr1 = ctx['state'];
|
||||||
let attr1 = ctx['state']['number'];
|
const expr1 = 'number';
|
||||||
let hdlr1 = [(ev) => { bExpr1['number'] = toNumber(ev.target.value); }];
|
let attr1 = bExpr1[expr1];
|
||||||
|
let hdlr1 = [(ev) => { bExpr1[expr1] = toNumber(ev.target.value); }];
|
||||||
let txt1 = ctx['state'].number;
|
let txt1 = ctx['state'].number;
|
||||||
return block1([attr1, hdlr1, txt1]);
|
return block1([attr1, hdlr1, txt1]);
|
||||||
}
|
}
|
||||||
@@ -46,8 +48,9 @@ exports[`t-model directive .trim modifier 1`] = `
|
|||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const bExpr1 = ctx['state'];
|
const bExpr1 = ctx['state'];
|
||||||
let attr1 = ctx['state']['text'];
|
const expr1 = 'text';
|
||||||
let hdlr1 = [(ev) => { bExpr1['text'] = ev.target.value.trim(); }];
|
let attr1 = bExpr1[expr1];
|
||||||
|
let hdlr1 = [(ev) => { bExpr1[expr1] = ev.target.value.trim(); }];
|
||||||
let txt1 = ctx['state'].text;
|
let txt1 = ctx['state'].text;
|
||||||
return block1([attr1, hdlr1, txt1]);
|
return block1([attr1, hdlr1, txt1]);
|
||||||
}
|
}
|
||||||
@@ -64,8 +67,9 @@ exports[`t-model directive basic use, on an input 1`] = `
|
|||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const bExpr1 = ctx['state'];
|
const bExpr1 = ctx['state'];
|
||||||
let attr1 = ctx['state']['text'];
|
const expr1 = 'text';
|
||||||
let hdlr1 = [(ev) => { bExpr1['text'] = ev.target.value; }];
|
let attr1 = bExpr1[expr1];
|
||||||
|
let hdlr1 = [(ev) => { bExpr1[expr1] = ev.target.value; }];
|
||||||
let txt1 = ctx['state'].text;
|
let txt1 = ctx['state'].text;
|
||||||
return block1([attr1, hdlr1, txt1]);
|
return block1([attr1, hdlr1, txt1]);
|
||||||
}
|
}
|
||||||
@@ -82,8 +86,9 @@ exports[`t-model directive basic use, on an input with bracket expression 1`] =
|
|||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const bExpr1 = ctx['state'];
|
const bExpr1 = ctx['state'];
|
||||||
let attr1 = ctx['state']['text'];
|
const expr1 = 'text';
|
||||||
let hdlr1 = [(ev) => { bExpr1['text'] = ev.target.value; }];
|
let attr1 = bExpr1[expr1];
|
||||||
|
let hdlr1 = [(ev) => { bExpr1[expr1] = ev.target.value; }];
|
||||||
let txt1 = ctx['state'].text;
|
let txt1 = ctx['state'].text;
|
||||||
return block1([attr1, hdlr1, txt1]);
|
return block1([attr1, hdlr1, txt1]);
|
||||||
}
|
}
|
||||||
@@ -100,8 +105,9 @@ exports[`t-model directive basic use, on another key in component 1`] = `
|
|||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const bExpr1 = ctx['some'];
|
const bExpr1 = ctx['some'];
|
||||||
let attr1 = ctx['some']['text'];
|
const expr1 = 'text';
|
||||||
let hdlr1 = [(ev) => { bExpr1['text'] = ev.target.value; }];
|
let attr1 = bExpr1[expr1];
|
||||||
|
let hdlr1 = [(ev) => { bExpr1[expr1] = ev.target.value; }];
|
||||||
let txt1 = ctx['some'].text;
|
let txt1 = ctx['some'].text;
|
||||||
return block1([attr1, hdlr1, txt1]);
|
return block1([attr1, hdlr1, txt1]);
|
||||||
}
|
}
|
||||||
@@ -119,8 +125,9 @@ exports[`t-model directive can also define t-on directive on same event, part 1
|
|||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
let hdlr1 = [ctx['onInput'], ctx];
|
let hdlr1 = [ctx['onInput'], ctx];
|
||||||
const bExpr1 = ctx['state'];
|
const bExpr1 = ctx['state'];
|
||||||
let attr1 = ctx['state']['text'];
|
const expr1 = 'text';
|
||||||
let hdlr2 = [(ev) => { bExpr1['text'] = ev.target.value; }];
|
let attr1 = bExpr1[expr1];
|
||||||
|
let hdlr2 = [(ev) => { bExpr1[expr1] = ev.target.value; }];
|
||||||
return block1([hdlr1, attr1, hdlr2]);
|
return block1([hdlr1, attr1, hdlr2]);
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
@@ -137,16 +144,19 @@ exports[`t-model directive can also define t-on directive on same event, part 2
|
|||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
let hdlr1 = [ctx['onClick'], ctx];
|
let hdlr1 = [ctx['onClick'], ctx];
|
||||||
const bExpr1 = ctx['state'];
|
const bExpr1 = ctx['state'];
|
||||||
let attr1 = ctx['state']['choice'] === 'One';
|
const expr1 = 'choice';
|
||||||
let hdlr2 = [(ev) => { bExpr1['choice'] = ev.target.value; }];
|
let attr1 = bExpr1[expr1] === 'One';
|
||||||
|
let hdlr2 = [(ev) => { bExpr1[expr1] = ev.target.value; }];
|
||||||
let hdlr3 = [ctx['onClick'], ctx];
|
let hdlr3 = [ctx['onClick'], ctx];
|
||||||
const bExpr2 = ctx['state'];
|
const bExpr2 = ctx['state'];
|
||||||
let attr2 = ctx['state']['choice'] === 'Two';
|
const expr2 = 'choice';
|
||||||
let hdlr4 = [(ev) => { bExpr2['choice'] = ev.target.value; }];
|
let attr2 = bExpr2[expr2] === 'Two';
|
||||||
|
let hdlr4 = [(ev) => { bExpr2[expr2] = ev.target.value; }];
|
||||||
let hdlr5 = [ctx['onClick'], ctx];
|
let hdlr5 = [ctx['onClick'], ctx];
|
||||||
const bExpr3 = ctx['state'];
|
const bExpr3 = ctx['state'];
|
||||||
let attr3 = ctx['state']['choice'] === 'Three';
|
const expr3 = 'choice';
|
||||||
let hdlr6 = [(ev) => { bExpr3['choice'] = ev.target.value; }];
|
let attr3 = bExpr3[expr3] === 'Three';
|
||||||
|
let hdlr6 = [(ev) => { bExpr3[expr3] = ev.target.value; }];
|
||||||
return block1([hdlr1, attr1, hdlr2, hdlr3, attr2, hdlr4, hdlr5, attr3, hdlr6]);
|
return block1([hdlr1, attr1, hdlr2, hdlr3, attr2, hdlr4, hdlr5, attr3, hdlr6]);
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
@@ -165,8 +175,9 @@ exports[`t-model directive following a scope protecting directive (e.g. t-set) 1
|
|||||||
ctx[isBoundary] = 1
|
ctx[isBoundary] = 1
|
||||||
setContextValue(ctx, \\"admiral\\", 'Bruno');
|
setContextValue(ctx, \\"admiral\\", 'Bruno');
|
||||||
const bExpr1 = ctx['state'];
|
const bExpr1 = ctx['state'];
|
||||||
let attr1 = ctx['state']['text'];
|
const expr1 = 'text';
|
||||||
let hdlr1 = [(ev) => { bExpr1['text'] = ev.target.value; }];
|
let attr1 = bExpr1[expr1];
|
||||||
|
let hdlr1 = [(ev) => { bExpr1[expr1] = ev.target.value; }];
|
||||||
return block1([attr1, hdlr1]);
|
return block1([attr1, hdlr1]);
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
@@ -188,8 +199,9 @@ exports[`t-model directive in a t-foreach 1`] = `
|
|||||||
ctx[\`thing\`] = v_block2[i1];
|
ctx[\`thing\`] = v_block2[i1];
|
||||||
let key1 = ctx['thing'].id;
|
let key1 = ctx['thing'].id;
|
||||||
const bExpr1 = ctx['thing'];
|
const bExpr1 = ctx['thing'];
|
||||||
let attr1 = ctx['thing']['f'];
|
const expr1 = 'f';
|
||||||
let hdlr1 = [(ev) => { bExpr1['f'] = ev.target.checked; }];
|
let attr1 = bExpr1[expr1];
|
||||||
|
let hdlr1 = [(ev) => { bExpr1[expr1] = ev.target.checked; }];
|
||||||
c_block2[i1] = withKey(block3([attr1, hdlr1]), key1);
|
c_block2[i1] = withKey(block3([attr1, hdlr1]), key1);
|
||||||
}
|
}
|
||||||
let b2 = list(c_block2);
|
let b2 = list(c_block2);
|
||||||
@@ -215,8 +227,37 @@ exports[`t-model directive in a t-foreach, part 2 1`] = `
|
|||||||
ctx[\`thing_index\`] = i1;
|
ctx[\`thing_index\`] = i1;
|
||||||
let key1 = ctx['thing_index'];
|
let key1 = ctx['thing_index'];
|
||||||
const bExpr1 = ctx['state'];
|
const bExpr1 = ctx['state'];
|
||||||
let attr1 = ctx['state'][ctx['thing_index']];
|
const expr1 = ctx['thing_index'];
|
||||||
let hdlr1 = [(ev) => { bExpr1[ctx['thing_index']] = ev.target.value; }];
|
let attr1 = bExpr1[expr1];
|
||||||
|
let hdlr1 = [(ev) => { bExpr1[expr1] = ev.target.value; }];
|
||||||
|
c_block2[i1] = withKey(block3([attr1, hdlr1]), key1);
|
||||||
|
}
|
||||||
|
let b2 = list(c_block2);
|
||||||
|
return block1([], [b2]);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`t-model directive in a t-foreach, part 3 1`] = `
|
||||||
|
"function anonymous(bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
|
||||||
|
let { prepareList, toNumber, withKey } = helpers;
|
||||||
|
|
||||||
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
let block3 = createBlock(\`<input block-attribute-0=\\"value\\" block-handler-1=\\"input\\"/>\`);
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
ctx = Object.create(ctx);
|
||||||
|
const [k_block2, v_block2, l_block2, c_block2] = prepareList(ctx['names']);
|
||||||
|
for (let i1 = 0; i1 < l_block2; i1++) {
|
||||||
|
ctx[\`name\`] = v_block2[i1];
|
||||||
|
ctx[\`name_index\`] = i1;
|
||||||
|
let key1 = ctx['name_index'];
|
||||||
|
const bExpr1 = ctx['state'].values;
|
||||||
|
const expr1 = ctx['name'];
|
||||||
|
let attr1 = bExpr1[expr1];
|
||||||
|
let hdlr1 = [(ev) => { bExpr1[expr1] = ev.target.value; }];
|
||||||
c_block2[i1] = withKey(block3([attr1, hdlr1]), key1);
|
c_block2[i1] = withKey(block3([attr1, hdlr1]), key1);
|
||||||
}
|
}
|
||||||
let b2 = list(c_block2);
|
let b2 = list(c_block2);
|
||||||
@@ -235,8 +276,9 @@ exports[`t-model directive on a select 1`] = `
|
|||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const bExpr1 = ctx['state'];
|
const bExpr1 = ctx['state'];
|
||||||
let attr1 = ctx['state']['color'];
|
const expr1 = 'color';
|
||||||
let hdlr1 = [(ev) => { bExpr1['color'] = ev.target.value; }];
|
let attr1 = bExpr1[expr1];
|
||||||
|
let hdlr1 = [(ev) => { bExpr1[expr1] = ev.target.value; }];
|
||||||
let txt1 = ctx['state'].color;
|
let txt1 = ctx['state'].color;
|
||||||
return block1([attr1, hdlr1, txt1]);
|
return block1([attr1, hdlr1, txt1]);
|
||||||
}
|
}
|
||||||
@@ -253,8 +295,9 @@ exports[`t-model directive on a select, initial state 1`] = `
|
|||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const bExpr1 = ctx['state'];
|
const bExpr1 = ctx['state'];
|
||||||
let attr1 = ctx['state']['color'];
|
const expr1 = 'color';
|
||||||
let hdlr1 = [(ev) => { bExpr1['color'] = ev.target.value; }];
|
let attr1 = bExpr1[expr1];
|
||||||
|
let hdlr1 = [(ev) => { bExpr1[expr1] = ev.target.value; }];
|
||||||
return block1([attr1, hdlr1]);
|
return block1([attr1, hdlr1]);
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
@@ -270,8 +313,9 @@ exports[`t-model directive on a sub state key 1`] = `
|
|||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const bExpr1 = ctx['state'].something;
|
const bExpr1 = ctx['state'].something;
|
||||||
let attr1 = ctx['state'].something['text'];
|
const expr1 = 'text';
|
||||||
let hdlr1 = [(ev) => { bExpr1['text'] = ev.target.value; }];
|
let attr1 = bExpr1[expr1];
|
||||||
|
let hdlr1 = [(ev) => { bExpr1[expr1] = ev.target.value; }];
|
||||||
let txt1 = ctx['state'].something.text;
|
let txt1 = ctx['state'].something.text;
|
||||||
return block1([attr1, hdlr1, txt1]);
|
return block1([attr1, hdlr1, txt1]);
|
||||||
}
|
}
|
||||||
@@ -288,11 +332,13 @@ exports[`t-model directive on an input type=radio 1`] = `
|
|||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const bExpr1 = ctx['state'];
|
const bExpr1 = ctx['state'];
|
||||||
let attr1 = ctx['state']['choice'] === 'One';
|
const expr1 = 'choice';
|
||||||
let hdlr1 = [(ev) => { bExpr1['choice'] = ev.target.value; }];
|
let attr1 = bExpr1[expr1] === 'One';
|
||||||
|
let hdlr1 = [(ev) => { bExpr1[expr1] = ev.target.value; }];
|
||||||
const bExpr2 = ctx['state'];
|
const bExpr2 = ctx['state'];
|
||||||
let attr2 = ctx['state']['choice'] === 'Two';
|
const expr2 = 'choice';
|
||||||
let hdlr2 = [(ev) => { bExpr2['choice'] = ev.target.value; }];
|
let attr2 = bExpr2[expr2] === 'Two';
|
||||||
|
let hdlr2 = [(ev) => { bExpr2[expr2] = ev.target.value; }];
|
||||||
let txt1 = ctx['state'].choice;
|
let txt1 = ctx['state'].choice;
|
||||||
return block1([attr1, hdlr1, attr2, hdlr2, txt1]);
|
return block1([attr1, hdlr1, attr2, hdlr2, txt1]);
|
||||||
}
|
}
|
||||||
@@ -309,11 +355,13 @@ exports[`t-model directive on an input type=radio, with initial value 1`] = `
|
|||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const bExpr1 = ctx['state'];
|
const bExpr1 = ctx['state'];
|
||||||
let attr1 = ctx['state']['choice'] === 'One';
|
const expr1 = 'choice';
|
||||||
let hdlr1 = [(ev) => { bExpr1['choice'] = ev.target.value; }];
|
let attr1 = bExpr1[expr1] === 'One';
|
||||||
|
let hdlr1 = [(ev) => { bExpr1[expr1] = ev.target.value; }];
|
||||||
const bExpr2 = ctx['state'];
|
const bExpr2 = ctx['state'];
|
||||||
let attr2 = ctx['state']['choice'] === 'Two';
|
const expr2 = 'choice';
|
||||||
let hdlr2 = [(ev) => { bExpr2['choice'] = ev.target.value; }];
|
let attr2 = bExpr2[expr2] === 'Two';
|
||||||
|
let hdlr2 = [(ev) => { bExpr2[expr2] = ev.target.value; }];
|
||||||
return block1([attr1, hdlr1, attr2, hdlr2]);
|
return block1([attr1, hdlr1, attr2, hdlr2]);
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
@@ -330,8 +378,9 @@ exports[`t-model directive on an input, type=checkbox 1`] = `
|
|||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
let b2,b3;
|
let b2,b3;
|
||||||
const bExpr1 = ctx['state'];
|
const bExpr1 = ctx['state'];
|
||||||
let attr1 = ctx['state']['flag'];
|
const expr1 = 'flag';
|
||||||
let hdlr1 = [(ev) => { bExpr1['flag'] = ev.target.checked; }];
|
let attr1 = bExpr1[expr1];
|
||||||
|
let hdlr1 = [(ev) => { bExpr1[expr1] = ev.target.checked; }];
|
||||||
if (ctx['state'].flag) {
|
if (ctx['state'].flag) {
|
||||||
b2 = text(\`yes\`);
|
b2 = text(\`yes\`);
|
||||||
} else {
|
} else {
|
||||||
@@ -352,14 +401,155 @@ exports[`t-model directive on an textarea 1`] = `
|
|||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
const bExpr1 = ctx['state'];
|
const bExpr1 = ctx['state'];
|
||||||
let attr1 = ctx['state']['text'];
|
const expr1 = 'text';
|
||||||
let hdlr1 = [(ev) => { bExpr1['text'] = ev.target.value; }];
|
let attr1 = bExpr1[expr1];
|
||||||
|
let hdlr1 = [(ev) => { bExpr1[expr1] = ev.target.value; }];
|
||||||
let txt1 = ctx['state'].text;
|
let txt1 = ctx['state'].text;
|
||||||
return block1([attr1, hdlr1, txt1]);
|
return block1([attr1, hdlr1, txt1]);
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`t-model directive t-model on an input with an undefined value 1`] = `
|
||||||
|
"function anonymous(bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
|
||||||
|
let { toNumber } = helpers;
|
||||||
|
|
||||||
|
let block1 = createBlock(\`<input block-attribute-0=\\"value\\" block-handler-1=\\"input\\"/>\`);
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
const bExpr1 = ctx['state'];
|
||||||
|
const expr1 = 'text';
|
||||||
|
let attr1 = bExpr1[expr1];
|
||||||
|
let hdlr1 = [(ev) => { bExpr1[expr1] = ev.target.value; }];
|
||||||
|
return block1([attr1, hdlr1]);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`t-model directive t-model on select with static options 1`] = `
|
||||||
|
"function anonymous(bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
|
||||||
|
let { toNumber } = helpers;
|
||||||
|
|
||||||
|
let block1 = createBlock(\`<div><select block-attribute-0=\\"value\\" block-handler-1=\\"change\\"><option value=\\"a\\"><block-text-2/></option><option value=\\"b\\"><block-text-3/></option><option value=\\"c\\"><block-text-4/></option></select></div>\`);
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
const bExpr1 = ctx['state'];
|
||||||
|
const expr1 = 'model';
|
||||||
|
let attr1 = bExpr1[expr1];
|
||||||
|
let hdlr1 = [(ev) => { bExpr1[expr1] = ev.target.value; }];
|
||||||
|
let txt1 = 'a';
|
||||||
|
let txt2 = 'b';
|
||||||
|
let txt3 = 'c';
|
||||||
|
return block1([attr1, hdlr1, txt1, txt2, txt3]);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`t-model directive t-model with dynamic values on select options -- 2 1`] = `
|
||||||
|
"function anonymous(bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
|
||||||
|
let { toNumber } = helpers;
|
||||||
|
|
||||||
|
let block1 = createBlock(\`<div><select block-handler-0=\\"change\\"><option block-attribute-1=\\"value\\" block-attribute-2=\\"selected\\"><block-text-3/></option><option block-attribute-4=\\"value\\" block-attribute-5=\\"selected\\"><block-text-6/></option></select></div>\`);
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
const bExpr1 = ctx['state'];
|
||||||
|
const expr1 = 'model';
|
||||||
|
let bValue1 = bExpr1[expr1]
|
||||||
|
let hdlr1 = [(ev) => { bExpr1[expr1] = ev.target.value; }];
|
||||||
|
let attr1 = ctx['options'][0];
|
||||||
|
let attr2 = bValue1 === ctx['options'][0];
|
||||||
|
let txt1 = ctx['options'][0];
|
||||||
|
let attr3 = (ctx['options'][1]);
|
||||||
|
let attr4 = bValue1 === (ctx['options'][1]);
|
||||||
|
let txt2 = ctx['options'][1];
|
||||||
|
return block1([hdlr1, attr1, attr2, txt1, attr3, attr4, txt2]);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`t-model directive t-model with dynamic values on select options -- 3 1`] = `
|
||||||
|
"function anonymous(bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
|
||||||
|
let { toNumber } = helpers;
|
||||||
|
|
||||||
|
let block1 = createBlock(\`<div><select block-handler-0=\\"change\\"><option block-attribute-1=\\"value\\" block-attribute-2=\\"selected\\"><block-text-3/></option><option value=\\"b\\" block-attribute-4=\\"selected\\"><block-text-5/></option></select></div>\`);
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
const bExpr1 = ctx['state'];
|
||||||
|
const expr1 = 'model';
|
||||||
|
let bValue1 = bExpr1[expr1]
|
||||||
|
let hdlr1 = [(ev) => { bExpr1[expr1] = ev.target.value; }];
|
||||||
|
let attr1 = ctx['options'][0];
|
||||||
|
let attr2 = bValue1 === ctx['options'][0];
|
||||||
|
let txt1 = ctx['options'][0];
|
||||||
|
let attr3 = bValue1 === \\"b\\";
|
||||||
|
let txt2 = ctx['options'][1];
|
||||||
|
return block1([hdlr1, attr1, attr2, txt1, attr3, txt2]);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`t-model directive t-model with dynamic values on select options 1`] = `
|
||||||
|
"function anonymous(bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
|
||||||
|
let { toNumber } = helpers;
|
||||||
|
|
||||||
|
let block1 = createBlock(\`<div><select block-handler-0=\\"change\\"><option block-attribute-1=\\"value\\" block-attribute-2=\\"selected\\"><block-text-3/></option><option block-attribute-4=\\"value\\" block-attribute-5=\\"selected\\"><block-text-6/></option></select></div>\`);
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
const bExpr1 = ctx['state'];
|
||||||
|
const expr1 = 'model';
|
||||||
|
let bValue1 = bExpr1[expr1]
|
||||||
|
let hdlr1 = [(ev) => { bExpr1[expr1] = ev.target.value; }];
|
||||||
|
let attr1 = ctx['options'][0];
|
||||||
|
let attr2 = bValue1 === ctx['options'][0];
|
||||||
|
let txt1 = ctx['options'][0];
|
||||||
|
let attr3 = ctx['options'][1];
|
||||||
|
let attr4 = bValue1 === ctx['options'][1];
|
||||||
|
let txt2 = ctx['options'][1];
|
||||||
|
return block1([hdlr1, attr1, attr2, txt1, attr3, attr4, txt2]);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`t-model directive t-model with dynamic values on select options in foreach 1`] = `
|
||||||
|
"function anonymous(bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
|
||||||
|
let { toNumber, prepareList, withKey } = helpers;
|
||||||
|
|
||||||
|
let block1 = createBlock(\`<div><select block-handler-0=\\"change\\"><block-child-0/></select></div>\`);
|
||||||
|
let block3 = createBlock(\`<option block-attribute-0=\\"value\\" block-attribute-1=\\"selected\\"><block-text-2/></option>\`);
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
const bExpr1 = ctx['state'];
|
||||||
|
const expr1 = 'model';
|
||||||
|
let bValue1 = bExpr1[expr1]
|
||||||
|
let hdlr1 = [(ev) => { bExpr1[expr1] = ev.target.value; }];
|
||||||
|
ctx = Object.create(ctx);
|
||||||
|
const [k_block2, v_block2, l_block2, c_block2] = prepareList(ctx['options']);
|
||||||
|
for (let i1 = 0; i1 < l_block2; i1++) {
|
||||||
|
ctx[\`v\`] = v_block2[i1];
|
||||||
|
let key1 = ctx['v'];
|
||||||
|
let attr1 = ctx['v'];
|
||||||
|
let attr2 = bValue1 === ctx['v'];
|
||||||
|
let txt1 = ctx['v'];
|
||||||
|
c_block2[i1] = withKey(block3([attr1, attr2, txt1]), key1);
|
||||||
|
}
|
||||||
|
let b2 = list(c_block2);
|
||||||
|
return block1([hdlr1], [b2]);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`t-model directive two inputs in a div alternating with a t-if 1`] = `
|
exports[`t-model directive two inputs in a div alternating with a t-if 1`] = `
|
||||||
"function anonymous(bdom, helpers
|
"function anonymous(bdom, helpers
|
||||||
) {
|
) {
|
||||||
@@ -374,17 +564,38 @@ exports[`t-model directive two inputs in a div alternating with a t-if 1`] = `
|
|||||||
let b2,b3;
|
let b2,b3;
|
||||||
if (ctx['state'].flag) {
|
if (ctx['state'].flag) {
|
||||||
const bExpr1 = ctx['state'];
|
const bExpr1 = ctx['state'];
|
||||||
let attr1 = ctx['state']['text1'];
|
const expr1 = 'text1';
|
||||||
let hdlr1 = [(ev) => { bExpr1['text1'] = ev.target.value; }];
|
let attr1 = bExpr1[expr1];
|
||||||
|
let hdlr1 = [(ev) => { bExpr1[expr1] = ev.target.value; }];
|
||||||
b2 = block2([attr1, hdlr1]);
|
b2 = block2([attr1, hdlr1]);
|
||||||
}
|
}
|
||||||
if (!ctx['state'].flag) {
|
if (!ctx['state'].flag) {
|
||||||
const bExpr2 = ctx['state'];
|
const bExpr2 = ctx['state'];
|
||||||
let attr2 = ctx['state']['text2'];
|
const expr2 = 'text2';
|
||||||
let hdlr2 = [(ev) => { bExpr2['text2'] = ev.target.value; }];
|
let attr2 = bExpr2[expr2];
|
||||||
|
let hdlr2 = [(ev) => { bExpr2[expr2] = ev.target.value; }];
|
||||||
b3 = block3([attr2, hdlr2]);
|
b3 = block3([attr2, hdlr2]);
|
||||||
}
|
}
|
||||||
return block1([], [b2, b3]);
|
return block1([], [b2, b3]);
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`t-model directive with expression having a changing key 1`] = `
|
||||||
|
"function anonymous(bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
|
||||||
|
let { toNumber } = helpers;
|
||||||
|
|
||||||
|
let block1 = createBlock(\`<div><input block-attribute-0=\\"value\\" block-handler-1=\\"input\\"/><span><block-text-2/></span></div>\`);
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
const bExpr1 = ctx['state'].something;
|
||||||
|
const expr1 = ctx['text'].key;
|
||||||
|
let attr1 = bExpr1[expr1];
|
||||||
|
let hdlr1 = [(ev) => { bExpr1[expr1] = ev.target.value; }];
|
||||||
|
let txt1 = ctx['state'].something[ctx['text'].key];
|
||||||
|
return block1([attr1, hdlr1, txt1]);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ exports[`t-props basic use 1`] = `
|
|||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
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]);
|
return block1([], [b2]);
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
@@ -28,6 +28,30 @@ exports[`t-props basic use 2`] = `
|
|||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`t-props child receives a copy of the t-props object, not the original 1`] = `
|
||||||
|
"function anonymous(bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
return component(\`Child\`, Object.assign({}, ctx['childProps']), key + \`__1\`, node, ctx);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`t-props child receives a copy of the t-props object, not the original 2`] = `
|
||||||
|
"function anonymous(bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
|
||||||
|
|
||||||
|
let block1 = createBlock(\`<div/>\`);
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
return block1();
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`t-props t-props and other props 1`] = `
|
exports[`t-props t-props and other props 1`] = `
|
||||||
"function anonymous(bdom, helpers
|
"function anonymous(bdom, helpers
|
||||||
) {
|
) {
|
||||||
@@ -65,7 +89,7 @@ exports[`t-props t-props only 1`] = `
|
|||||||
let block1 = createBlock(\`<div><div><block-child-0/></div></div>\`);
|
let block1 = createBlock(\`<div><div><block-child-0/></div></div>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
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]);
|
return block1([], [b2]);
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
@@ -93,7 +117,7 @@ exports[`t-props t-props with props 1`] = `
|
|||||||
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
return function template(ctx, node, key = \\"\\") {
|
||||||
let b2 = component(\`Child\`, Object.assign({}, ctx['childProps'], {a: 1,b: 2}), key + \`__1\`, node, ctx);
|
let b2 = component(\`Child\`, Object.assign({}, ctx['childProps'], {a: 1, b: 2}), key + \`__1\`, node, ctx);
|
||||||
return block1([], [b2]);
|
return block1([], [b2]);
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
|
|||||||
@@ -171,6 +171,27 @@ describe("basics", () => {
|
|||||||
expect(error!.message).toBe("Cannot mount a component on a detached dom node");
|
expect(error!.message).toBe("Cannot mount a component on a detached dom node");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("a component cannot be mounted in a detached node (even if node is detached later)", async () => {
|
||||||
|
const warn = console.warn;
|
||||||
|
console.warn = jest.fn();
|
||||||
|
class Test extends Component {
|
||||||
|
static template = xml`<div/>`;
|
||||||
|
}
|
||||||
|
let error: Error;
|
||||||
|
const prom = mount(Test, fixture);
|
||||||
|
await Promise.resolve();
|
||||||
|
fixture.remove();
|
||||||
|
try {
|
||||||
|
await prom;
|
||||||
|
} catch (e) {
|
||||||
|
error = e as Error;
|
||||||
|
}
|
||||||
|
expect(error!).toBeDefined();
|
||||||
|
expect(error!.message).toBe("Cannot mount a component on a detached dom node");
|
||||||
|
expect(console.warn).toBeCalledTimes(1);
|
||||||
|
console.warn = warn;
|
||||||
|
});
|
||||||
|
|
||||||
test("crashes if it cannot find a template", async () => {
|
test("crashes if it cannot find a template", async () => {
|
||||||
class Test extends Component {
|
class Test extends Component {
|
||||||
static template = "wrongtemplate";
|
static template = "wrongtemplate";
|
||||||
@@ -183,7 +204,7 @@ describe("basics", () => {
|
|||||||
error = e as Error;
|
error = e as Error;
|
||||||
}
|
}
|
||||||
expect(error!).toBeDefined();
|
expect(error!).toBeDefined();
|
||||||
expect(error!.message).toBe('Missing template: "wrongtemplate"');
|
expect(error!.message).toBe('Missing template: "wrongtemplate" (for component "Test")');
|
||||||
});
|
});
|
||||||
|
|
||||||
test("class component with dynamic text", async () => {
|
test("class component with dynamic text", async () => {
|
||||||
@@ -1006,4 +1027,31 @@ describe("t-out in components", () => {
|
|||||||
"<div><b>one</b><b>one</b><b>two</b><b>two</b><b>tree</b><b>tree</b></div>"
|
"<div><b>one</b><b>one</b><b>two</b><b>two</b><b>tree</b><b>tree</b></div>"
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("can switch the contents of two t-out repeatedly", async () => {
|
||||||
|
class Test extends Component {
|
||||||
|
static template = xml`
|
||||||
|
<t t-out="state.a"/>
|
||||||
|
<t t-out="state.b"/>
|
||||||
|
`;
|
||||||
|
state = useState({
|
||||||
|
a: markup("<div>1</div>"),
|
||||||
|
b: markup("<div>2</div>"),
|
||||||
|
});
|
||||||
|
|
||||||
|
reverse() {
|
||||||
|
const { state } = this;
|
||||||
|
[state.a, state.b] = [state.b, state.a];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const comp = await mount(Test, fixture);
|
||||||
|
expect(fixture.innerHTML).toBe("<div>1</div><div>2</div>");
|
||||||
|
comp.reverse();
|
||||||
|
await nextTick();
|
||||||
|
expect(fixture.innerHTML).toBe("<div>2</div><div>1</div>");
|
||||||
|
comp.reverse();
|
||||||
|
await nextTick();
|
||||||
|
expect(fixture.innerHTML).toBe("<div>1</div><div>2</div>");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -97,17 +97,14 @@ test("destroying/recreating a subwidget with different props (if start is not ov
|
|||||||
expect(["W:setup", "W:willStart", "W:willRender", "W:rendered", "W:mounted"]).toBeLogged();
|
expect(["W:setup", "W:willStart", "W:willRender", "W:rendered", "W:mounted"]).toBeLogged();
|
||||||
|
|
||||||
expect(n).toBe(0);
|
expect(n).toBe(0);
|
||||||
|
|
||||||
w.state.val = 2;
|
w.state.val = 2;
|
||||||
await nextMicroTick();
|
await new Promise(requestAnimationFrame); // calls to _render are batched per animation frame
|
||||||
await nextMicroTick();
|
|
||||||
expect(n).toBe(1);
|
expect(n).toBe(1);
|
||||||
|
|
||||||
expect(["W:willRender", "Child:setup", "Child:willStart", "W:rendered"]).toBeLogged();
|
expect(["W:willRender", "Child:setup", "Child:willStart", "W:rendered"]).toBeLogged();
|
||||||
|
|
||||||
w.state.val = 3;
|
w.state.val = 3;
|
||||||
await nextMicroTick();
|
await new Promise(requestAnimationFrame);
|
||||||
await nextMicroTick();
|
|
||||||
expect(n).toBe(2);
|
expect(n).toBe(2);
|
||||||
|
|
||||||
expect([
|
expect([
|
||||||
|
|||||||
@@ -81,6 +81,25 @@ describe("basics", () => {
|
|||||||
expect(mockConsoleWarn).toBeCalledTimes(1);
|
expect(mockConsoleWarn).toBeCalledTimes(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("display a nice error if it cannot find component (in dev mode)", async () => {
|
||||||
|
class SomeComponent extends Component {}
|
||||||
|
class Parent extends Component {
|
||||||
|
static template = xml`<SomeMispelledComponent />`;
|
||||||
|
static components = { SomeComponent };
|
||||||
|
}
|
||||||
|
let error: Error;
|
||||||
|
try {
|
||||||
|
await mount(Parent, fixture, { test: true });
|
||||||
|
} catch (e) {
|
||||||
|
error = e as Error;
|
||||||
|
}
|
||||||
|
expect(error!).toBeDefined();
|
||||||
|
expect(error!.message).toBe('Cannot find the definition of component "SomeMispelledComponent"');
|
||||||
|
expect(console.error).toBeCalledTimes(0);
|
||||||
|
expect(mockConsoleError).toBeCalledTimes(0);
|
||||||
|
expect(mockConsoleWarn).toBeCalledTimes(1);
|
||||||
|
});
|
||||||
|
|
||||||
test("simple catchError", async () => {
|
test("simple catchError", async () => {
|
||||||
class Boom extends Component {
|
class Boom extends Component {
|
||||||
static template = xml`<div t-esc="a.b.c"/>`;
|
static template = xml`<div t-esc="a.b.c"/>`;
|
||||||
@@ -156,6 +175,30 @@ describe("errors and promises", () => {
|
|||||||
expect(mockConsoleWarn).toBeCalledTimes(1);
|
expect(mockConsoleWarn).toBeCalledTimes(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("an error in onMounted callback will have the component's setup in its stack trace", async () => {
|
||||||
|
class App extends Component {
|
||||||
|
static template = xml`<div>abc</div>`;
|
||||||
|
setup() {
|
||||||
|
onMounted(() => {
|
||||||
|
throw new Error("boom");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let error: Error;
|
||||||
|
try {
|
||||||
|
await mount(App, fixture, { test: true });
|
||||||
|
} catch (e) {
|
||||||
|
error = e as Error;
|
||||||
|
}
|
||||||
|
expect(error!).toBeDefined();
|
||||||
|
expect(error!.stack).toContain("App.setup");
|
||||||
|
expect(error!.stack).toContain("error_handling.test.ts");
|
||||||
|
expect(fixture.innerHTML).toBe("");
|
||||||
|
expect(mockConsoleError).toBeCalledTimes(0);
|
||||||
|
expect(mockConsoleWarn).toBeCalledTimes(1);
|
||||||
|
});
|
||||||
|
|
||||||
test("an error in willPatch call will reject the render promise", async () => {
|
test("an error in willPatch call will reject the render promise", async () => {
|
||||||
class Root extends Component {
|
class Root extends Component {
|
||||||
static template = xml`<div><t t-esc="val"/></div>`;
|
static template = xml`<div><t t-esc="val"/></div>`;
|
||||||
@@ -168,13 +211,13 @@ describe("errors and promises", () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const root = await mount(Root, fixture);
|
const root = await mount(Root, fixture, { test: true });
|
||||||
root.val = 4;
|
root.val = 4;
|
||||||
let error: Error;
|
let error: Error;
|
||||||
root.render();
|
root.render();
|
||||||
await nextTick();
|
await nextTick();
|
||||||
expect(error!).toBeDefined();
|
expect(error!).toBeDefined();
|
||||||
expect(error!.message).toBe("boom");
|
expect(error!.message).toBe(`The following error occurred in onWillPatch: "boom"`);
|
||||||
expect(mockConsoleError).toBeCalledTimes(0);
|
expect(mockConsoleError).toBeCalledTimes(0);
|
||||||
expect(mockConsoleWarn).toBeCalledTimes(0);
|
expect(mockConsoleWarn).toBeCalledTimes(0);
|
||||||
});
|
});
|
||||||
@@ -191,13 +234,13 @@ describe("errors and promises", () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const root = await mount(Root, fixture);
|
const root = await mount(Root, fixture, { test: true });
|
||||||
root.val = 4;
|
root.val = 4;
|
||||||
let error: Error;
|
let error: Error;
|
||||||
root.render();
|
root.render();
|
||||||
await nextTick();
|
await nextTick();
|
||||||
expect(error!).toBeDefined();
|
expect(error!).toBeDefined();
|
||||||
expect(error!.message).toBe("boom");
|
expect(error!.message).toBe(`The following error occurred in onPatched: "boom"`);
|
||||||
expect(mockConsoleError).toBeCalledTimes(0);
|
expect(mockConsoleError).toBeCalledTimes(0);
|
||||||
expect(mockConsoleWarn).toBeCalledTimes(0);
|
expect(mockConsoleWarn).toBeCalledTimes(0);
|
||||||
});
|
});
|
||||||
@@ -273,7 +316,6 @@ describe("errors and promises", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test("errors in mounted and in willUnmount", async () => {
|
test("errors in mounted and in willUnmount", async () => {
|
||||||
expect.assertions(4);
|
|
||||||
class Example extends Component {
|
class Example extends Component {
|
||||||
static template = xml`<div/>`;
|
static template = xml`<div/>`;
|
||||||
val: any;
|
val: any;
|
||||||
@@ -290,9 +332,11 @@ describe("errors and promises", () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await mount(Example, fixture);
|
await mount(Example, fixture, { test: true });
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
expect((e as Error).message).toBe("Error in mounted");
|
expect((e as Error).message).toBe(
|
||||||
|
`The following error occurred in onMounted: "Error in mounted"`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
// 1 additional error is logged because the destruction of the app causes
|
// 1 additional error is logged because the destruction of the app causes
|
||||||
// the onWillUnmount hook to be called and to fail
|
// the onWillUnmount hook to be called and to fail
|
||||||
@@ -351,6 +395,28 @@ describe("can catch errors", () => {
|
|||||||
expect(mockConsoleWarn).toBeCalledTimes(0);
|
expect(mockConsoleWarn).toBeCalledTimes(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("calling a hook outside setup should crash", async () => {
|
||||||
|
class Root extends Component {
|
||||||
|
static template = xml`<t t-esc="state.value"/>`;
|
||||||
|
state = useState({ value: 1 });
|
||||||
|
|
||||||
|
setup() {
|
||||||
|
onWillStart(() => {
|
||||||
|
this.state = useState({ value: 2 });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let e: Error;
|
||||||
|
try {
|
||||||
|
await mount(Root, fixture, { test: true });
|
||||||
|
} catch (error) {
|
||||||
|
e = error as Error;
|
||||||
|
}
|
||||||
|
expect(e!.message).toBe(
|
||||||
|
`The following error occurred in onWillStart: "No active component (a hook function should only be called in 'setup')"`
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
test("can catch an error in the initial call of a component render function (parent mounted)", async () => {
|
test("can catch an error in the initial call of a component render function (parent mounted)", async () => {
|
||||||
class ErrorComponent extends Component {
|
class ErrorComponent extends Component {
|
||||||
static template = xml`<div>hey<t t-esc="state.this.will.crash"/></div>`;
|
static template = xml`<div>hey<t t-esc="state.this.will.crash"/></div>`;
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import {
|
|||||||
useExternalListener,
|
useExternalListener,
|
||||||
useRef,
|
useRef,
|
||||||
useState,
|
useState,
|
||||||
|
useChildSubEnv,
|
||||||
useSubEnv,
|
useSubEnv,
|
||||||
xml,
|
xml,
|
||||||
} from "../../src/index";
|
} from "../../src/index";
|
||||||
@@ -180,7 +181,7 @@ describe("hooks", () => {
|
|||||||
expect(fixture.innerHTML).toBe("<div>1</div>");
|
expect(fixture.innerHTML).toBe("<div>1</div>");
|
||||||
});
|
});
|
||||||
|
|
||||||
test("use sub env does not pollute user env", async () => {
|
test("useSubEnv modifies user env", async () => {
|
||||||
class Test extends Component {
|
class Test extends Component {
|
||||||
static template = xml`<div><t t-esc="env.val"/></div>`;
|
static template = xml`<div><t t-esc="env.val"/></div>`;
|
||||||
setup() {
|
setup() {
|
||||||
@@ -190,11 +191,25 @@ describe("hooks", () => {
|
|||||||
const env = { val: 3 };
|
const env = { val: 3 };
|
||||||
const component = await mount(Test, fixture, { env });
|
const component = await mount(Test, fixture, { env });
|
||||||
expect(fixture.innerHTML).toBe("<div>3</div>");
|
expect(fixture.innerHTML).toBe("<div>3</div>");
|
||||||
|
expect(component.env).toHaveProperty("val2");
|
||||||
|
expect(component.env).toHaveProperty("val");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("useChildSubEnv does not pollute user env", async () => {
|
||||||
|
class Test extends Component {
|
||||||
|
static template = xml`<div><t t-esc="env.val"/></div>`;
|
||||||
|
setup() {
|
||||||
|
useChildSubEnv({ val2: 1 });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const env = { val: 3 };
|
||||||
|
const component = await mount(Test, fixture, { env });
|
||||||
|
expect(fixture.innerHTML).toBe("<div>3</div>");
|
||||||
expect(component.env).not.toHaveProperty("val2");
|
expect(component.env).not.toHaveProperty("val2");
|
||||||
expect(component.env).toHaveProperty("val");
|
expect(component.env).toHaveProperty("val");
|
||||||
});
|
});
|
||||||
|
|
||||||
test("use sub env supports arbitrary descriptor", async () => {
|
test("useSubEnv supports arbitrary descriptor", async () => {
|
||||||
let someVal = "maggot";
|
let someVal = "maggot";
|
||||||
let someVal2 = "brain";
|
let someVal2 = "brain";
|
||||||
|
|
||||||
@@ -213,6 +228,40 @@ describe("hooks", () => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const env = {
|
||||||
|
get someVal() {
|
||||||
|
return someVal;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const component = await mount(Test, fixture, { env });
|
||||||
|
expect(fixture.innerHTML).toBe("<div>maggot brain</div>");
|
||||||
|
someVal = "brain";
|
||||||
|
someVal2 = "maggot";
|
||||||
|
component.render();
|
||||||
|
await nextTick();
|
||||||
|
expect(fixture.innerHTML).toBe("<div>brain maggot</div>");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("useChildSubEnv supports arbitrary descriptor", async () => {
|
||||||
|
let someVal = "maggot";
|
||||||
|
let someVal2 = "brain";
|
||||||
|
|
||||||
|
class Child extends Component {
|
||||||
|
static template = xml`<div><t t-esc="env.someVal" /> <t t-esc="env.someVal2" /></div>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
class Test extends Component {
|
||||||
|
static template = xml`<Child />`;
|
||||||
|
static components = { Child };
|
||||||
|
setup() {
|
||||||
|
useChildSubEnv({
|
||||||
|
get someVal2() {
|
||||||
|
return someVal2;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
someVal = "maggot";
|
someVal = "maggot";
|
||||||
const env = {
|
const env = {
|
||||||
get someVal() {
|
get someVal() {
|
||||||
@@ -239,7 +288,7 @@ describe("hooks", () => {
|
|||||||
await mount(Test, fixture);
|
await mount(Test, fixture);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("parent and child env", async () => {
|
test("parent and child env (with useSubEnv)", async () => {
|
||||||
class Child extends Component {
|
class Child extends Component {
|
||||||
static template = xml`<div><t t-esc="env.val"/></div>`;
|
static template = xml`<div><t t-esc="env.val"/></div>`;
|
||||||
}
|
}
|
||||||
@@ -253,9 +302,44 @@ describe("hooks", () => {
|
|||||||
}
|
}
|
||||||
const env = { val: 3 };
|
const env = { val: 3 };
|
||||||
await mount(Parent, fixture, { env });
|
await mount(Parent, fixture, { env });
|
||||||
|
expect(fixture.innerHTML).toBe("5<div>5</div>");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("parent and child env (with useChildSubEnv)", async () => {
|
||||||
|
class Child extends Component {
|
||||||
|
static template = xml`<div><t t-esc="env.val"/></div>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
class Parent extends Component {
|
||||||
|
static template = xml`<t t-esc="env.val"/><Child/>`;
|
||||||
|
static components = { Child };
|
||||||
|
setup() {
|
||||||
|
useChildSubEnv({ val: 5 });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const env = { val: 3 };
|
||||||
|
await mount(Parent, fixture, { env });
|
||||||
expect(fixture.innerHTML).toBe("3<div>5</div>");
|
expect(fixture.innerHTML).toBe("3<div>5</div>");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("parent and child env (with useChildSubEnv then useSubEnv)", async () => {
|
||||||
|
class Child extends Component {
|
||||||
|
static template = xml`<div t-if="env.hasParent"><t t-esc="env.val"/></div>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
class Parent extends Component {
|
||||||
|
static template = xml`<t t-esc="env.val"/><Child/>`;
|
||||||
|
static components = { Child };
|
||||||
|
setup() {
|
||||||
|
useChildSubEnv({ hasParent: true });
|
||||||
|
useSubEnv({ val: 5 });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const env = { val: 3 };
|
||||||
|
await mount(Parent, fixture, { env });
|
||||||
|
expect(fixture.innerHTML).toBe("5<div>5</div>");
|
||||||
|
});
|
||||||
|
|
||||||
test("can use onWillStart, onWillUpdateProps", async () => {
|
test("can use onWillStart, onWillUpdateProps", async () => {
|
||||||
const steps: string[] = [];
|
const steps: string[] = [];
|
||||||
async function slow(): Promise<string> {
|
async function slow(): Promise<string> {
|
||||||
|
|||||||
@@ -1253,4 +1253,56 @@ describe("lifecycle hooks", () => {
|
|||||||
"onWillDestroy",
|
"onWillDestroy",
|
||||||
]).toBeLogged();
|
]).toBeLogged();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("destroy new children before being mountged", async () => {
|
||||||
|
class Child extends Component {
|
||||||
|
static template = xml`child`;
|
||||||
|
setup() {
|
||||||
|
useLogLifecycle();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Parent extends Component {
|
||||||
|
static template = xml`before<Child t-if="state.flag"/>after`;
|
||||||
|
static components = { Child };
|
||||||
|
|
||||||
|
state = useState({ flag: false });
|
||||||
|
setup() {
|
||||||
|
useLogLifecycle();
|
||||||
|
onRendered(async () => {
|
||||||
|
// we destroy here the app after the new child component has been
|
||||||
|
// created, but before this rendering has been patched to the DOM
|
||||||
|
if (this.state.flag) {
|
||||||
|
await Promise.resolve();
|
||||||
|
app.destroy();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const app = new App(Parent);
|
||||||
|
const parent = await app.mount(fixture);
|
||||||
|
expect(fixture.innerHTML).toBe("beforeafter");
|
||||||
|
expect([
|
||||||
|
"Parent:setup",
|
||||||
|
"Parent:willStart",
|
||||||
|
"Parent:willRender",
|
||||||
|
"Parent:rendered",
|
||||||
|
"Parent:mounted",
|
||||||
|
]).toBeLogged();
|
||||||
|
|
||||||
|
parent.state.flag = true;
|
||||||
|
|
||||||
|
await nextTick();
|
||||||
|
expect(fixture.innerHTML).toBe("");
|
||||||
|
expect([
|
||||||
|
"Parent:willRender",
|
||||||
|
"Child:setup",
|
||||||
|
"Child:willStart",
|
||||||
|
"Parent:rendered",
|
||||||
|
"Parent:willUnmount",
|
||||||
|
"Child:willDestroy",
|
||||||
|
"Parent:willDestroy",
|
||||||
|
]).toBeLogged();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ let mockConsoleWarn: any;
|
|||||||
|
|
||||||
beforeAll(() => {
|
beforeAll(() => {
|
||||||
console.info = (message: any) => {
|
console.info = (message: any) => {
|
||||||
if (message === DEV_MSG) {
|
if (message === DEV_MSG()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
info(message);
|
info(message);
|
||||||
@@ -684,7 +684,7 @@ describe("props validation", () => {
|
|||||||
test("default values are applied before validating props at update", async () => {
|
test("default values are applied before validating props at update", async () => {
|
||||||
// need to do something about errors catched in render
|
// need to do something about errors catched in render
|
||||||
class SubComp extends Component {
|
class SubComp extends Component {
|
||||||
static props = { p: { type: Number } };
|
static props = { p: { type: Number, optional: true } };
|
||||||
static template = xml`<div><t t-esc="props.p"/></div>`;
|
static template = xml`<div><t t-esc="props.p"/></div>`;
|
||||||
static defaultProps = { p: 4 };
|
static defaultProps = { p: 4 };
|
||||||
}
|
}
|
||||||
@@ -791,9 +791,9 @@ describe("default props", () => {
|
|||||||
expect(fixture.innerHTML).toBe("<div><div>4</div></div>");
|
expect(fixture.innerHTML).toBe("<div><div>4</div></div>");
|
||||||
});
|
});
|
||||||
|
|
||||||
test("can set default required boolean values", async () => {
|
test("can set default boolean values", async () => {
|
||||||
class SubComp extends Component {
|
class SubComp extends Component {
|
||||||
static props = ["p", "q"];
|
static props = ["p?", "q?"];
|
||||||
static defaultProps = { p: true, q: false };
|
static defaultProps = { p: true, q: false };
|
||||||
static template = xml`<span><t t-if="props.p">hey</t><t t-if="!props.q">hey</t></span>`;
|
static template = xml`<span><t t-if="props.p">hey</t><t t-if="!props.q">hey</t></span>`;
|
||||||
}
|
}
|
||||||
@@ -804,4 +804,28 @@ describe("default props", () => {
|
|||||||
await mount(Parent, fixture, { dev: true });
|
await mount(Parent, fixture, { dev: true });
|
||||||
expect(fixture.innerHTML).toBe("<div><span>heyhey</span></div>");
|
expect(fixture.innerHTML).toBe("<div><span>heyhey</span></div>");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("a default prop cannot be defined on a mandatory prop", async () => {
|
||||||
|
class Child extends Component {
|
||||||
|
static props = {
|
||||||
|
mandatory: Number,
|
||||||
|
};
|
||||||
|
static defaultProps = { mandatory: 3 };
|
||||||
|
static template = xml` <div><t t-esc="props.mandatory"/></div>`;
|
||||||
|
}
|
||||||
|
class Parent extends Component {
|
||||||
|
static components = { Child };
|
||||||
|
static template = xml`<Child/>`;
|
||||||
|
}
|
||||||
|
let error: Error;
|
||||||
|
try {
|
||||||
|
await mount(Parent, fixture, { dev: true });
|
||||||
|
} catch (e) {
|
||||||
|
error = e as Error;
|
||||||
|
}
|
||||||
|
expect(error!).toBeDefined();
|
||||||
|
expect(error!.message).toBe(
|
||||||
|
"A default value cannot be defined for a mandatory prop (name: 'mandatory', component: Child)"
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Component, mount, useRef, useState } from "../../src/index";
|
import { Component, mount, onMounted, useRef, useState } from "../../src/index";
|
||||||
import { makeTestFixture, nextTick, snapshotEverything } from "../helpers";
|
import { logStep, makeTestFixture, nextTick, snapshotEverything } from "../helpers";
|
||||||
import { xml } from "../../src/index";
|
import { xml } from "../../src/index";
|
||||||
|
|
||||||
snapshotEverything();
|
snapshotEverything();
|
||||||
@@ -100,4 +100,27 @@ describe("refs", () => {
|
|||||||
expect(console.warn).toBeCalledTimes(1);
|
expect(console.warn).toBeCalledTimes(1);
|
||||||
console.warn = consoleWarn;
|
console.warn = consoleWarn;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("refs and recursive templates", async () => {
|
||||||
|
class Test extends Component {
|
||||||
|
static components = {};
|
||||||
|
static template = xml`
|
||||||
|
<p t-ref="root">
|
||||||
|
<t t-esc="props.tree.value"/>
|
||||||
|
<t t-if="props.tree.child"><Test tree="props.tree.child"/></t>
|
||||||
|
</p>`;
|
||||||
|
root = useRef("root");
|
||||||
|
|
||||||
|
setup() {
|
||||||
|
onMounted(() => logStep(this.root.el!.outerHTML));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Test.components = { Test };
|
||||||
|
|
||||||
|
const tree = { value: "a", child: { value: "b", child: null } };
|
||||||
|
|
||||||
|
await mount(Test, fixture, { props: { tree } });
|
||||||
|
expect(fixture.innerHTML).toBe("<p>a<p>b</p></p>");
|
||||||
|
expect(["<p>b</p>", "<p>a<p>b</p></p>"]).toBeLogged();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -45,7 +45,36 @@ describe("slots", () => {
|
|||||||
expect(fixture.innerHTML).toBe("some text");
|
expect(fixture.innerHTML).toBe("some text");
|
||||||
});
|
});
|
||||||
|
|
||||||
test("simple default slot with params", async () => {
|
test("simple slot with slot scope", async () => {
|
||||||
|
let child: any;
|
||||||
|
class Child extends Component {
|
||||||
|
static template = xml`<span><t t-slot="slotName" bool="state.bool"/></span>`;
|
||||||
|
state = useState({ bool: true });
|
||||||
|
setup() {
|
||||||
|
child = this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Parent extends Component {
|
||||||
|
static template = xml`
|
||||||
|
<Child>
|
||||||
|
<t t-set-slot="slotName" t-slot-scope="slotScope">
|
||||||
|
<t t-if="slotScope.bool">some text</t>
|
||||||
|
<t t-else="slotScope.bool">other text</t>
|
||||||
|
</t>
|
||||||
|
</Child>`;
|
||||||
|
static components = { Child };
|
||||||
|
}
|
||||||
|
|
||||||
|
await mount(Parent, fixture);
|
||||||
|
expect(fixture.innerHTML).toBe("<span>some text</span>");
|
||||||
|
|
||||||
|
child.state.bool = false;
|
||||||
|
await nextTick();
|
||||||
|
expect(fixture.innerHTML).toBe("<span>other text</span>");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("default slot with slot scope: shorthand syntax", async () => {
|
||||||
let child: any;
|
let child: any;
|
||||||
class Child extends Component {
|
class Child extends Component {
|
||||||
static template = xml`<span><t t-slot="default" bool="state.bool"/></span>`;
|
static template = xml`<span><t t-slot="default" bool="state.bool"/></span>`;
|
||||||
@@ -57,11 +86,9 @@ describe("slots", () => {
|
|||||||
|
|
||||||
class Parent extends Component {
|
class Parent extends Component {
|
||||||
static template = xml`
|
static template = xml`
|
||||||
<Child>
|
<Child t-slot-scope="slotScope">
|
||||||
<t t-set-slot="default" t-slot-scope="slotScope">
|
<t t-if="slotScope.bool">some text</t>
|
||||||
<t t-if="slotScope.bool">some text</t>
|
<t t-else="slotScope.bool">other text</t>
|
||||||
<t t-else="slotScope.bool">other text</t>
|
|
||||||
</t>
|
|
||||||
</Child>`;
|
</Child>`;
|
||||||
static components = { Child };
|
static components = { Child };
|
||||||
}
|
}
|
||||||
@@ -99,6 +126,41 @@ describe("slots", () => {
|
|||||||
expect(mockConsoleWarn).toBeCalledTimes(1);
|
expect(mockConsoleWarn).toBeCalledTimes(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("simple default slot with params and bound function", async () => {
|
||||||
|
class Child extends Component {
|
||||||
|
static template = xml`<t t-slot="default" fn.bind="getValue"/>`;
|
||||||
|
state = useState({ value: 123 });
|
||||||
|
getValue() {
|
||||||
|
return this.state.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Parent extends Component {
|
||||||
|
static template = xml`
|
||||||
|
<Child t-slot-scope="slotScope"><t t-esc="slotScope.fn()"/></Child>`;
|
||||||
|
static components = { Child };
|
||||||
|
}
|
||||||
|
|
||||||
|
await mount(Parent, fixture);
|
||||||
|
expect(fixture.innerHTML).toBe("123");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("default slot with params with - in it", async () => {
|
||||||
|
class Child extends Component {
|
||||||
|
static template = xml`<t t-slot="default" some-value="state.value"/>`;
|
||||||
|
state = useState({ value: 123 });
|
||||||
|
}
|
||||||
|
|
||||||
|
class Parent extends Component {
|
||||||
|
static template = xml`
|
||||||
|
<Child t-slot-scope="slotScope"><t t-esc="slotScope['some-value']"/></Child>`;
|
||||||
|
static components = { Child };
|
||||||
|
}
|
||||||
|
|
||||||
|
await mount(Parent, fixture);
|
||||||
|
expect(fixture.innerHTML).toBe("123");
|
||||||
|
});
|
||||||
|
|
||||||
test("fun: two calls to the same slot", async () => {
|
test("fun: two calls to the same slot", async () => {
|
||||||
class Child extends Component {
|
class Child extends Component {
|
||||||
static template = xml`<t t-slot="default"/><t t-slot="default"/>`;
|
static template = xml`<t t-slot="default"/><t t-slot="default"/>`;
|
||||||
@@ -215,6 +277,30 @@ describe("slots", () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("can define and call slots with bound params", async () => {
|
||||||
|
class Child extends Component {
|
||||||
|
static template = xml`
|
||||||
|
<t t-slot="abc"/>
|
||||||
|
<t t-esc="props.slots['abc'].getValue()"/>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
class Parent extends Component {
|
||||||
|
static components = { Child };
|
||||||
|
static template = xml`
|
||||||
|
<Child>
|
||||||
|
<t t-set-slot="abc" getValue.bind="getValue">abc</t>
|
||||||
|
</Child>`;
|
||||||
|
state = useState({ value: 444 });
|
||||||
|
getValue() {
|
||||||
|
return this.state.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await mount(Parent, fixture);
|
||||||
|
|
||||||
|
expect(fixture.innerHTML).toBe("abc444");
|
||||||
|
});
|
||||||
|
|
||||||
test("no named slot content => just no children", async () => {
|
test("no named slot content => just no children", async () => {
|
||||||
class Dialog extends Component {
|
class Dialog extends Component {
|
||||||
static template = xml`<span><t t-slot="header"/></span>`;
|
static template = xml`<span><t t-slot="header"/></span>`;
|
||||||
|
|||||||
@@ -30,6 +30,19 @@ describe("t-model directive", () => {
|
|||||||
expect(fixture.innerHTML).toBe("<div><input><span>test</span></div>");
|
expect(fixture.innerHTML).toBe("<div><input><span>test</span></div>");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("t-model on an input with an undefined value", async () => {
|
||||||
|
class SomeComponent extends Component {
|
||||||
|
static template = xml`<input t-model="state.text"/>`;
|
||||||
|
state = useState({ text: undefined });
|
||||||
|
}
|
||||||
|
await mount(SomeComponent, fixture);
|
||||||
|
|
||||||
|
expect(fixture.innerHTML).toBe("<input>");
|
||||||
|
|
||||||
|
const input = fixture.querySelector("input")!;
|
||||||
|
expect(input.value).toBe("");
|
||||||
|
});
|
||||||
|
|
||||||
test("basic use, on an input with bracket expression", async () => {
|
test("basic use, on an input with bracket expression", async () => {
|
||||||
class SomeComponent extends Component {
|
class SomeComponent extends Component {
|
||||||
static template = xml`
|
static template = xml`
|
||||||
@@ -246,6 +259,34 @@ describe("t-model directive", () => {
|
|||||||
expect(fixture.innerHTML).toBe("<div><input><span>test</span></div>");
|
expect(fixture.innerHTML).toBe("<div><input><span>test</span></div>");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("with expression having a changing key", async () => {
|
||||||
|
class SomeComponent extends Component {
|
||||||
|
static template = xml`
|
||||||
|
<div>
|
||||||
|
<input t-model="state.something[text.key]"/>
|
||||||
|
<span><t t-esc="state.something[text.key]"/></span>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
state: { something: { [key: string]: string } } = useState({ something: {} });
|
||||||
|
text = useState({ key: "foo" });
|
||||||
|
}
|
||||||
|
const comp = await mount(SomeComponent, fixture);
|
||||||
|
|
||||||
|
expect(fixture.innerHTML).toBe("<div><input><span></span></div>");
|
||||||
|
|
||||||
|
let input = fixture.querySelector("input")!;
|
||||||
|
await editInput(input, "footest");
|
||||||
|
expect(comp.state.something[comp.text.key]).toBe("footest");
|
||||||
|
expect(fixture.innerHTML).toBe("<div><input><span>footest</span></div>");
|
||||||
|
|
||||||
|
comp.text.key = "bar";
|
||||||
|
await nextTick();
|
||||||
|
input = fixture.querySelector("input")!;
|
||||||
|
await editInput(input, "test bar");
|
||||||
|
expect(comp.state.something[comp.text.key]).toBe("test bar");
|
||||||
|
expect(fixture.innerHTML).toBe("<div><input><span>test bar</span></div>");
|
||||||
|
});
|
||||||
|
|
||||||
test(".lazy modifier", async () => {
|
test(".lazy modifier", async () => {
|
||||||
class SomeComponent extends Component {
|
class SomeComponent extends Component {
|
||||||
static template = xml`
|
static template = xml`
|
||||||
@@ -360,6 +401,26 @@ describe("t-model directive", () => {
|
|||||||
expect(comp.state).toEqual(["zuko", "uncle iroh"]);
|
expect(comp.state).toEqual(["zuko", "uncle iroh"]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("in a t-foreach, part 3", async () => {
|
||||||
|
class SomeComponent extends Component {
|
||||||
|
static template = xml`
|
||||||
|
<div>
|
||||||
|
<t t-foreach="names" t-as="name" t-key="name_index">
|
||||||
|
<input t-model="state.values[name]"/>
|
||||||
|
</t>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
names = ["Crusher", "Data", "Riker", "Worf"];
|
||||||
|
state = useState({ values: {} });
|
||||||
|
}
|
||||||
|
const comp = await mount(SomeComponent, fixture);
|
||||||
|
expect(comp.state).toEqual({ values: {} });
|
||||||
|
|
||||||
|
const input = fixture.querySelectorAll("input")[1]!;
|
||||||
|
await editInput(input, "Commander");
|
||||||
|
expect(comp.state).toEqual({ values: { Data: "Commander" } });
|
||||||
|
});
|
||||||
|
|
||||||
test("two inputs in a div alternating with a t-if", async () => {
|
test("two inputs in a div alternating with a t-if", async () => {
|
||||||
class SomeComponent extends Component {
|
class SomeComponent extends Component {
|
||||||
static template = xml`
|
static template = xml`
|
||||||
@@ -452,4 +513,116 @@ describe("t-model directive", () => {
|
|||||||
expect(comp.state.choice).toBe("Three");
|
expect(comp.state.choice).toBe("Three");
|
||||||
expect(comp.state.lastClicked).toBe("Three");
|
expect(comp.state.lastClicked).toBe("Three");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("t-model on select with static options", async () => {
|
||||||
|
class Test extends Component {
|
||||||
|
static template = xml`
|
||||||
|
<div>
|
||||||
|
<select t-model="state.model">
|
||||||
|
<option value="a" t-esc="'a'"/>
|
||||||
|
<option value="b" t-esc="'b'"/>
|
||||||
|
<option value="c" t-esc="'c'"/>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
state: any;
|
||||||
|
options: any;
|
||||||
|
setup() {
|
||||||
|
this.state = useState({ model: "b" });
|
||||||
|
this.options = ["a", "b", "c"];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await mount(Test, fixture);
|
||||||
|
expect(fixture.querySelector("select")!.value).toEqual("b");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("t-model with dynamic values on select options", async () => {
|
||||||
|
class Test extends Component {
|
||||||
|
static template = xml`
|
||||||
|
<div>
|
||||||
|
<select t-model="state.model">
|
||||||
|
<option t-att-value="options[0]" t-esc="options[0]"/>
|
||||||
|
<option t-att-value="options[1]" t-esc="options[1]"/>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
state: any;
|
||||||
|
options: any;
|
||||||
|
setup() {
|
||||||
|
this.state = useState({ model: "b" });
|
||||||
|
this.options = ["a", "b"];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await mount(Test, fixture);
|
||||||
|
expect(fixture.querySelector("select")!.value).toEqual("b");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("t-model with dynamic values on select options -- 2", async () => {
|
||||||
|
class Test extends Component {
|
||||||
|
static template = xml`
|
||||||
|
<div>
|
||||||
|
<select t-model="state.model">
|
||||||
|
<option t-att-value="options[0]" t-esc="options[0]"/>
|
||||||
|
<option t-attf-value="{{ options[1] }}" t-esc="options[1]"/>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
state: any;
|
||||||
|
options: any;
|
||||||
|
setup() {
|
||||||
|
this.state = useState({ model: "b" });
|
||||||
|
this.options = ["a", "b"];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await mount(Test, fixture);
|
||||||
|
expect(fixture.querySelector("select")!.value).toEqual("b");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("t-model with dynamic values on select options -- 3", async () => {
|
||||||
|
class Test extends Component {
|
||||||
|
static template = xml`
|
||||||
|
<div>
|
||||||
|
<select t-model="state.model">
|
||||||
|
<option t-att-value="options[0]" t-esc="options[0]"/>
|
||||||
|
<option value="b" t-esc="options[1]"/>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
state: any;
|
||||||
|
options: any;
|
||||||
|
setup() {
|
||||||
|
this.state = useState({ model: "b" });
|
||||||
|
this.options = ["a", "b"];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await mount(Test, fixture);
|
||||||
|
expect(fixture.querySelector("select")!.value).toEqual("b");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("t-model with dynamic values on select options in foreach", async () => {
|
||||||
|
class Test extends Component {
|
||||||
|
static template = xml`
|
||||||
|
<div>
|
||||||
|
<select t-model="state.model">
|
||||||
|
<t t-foreach="options" t-as="v" t-key="v">
|
||||||
|
<option t-att-value="v" t-esc="v"/>
|
||||||
|
</t>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
state: any;
|
||||||
|
options: any;
|
||||||
|
setup() {
|
||||||
|
this.state = useState({ model: "b" });
|
||||||
|
this.options = ["a", "b", "c"];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await mount(Test, fixture);
|
||||||
|
expect(fixture.querySelector("select")!.value).toEqual("b");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ describe("t-props", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test("basic use", async () => {
|
test("basic use", async () => {
|
||||||
expect.assertions(5);
|
expect.assertions(4);
|
||||||
|
|
||||||
let props = { a: 1, b: 2 };
|
let props = { a: 1, b: 2 };
|
||||||
|
|
||||||
@@ -65,7 +65,6 @@ describe("t-props", () => {
|
|||||||
`;
|
`;
|
||||||
setup() {
|
setup() {
|
||||||
expect(this.props).toEqual({ a: 1, b: 2 });
|
expect(this.props).toEqual({ a: 1, b: 2 });
|
||||||
expect(this.props).toBe(props);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
class Parent extends Component {
|
class Parent extends Component {
|
||||||
@@ -105,4 +104,23 @@ describe("t-props", () => {
|
|||||||
|
|
||||||
await mount(Parent, fixture);
|
await mount(Parent, fixture);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("child receives a copy of the t-props object, not the original", async () => {
|
||||||
|
class Child extends Component {
|
||||||
|
static template = xml`<div/>`;
|
||||||
|
setup() {
|
||||||
|
expect(this.props).toEqual({ a: 1, b: 2 });
|
||||||
|
this.props.d = 5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
class Parent extends Component {
|
||||||
|
static template = xml`<Child t-props="childProps"/>`;
|
||||||
|
static components = { Child };
|
||||||
|
|
||||||
|
childProps = { a: 1, b: 2 };
|
||||||
|
}
|
||||||
|
|
||||||
|
const parent = await mount(Parent, fixture);
|
||||||
|
expect(parent.childProps).not.toHaveProperty("d");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
+1
-1
@@ -123,7 +123,7 @@ export function snapshotEverything() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const originalCompileTemplate = TemplateSet.prototype._compileTemplate;
|
const originalCompileTemplate = TemplateSet.prototype._compileTemplate;
|
||||||
TemplateSet.prototype._compileTemplate = function (name: string, template: string | Node) {
|
TemplateSet.prototype._compileTemplate = function (name: string, template: string | Element) {
|
||||||
const fn = originalCompileTemplate.call(this, "", template);
|
const fn = originalCompileTemplate.call(this, "", template);
|
||||||
if (!globalTemplateNames.has(name)) {
|
if (!globalTemplateNames.has(name)) {
|
||||||
expect(fn.toString()).toMatchSnapshot();
|
expect(fn.toString()).toMatchSnapshot();
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ exports[`Memo if no prop change, prevent renderings from above 1`] = `
|
|||||||
let b2 = text(ctx['state'].a);
|
let b2 = text(ctx['state'].a);
|
||||||
let b3 = text(ctx['state'].b);
|
let b3 = text(ctx['state'].b);
|
||||||
let b4 = text(ctx['state'].c);
|
let b4 = text(ctx['state'].c);
|
||||||
let b9 = component(\`Memo\`, {a: ctx['state'].a,b: ctx['state'].b,slots: {'default': {__render: slot1, __ctx: ctx}}}, key + \`__1\`, node, ctx);
|
let b9 = component(\`Memo\`, {a: ctx['state'].a, b: ctx['state'].b,slots: {'default': {__render: slot1, __ctx: ctx}}}, key + \`__1\`, node, ctx);
|
||||||
return multi([b2, b3, b4, b9]);
|
return multi([b2, b3, b4, b9]);
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ snapshotEverything();
|
|||||||
|
|
||||||
beforeAll(() => {
|
beforeAll(() => {
|
||||||
console.info = (message: any) => {
|
console.info = (message: any) => {
|
||||||
if (message === DEV_MSG) {
|
if (message === DEV_MSG()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
info(message);
|
info(message);
|
||||||
|
|||||||
@@ -6,8 +6,10 @@ import {
|
|||||||
onWillUpdateProps,
|
onWillUpdateProps,
|
||||||
useState,
|
useState,
|
||||||
xml,
|
xml,
|
||||||
|
markRaw,
|
||||||
|
toRaw,
|
||||||
} from "../src";
|
} from "../src";
|
||||||
import { reactive } from "../src/reactivity";
|
import { reactive, Reactive } from "../src/reactivity";
|
||||||
import { batched } from "../src/utils";
|
import { batched } from "../src/utils";
|
||||||
import {
|
import {
|
||||||
makeDeferred,
|
makeDeferred,
|
||||||
@@ -1091,6 +1093,49 @@ describe("Reactivity", () => {
|
|||||||
expect(n).toBe(1);
|
expect(n).toBe(1);
|
||||||
expect(state.k).toEqual({ n: 2 });
|
expect(state.k).toEqual({ n: 2 });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("can add collections set/weakset/map/weakmap in a reactive object", () => {
|
||||||
|
const rawSet = new Set();
|
||||||
|
const rawWeakSet = new WeakSet();
|
||||||
|
const rawMap = new Map();
|
||||||
|
const rawWeakMap = new WeakMap();
|
||||||
|
|
||||||
|
const obj = reactive({ rawSet, rawWeakSet, rawMap, rawWeakMap });
|
||||||
|
expect(obj.rawSet).toBe(rawSet);
|
||||||
|
expect(obj.rawWeakSet).toBe(rawWeakSet);
|
||||||
|
expect(obj.rawMap).toBe(rawMap);
|
||||||
|
expect(obj.rawWeakMap).toBe(rawWeakMap);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("markRaw", () => {
|
||||||
|
test("markRaw works as expected: value is not observed", () => {
|
||||||
|
const obj1: any = markRaw({ value: 1 });
|
||||||
|
const obj2 = { value: 1 };
|
||||||
|
let n = 0;
|
||||||
|
const r = reactive({ obj1, obj2 }, () => n++);
|
||||||
|
expect(n).toBe(0);
|
||||||
|
r.obj1.value = r.obj1.value + 1;
|
||||||
|
expect(n).toBe(0);
|
||||||
|
r.obj2.value = r.obj2.value + 1;
|
||||||
|
expect(n).toBe(1);
|
||||||
|
expect(r.obj1).toBe(obj1);
|
||||||
|
expect(r.obj2).not.toBe(obj2);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("toRaw", () => {
|
||||||
|
test("toRaw works as expected", () => {
|
||||||
|
const obj = { value: 1 };
|
||||||
|
const reactiveObj = reactive(obj);
|
||||||
|
expect(reactiveObj).not.toBe(obj);
|
||||||
|
expect(toRaw(reactiveObj as Reactive<typeof obj>)).toBe(obj);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("giving a non reactive to toRaw return the object itself", () => {
|
||||||
|
const obj = { value: 1 };
|
||||||
|
expect(toRaw(obj as Reactive<typeof obj>)).toBe(obj);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("Reactivity: useState", () => {
|
describe("Reactivity: useState", () => {
|
||||||
|
|||||||
+192
-2
@@ -24,8 +24,8 @@ class Root extends Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Application setup
|
// dev=false for benchmarking. we don't want to benchmark dev code!
|
||||||
mount(Root, document.body, { templates: TEMPLATES, dev: true });`;
|
mount(Root, document.body, { templates: TEMPLATES, dev: false });`;
|
||||||
|
|
||||||
const COMPONENTS_XML = /*xml*/`
|
const COMPONENTS_XML = /*xml*/`
|
||||||
<templates>
|
<templates>
|
||||||
@@ -1573,6 +1573,191 @@ class Root extends Component {
|
|||||||
mount(Root, document.body, { templates: TEMPLATES, dev: true});
|
mount(Root, document.body, { templates: TEMPLATES, dev: true});
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
const BENCHMARK_JS = `
|
||||||
|
const { Component, mount, xml, useState, onWillRender, onPatched} = owl;
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// Data generation
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
let idCounter = 1;
|
||||||
|
const adjectives = [
|
||||||
|
"pretty", "large", "big", "small", "tall", "short", "long", "handsome", "plain",
|
||||||
|
"quaint", "clean", "elegant", "easy", "angry", "crazy", "helpful", "mushy", "odd",
|
||||||
|
"unsightly", "adorable", "important", "inexpensive", "cheap", "expensive", "fancy"];
|
||||||
|
const colours = ["red", "yellow", "blue", "green", "pink", "brown", "purple", "brown", "white", "black", "orange"];
|
||||||
|
const nouns = ["table", "chair", "house", "bbq", "desk", "car", "pony", "cookie", "sandwich", "burger", "pizza", "mouse", "keyboard"];
|
||||||
|
|
||||||
|
function _random (max) { return Math.round(Math.random() * 1000) % max; };
|
||||||
|
|
||||||
|
function buildData(count) {
|
||||||
|
const data = new Array(count);
|
||||||
|
for (let i = 0; i < count; i++) {
|
||||||
|
const label = \`\${adjectives[_random(adjectives.length)]} \${colours[_random(colours.length)]} \${nouns[_random(nouns.length)]}\`;
|
||||||
|
data[i] = {
|
||||||
|
id: idCounter++,
|
||||||
|
label,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// Components
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
class Button extends Component {
|
||||||
|
static template = xml\`
|
||||||
|
<div class='col-sm-6 smallpad'>
|
||||||
|
<button t-att-id="props.id" class='btn btn-primary btn-block' type='button' t-on-click="props.onClick">
|
||||||
|
<t t-esc="props.text"/>
|
||||||
|
</button>
|
||||||
|
</div>\`;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class Row extends Component {
|
||||||
|
static template = xml\`
|
||||||
|
<tr t-att-class="props.isSelected ? 'danger' : ''">
|
||||||
|
<td class="col-md-1" t-esc="props.row.id" />
|
||||||
|
<td class="col-md-4">
|
||||||
|
<a t-on-click="() => props.onSelect(props.row.id)" t-esc="props.row.label" />
|
||||||
|
</td>
|
||||||
|
<td class="col-md-1">
|
||||||
|
<a t-on-click="() => props.onRemove(props.row.id)" class="remove">[x]
|
||||||
|
<span class='glyphicon glyphicon-remove' aria-hidden="true" />
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td class='col-md-6'/>
|
||||||
|
</tr>\`
|
||||||
|
}
|
||||||
|
|
||||||
|
class Root extends Component {
|
||||||
|
static template = xml\`
|
||||||
|
<div class='container'>
|
||||||
|
<div class='jumbotron'>
|
||||||
|
<div class='row'>
|
||||||
|
<div class='col-md-6'>
|
||||||
|
<h1>Owl Keyed</h1>
|
||||||
|
</div>
|
||||||
|
<div class='col-md-6'>
|
||||||
|
<div class='row'>
|
||||||
|
<Button id="'run'" onClick.bind="run" text="'Create 1,000 rows'" />
|
||||||
|
<Button id="'runlots'" onClick.bind="runLots" text="'Create 10,000 rows'" />
|
||||||
|
<Button id="'add'" onClick.bind="add" text="'Append 1,000 rows'" />
|
||||||
|
<Button id="'update'" onClick.bind="update" text="'Update every 10th row'" />
|
||||||
|
<Button id="'clear'" onClick.bind="clear" text="'Clear'" />
|
||||||
|
<Button id="'swaprows'" onClick.bind="swapRows" text="'Swap Rows'" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<table class='table table-hover table-striped test-data'>
|
||||||
|
<tbody>
|
||||||
|
<t t-foreach="state.rows" t-as="row" t-key="row.id">
|
||||||
|
<Row row="row" isSelected="row.id === state.selectedRowId" onSelect.bind="selectRow" onRemove.bind="removeRow"/>
|
||||||
|
</t>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<span class='preloadicon glyphicon glyphicon-remove' aria-hidden="true" />
|
||||||
|
</div>\`;
|
||||||
|
|
||||||
|
static components = { Button, Row };
|
||||||
|
|
||||||
|
setup() {
|
||||||
|
this.state = useState({
|
||||||
|
rows: [],
|
||||||
|
selectedRowId: null
|
||||||
|
});
|
||||||
|
this.benchmarking = false;
|
||||||
|
onPatched(() => {
|
||||||
|
if (this.benchmarking) {
|
||||||
|
this.stop();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
start(descr) {
|
||||||
|
this.benchmarking = \`[\${descr}]\`;
|
||||||
|
console.time(this.benchmarking);
|
||||||
|
}
|
||||||
|
stop() {
|
||||||
|
console.timeEnd(this.benchmarking);
|
||||||
|
this.benchmarking = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
run() {
|
||||||
|
this.start('add1000');
|
||||||
|
this.state.rows = buildData(1000);
|
||||||
|
this.state.selectedRowId = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
runLots() {
|
||||||
|
this.start('add10_000');
|
||||||
|
this.state.rows = buildData(10_000);
|
||||||
|
this.state.selectedRowId = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
add() {
|
||||||
|
this.start('append1000');
|
||||||
|
this.state.rows = this.state.rows.concat(buildData(1000));
|
||||||
|
}
|
||||||
|
|
||||||
|
update() {
|
||||||
|
this.start('update1/10th');
|
||||||
|
let index = 0;
|
||||||
|
const rows = this.state.rows;
|
||||||
|
while (index < rows.length) {
|
||||||
|
rows[index].label = rows[index].label + " !!!";
|
||||||
|
index += 10;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
clear() {
|
||||||
|
this.start('clear');
|
||||||
|
this.state.rows = [];
|
||||||
|
this.state.selectedRowId = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
swapRows() {
|
||||||
|
this.start('swap');
|
||||||
|
const rows = this.state.rows;
|
||||||
|
if (rows.length > 998) {
|
||||||
|
let tmp = rows[1];
|
||||||
|
rows[1] = rows[998];
|
||||||
|
rows[998] = tmp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
selectRow(id) {
|
||||||
|
this.start('select');
|
||||||
|
this.state.selectedRowId = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
removeRow(id) {
|
||||||
|
this.start('remove1');
|
||||||
|
const rows = this.state.rows;
|
||||||
|
rows.splice(rows.findIndex(row => row.id === id), 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
mount(Root, document.body, { templates: TEMPLATES, dev: true });
|
||||||
|
|
||||||
|
`;
|
||||||
|
|
||||||
|
const BENCHMARK_CSS = `
|
||||||
|
tr.danger {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.remove:hover {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
.remove {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
export const SAMPLES = [
|
export const SAMPLES = [
|
||||||
{
|
{
|
||||||
description: "Components",
|
description: "Components",
|
||||||
@@ -1625,4 +1810,9 @@ export const SAMPLES = [
|
|||||||
xml: WMS_XML,
|
xml: WMS_XML,
|
||||||
css: WMS_CSS,
|
css: WMS_CSS,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
description: "Benchmark example",
|
||||||
|
code: BENCHMARK_JS,
|
||||||
|
css: BENCHMARK_CSS,
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
Reference in New Issue
Block a user