mirror of
https://github.com/odoo/owl.git
synced 2025-10-06 19:59:41 +07:00
Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| fb7d25ba0e | |||
| 5187f01c44 | |||
| 521111644c | |||
| c2728c9daf |
@@ -320,6 +320,28 @@ class ComponentB extends owl.Component {
|
|||||||
|
|
||||||
Note: the props validation code is done by using the [validate utility function](utils.md#validate).
|
Note: the props validation code is done by using the [validate utility function](utils.md#validate).
|
||||||
|
|
||||||
|
### `slots` prop
|
||||||
|
|
||||||
|
If a component that uses [slots](slots.md) also lists or validates its props, then
|
||||||
|
you will have to explicitely allow the `slots` prop (with an `Object` type), or
|
||||||
|
allow extra props using the `*` notation mentioned above. This is because slots
|
||||||
|
are provided to a component [as props](slots.md#slots-and-props).
|
||||||
|
|
||||||
|
For example:
|
||||||
|
|
||||||
|
```js
|
||||||
|
class MyComponent extends Component {
|
||||||
|
static props = [someProp, slots?];
|
||||||
|
}
|
||||||
|
|
||||||
|
class MyComponentWithValidation extends Component {
|
||||||
|
static props = {
|
||||||
|
someProp: {type: Number, optional: true},
|
||||||
|
slots : {type: Object, optional: true},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
## Good Practices
|
## Good Practices
|
||||||
|
|
||||||
A `props` object is a collection of values that come from the parent. As such,
|
A `props` object is a collection of values that come from the parent. As such,
|
||||||
|
|||||||
+72
-26
@@ -4539,7 +4539,7 @@ class CodeGenerator {
|
|||||||
const isNewBlock = !block || forceNewBlock;
|
const isNewBlock = !block || forceNewBlock;
|
||||||
let codeIdx = this.target.code.length;
|
let codeIdx = this.target.code.length;
|
||||||
if (isNewBlock) {
|
if (isNewBlock) {
|
||||||
const n = ast.content.filter((c) => c.type !== 6 /* TSet */).length;
|
const n = ast.content.filter((c) => !c.hasNoRepresentation).length;
|
||||||
let result = null;
|
let result = null;
|
||||||
if (n <= 1) {
|
if (n <= 1) {
|
||||||
for (let child of ast.content) {
|
for (let child of ast.content) {
|
||||||
@@ -4553,15 +4553,15 @@ class CodeGenerator {
|
|||||||
let index = 0;
|
let index = 0;
|
||||||
for (let i = 0, l = ast.content.length; i < l; i++) {
|
for (let i = 0, l = ast.content.length; i < l; i++) {
|
||||||
const child = ast.content[i];
|
const child = ast.content[i];
|
||||||
const isTSet = child.type === 6 /* TSet */;
|
const forceNewBlock = !child.hasNoRepresentation;
|
||||||
const subCtx = createContext(ctx, {
|
const subCtx = createContext(ctx, {
|
||||||
block,
|
block,
|
||||||
index,
|
index,
|
||||||
forceNewBlock: !isTSet,
|
forceNewBlock,
|
||||||
isLast: ctx.isLast && i === l - 1,
|
isLast: ctx.isLast && i === l - 1,
|
||||||
});
|
});
|
||||||
this.compileAST(child, subCtx);
|
this.compileAST(child, subCtx);
|
||||||
if (!isTSet) {
|
if (forceNewBlock) {
|
||||||
index++;
|
index++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4979,9 +4979,9 @@ function parseNode(node, ctx) {
|
|||||||
parseTCallBlock(node) ||
|
parseTCallBlock(node) ||
|
||||||
parseTTranslation(node, ctx) ||
|
parseTTranslation(node, ctx) ||
|
||||||
parseTTranslationContext(node, ctx) ||
|
parseTTranslationContext(node, ctx) ||
|
||||||
|
parseTKey(node, ctx) ||
|
||||||
parseTEscNode(node, ctx) ||
|
parseTEscNode(node, ctx) ||
|
||||||
parseTOutNode(node, ctx) ||
|
parseTOutNode(node, ctx) ||
|
||||||
parseTKey(node, ctx) ||
|
|
||||||
parseTSlot(node, ctx) ||
|
parseTSlot(node, ctx) ||
|
||||||
parseComponent(node, ctx) ||
|
parseComponent(node, ctx) ||
|
||||||
parseDOMNode(node, ctx) ||
|
parseDOMNode(node, ctx) ||
|
||||||
@@ -5049,19 +5049,29 @@ function parseTCustom(node, ctx) {
|
|||||||
function parseTDebugLog(node, ctx) {
|
function parseTDebugLog(node, ctx) {
|
||||||
if (node.hasAttribute("t-debug")) {
|
if (node.hasAttribute("t-debug")) {
|
||||||
node.removeAttribute("t-debug");
|
node.removeAttribute("t-debug");
|
||||||
return {
|
const content = parseNode(node, ctx);
|
||||||
|
const ast = {
|
||||||
type: 12 /* TDebug */,
|
type: 12 /* TDebug */,
|
||||||
content: parseNode(node, ctx),
|
content,
|
||||||
};
|
};
|
||||||
|
if (content === null || content === void 0 ? void 0 : content.hasNoRepresentation) {
|
||||||
|
ast.hasNoRepresentation = true;
|
||||||
|
}
|
||||||
|
return ast;
|
||||||
}
|
}
|
||||||
if (node.hasAttribute("t-log")) {
|
if (node.hasAttribute("t-log")) {
|
||||||
const expr = node.getAttribute("t-log");
|
const expr = node.getAttribute("t-log");
|
||||||
node.removeAttribute("t-log");
|
node.removeAttribute("t-log");
|
||||||
return {
|
const content = parseNode(node, ctx);
|
||||||
|
const ast = {
|
||||||
type: 13 /* TLog */,
|
type: 13 /* TLog */,
|
||||||
expr,
|
expr,
|
||||||
content: parseNode(node, ctx),
|
content,
|
||||||
};
|
};
|
||||||
|
if (content === null || content === void 0 ? void 0 : content.hasNoRepresentation) {
|
||||||
|
ast.hasNoRepresentation = true;
|
||||||
|
}
|
||||||
|
return ast;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -5291,11 +5301,19 @@ function parseTKey(node, ctx) {
|
|||||||
}
|
}
|
||||||
const key = node.getAttribute("t-key");
|
const key = node.getAttribute("t-key");
|
||||||
node.removeAttribute("t-key");
|
node.removeAttribute("t-key");
|
||||||
const body = parseNode(node, ctx);
|
const content = parseNode(node, ctx);
|
||||||
if (!body) {
|
if (!content) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return { type: 10 /* TKey */, expr: key, content: body };
|
const ast = {
|
||||||
|
type: 10 /* TKey */,
|
||||||
|
expr: key,
|
||||||
|
content,
|
||||||
|
};
|
||||||
|
if (content.hasNoRepresentation) {
|
||||||
|
ast.hasNoRepresentation = true;
|
||||||
|
}
|
||||||
|
return ast;
|
||||||
}
|
}
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// t-call
|
// t-call
|
||||||
@@ -5404,7 +5422,7 @@ function parseTSetNode(node, ctx) {
|
|||||||
if (node.textContent !== node.innerHTML) {
|
if (node.textContent !== node.innerHTML) {
|
||||||
body = parseChildren(node, ctx);
|
body = parseChildren(node, ctx);
|
||||||
}
|
}
|
||||||
return { type: 6 /* TSet */, name, value, defaultValue, body };
|
return { type: 6 /* TSet */, name, value, defaultValue, body, hasNoRepresentation: true };
|
||||||
}
|
}
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// Components
|
// Components
|
||||||
@@ -5583,30 +5601,51 @@ function parseTSlot(node, ctx) {
|
|||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// Translation
|
// Translation
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
function wrapInTTranslationAST(r) {
|
||||||
|
const ast = { type: 16 /* TTranslation */, content: r };
|
||||||
|
if (r === null || r === void 0 ? void 0 : r.hasNoRepresentation) {
|
||||||
|
ast.hasNoRepresentation = true;
|
||||||
|
}
|
||||||
|
return ast;
|
||||||
|
}
|
||||||
function parseTTranslation(node, ctx) {
|
function parseTTranslation(node, ctx) {
|
||||||
if (node.getAttribute("t-translation") !== "off") {
|
if (node.getAttribute("t-translation") !== "off") {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
node.removeAttribute("t-translation");
|
node.removeAttribute("t-translation");
|
||||||
return {
|
const result = parseNode(node, ctx);
|
||||||
type: 16 /* TTranslation */,
|
if ((result === null || result === void 0 ? void 0 : result.type) === 3 /* Multi */) {
|
||||||
content: parseNode(node, ctx),
|
const children = result.content.map(wrapInTTranslationAST);
|
||||||
};
|
return makeASTMulti(children);
|
||||||
|
}
|
||||||
|
return wrapInTTranslationAST(result);
|
||||||
}
|
}
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// Translation Context
|
// Translation Context
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
function wrapInTTranslationContextAST(r, translationCtx) {
|
||||||
|
const ast = {
|
||||||
|
type: 17 /* TTranslationContext */,
|
||||||
|
content: r,
|
||||||
|
translationCtx,
|
||||||
|
};
|
||||||
|
if (r === null || r === void 0 ? void 0 : r.hasNoRepresentation) {
|
||||||
|
ast.hasNoRepresentation = true;
|
||||||
|
}
|
||||||
|
return ast;
|
||||||
|
}
|
||||||
function parseTTranslationContext(node, ctx) {
|
function parseTTranslationContext(node, ctx) {
|
||||||
const translationCtx = node.getAttribute("t-translation-context");
|
const translationCtx = node.getAttribute("t-translation-context");
|
||||||
if (!translationCtx) {
|
if (!translationCtx) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
node.removeAttribute("t-translation-context");
|
node.removeAttribute("t-translation-context");
|
||||||
return {
|
const result = parseNode(node, ctx);
|
||||||
type: 17 /* TTranslationContext */,
|
if ((result === null || result === void 0 ? void 0 : result.type) === 3 /* Multi */) {
|
||||||
content: parseNode(node, ctx),
|
const children = result.content.map((c) => wrapInTTranslationContextAST(c, translationCtx));
|
||||||
translationCtx,
|
return makeASTMulti(children);
|
||||||
};
|
}
|
||||||
|
return wrapInTTranslationContextAST(result, translationCtx);
|
||||||
}
|
}
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// Portal
|
// Portal
|
||||||
@@ -5651,6 +5690,13 @@ function parseChildren(node, ctx) {
|
|||||||
}
|
}
|
||||||
return children;
|
return children;
|
||||||
}
|
}
|
||||||
|
function makeASTMulti(children) {
|
||||||
|
const ast = { type: 3 /* Multi */, content: children };
|
||||||
|
if (children.every((c) => c.hasNoRepresentation)) {
|
||||||
|
ast.hasNoRepresentation = true;
|
||||||
|
}
|
||||||
|
return 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.
|
||||||
@@ -5663,7 +5709,7 @@ function parseChildNodes(node, ctx) {
|
|||||||
case 1:
|
case 1:
|
||||||
return children[0];
|
return children[0];
|
||||||
default:
|
default:
|
||||||
return { type: 3 /* Multi */, content: children };
|
return makeASTMulti(children);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
@@ -5767,7 +5813,7 @@ function compile(template, options = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// do not modify manually. This file is generated by the release script.
|
// do not modify manually. This file is generated by the release script.
|
||||||
const version = "2.8.0";
|
const version = "2.8.1";
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// Scheduler
|
// Scheduler
|
||||||
@@ -6238,6 +6284,6 @@ TemplateSet.prototype._compileTemplate = function _compileTemplate(name, templat
|
|||||||
export { App, Component, EventBus, OwlError, __info__, batched, blockDom, htmlEscape, loadFile, markRaw, markup, mount, onError, onMounted, onPatched, onRendered, onWillDestroy, onWillPatch, onWillRender, onWillStart, onWillUnmount, onWillUpdateProps, reactive, status, toRaw, useChildSubEnv, useComponent, useEffect, useEnv, useExternalListener, useRef, useState, useSubEnv, validate, validateType, whenReady, xml };
|
export { App, Component, EventBus, OwlError, __info__, batched, blockDom, htmlEscape, loadFile, markRaw, markup, mount, onError, onMounted, onPatched, onRendered, onWillDestroy, onWillPatch, onWillRender, onWillStart, onWillUnmount, onWillUpdateProps, reactive, status, toRaw, useChildSubEnv, useComponent, useEffect, useEnv, useExternalListener, useRef, useState, useSubEnv, validate, validateType, whenReady, xml };
|
||||||
|
|
||||||
|
|
||||||
__info__.date = '2025-06-30T12:46:06.424Z';
|
__info__.date = '2025-09-23T07:17:45.055Z';
|
||||||
__info__.hash = 'b620502';
|
__info__.hash = '5211116';
|
||||||
__info__.url = 'https://github.com/odoo/owl';
|
__info__.url = 'https://github.com/odoo/owl';
|
||||||
|
|||||||
Generated
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@odoo/owl",
|
"name": "@odoo/owl",
|
||||||
"version": "2.8.0",
|
"version": "2.8.1",
|
||||||
"lockfileVersion": 1,
|
"lockfileVersion": 1,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@odoo/owl",
|
"name": "@odoo/owl",
|
||||||
"version": "2.8.0",
|
"version": "2.8.1",
|
||||||
"description": "Odoo Web Library (OWL)",
|
"description": "Odoo Web Library (OWL)",
|
||||||
"main": "dist/owl.cjs.js",
|
"main": "dist/owl.cjs.js",
|
||||||
"module": "dist/owl.es.js",
|
"module": "dist/owl.es.js",
|
||||||
|
|||||||
@@ -995,7 +995,7 @@ export class CodeGenerator {
|
|||||||
const isNewBlock = !block || forceNewBlock;
|
const isNewBlock = !block || forceNewBlock;
|
||||||
let codeIdx = this.target.code.length;
|
let codeIdx = this.target.code.length;
|
||||||
if (isNewBlock) {
|
if (isNewBlock) {
|
||||||
const n = ast.content.filter((c) => c.type !== ASTType.TSet).length;
|
const n = ast.content.filter((c) => !c.hasNoRepresentation).length;
|
||||||
let result: string | null = null;
|
let result: string | null = null;
|
||||||
if (n <= 1) {
|
if (n <= 1) {
|
||||||
for (let child of ast.content) {
|
for (let child of ast.content) {
|
||||||
@@ -1009,15 +1009,15 @@ export class CodeGenerator {
|
|||||||
let index = 0;
|
let index = 0;
|
||||||
for (let i = 0, l = ast.content.length; i < l; i++) {
|
for (let i = 0, l = ast.content.length; i < l; i++) {
|
||||||
const child = ast.content[i];
|
const child = ast.content[i];
|
||||||
const isTSet = child.type === ASTType.TSet;
|
const forceNewBlock = !child.hasNoRepresentation;
|
||||||
const subCtx = createContext(ctx, {
|
const subCtx = createContext(ctx, {
|
||||||
block,
|
block,
|
||||||
index,
|
index,
|
||||||
forceNewBlock: !isTSet,
|
forceNewBlock,
|
||||||
isLast: ctx.isLast && i === l - 1,
|
isLast: ctx.isLast && i === l - 1,
|
||||||
});
|
});
|
||||||
this.compileAST(child, subCtx);
|
this.compileAST(child, subCtx);
|
||||||
if (!isTSet) {
|
if (forceNewBlock) {
|
||||||
index++;
|
index++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+93
-38
@@ -31,12 +31,17 @@ export const enum ASTType {
|
|||||||
TPortal,
|
TPortal,
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ASTText {
|
export interface BaseAST {
|
||||||
|
type: ASTType;
|
||||||
|
hasNoRepresentation?: true;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ASTText extends BaseAST {
|
||||||
type: ASTType.Text;
|
type: ASTType.Text;
|
||||||
value: string;
|
value: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ASTComment {
|
export interface ASTComment extends BaseAST {
|
||||||
type: ASTType.Comment;
|
type: ASTType.Comment;
|
||||||
value: string;
|
value: string;
|
||||||
}
|
}
|
||||||
@@ -52,7 +57,7 @@ interface TModelInfo {
|
|||||||
specialInitTargetAttr: string | null;
|
specialInitTargetAttr: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ASTDomNode {
|
export interface ASTDomNode extends BaseAST {
|
||||||
type: ASTType.DomNode;
|
type: ASTType.DomNode;
|
||||||
tag: string;
|
tag: string;
|
||||||
content: AST[];
|
content: AST[];
|
||||||
@@ -65,24 +70,24 @@ export interface ASTDomNode {
|
|||||||
ns: string | null;
|
ns: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ASTMulti {
|
export interface ASTMulti extends BaseAST {
|
||||||
type: ASTType.Multi;
|
type: ASTType.Multi;
|
||||||
content: AST[];
|
content: AST[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ASTTEsc {
|
export interface ASTTEsc extends BaseAST {
|
||||||
type: ASTType.TEsc;
|
type: ASTType.TEsc;
|
||||||
expr: string;
|
expr: string;
|
||||||
defaultValue: string;
|
defaultValue: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ASTTOut {
|
export interface ASTTOut extends BaseAST {
|
||||||
type: ASTType.TOut;
|
type: ASTType.TOut;
|
||||||
expr: string;
|
expr: string;
|
||||||
body: AST[] | null;
|
body: AST[] | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ASTTif {
|
export interface ASTTif extends BaseAST {
|
||||||
type: ASTType.TIf;
|
type: ASTType.TIf;
|
||||||
condition: string;
|
condition: string;
|
||||||
content: AST;
|
content: AST;
|
||||||
@@ -90,15 +95,16 @@ export interface ASTTif {
|
|||||||
tElse: AST | null;
|
tElse: AST | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ASTTSet {
|
export interface ASTTSet extends BaseAST {
|
||||||
type: ASTType.TSet;
|
type: ASTType.TSet;
|
||||||
name: string;
|
name: string;
|
||||||
value: string | null; // value defined in attribute
|
value: string | null; // value defined in attribute
|
||||||
defaultValue: string | null; // value defined in body, if text
|
defaultValue: string | null; // value defined in body, if text
|
||||||
body: AST[] | null; // content of body if not text
|
body: AST[] | null; // content of body if not text
|
||||||
|
hasNoRepresentation: true;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ASTTForEach {
|
export interface ASTTForEach extends BaseAST {
|
||||||
type: ASTType.TForEach;
|
type: ASTType.TForEach;
|
||||||
collection: string;
|
collection: string;
|
||||||
elem: string;
|
elem: string;
|
||||||
@@ -111,13 +117,13 @@ export interface ASTTForEach {
|
|||||||
key: string | null;
|
key: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ASTTKey {
|
export interface ASTTKey extends BaseAST {
|
||||||
type: ASTType.TKey;
|
type: ASTType.TKey;
|
||||||
expr: string;
|
expr: string;
|
||||||
content: AST;
|
content: AST;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ASTTCall {
|
export interface ASTTCall extends BaseAST {
|
||||||
type: ASTType.TCall;
|
type: ASTType.TCall;
|
||||||
name: string;
|
name: string;
|
||||||
body: AST[] | null;
|
body: AST[] | null;
|
||||||
@@ -132,7 +138,7 @@ interface SlotDefinition {
|
|||||||
attrsTranslationCtx: Attrs | null;
|
attrsTranslationCtx: Attrs | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ASTComponent {
|
export interface ASTComponent extends BaseAST {
|
||||||
type: ASTType.TComponent;
|
type: ASTType.TComponent;
|
||||||
name: string;
|
name: string;
|
||||||
isDynamic: boolean;
|
isDynamic: boolean;
|
||||||
@@ -143,7 +149,7 @@ export interface ASTComponent {
|
|||||||
slots: { [name: string]: SlotDefinition } | null;
|
slots: { [name: string]: SlotDefinition } | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ASTSlot {
|
export interface ASTSlot extends BaseAST {
|
||||||
type: ASTType.TSlot;
|
type: ASTType.TSlot;
|
||||||
name: string;
|
name: string;
|
||||||
attrs: Attrs | null;
|
attrs: Attrs | null;
|
||||||
@@ -152,34 +158,34 @@ export interface ASTSlot {
|
|||||||
defaultContent: AST | null;
|
defaultContent: AST | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ASTTCallBlock {
|
export interface ASTTCallBlock extends BaseAST {
|
||||||
type: ASTType.TCallBlock;
|
type: ASTType.TCallBlock;
|
||||||
name: string;
|
name: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ASTDebug {
|
export interface ASTDebug extends BaseAST {
|
||||||
type: ASTType.TDebug;
|
type: ASTType.TDebug;
|
||||||
content: AST | null;
|
content: AST | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ASTLog {
|
export interface ASTLog extends BaseAST {
|
||||||
type: ASTType.TLog;
|
type: ASTType.TLog;
|
||||||
expr: string;
|
expr: string;
|
||||||
content: AST | null;
|
content: AST | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ASTTranslation {
|
export interface ASTTranslation extends BaseAST {
|
||||||
type: ASTType.TTranslation;
|
type: ASTType.TTranslation;
|
||||||
content: AST | null;
|
content: AST | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ASTTranslationContext {
|
export interface ASTTranslationContext extends BaseAST {
|
||||||
type: ASTType.TTranslationContext;
|
type: ASTType.TTranslationContext;
|
||||||
content: AST | null;
|
content: AST | null;
|
||||||
translationCtx: string;
|
translationCtx: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ASTTPortal {
|
export interface ASTTPortal extends BaseAST {
|
||||||
type: ASTType.TPortal;
|
type: ASTType.TPortal;
|
||||||
target: string;
|
target: string;
|
||||||
content: AST;
|
content: AST;
|
||||||
@@ -255,9 +261,9 @@ function parseNode(node: Node, ctx: ParsingContext): AST | null {
|
|||||||
parseTCallBlock(node, ctx) ||
|
parseTCallBlock(node, ctx) ||
|
||||||
parseTTranslation(node, ctx) ||
|
parseTTranslation(node, ctx) ||
|
||||||
parseTTranslationContext(node, ctx) ||
|
parseTTranslationContext(node, ctx) ||
|
||||||
|
parseTKey(node, ctx) ||
|
||||||
parseTEscNode(node, ctx) ||
|
parseTEscNode(node, ctx) ||
|
||||||
parseTOutNode(node, ctx) ||
|
parseTOutNode(node, ctx) ||
|
||||||
parseTKey(node, ctx) ||
|
|
||||||
parseTSlot(node, ctx) ||
|
parseTSlot(node, ctx) ||
|
||||||
parseComponent(node, ctx) ||
|
parseComponent(node, ctx) ||
|
||||||
parseDOMNode(node, ctx) ||
|
parseDOMNode(node, ctx) ||
|
||||||
@@ -334,20 +340,30 @@ function parseTCustom(node: Element, ctx: ParsingContext): AST | null {
|
|||||||
function parseTDebugLog(node: Element, ctx: ParsingContext): AST | null {
|
function parseTDebugLog(node: Element, ctx: ParsingContext): AST | null {
|
||||||
if (node.hasAttribute("t-debug")) {
|
if (node.hasAttribute("t-debug")) {
|
||||||
node.removeAttribute("t-debug");
|
node.removeAttribute("t-debug");
|
||||||
return {
|
const content = parseNode(node, ctx);
|
||||||
|
const ast: ASTDebug = {
|
||||||
type: ASTType.TDebug,
|
type: ASTType.TDebug,
|
||||||
content: parseNode(node, ctx),
|
content,
|
||||||
};
|
};
|
||||||
|
if (content?.hasNoRepresentation) {
|
||||||
|
ast.hasNoRepresentation = true;
|
||||||
|
}
|
||||||
|
return ast;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (node.hasAttribute("t-log")) {
|
if (node.hasAttribute("t-log")) {
|
||||||
const expr = node.getAttribute("t-log")!;
|
const expr = node.getAttribute("t-log")!;
|
||||||
node.removeAttribute("t-log");
|
node.removeAttribute("t-log");
|
||||||
return {
|
const content = parseNode(node, ctx);
|
||||||
|
const ast: ASTLog = {
|
||||||
type: ASTType.TLog,
|
type: ASTType.TLog,
|
||||||
expr,
|
expr,
|
||||||
content: parseNode(node, ctx),
|
content,
|
||||||
};
|
};
|
||||||
|
if (content?.hasNoRepresentation) {
|
||||||
|
ast.hasNoRepresentation = true;
|
||||||
|
}
|
||||||
|
return ast;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -598,11 +614,19 @@ function parseTKey(node: Element, ctx: ParsingContext): AST | null {
|
|||||||
}
|
}
|
||||||
const key = node.getAttribute("t-key")!;
|
const key = node.getAttribute("t-key")!;
|
||||||
node.removeAttribute("t-key");
|
node.removeAttribute("t-key");
|
||||||
const body = parseNode(node, ctx);
|
const content = parseNode(node, ctx);
|
||||||
if (!body) {
|
if (!content) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return { type: ASTType.TKey, expr: key, content: body };
|
const ast: ASTTKey = {
|
||||||
|
type: ASTType.TKey,
|
||||||
|
expr: key,
|
||||||
|
content,
|
||||||
|
};
|
||||||
|
if (content.hasNoRepresentation) {
|
||||||
|
ast.hasNoRepresentation = true;
|
||||||
|
}
|
||||||
|
return ast;
|
||||||
}
|
}
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
@@ -724,7 +748,7 @@ function parseTSetNode(node: Element, ctx: ParsingContext): AST | null {
|
|||||||
if (node.textContent !== node.innerHTML) {
|
if (node.textContent !== node.innerHTML) {
|
||||||
body = parseChildren(node, ctx);
|
body = parseChildren(node, ctx);
|
||||||
}
|
}
|
||||||
return { type: ASTType.TSet, name, value, defaultValue, body };
|
return { type: ASTType.TSet, name, value, defaultValue, body, hasNoRepresentation: true };
|
||||||
}
|
}
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
@@ -916,32 +940,55 @@ function parseTSlot(node: Element, ctx: ParsingContext): AST | null {
|
|||||||
// Translation
|
// Translation
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
function wrapInTTranslationAST(r: AST | null) {
|
||||||
|
const ast: ASTTranslation = { type: ASTType.TTranslation, content: r };
|
||||||
|
if (r?.hasNoRepresentation) {
|
||||||
|
ast.hasNoRepresentation = true;
|
||||||
|
}
|
||||||
|
return ast;
|
||||||
|
}
|
||||||
|
|
||||||
function parseTTranslation(node: Element, ctx: ParsingContext): AST | null {
|
function parseTTranslation(node: Element, ctx: ParsingContext): AST | null {
|
||||||
if (node.getAttribute("t-translation") !== "off") {
|
if (node.getAttribute("t-translation") !== "off") {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
node.removeAttribute("t-translation");
|
node.removeAttribute("t-translation");
|
||||||
return {
|
const result = parseNode(node, ctx);
|
||||||
type: ASTType.TTranslation,
|
if (result?.type === ASTType.Multi) {
|
||||||
content: parseNode(node, ctx),
|
const children = result.content.map(wrapInTTranslationAST);
|
||||||
};
|
return makeASTMulti(children);
|
||||||
|
}
|
||||||
|
return wrapInTTranslationAST(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// Translation Context
|
// Translation Context
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
function wrapInTTranslationContextAST(r: AST | null, translationCtx: string) {
|
||||||
|
const ast: ASTTranslationContext = {
|
||||||
|
type: ASTType.TTranslationContext,
|
||||||
|
content: r,
|
||||||
|
translationCtx,
|
||||||
|
};
|
||||||
|
if (r?.hasNoRepresentation) {
|
||||||
|
ast.hasNoRepresentation = true;
|
||||||
|
}
|
||||||
|
return ast;
|
||||||
|
}
|
||||||
|
|
||||||
function parseTTranslationContext(node: Element, ctx: ParsingContext): AST | null {
|
function parseTTranslationContext(node: Element, ctx: ParsingContext): AST | null {
|
||||||
const translationCtx = node.getAttribute("t-translation-context");
|
const translationCtx = node.getAttribute("t-translation-context");
|
||||||
if (!translationCtx) {
|
if (!translationCtx) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
node.removeAttribute("t-translation-context");
|
node.removeAttribute("t-translation-context");
|
||||||
return {
|
const result = parseNode(node, ctx);
|
||||||
type: ASTType.TTranslationContext,
|
if (result?.type === ASTType.Multi) {
|
||||||
content: parseNode(node, ctx),
|
const children = result.content.map((c) => wrapInTTranslationContextAST(c, translationCtx));
|
||||||
translationCtx,
|
return makeASTMulti(children);
|
||||||
};
|
}
|
||||||
|
return wrapInTTranslationContextAST(result, translationCtx);
|
||||||
}
|
}
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
@@ -990,6 +1037,14 @@ function parseChildren(node: Element, ctx: ParsingContext): AST[] {
|
|||||||
return children;
|
return children;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function makeASTMulti(children: AST[]) {
|
||||||
|
const ast: ASTMulti = { type: ASTType.Multi, content: children };
|
||||||
|
if (children.every((c) => c.hasNoRepresentation)) {
|
||||||
|
ast.hasNoRepresentation = true;
|
||||||
|
}
|
||||||
|
return 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.
|
||||||
@@ -1002,7 +1057,7 @@ function parseChildNodes(node: Element, ctx: ParsingContext): AST | null {
|
|||||||
case 1:
|
case 1:
|
||||||
return children[0];
|
return children[0];
|
||||||
default:
|
default:
|
||||||
return { type: ASTType.Multi, content: children };
|
return makeASTMulti(children);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ export function handleError(params: ErrorParams) {
|
|||||||
let current: Fiber | null = fiber;
|
let current: Fiber | null = fiber;
|
||||||
do {
|
do {
|
||||||
current.node.fiber = current;
|
current.node.fiber = current;
|
||||||
|
fibersInError.set(current, error);
|
||||||
current = current.parent;
|
current = current.parent;
|
||||||
} while (current);
|
} while (current);
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,2 +1,2 @@
|
|||||||
// do not modify manually. This file is generated by the release script.
|
// do not modify manually. This file is generated by the release script.
|
||||||
export const version = "2.8.0";
|
export const version = "2.8.1";
|
||||||
|
|||||||
@@ -49,6 +49,27 @@ exports[`debugging t-debug on sub template 2`] = `
|
|||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`debugging t-debug: interaction with t-set 1`] = `
|
||||||
|
"function anonymous(app, bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||||
|
let { isBoundary, withDefault, setContextValue } = helpers;
|
||||||
|
|
||||||
|
let block1 = createBlock(\`<span><block-text-0/></span>\`);
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
ctx = Object.create(ctx);
|
||||||
|
ctx[isBoundary] = 1
|
||||||
|
debugger;
|
||||||
|
setContextValue(ctx, \\"foo\\", 42);
|
||||||
|
debugger;
|
||||||
|
setContextValue(ctx, \\"bar\\", 49);
|
||||||
|
let txt1 = ctx['foo']+ctx['bar'];
|
||||||
|
return block1([txt1]);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`debugging t-log 1`] = `
|
exports[`debugging t-log 1`] = `
|
||||||
"function anonymous(app, bdom, helpers
|
"function anonymous(app, bdom, helpers
|
||||||
) {
|
) {
|
||||||
@@ -66,3 +87,24 @@ exports[`debugging t-log 1`] = `
|
|||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`debugging t-log: interaction with t-set 1`] = `
|
||||||
|
"function anonymous(app, bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||||
|
let { isBoundary, withDefault, setContextValue } = helpers;
|
||||||
|
|
||||||
|
let block1 = createBlock(\`<span><block-text-0/></span>\`);
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
ctx = Object.create(ctx);
|
||||||
|
ctx[isBoundary] = 1
|
||||||
|
console.log(ctx['foo']);
|
||||||
|
setContextValue(ctx, \\"foo\\", 42);
|
||||||
|
console.log(ctx['bar']);
|
||||||
|
setContextValue(ctx, \\"bar\\", 49);
|
||||||
|
let txt1 = ctx['foo']+ctx['bar'];
|
||||||
|
return block1([txt1]);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|||||||
@@ -103,3 +103,18 @@ exports[`t-key t-key on sub dom node pushes a child block in its parent 2`] = `
|
|||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`t-key t-key: interaction with t-esc 1`] = `
|
||||||
|
"function anonymous(app, bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||||
|
|
||||||
|
let block1 = createBlock(\`<p><block-text-0/></p>\`);
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
const tKey_1 = ctx['key'];
|
||||||
|
let txt1 = ctx['text'];
|
||||||
|
return toggler(tKey_1, block1([txt1]));
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|||||||
@@ -10,8 +10,7 @@ exports[`translation context body of t-sets are translated in context 1`] = `
|
|||||||
ctx = Object.create(ctx);
|
ctx = Object.create(ctx);
|
||||||
ctx[isBoundary] = 1
|
ctx[isBoundary] = 1
|
||||||
setContextValue(ctx, \\"label\\", \`traduit\`);
|
setContextValue(ctx, \\"label\\", \`traduit\`);
|
||||||
const b2 = text(ctx['label']);
|
return text(ctx['label']);
|
||||||
return multi([b2]);
|
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
@@ -94,6 +93,23 @@ exports[`translation context slot attrs and text contents are translated in cont
|
|||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`translation context t-translation-context with several children 1`] = `
|
||||||
|
"function anonymous(app, bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||||
|
|
||||||
|
let block1 = createBlock(\`<div><div/><div/><block-child-0/></div>\`);
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
let b2;
|
||||||
|
if (true) {
|
||||||
|
b2 = text(\`\`);
|
||||||
|
}
|
||||||
|
return block1([], [b2]);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`translation context translation of attributes in context 1`] = `
|
exports[`translation context translation of attributes in context 1`] = `
|
||||||
"function anonymous(app, bdom, helpers
|
"function anonymous(app, bdom, helpers
|
||||||
) {
|
) {
|
||||||
@@ -153,6 +169,21 @@ exports[`translation support body of t-sets inside translation=off are not trans
|
|||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`translation support body of t-sets inside translation=off are not translated 2 1`] = `
|
||||||
|
"function anonymous(app, bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||||
|
let { isBoundary, withDefault, setContextValue } = helpers;
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
ctx = Object.create(ctx);
|
||||||
|
ctx[isBoundary] = 1
|
||||||
|
setContextValue(ctx, \\"label\\", \`untranslated\`);
|
||||||
|
return text(ctx['label']);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`translation support body of t-sets with html content are translated 1`] = `
|
exports[`translation support body of t-sets with html content are translated 1`] = `
|
||||||
"function anonymous(app, bdom, helpers
|
"function anonymous(app, bdom, helpers
|
||||||
) {
|
) {
|
||||||
@@ -264,6 +295,23 @@ exports[`translation support t-set and falsy t-value: t-body are translated 1`]
|
|||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`translation support t-translation with several children 1`] = `
|
||||||
|
"function anonymous(app, bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||||
|
|
||||||
|
let block1 = createBlock(\`<div><div/><div/><block-child-0/></div>\`);
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
let b2;
|
||||||
|
if (true) {
|
||||||
|
b2 = text(\`\`);
|
||||||
|
}
|
||||||
|
return block1([], [b2]);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`translation support translation is done on the trimmed text, with extra spaces readded after 1`] = `
|
exports[`translation support translation is done on the trimmed text, with extra spaces readded after 1`] = `
|
||||||
"function anonymous(app, bdom, helpers
|
"function anonymous(app, bdom, helpers
|
||||||
) {
|
) {
|
||||||
|
|||||||
@@ -692,6 +692,7 @@ describe("qweb parser", () => {
|
|||||||
value: "value",
|
value: "value",
|
||||||
defaultValue: null,
|
defaultValue: null,
|
||||||
body: null,
|
body: null,
|
||||||
|
hasNoRepresentation: true,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -702,6 +703,7 @@ describe("qweb parser", () => {
|
|||||||
defaultValue: "ok",
|
defaultValue: "ok",
|
||||||
value: null,
|
value: null,
|
||||||
body: null,
|
body: null,
|
||||||
|
hasNoRepresentation: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(parse(`<t t-set="v"><div>ok</div></t>`)).toEqual({
|
expect(parse(`<t t-set="v"><div>ok</div></t>`)).toEqual({
|
||||||
@@ -723,6 +725,7 @@ describe("qweb parser", () => {
|
|||||||
content: [{ type: ASTType.Text, value: "ok" }],
|
content: [{ type: ASTType.Text, value: "ok" }],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
hasNoRepresentation: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(parse(`<t t-set="v"><div>ok</div>abc</t>`)).toEqual({
|
expect(parse(`<t t-set="v"><div>ok</div>abc</t>`)).toEqual({
|
||||||
@@ -745,6 +748,7 @@ describe("qweb parser", () => {
|
|||||||
},
|
},
|
||||||
{ type: ASTType.Text, value: "abc" },
|
{ type: ASTType.Text, value: "abc" },
|
||||||
],
|
],
|
||||||
|
hasNoRepresentation: true,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -758,6 +762,7 @@ describe("qweb parser", () => {
|
|||||||
defaultValue: "ok",
|
defaultValue: "ok",
|
||||||
value: null,
|
value: null,
|
||||||
body: null,
|
body: null,
|
||||||
|
hasNoRepresentation: true,
|
||||||
},
|
},
|
||||||
tElif: null,
|
tElif: null,
|
||||||
tElse: null,
|
tElse: null,
|
||||||
@@ -783,7 +788,14 @@ describe("qweb parser", () => {
|
|||||||
condition: "flag",
|
condition: "flag",
|
||||||
content: { type: ASTType.Text, value: "1" },
|
content: { type: ASTType.Text, value: "1" },
|
||||||
tElif: null,
|
tElif: null,
|
||||||
tElse: { type: ASTType.TSet, name: "ourvar", value: "0", defaultValue: null, body: null },
|
tElse: {
|
||||||
|
type: ASTType.TSet,
|
||||||
|
name: "ourvar",
|
||||||
|
value: "0",
|
||||||
|
defaultValue: null,
|
||||||
|
body: null,
|
||||||
|
hasNoRepresentation: true,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -38,4 +38,34 @@ describe("debugging", () => {
|
|||||||
expect(console.log).toHaveBeenCalledWith(45);
|
expect(console.log).toHaveBeenCalledWith(45);
|
||||||
console.log = consoleLog;
|
console.log = consoleLog;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("t-log: interaction with t-set", () => {
|
||||||
|
const consoleLog = console.log;
|
||||||
|
console.log = jest.fn();
|
||||||
|
|
||||||
|
const template = `
|
||||||
|
<t>
|
||||||
|
<t t-log="foo" t-set="foo" t-value="42"/>
|
||||||
|
<t t-log="bar" t-set="bar" t-value="49"/>
|
||||||
|
<span t-esc="foo + bar"/>
|
||||||
|
</t>
|
||||||
|
`;
|
||||||
|
snapshotTemplate(template);
|
||||||
|
renderToString(template);
|
||||||
|
expect(console.log).toHaveBeenCalledWith(undefined);
|
||||||
|
expect(console.log).toHaveBeenCalledWith(undefined);
|
||||||
|
console.log = consoleLog;
|
||||||
|
});
|
||||||
|
|
||||||
|
test("t-debug: interaction with t-set", () => {
|
||||||
|
const template = `
|
||||||
|
<t>
|
||||||
|
<t t-debug="" t-set="foo" t-value="42"/>
|
||||||
|
<t t-debug="" t-set="bar" t-value="49"/>
|
||||||
|
<span t-esc="foo + bar"/>
|
||||||
|
</t>
|
||||||
|
`;
|
||||||
|
snapshotTemplate(template);
|
||||||
|
renderToString(template);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -63,4 +63,10 @@ describe("t-key", () => {
|
|||||||
|
|
||||||
expect(renderToString(template2, { key: "1" })).toBe("<div><h1></h1></div>");
|
expect(renderToString(template2, { key: "1" })).toBe("<div><h1></h1></div>");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("t-key: interaction with t-esc", async () => {
|
||||||
|
const template = `<p t-key="key" t-esc="text"/>`;
|
||||||
|
|
||||||
|
expect(renderToString(template, { key: "1", text: "abc" })).toBe("<p>abc</p>");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -129,6 +129,21 @@ describe("translation support", () => {
|
|||||||
expect(fixture.innerHTML).toBe("untranslated");
|
expect(fixture.innerHTML).toBe("untranslated");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("body of t-sets inside translation=off are not translated 2", async () => {
|
||||||
|
class SomeComponent extends Component {
|
||||||
|
static template = xml`
|
||||||
|
<t>
|
||||||
|
<t t-translation="off" t-set="label">untranslated</t>
|
||||||
|
<t t-esc="label"/>
|
||||||
|
</t>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const translateFn = () => "translated";
|
||||||
|
|
||||||
|
await mount(SomeComponent, fixture, { translateFn });
|
||||||
|
expect(fixture.innerHTML).toBe("untranslated");
|
||||||
|
});
|
||||||
|
|
||||||
test("body of t-sets with html content are translated", async () => {
|
test("body of t-sets with html content are translated", async () => {
|
||||||
class SomeComponent extends Component {
|
class SomeComponent extends Component {
|
||||||
static template = xml`
|
static template = xml`
|
||||||
@@ -170,6 +185,22 @@ describe("translation support", () => {
|
|||||||
await mount(SomeComponent, fixture, { translateFn });
|
await mount(SomeComponent, fixture, { translateFn });
|
||||||
expect(fixture.innerHTML).toBe("translated");
|
expect(fixture.innerHTML).toBe("translated");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("t-translation with several children", async () => {
|
||||||
|
class SomeComponent extends Component {
|
||||||
|
static template = xml`
|
||||||
|
<div>
|
||||||
|
<t t-translation="off">
|
||||||
|
<div/>
|
||||||
|
<div/>
|
||||||
|
</t>
|
||||||
|
<t t-if="true"/>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
await mount(SomeComponent, fixture);
|
||||||
|
expect(fixture.outerHTML).toBe("<div><div><div></div><div></div></div></div>");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("translation context", () => {
|
describe("translation context", () => {
|
||||||
@@ -293,4 +324,20 @@ describe("translation context", () => {
|
|||||||
expect(translateFn).toHaveBeenCalledWith("param", "fr");
|
expect(translateFn).toHaveBeenCalledWith("param", "fr");
|
||||||
expect(translateFn).toHaveBeenCalledWith("title", "pt");
|
expect(translateFn).toHaveBeenCalledWith("title", "pt");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("t-translation-context with several children", async () => {
|
||||||
|
class SomeComponent extends Component {
|
||||||
|
static template = xml`
|
||||||
|
<div>
|
||||||
|
<t t-translation-context="ctx">
|
||||||
|
<div/>
|
||||||
|
<div/>
|
||||||
|
</t>
|
||||||
|
<t t-if="true"/>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
await mount(SomeComponent, fixture);
|
||||||
|
expect(fixture.outerHTML).toBe("<div><div><div></div><div></div></div></div>");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -94,6 +94,52 @@ exports[`basics no component catching error lead to full app destruction 2`] = `
|
|||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`basics render from above on error -- handler is not a Root or MountFiber 1`] = `
|
||||||
|
"function anonymous(app, bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||||
|
const comp1 = app.createComponent(\`Parent\`, true, false, false, []);
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
return comp1({}, key + \`__1\`, node, this, null);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`basics render from above on error -- handler is not a Root or MountFiber 2`] = `
|
||||||
|
"function anonymous(app, bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||||
|
const comp1 = app.createComponent(\`Boom\`, true, false, false, []);
|
||||||
|
|
||||||
|
let block1 = createBlock(\`<div><block-child-0/><block-child-1/></div>\`);
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
let b2, b3;
|
||||||
|
if (ctx['error']) {
|
||||||
|
b2 = text(\`Error\`);
|
||||||
|
} else {
|
||||||
|
b3 = comp1({onError: (ctx['handleError']).bind(this)}, key + \`__1\`, node, this, null);
|
||||||
|
}
|
||||||
|
return block1([], [b2, b3]);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`basics render from above on error -- handler is not a Root or MountFiber 3`] = `
|
||||||
|
"function anonymous(app, bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||||
|
|
||||||
|
let block1 = createBlock(\`<div><block-text-0/></div>\`);
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
let txt1 = ctx['a'].b.c;
|
||||||
|
return block1([txt1]);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`basics simple catchError 1`] = `
|
exports[`basics simple catchError 1`] = `
|
||||||
"function anonymous(app, bdom, helpers
|
"function anonymous(app, bdom, helpers
|
||||||
) {
|
) {
|
||||||
|
|||||||
@@ -233,6 +233,44 @@ function(app, bdom, helpers) {
|
|||||||
expect(mockConsoleError).toBeCalledTimes(0);
|
expect(mockConsoleError).toBeCalledTimes(0);
|
||||||
expect(mockConsoleWarn).toBeCalledTimes(0);
|
expect(mockConsoleWarn).toBeCalledTimes(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("render from above on error -- handler is not a Root or MountFiber", async () => {
|
||||||
|
class Boom extends Component {
|
||||||
|
static template = xml`<div t-esc="a.b.c"/>`;
|
||||||
|
setup() {
|
||||||
|
onError((err) => {
|
||||||
|
this.props.onError(err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Parent extends Component {
|
||||||
|
static template = xml`
|
||||||
|
<div>
|
||||||
|
<t t-if="error">Error</t>
|
||||||
|
<t t-else="">
|
||||||
|
<Boom onError.bind="handleError"/>
|
||||||
|
</t>
|
||||||
|
</div>`;
|
||||||
|
static components = { Boom };
|
||||||
|
|
||||||
|
error: any = false;
|
||||||
|
|
||||||
|
handleError(err: Error) {
|
||||||
|
this.error = err;
|
||||||
|
this.render();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class GrandParent extends Component {
|
||||||
|
static template: string = xml`<Parent />`;
|
||||||
|
static components = { Parent };
|
||||||
|
}
|
||||||
|
await mount(GrandParent, fixture);
|
||||||
|
expect(fixture.innerHTML).toBe("<div>Error</div>");
|
||||||
|
expect(mockConsoleError).toBeCalledTimes(0);
|
||||||
|
expect(mockConsoleWarn).toBeCalledTimes(0);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("errors and promises", () => {
|
describe("errors and promises", () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user