mirror of
https://github.com/odoo/owl.git
synced 2025-10-06 19:59:41 +07:00
[REF] tests: move slots tests out of main component file
This commit is contained in:
@@ -1291,493 +1291,6 @@ exports[`t-model directive on an input, type=checkbox 1`] = `
|
|||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`t-slot directive can define and call slots 1`] = `
|
|
||||||
"function anonymous(context, extra
|
|
||||||
) {
|
|
||||||
// Template name: \\"Parent\\"
|
|
||||||
let utils = this.constructor.utils;
|
|
||||||
let QWeb = this.constructor;
|
|
||||||
let parent = context;
|
|
||||||
let scope = Object.create(context);
|
|
||||||
var h = this.h;
|
|
||||||
let c1 = [], p1 = {key:1};
|
|
||||||
var vn1 = h('div', p1, c1);
|
|
||||||
//COMPONENT
|
|
||||||
let k3 = \`__4__\`;
|
|
||||||
let w2 = k3 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[k3]] : false;
|
|
||||||
let props2 = {};
|
|
||||||
if (w2 && w2.__owl__.currentFiber && !w2.__owl__.vnode) {
|
|
||||||
w2.destroy();
|
|
||||||
w2 = false;
|
|
||||||
}
|
|
||||||
if (w2) {
|
|
||||||
w2.__updateProps(props2, extra.fiber, Object.assign(Object.create(context), scope));
|
|
||||||
let pvnode = w2.__owl__.pvnode;
|
|
||||||
c1.push(pvnode);
|
|
||||||
} else {
|
|
||||||
let componentKey2 = \`Dialog\`;
|
|
||||||
let W2 = context.constructor.components[componentKey2] || QWeb.components[componentKey2]|| scope['Dialog'];
|
|
||||||
if (!W2) {throw new Error('Cannot find the definition of component \\"' + componentKey2 + '\\"')}
|
|
||||||
w2 = new W2(parent, props2);
|
|
||||||
parent.__owl__.cmap[k3] = w2.__owl__.id;
|
|
||||||
w2.__owl__.slotId = 1;
|
|
||||||
let fiber = w2.__prepare(extra.fiber, Object.assign(Object.create(context), scope), () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
|
|
||||||
let pvnode = h('dummy', {key: k3, hook: {remove() {},destroy(vn) {w2.destroy();}}});
|
|
||||||
c1.push(pvnode);
|
|
||||||
w2.__owl__.pvnode = pvnode;
|
|
||||||
}
|
|
||||||
w2.__owl__.parentLastFiberId = extra.fiber.id;
|
|
||||||
return vn1;
|
|
||||||
}"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`t-slot directive can define and call slots 2`] = `
|
|
||||||
"function anonymous(context, extra
|
|
||||||
) {
|
|
||||||
// Template name: \\"Dialog\\"
|
|
||||||
var h = this.h;
|
|
||||||
let c7 = [], p7 = {key:7};
|
|
||||||
var vn7 = h('div', p7, c7);
|
|
||||||
let c8 = [], p8 = {key:8};
|
|
||||||
var vn8 = h('div', p8, c8);
|
|
||||||
c7.push(vn8);
|
|
||||||
const slot9 = this.constructor.slots[context.__owl__.slotId + '_' + 'header'];
|
|
||||||
if (slot9) {
|
|
||||||
slot9.call(this, context.__owl__.scope, Object.assign({}, extra, {parentNode: c8, parent: extra.parent || context}));
|
|
||||||
}
|
|
||||||
let c10 = [], p10 = {key:10};
|
|
||||||
var vn10 = h('div', p10, c10);
|
|
||||||
c7.push(vn10);
|
|
||||||
const slot11 = this.constructor.slots[context.__owl__.slotId + '_' + 'footer'];
|
|
||||||
if (slot11) {
|
|
||||||
slot11.call(this, context.__owl__.scope, Object.assign({}, extra, {parentNode: c10, parent: extra.parent || context}));
|
|
||||||
}
|
|
||||||
return vn7;
|
|
||||||
}"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`t-slot directive can define and call slots 3`] = `
|
|
||||||
"function anonymous(context, extra
|
|
||||||
) {
|
|
||||||
// Template name: \\"slot_header_template\\"
|
|
||||||
var h = this.h;
|
|
||||||
let c1 = extra.parentNode;
|
|
||||||
let c5 = [], p5 = {key:5};
|
|
||||||
var vn5 = h('span', p5, c5);
|
|
||||||
c1.push(vn5);
|
|
||||||
c5.push({text: \`header\`});
|
|
||||||
}"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`t-slot directive can define and call slots 4`] = `
|
|
||||||
"function anonymous(context, extra
|
|
||||||
) {
|
|
||||||
// Template name: \\"slot_footer_template\\"
|
|
||||||
var h = this.h;
|
|
||||||
let c1 = extra.parentNode;
|
|
||||||
let c6 = [], p6 = {key:6};
|
|
||||||
var vn6 = h('span', p6, c6);
|
|
||||||
c1.push(vn6);
|
|
||||||
c6.push({text: \`footer\`});
|
|
||||||
}"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`t-slot directive content is the default slot 1`] = `
|
|
||||||
"function anonymous(context, extra
|
|
||||||
) {
|
|
||||||
// Template name: \\"slot_default_template\\"
|
|
||||||
var h = this.h;
|
|
||||||
let c1 = extra.parentNode;
|
|
||||||
let c5 = [], p5 = {key:5};
|
|
||||||
var vn5 = h('span', p5, c5);
|
|
||||||
c1.push(vn5);
|
|
||||||
c5.push({text: \`sts rocks\`});
|
|
||||||
}"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`t-slot directive dafault slots can define a default content 1`] = `
|
|
||||||
"function anonymous(context, extra
|
|
||||||
) {
|
|
||||||
// Template name: \\"__template__1\\"
|
|
||||||
var h = this.h;
|
|
||||||
let c5 = [], p5 = {key:5};
|
|
||||||
var vn5 = h('span', p5, c5);
|
|
||||||
const slot6 = this.constructor.slots[context.__owl__.slotId + '_' + 'default'];
|
|
||||||
if (slot6) {
|
|
||||||
slot6.call(this, context.__owl__.scope, Object.assign({}, extra, {parentNode: c5, parent: extra.parent || context}));
|
|
||||||
} else {
|
|
||||||
c5.push({text: \`default content\`});
|
|
||||||
}
|
|
||||||
return vn5;
|
|
||||||
}"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`t-slot directive default slot work with text nodes 1`] = `
|
|
||||||
"function anonymous(context, extra
|
|
||||||
) {
|
|
||||||
// Template name: \\"slot_default_template\\"
|
|
||||||
var h = this.h;
|
|
||||||
let c1 = extra.parentNode;
|
|
||||||
c1.push({text: \`sts rocks\`});
|
|
||||||
}"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`t-slot directive multiple roots are allowed in a default slot 1`] = `
|
|
||||||
"function anonymous(context, extra
|
|
||||||
) {
|
|
||||||
// Template name: \\"slot_default_template\\"
|
|
||||||
var h = this.h;
|
|
||||||
let c1 = extra.parentNode;
|
|
||||||
let c5 = [], p5 = {key:5};
|
|
||||||
var vn5 = h('span', p5, c5);
|
|
||||||
c1.push(vn5);
|
|
||||||
c5.push({text: \`sts\`});
|
|
||||||
let c6 = [], p6 = {key:6};
|
|
||||||
var vn6 = h('span', p6, c6);
|
|
||||||
c1.push(vn6);
|
|
||||||
c6.push({text: \`rocks\`});
|
|
||||||
}"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`t-slot directive multiple roots are allowed in a named slot 1`] = `
|
|
||||||
"function anonymous(context, extra
|
|
||||||
) {
|
|
||||||
// Template name: \\"slot_content_template\\"
|
|
||||||
var h = this.h;
|
|
||||||
let c1 = extra.parentNode;
|
|
||||||
let c5 = [], p5 = {key:5};
|
|
||||||
var vn5 = h('span', p5, c5);
|
|
||||||
c1.push(vn5);
|
|
||||||
c5.push({text: \`sts\`});
|
|
||||||
let c6 = [], p6 = {key:6};
|
|
||||||
var vn6 = h('span', p6, c6);
|
|
||||||
c1.push(vn6);
|
|
||||||
c6.push({text: \`rocks\`});
|
|
||||||
}"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`t-slot directive named slots can define a default content 1`] = `
|
|
||||||
"function anonymous(context, extra
|
|
||||||
) {
|
|
||||||
// Template name: \\"__template__1\\"
|
|
||||||
var h = this.h;
|
|
||||||
let c5 = [], p5 = {key:5};
|
|
||||||
var vn5 = h('span', p5, c5);
|
|
||||||
const slot6 = this.constructor.slots[context.__owl__.slotId + '_' + 'header'];
|
|
||||||
if (slot6) {
|
|
||||||
slot6.call(this, context.__owl__.scope, Object.assign({}, extra, {parentNode: c5, parent: extra.parent || context}));
|
|
||||||
} else {
|
|
||||||
c5.push({text: \`default content\`});
|
|
||||||
}
|
|
||||||
return vn5;
|
|
||||||
}"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`t-slot directive refs are properly bound in slots 1`] = `
|
|
||||||
"function anonymous(context, extra
|
|
||||||
) {
|
|
||||||
// Template name: \\"slot_footer_template\\"
|
|
||||||
context.__owl__.refs = context.__owl__.refs || {};
|
|
||||||
var h = this.h;
|
|
||||||
let c1 = extra.parentNode;
|
|
||||||
let c9 = [], p9 = {key:9,on:{}};
|
|
||||||
var vn9 = h('button', p9, c9);
|
|
||||||
c1.push(vn9);
|
|
||||||
extra.handlers['click' + 9] = extra.handlers['click' + 9] || function (e) {if (!context.__owl__.isMounted){return}const fn = context['doSomething'];if (fn) { fn.call(context, e); } else { context.doSomething; }};
|
|
||||||
p9.on['click'] = extra.handlers['click' + 9];
|
|
||||||
const ref10 = \`myButton\`;
|
|
||||||
p9.hook = {
|
|
||||||
create: (_, n) => {
|
|
||||||
context.__owl__.refs[ref10] = n.elm;
|
|
||||||
},
|
|
||||||
destroy: () => {
|
|
||||||
delete context.__owl__.refs[ref10];
|
|
||||||
},
|
|
||||||
};
|
|
||||||
c9.push({text: \`do something\`});
|
|
||||||
}"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`t-slot directive slots are rendered with proper context 1`] = `
|
|
||||||
"function anonymous(context, extra
|
|
||||||
) {
|
|
||||||
// Template name: \\"slot_footer_template\\"
|
|
||||||
var h = this.h;
|
|
||||||
let c1 = extra.parentNode;
|
|
||||||
let c9 = [], p9 = {key:9,on:{}};
|
|
||||||
var vn9 = h('button', p9, c9);
|
|
||||||
c1.push(vn9);
|
|
||||||
extra.handlers['click' + 9] = extra.handlers['click' + 9] || function (e) {if (!context.__owl__.isMounted){return}const fn = context['doSomething'];if (fn) { fn.call(context, e); } else { context.doSomething; }};
|
|
||||||
p9.on['click'] = extra.handlers['click' + 9];
|
|
||||||
c9.push({text: \`do something\`});
|
|
||||||
}"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`t-slot directive slots are rendered with proper context, part 2 1`] = `
|
|
||||||
"function anonymous(context, extra
|
|
||||||
) {
|
|
||||||
// Template name: \\"Link\\"
|
|
||||||
let scope = Object.create(context);
|
|
||||||
var h = this.h;
|
|
||||||
var _13 = scope['props'].to;
|
|
||||||
let c14 = [], p14 = {key:14,attrs:{href: _13}};
|
|
||||||
var vn14 = h('a', p14, c14);
|
|
||||||
const slot15 = this.constructor.slots[context.__owl__.slotId + '_' + 'default'];
|
|
||||||
if (slot15) {
|
|
||||||
slot15.call(this, context.__owl__.scope, Object.assign({}, extra, {parentNode: c14, parent: extra.parent || context}));
|
|
||||||
}
|
|
||||||
return vn14;
|
|
||||||
}"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`t-slot directive slots are rendered with proper context, part 2 2`] = `
|
|
||||||
"function anonymous(context, extra
|
|
||||||
) {
|
|
||||||
// Template name: \\"App\\"
|
|
||||||
let utils = this.constructor.utils;
|
|
||||||
let QWeb = this.constructor;
|
|
||||||
let parent = context;
|
|
||||||
let scope = Object.create(context);
|
|
||||||
var h = this.h;
|
|
||||||
let c1 = [], p1 = {key:1};
|
|
||||||
var vn1 = h('div', p1, c1);
|
|
||||||
let c2 = [], p2 = {key:2};
|
|
||||||
var vn2 = h('u', p2, c2);
|
|
||||||
c1.push(vn2);
|
|
||||||
var _3 = scope['state'].users;
|
|
||||||
if (!_3) { throw new Error('QWeb error: Invalid loop expression')}
|
|
||||||
var _4 = _5 = _3;
|
|
||||||
if (!(_3 instanceof Array)) {
|
|
||||||
_4 = Object.keys(_3);
|
|
||||||
_5 = Object.values(_3);
|
|
||||||
}
|
|
||||||
var _length4 = _4.length;
|
|
||||||
const _origScope6 = scope;
|
|
||||||
scope = Object.assign(Object.create(context), scope);
|
|
||||||
for (let i1 = 0; i1 < _length4; i1++) {
|
|
||||||
scope.user_first = i1 === 0
|
|
||||||
scope.user_last = i1 === _length4 - 1
|
|
||||||
scope.user_index = i1
|
|
||||||
scope.user = _4[i1]
|
|
||||||
scope.user_value = _5[i1]
|
|
||||||
const nodeKey7 = scope['user'].id;
|
|
||||||
let c8 = [], p8 = {key:nodeKey7};
|
|
||||||
var vn8 = h('li', p8, c8);
|
|
||||||
c2.push(vn8);
|
|
||||||
//COMPONENT
|
|
||||||
let k10 = \`__11__\` + nodeKey7;
|
|
||||||
let w9 = k10 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[k10]] : false;
|
|
||||||
let props9 = {to:'/user/'+scope['user'].id};
|
|
||||||
if (w9 && w9.__owl__.currentFiber && !w9.__owl__.vnode) {
|
|
||||||
w9.destroy();
|
|
||||||
w9 = false;
|
|
||||||
}
|
|
||||||
if (w9) {
|
|
||||||
w9.__updateProps(props9, extra.fiber, Object.assign(Object.create(context), scope));
|
|
||||||
let pvnode = w9.__owl__.pvnode;
|
|
||||||
c8.push(pvnode);
|
|
||||||
} else {
|
|
||||||
let componentKey9 = \`Link\`;
|
|
||||||
let W9 = context.constructor.components[componentKey9] || QWeb.components[componentKey9]|| scope['Link'];
|
|
||||||
if (!W9) {throw new Error('Cannot find the definition of component \\"' + componentKey9 + '\\"')}
|
|
||||||
w9 = new W9(parent, props9);
|
|
||||||
parent.__owl__.cmap[k10] = w9.__owl__.id;
|
|
||||||
w9.__owl__.slotId = 1;
|
|
||||||
let fiber = w9.__prepare(extra.fiber, Object.assign(Object.create(context), scope), () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
|
|
||||||
let pvnode = h('dummy', {key: k10, hook: {remove() {},destroy(vn) {w9.destroy();}}});
|
|
||||||
c8.push(pvnode);
|
|
||||||
w9.__owl__.pvnode = pvnode;
|
|
||||||
}
|
|
||||||
w9.__owl__.parentLastFiberId = extra.fiber.id;
|
|
||||||
}
|
|
||||||
scope = _origScope6;
|
|
||||||
return vn1;
|
|
||||||
}"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`t-slot directive slots are rendered with proper context, part 2 3`] = `
|
|
||||||
"function anonymous(context, extra
|
|
||||||
) {
|
|
||||||
// Template name: \\"slot_default_template\\"
|
|
||||||
let scope = Object.create(context);
|
|
||||||
var h = this.h;
|
|
||||||
let c8 = extra.parentNode;
|
|
||||||
c8.push({text: \`User \`});
|
|
||||||
var _12 = scope['user'].name;
|
|
||||||
if (_12 || _12 === 0) {
|
|
||||||
c8.push({text: _12});
|
|
||||||
}
|
|
||||||
}"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`t-slot directive slots are rendered with proper context, part 3 1`] = `
|
|
||||||
"function anonymous(context, extra
|
|
||||||
) {
|
|
||||||
// Template name: \\"Link\\"
|
|
||||||
let scope = Object.create(context);
|
|
||||||
var h = this.h;
|
|
||||||
var _12 = scope['props'].to;
|
|
||||||
let c13 = [], p13 = {key:13,attrs:{href: _12}};
|
|
||||||
var vn13 = h('a', p13, c13);
|
|
||||||
const slot14 = this.constructor.slots[context.__owl__.slotId + '_' + 'default'];
|
|
||||||
if (slot14) {
|
|
||||||
slot14.call(this, context.__owl__.scope, Object.assign({}, extra, {parentNode: c13, parent: extra.parent || context}));
|
|
||||||
}
|
|
||||||
return vn13;
|
|
||||||
}"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`t-slot directive slots are rendered with proper context, part 3 2`] = `
|
|
||||||
"function anonymous(context, extra
|
|
||||||
) {
|
|
||||||
// Template name: \\"App\\"
|
|
||||||
let utils = this.constructor.utils;
|
|
||||||
let QWeb = this.constructor;
|
|
||||||
let parent = context;
|
|
||||||
let scope = Object.create(context);
|
|
||||||
var h = this.h;
|
|
||||||
let c1 = [], p1 = {key:1};
|
|
||||||
var vn1 = h('div', p1, c1);
|
|
||||||
let c2 = [], p2 = {key:2};
|
|
||||||
var vn2 = h('u', p2, c2);
|
|
||||||
c1.push(vn2);
|
|
||||||
var _3 = scope['state'].users;
|
|
||||||
if (!_3) { throw new Error('QWeb error: Invalid loop expression')}
|
|
||||||
var _4 = _5 = _3;
|
|
||||||
if (!(_3 instanceof Array)) {
|
|
||||||
_4 = Object.keys(_3);
|
|
||||||
_5 = Object.values(_3);
|
|
||||||
}
|
|
||||||
var _length4 = _4.length;
|
|
||||||
const _origScope6 = scope;
|
|
||||||
scope = Object.assign(Object.create(context), scope);
|
|
||||||
for (let i1 = 0; i1 < _length4; i1++) {
|
|
||||||
scope.user_first = i1 === 0
|
|
||||||
scope.user_last = i1 === _length4 - 1
|
|
||||||
scope.user_index = i1
|
|
||||||
scope.user = _4[i1]
|
|
||||||
scope.user_value = _5[i1]
|
|
||||||
const nodeKey7 = scope['user'].id;
|
|
||||||
let c8 = [], p8 = {key:nodeKey7};
|
|
||||||
var vn8 = h('li', p8, c8);
|
|
||||||
c2.push(vn8);
|
|
||||||
scope.userdescr = 'User '+scope['user'].name;
|
|
||||||
//COMPONENT
|
|
||||||
let k10 = \`__11__\` + nodeKey7;
|
|
||||||
let w9 = k10 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[k10]] : false;
|
|
||||||
let props9 = {to:'/user/'+scope['user'].id};
|
|
||||||
if (w9 && w9.__owl__.currentFiber && !w9.__owl__.vnode) {
|
|
||||||
w9.destroy();
|
|
||||||
w9 = false;
|
|
||||||
}
|
|
||||||
if (w9) {
|
|
||||||
w9.__updateProps(props9, extra.fiber, Object.assign(Object.create(context), scope));
|
|
||||||
let pvnode = w9.__owl__.pvnode;
|
|
||||||
c8.push(pvnode);
|
|
||||||
} else {
|
|
||||||
let componentKey9 = \`Link\`;
|
|
||||||
let W9 = context.constructor.components[componentKey9] || QWeb.components[componentKey9]|| scope['Link'];
|
|
||||||
if (!W9) {throw new Error('Cannot find the definition of component \\"' + componentKey9 + '\\"')}
|
|
||||||
w9 = new W9(parent, props9);
|
|
||||||
parent.__owl__.cmap[k10] = w9.__owl__.id;
|
|
||||||
w9.__owl__.slotId = 1;
|
|
||||||
let fiber = w9.__prepare(extra.fiber, Object.assign(Object.create(context), scope), () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
|
|
||||||
let pvnode = h('dummy', {key: k10, hook: {remove() {},destroy(vn) {w9.destroy();}}});
|
|
||||||
c8.push(pvnode);
|
|
||||||
w9.__owl__.pvnode = pvnode;
|
|
||||||
}
|
|
||||||
w9.__owl__.parentLastFiberId = extra.fiber.id;
|
|
||||||
}
|
|
||||||
scope = _origScope6;
|
|
||||||
return vn1;
|
|
||||||
}"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`t-slot directive slots are rendered with proper context, part 3 3`] = `
|
|
||||||
"function anonymous(context, extra
|
|
||||||
) {
|
|
||||||
// Template name: \\"slot_default_template\\"
|
|
||||||
let scope = Object.create(context);
|
|
||||||
var h = this.h;
|
|
||||||
let c8 = extra.parentNode;
|
|
||||||
if (scope.userdescr || scope.userdescr === 0) {
|
|
||||||
c8.push({text: scope.userdescr});
|
|
||||||
}
|
|
||||||
}"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`t-slot directive slots are rendered with proper context, part 4 1`] = `
|
|
||||||
"function anonymous(context, extra
|
|
||||||
) {
|
|
||||||
// Template name: \\"App\\"
|
|
||||||
let utils = this.constructor.utils;
|
|
||||||
let QWeb = this.constructor;
|
|
||||||
let parent = context;
|
|
||||||
let scope = Object.create(context);
|
|
||||||
var h = this.h;
|
|
||||||
let c1 = [], p1 = {key:1};
|
|
||||||
var vn1 = h('div', p1, c1);
|
|
||||||
scope.userdescr = 'User '+scope['state'].user.name;
|
|
||||||
//COMPONENT
|
|
||||||
let k3 = \`__4__\`;
|
|
||||||
let w2 = k3 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[k3]] : false;
|
|
||||||
let props2 = {to:'/user/'+scope['state'].user.id};
|
|
||||||
if (w2 && w2.__owl__.currentFiber && !w2.__owl__.vnode) {
|
|
||||||
w2.destroy();
|
|
||||||
w2 = false;
|
|
||||||
}
|
|
||||||
if (w2) {
|
|
||||||
w2.__updateProps(props2, extra.fiber, Object.assign(Object.create(context), scope));
|
|
||||||
let pvnode = w2.__owl__.pvnode;
|
|
||||||
c1.push(pvnode);
|
|
||||||
} else {
|
|
||||||
let componentKey2 = \`Link\`;
|
|
||||||
let W2 = context.constructor.components[componentKey2] || QWeb.components[componentKey2]|| scope['Link'];
|
|
||||||
if (!W2) {throw new Error('Cannot find the definition of component \\"' + componentKey2 + '\\"')}
|
|
||||||
w2 = new W2(parent, props2);
|
|
||||||
parent.__owl__.cmap[k3] = w2.__owl__.id;
|
|
||||||
w2.__owl__.slotId = 1;
|
|
||||||
let fiber = w2.__prepare(extra.fiber, Object.assign(Object.create(context), scope), () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
|
|
||||||
let pvnode = h('dummy', {key: k3, hook: {remove() {},destroy(vn) {w2.destroy();}}});
|
|
||||||
c1.push(pvnode);
|
|
||||||
w2.__owl__.pvnode = pvnode;
|
|
||||||
}
|
|
||||||
w2.__owl__.parentLastFiberId = extra.fiber.id;
|
|
||||||
return vn1;
|
|
||||||
}"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`t-slot directive slots are rendered with proper context, part 4 2`] = `
|
|
||||||
"function anonymous(context, extra
|
|
||||||
) {
|
|
||||||
// Template name: \\"slot_default_template\\"
|
|
||||||
let scope = Object.create(context);
|
|
||||||
var h = this.h;
|
|
||||||
let c1 = extra.parentNode;
|
|
||||||
if (scope.userdescr || scope.userdescr === 0) {
|
|
||||||
c1.push({text: scope.userdescr});
|
|
||||||
}
|
|
||||||
}"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`t-slot directive template can just return a slot 1`] = `
|
|
||||||
"function anonymous(context, extra
|
|
||||||
) {
|
|
||||||
// Template name: \\"__template__2\\"
|
|
||||||
let utils = this.constructor.utils;
|
|
||||||
let result;
|
|
||||||
var h = this.h;
|
|
||||||
const slot8 = this.constructor.slots[context.__owl__.slotId + '_' + 'default'];
|
|
||||||
if (slot8) {
|
|
||||||
let children9= []
|
|
||||||
result = {}
|
|
||||||
slot8.call(this, context.__owl__.scope, Object.assign({}, extra, {parentNode: children9, parent: extra.parent || context}));
|
|
||||||
utils.defineProxy(result, children9[0]);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`top level sub widgets basic use 1`] = `
|
exports[`top level sub widgets basic use 1`] = `
|
||||||
"function anonymous(context, extra
|
"function anonymous(context, extra
|
||||||
) {
|
) {
|
||||||
|
|||||||
@@ -0,0 +1,488 @@
|
|||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`t-slot directive can define and call slots 1`] = `
|
||||||
|
"function anonymous(context, extra
|
||||||
|
) {
|
||||||
|
// Template name: \\"Parent\\"
|
||||||
|
let utils = this.constructor.utils;
|
||||||
|
let QWeb = this.constructor;
|
||||||
|
let parent = context;
|
||||||
|
let scope = Object.create(context);
|
||||||
|
var h = this.h;
|
||||||
|
let c1 = [], p1 = {key:1};
|
||||||
|
var vn1 = h('div', p1, c1);
|
||||||
|
//COMPONENT
|
||||||
|
let k3 = \`__4__\`;
|
||||||
|
let w2 = k3 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[k3]] : false;
|
||||||
|
let props2 = {};
|
||||||
|
if (w2 && w2.__owl__.currentFiber && !w2.__owl__.vnode) {
|
||||||
|
w2.destroy();
|
||||||
|
w2 = false;
|
||||||
|
}
|
||||||
|
if (w2) {
|
||||||
|
w2.__updateProps(props2, extra.fiber, Object.assign(Object.create(context), scope));
|
||||||
|
let pvnode = w2.__owl__.pvnode;
|
||||||
|
c1.push(pvnode);
|
||||||
|
} else {
|
||||||
|
let componentKey2 = \`Dialog\`;
|
||||||
|
let W2 = context.constructor.components[componentKey2] || QWeb.components[componentKey2]|| scope['Dialog'];
|
||||||
|
if (!W2) {throw new Error('Cannot find the definition of component \\"' + componentKey2 + '\\"')}
|
||||||
|
w2 = new W2(parent, props2);
|
||||||
|
parent.__owl__.cmap[k3] = w2.__owl__.id;
|
||||||
|
w2.__owl__.slotId = 1;
|
||||||
|
let fiber = w2.__prepare(extra.fiber, Object.assign(Object.create(context), scope), () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
|
||||||
|
let pvnode = h('dummy', {key: k3, hook: {remove() {},destroy(vn) {w2.destroy();}}});
|
||||||
|
c1.push(pvnode);
|
||||||
|
w2.__owl__.pvnode = pvnode;
|
||||||
|
}
|
||||||
|
w2.__owl__.parentLastFiberId = extra.fiber.id;
|
||||||
|
return vn1;
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`t-slot directive can define and call slots 2`] = `
|
||||||
|
"function anonymous(context, extra
|
||||||
|
) {
|
||||||
|
// Template name: \\"Dialog\\"
|
||||||
|
var h = this.h;
|
||||||
|
let c7 = [], p7 = {key:7};
|
||||||
|
var vn7 = h('div', p7, c7);
|
||||||
|
let c8 = [], p8 = {key:8};
|
||||||
|
var vn8 = h('div', p8, c8);
|
||||||
|
c7.push(vn8);
|
||||||
|
const slot9 = this.constructor.slots[context.__owl__.slotId + '_' + 'header'];
|
||||||
|
if (slot9) {
|
||||||
|
slot9.call(this, context.__owl__.scope, Object.assign({}, extra, {parentNode: c8, parent: extra.parent || context}));
|
||||||
|
}
|
||||||
|
let c10 = [], p10 = {key:10};
|
||||||
|
var vn10 = h('div', p10, c10);
|
||||||
|
c7.push(vn10);
|
||||||
|
const slot11 = this.constructor.slots[context.__owl__.slotId + '_' + 'footer'];
|
||||||
|
if (slot11) {
|
||||||
|
slot11.call(this, context.__owl__.scope, Object.assign({}, extra, {parentNode: c10, parent: extra.parent || context}));
|
||||||
|
}
|
||||||
|
return vn7;
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`t-slot directive can define and call slots 3`] = `
|
||||||
|
"function anonymous(context, extra
|
||||||
|
) {
|
||||||
|
// Template name: \\"slot_header_template\\"
|
||||||
|
var h = this.h;
|
||||||
|
let c1 = extra.parentNode;
|
||||||
|
let c5 = [], p5 = {key:5};
|
||||||
|
var vn5 = h('span', p5, c5);
|
||||||
|
c1.push(vn5);
|
||||||
|
c5.push({text: \`header\`});
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`t-slot directive can define and call slots 4`] = `
|
||||||
|
"function anonymous(context, extra
|
||||||
|
) {
|
||||||
|
// Template name: \\"slot_footer_template\\"
|
||||||
|
var h = this.h;
|
||||||
|
let c1 = extra.parentNode;
|
||||||
|
let c6 = [], p6 = {key:6};
|
||||||
|
var vn6 = h('span', p6, c6);
|
||||||
|
c1.push(vn6);
|
||||||
|
c6.push({text: \`footer\`});
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`t-slot directive content is the default slot 1`] = `
|
||||||
|
"function anonymous(context, extra
|
||||||
|
) {
|
||||||
|
// Template name: \\"slot_default_template\\"
|
||||||
|
var h = this.h;
|
||||||
|
let c1 = extra.parentNode;
|
||||||
|
let c5 = [], p5 = {key:5};
|
||||||
|
var vn5 = h('span', p5, c5);
|
||||||
|
c1.push(vn5);
|
||||||
|
c5.push({text: \`sts rocks\`});
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`t-slot directive dafault slots can define a default content 1`] = `
|
||||||
|
"function anonymous(context, extra
|
||||||
|
) {
|
||||||
|
// Template name: \\"__template__1\\"
|
||||||
|
var h = this.h;
|
||||||
|
let c5 = [], p5 = {key:5};
|
||||||
|
var vn5 = h('span', p5, c5);
|
||||||
|
const slot6 = this.constructor.slots[context.__owl__.slotId + '_' + 'default'];
|
||||||
|
if (slot6) {
|
||||||
|
slot6.call(this, context.__owl__.scope, Object.assign({}, extra, {parentNode: c5, parent: extra.parent || context}));
|
||||||
|
} else {
|
||||||
|
c5.push({text: \`default content\`});
|
||||||
|
}
|
||||||
|
return vn5;
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`t-slot directive default slot work with text nodes 1`] = `
|
||||||
|
"function anonymous(context, extra
|
||||||
|
) {
|
||||||
|
// Template name: \\"slot_default_template\\"
|
||||||
|
var h = this.h;
|
||||||
|
let c1 = extra.parentNode;
|
||||||
|
c1.push({text: \`sts rocks\`});
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`t-slot directive multiple roots are allowed in a default slot 1`] = `
|
||||||
|
"function anonymous(context, extra
|
||||||
|
) {
|
||||||
|
// Template name: \\"slot_default_template\\"
|
||||||
|
var h = this.h;
|
||||||
|
let c1 = extra.parentNode;
|
||||||
|
let c5 = [], p5 = {key:5};
|
||||||
|
var vn5 = h('span', p5, c5);
|
||||||
|
c1.push(vn5);
|
||||||
|
c5.push({text: \`sts\`});
|
||||||
|
let c6 = [], p6 = {key:6};
|
||||||
|
var vn6 = h('span', p6, c6);
|
||||||
|
c1.push(vn6);
|
||||||
|
c6.push({text: \`rocks\`});
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`t-slot directive multiple roots are allowed in a named slot 1`] = `
|
||||||
|
"function anonymous(context, extra
|
||||||
|
) {
|
||||||
|
// Template name: \\"slot_content_template\\"
|
||||||
|
var h = this.h;
|
||||||
|
let c1 = extra.parentNode;
|
||||||
|
let c5 = [], p5 = {key:5};
|
||||||
|
var vn5 = h('span', p5, c5);
|
||||||
|
c1.push(vn5);
|
||||||
|
c5.push({text: \`sts\`});
|
||||||
|
let c6 = [], p6 = {key:6};
|
||||||
|
var vn6 = h('span', p6, c6);
|
||||||
|
c1.push(vn6);
|
||||||
|
c6.push({text: \`rocks\`});
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`t-slot directive named slots can define a default content 1`] = `
|
||||||
|
"function anonymous(context, extra
|
||||||
|
) {
|
||||||
|
// Template name: \\"__template__1\\"
|
||||||
|
var h = this.h;
|
||||||
|
let c5 = [], p5 = {key:5};
|
||||||
|
var vn5 = h('span', p5, c5);
|
||||||
|
const slot6 = this.constructor.slots[context.__owl__.slotId + '_' + 'header'];
|
||||||
|
if (slot6) {
|
||||||
|
slot6.call(this, context.__owl__.scope, Object.assign({}, extra, {parentNode: c5, parent: extra.parent || context}));
|
||||||
|
} else {
|
||||||
|
c5.push({text: \`default content\`});
|
||||||
|
}
|
||||||
|
return vn5;
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`t-slot directive refs are properly bound in slots 1`] = `
|
||||||
|
"function anonymous(context, extra
|
||||||
|
) {
|
||||||
|
// Template name: \\"slot_footer_template\\"
|
||||||
|
context.__owl__.refs = context.__owl__.refs || {};
|
||||||
|
var h = this.h;
|
||||||
|
let c1 = extra.parentNode;
|
||||||
|
let c9 = [], p9 = {key:9,on:{}};
|
||||||
|
var vn9 = h('button', p9, c9);
|
||||||
|
c1.push(vn9);
|
||||||
|
extra.handlers['click' + 9] = extra.handlers['click' + 9] || function (e) {if (!context.__owl__.isMounted){return}const fn = context['doSomething'];if (fn) { fn.call(context, e); } else { context.doSomething; }};
|
||||||
|
p9.on['click'] = extra.handlers['click' + 9];
|
||||||
|
const ref10 = \`myButton\`;
|
||||||
|
p9.hook = {
|
||||||
|
create: (_, n) => {
|
||||||
|
context.__owl__.refs[ref10] = n.elm;
|
||||||
|
},
|
||||||
|
destroy: () => {
|
||||||
|
delete context.__owl__.refs[ref10];
|
||||||
|
},
|
||||||
|
};
|
||||||
|
c9.push({text: \`do something\`});
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`t-slot directive slots are rendered with proper context 1`] = `
|
||||||
|
"function anonymous(context, extra
|
||||||
|
) {
|
||||||
|
// Template name: \\"slot_footer_template\\"
|
||||||
|
var h = this.h;
|
||||||
|
let c1 = extra.parentNode;
|
||||||
|
let c9 = [], p9 = {key:9,on:{}};
|
||||||
|
var vn9 = h('button', p9, c9);
|
||||||
|
c1.push(vn9);
|
||||||
|
extra.handlers['click' + 9] = extra.handlers['click' + 9] || function (e) {if (!context.__owl__.isMounted){return}const fn = context['doSomething'];if (fn) { fn.call(context, e); } else { context.doSomething; }};
|
||||||
|
p9.on['click'] = extra.handlers['click' + 9];
|
||||||
|
c9.push({text: \`do something\`});
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`t-slot directive slots are rendered with proper context, part 2 1`] = `
|
||||||
|
"function anonymous(context, extra
|
||||||
|
) {
|
||||||
|
// Template name: \\"Link\\"
|
||||||
|
let scope = Object.create(context);
|
||||||
|
var h = this.h;
|
||||||
|
var _13 = scope['props'].to;
|
||||||
|
let c14 = [], p14 = {key:14,attrs:{href: _13}};
|
||||||
|
var vn14 = h('a', p14, c14);
|
||||||
|
const slot15 = this.constructor.slots[context.__owl__.slotId + '_' + 'default'];
|
||||||
|
if (slot15) {
|
||||||
|
slot15.call(this, context.__owl__.scope, Object.assign({}, extra, {parentNode: c14, parent: extra.parent || context}));
|
||||||
|
}
|
||||||
|
return vn14;
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`t-slot directive slots are rendered with proper context, part 2 2`] = `
|
||||||
|
"function anonymous(context, extra
|
||||||
|
) {
|
||||||
|
// Template name: \\"App\\"
|
||||||
|
let utils = this.constructor.utils;
|
||||||
|
let QWeb = this.constructor;
|
||||||
|
let parent = context;
|
||||||
|
let scope = Object.create(context);
|
||||||
|
var h = this.h;
|
||||||
|
let c1 = [], p1 = {key:1};
|
||||||
|
var vn1 = h('div', p1, c1);
|
||||||
|
let c2 = [], p2 = {key:2};
|
||||||
|
var vn2 = h('u', p2, c2);
|
||||||
|
c1.push(vn2);
|
||||||
|
var _3 = scope['state'].users;
|
||||||
|
if (!_3) { throw new Error('QWeb error: Invalid loop expression')}
|
||||||
|
var _4 = _5 = _3;
|
||||||
|
if (!(_3 instanceof Array)) {
|
||||||
|
_4 = Object.keys(_3);
|
||||||
|
_5 = Object.values(_3);
|
||||||
|
}
|
||||||
|
var _length4 = _4.length;
|
||||||
|
const _origScope6 = scope;
|
||||||
|
scope = Object.assign(Object.create(context), scope);
|
||||||
|
for (let i1 = 0; i1 < _length4; i1++) {
|
||||||
|
scope.user_first = i1 === 0
|
||||||
|
scope.user_last = i1 === _length4 - 1
|
||||||
|
scope.user_index = i1
|
||||||
|
scope.user = _4[i1]
|
||||||
|
scope.user_value = _5[i1]
|
||||||
|
const nodeKey7 = scope['user'].id;
|
||||||
|
let c8 = [], p8 = {key:nodeKey7};
|
||||||
|
var vn8 = h('li', p8, c8);
|
||||||
|
c2.push(vn8);
|
||||||
|
//COMPONENT
|
||||||
|
let k10 = \`__11__\` + nodeKey7;
|
||||||
|
let w9 = k10 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[k10]] : false;
|
||||||
|
let props9 = {to:'/user/'+scope['user'].id};
|
||||||
|
if (w9 && w9.__owl__.currentFiber && !w9.__owl__.vnode) {
|
||||||
|
w9.destroy();
|
||||||
|
w9 = false;
|
||||||
|
}
|
||||||
|
if (w9) {
|
||||||
|
w9.__updateProps(props9, extra.fiber, Object.assign(Object.create(context), scope));
|
||||||
|
let pvnode = w9.__owl__.pvnode;
|
||||||
|
c8.push(pvnode);
|
||||||
|
} else {
|
||||||
|
let componentKey9 = \`Link\`;
|
||||||
|
let W9 = context.constructor.components[componentKey9] || QWeb.components[componentKey9]|| scope['Link'];
|
||||||
|
if (!W9) {throw new Error('Cannot find the definition of component \\"' + componentKey9 + '\\"')}
|
||||||
|
w9 = new W9(parent, props9);
|
||||||
|
parent.__owl__.cmap[k10] = w9.__owl__.id;
|
||||||
|
w9.__owl__.slotId = 1;
|
||||||
|
let fiber = w9.__prepare(extra.fiber, Object.assign(Object.create(context), scope), () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
|
||||||
|
let pvnode = h('dummy', {key: k10, hook: {remove() {},destroy(vn) {w9.destroy();}}});
|
||||||
|
c8.push(pvnode);
|
||||||
|
w9.__owl__.pvnode = pvnode;
|
||||||
|
}
|
||||||
|
w9.__owl__.parentLastFiberId = extra.fiber.id;
|
||||||
|
}
|
||||||
|
scope = _origScope6;
|
||||||
|
return vn1;
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`t-slot directive slots are rendered with proper context, part 2 3`] = `
|
||||||
|
"function anonymous(context, extra
|
||||||
|
) {
|
||||||
|
// Template name: \\"slot_default_template\\"
|
||||||
|
let scope = Object.create(context);
|
||||||
|
var h = this.h;
|
||||||
|
let c8 = extra.parentNode;
|
||||||
|
c8.push({text: \`User \`});
|
||||||
|
var _12 = scope['user'].name;
|
||||||
|
if (_12 || _12 === 0) {
|
||||||
|
c8.push({text: _12});
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`t-slot directive slots are rendered with proper context, part 3 1`] = `
|
||||||
|
"function anonymous(context, extra
|
||||||
|
) {
|
||||||
|
// Template name: \\"Link\\"
|
||||||
|
let scope = Object.create(context);
|
||||||
|
var h = this.h;
|
||||||
|
var _12 = scope['props'].to;
|
||||||
|
let c13 = [], p13 = {key:13,attrs:{href: _12}};
|
||||||
|
var vn13 = h('a', p13, c13);
|
||||||
|
const slot14 = this.constructor.slots[context.__owl__.slotId + '_' + 'default'];
|
||||||
|
if (slot14) {
|
||||||
|
slot14.call(this, context.__owl__.scope, Object.assign({}, extra, {parentNode: c13, parent: extra.parent || context}));
|
||||||
|
}
|
||||||
|
return vn13;
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`t-slot directive slots are rendered with proper context, part 3 2`] = `
|
||||||
|
"function anonymous(context, extra
|
||||||
|
) {
|
||||||
|
// Template name: \\"App\\"
|
||||||
|
let utils = this.constructor.utils;
|
||||||
|
let QWeb = this.constructor;
|
||||||
|
let parent = context;
|
||||||
|
let scope = Object.create(context);
|
||||||
|
var h = this.h;
|
||||||
|
let c1 = [], p1 = {key:1};
|
||||||
|
var vn1 = h('div', p1, c1);
|
||||||
|
let c2 = [], p2 = {key:2};
|
||||||
|
var vn2 = h('u', p2, c2);
|
||||||
|
c1.push(vn2);
|
||||||
|
var _3 = scope['state'].users;
|
||||||
|
if (!_3) { throw new Error('QWeb error: Invalid loop expression')}
|
||||||
|
var _4 = _5 = _3;
|
||||||
|
if (!(_3 instanceof Array)) {
|
||||||
|
_4 = Object.keys(_3);
|
||||||
|
_5 = Object.values(_3);
|
||||||
|
}
|
||||||
|
var _length4 = _4.length;
|
||||||
|
const _origScope6 = scope;
|
||||||
|
scope = Object.assign(Object.create(context), scope);
|
||||||
|
for (let i1 = 0; i1 < _length4; i1++) {
|
||||||
|
scope.user_first = i1 === 0
|
||||||
|
scope.user_last = i1 === _length4 - 1
|
||||||
|
scope.user_index = i1
|
||||||
|
scope.user = _4[i1]
|
||||||
|
scope.user_value = _5[i1]
|
||||||
|
const nodeKey7 = scope['user'].id;
|
||||||
|
let c8 = [], p8 = {key:nodeKey7};
|
||||||
|
var vn8 = h('li', p8, c8);
|
||||||
|
c2.push(vn8);
|
||||||
|
scope.userdescr = 'User '+scope['user'].name;
|
||||||
|
//COMPONENT
|
||||||
|
let k10 = \`__11__\` + nodeKey7;
|
||||||
|
let w9 = k10 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[k10]] : false;
|
||||||
|
let props9 = {to:'/user/'+scope['user'].id};
|
||||||
|
if (w9 && w9.__owl__.currentFiber && !w9.__owl__.vnode) {
|
||||||
|
w9.destroy();
|
||||||
|
w9 = false;
|
||||||
|
}
|
||||||
|
if (w9) {
|
||||||
|
w9.__updateProps(props9, extra.fiber, Object.assign(Object.create(context), scope));
|
||||||
|
let pvnode = w9.__owl__.pvnode;
|
||||||
|
c8.push(pvnode);
|
||||||
|
} else {
|
||||||
|
let componentKey9 = \`Link\`;
|
||||||
|
let W9 = context.constructor.components[componentKey9] || QWeb.components[componentKey9]|| scope['Link'];
|
||||||
|
if (!W9) {throw new Error('Cannot find the definition of component \\"' + componentKey9 + '\\"')}
|
||||||
|
w9 = new W9(parent, props9);
|
||||||
|
parent.__owl__.cmap[k10] = w9.__owl__.id;
|
||||||
|
w9.__owl__.slotId = 1;
|
||||||
|
let fiber = w9.__prepare(extra.fiber, Object.assign(Object.create(context), scope), () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
|
||||||
|
let pvnode = h('dummy', {key: k10, hook: {remove() {},destroy(vn) {w9.destroy();}}});
|
||||||
|
c8.push(pvnode);
|
||||||
|
w9.__owl__.pvnode = pvnode;
|
||||||
|
}
|
||||||
|
w9.__owl__.parentLastFiberId = extra.fiber.id;
|
||||||
|
}
|
||||||
|
scope = _origScope6;
|
||||||
|
return vn1;
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`t-slot directive slots are rendered with proper context, part 3 3`] = `
|
||||||
|
"function anonymous(context, extra
|
||||||
|
) {
|
||||||
|
// Template name: \\"slot_default_template\\"
|
||||||
|
let scope = Object.create(context);
|
||||||
|
var h = this.h;
|
||||||
|
let c8 = extra.parentNode;
|
||||||
|
if (scope.userdescr || scope.userdescr === 0) {
|
||||||
|
c8.push({text: scope.userdescr});
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`t-slot directive slots are rendered with proper context, part 4 1`] = `
|
||||||
|
"function anonymous(context, extra
|
||||||
|
) {
|
||||||
|
// Template name: \\"App\\"
|
||||||
|
let utils = this.constructor.utils;
|
||||||
|
let QWeb = this.constructor;
|
||||||
|
let parent = context;
|
||||||
|
let scope = Object.create(context);
|
||||||
|
var h = this.h;
|
||||||
|
let c1 = [], p1 = {key:1};
|
||||||
|
var vn1 = h('div', p1, c1);
|
||||||
|
scope.userdescr = 'User '+scope['state'].user.name;
|
||||||
|
//COMPONENT
|
||||||
|
let k3 = \`__4__\`;
|
||||||
|
let w2 = k3 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[k3]] : false;
|
||||||
|
let props2 = {to:'/user/'+scope['state'].user.id};
|
||||||
|
if (w2 && w2.__owl__.currentFiber && !w2.__owl__.vnode) {
|
||||||
|
w2.destroy();
|
||||||
|
w2 = false;
|
||||||
|
}
|
||||||
|
if (w2) {
|
||||||
|
w2.__updateProps(props2, extra.fiber, Object.assign(Object.create(context), scope));
|
||||||
|
let pvnode = w2.__owl__.pvnode;
|
||||||
|
c1.push(pvnode);
|
||||||
|
} else {
|
||||||
|
let componentKey2 = \`Link\`;
|
||||||
|
let W2 = context.constructor.components[componentKey2] || QWeb.components[componentKey2]|| scope['Link'];
|
||||||
|
if (!W2) {throw new Error('Cannot find the definition of component \\"' + componentKey2 + '\\"')}
|
||||||
|
w2 = new W2(parent, props2);
|
||||||
|
parent.__owl__.cmap[k3] = w2.__owl__.id;
|
||||||
|
w2.__owl__.slotId = 1;
|
||||||
|
let fiber = w2.__prepare(extra.fiber, Object.assign(Object.create(context), scope), () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
|
||||||
|
let pvnode = h('dummy', {key: k3, hook: {remove() {},destroy(vn) {w2.destroy();}}});
|
||||||
|
c1.push(pvnode);
|
||||||
|
w2.__owl__.pvnode = pvnode;
|
||||||
|
}
|
||||||
|
w2.__owl__.parentLastFiberId = extra.fiber.id;
|
||||||
|
return vn1;
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`t-slot directive slots are rendered with proper context, part 4 2`] = `
|
||||||
|
"function anonymous(context, extra
|
||||||
|
) {
|
||||||
|
// Template name: \\"slot_default_template\\"
|
||||||
|
let scope = Object.create(context);
|
||||||
|
var h = this.h;
|
||||||
|
let c1 = extra.parentNode;
|
||||||
|
if (scope.userdescr || scope.userdescr === 0) {
|
||||||
|
c1.push({text: scope.userdescr});
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`t-slot directive template can just return a slot 1`] = `
|
||||||
|
"function anonymous(context, extra
|
||||||
|
) {
|
||||||
|
// Template name: \\"__template__2\\"
|
||||||
|
let utils = this.constructor.utils;
|
||||||
|
let result;
|
||||||
|
var h = this.h;
|
||||||
|
const slot8 = this.constructor.slots[context.__owl__.slotId + '_' + 'default'];
|
||||||
|
if (slot8) {
|
||||||
|
let children9= []
|
||||||
|
result = {}
|
||||||
|
slot8.call(this, context.__owl__.scope, Object.assign({}, extra, {parentNode: children9, parent: extra.parent || context}));
|
||||||
|
utils.defineProxy(result, children9[0]);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}"
|
||||||
|
`;
|
||||||
@@ -4000,845 +4000,6 @@ describe("can deduce template from name", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("t-slot directive", () => {
|
|
||||||
test("can define and call slots", async () => {
|
|
||||||
env.qweb.addTemplates(`
|
|
||||||
<templates>
|
|
||||||
<div t-name="Parent">
|
|
||||||
<Dialog>
|
|
||||||
<t t-set="header"><span>header</span></t>
|
|
||||||
<t t-set="footer"><span>footer</span></t>
|
|
||||||
</Dialog>
|
|
||||||
</div>
|
|
||||||
<div t-name="Dialog">
|
|
||||||
<div><t t-slot="header"/></div>
|
|
||||||
<div><t t-slot="footer"/></div>
|
|
||||||
</div>
|
|
||||||
</templates>
|
|
||||||
`);
|
|
||||||
class Dialog extends Widget {}
|
|
||||||
class Parent extends Widget {
|
|
||||||
static components = { Dialog };
|
|
||||||
}
|
|
||||||
const parent = new Parent();
|
|
||||||
await parent.mount(fixture);
|
|
||||||
|
|
||||||
expect(fixture.innerHTML).toBe(
|
|
||||||
"<div><div><div><span>header</span></div><div><span>footer</span></div></div></div>"
|
|
||||||
);
|
|
||||||
expect(env.qweb.templates.Parent.fn.toString()).toMatchSnapshot();
|
|
||||||
expect(env.qweb.templates.Dialog.fn.toString()).toMatchSnapshot();
|
|
||||||
expect(QWeb.slots["1_header"].toString()).toMatchSnapshot();
|
|
||||||
expect(QWeb.slots["1_footer"].toString()).toMatchSnapshot();
|
|
||||||
});
|
|
||||||
|
|
||||||
test("named slots can define a default content", async () => {
|
|
||||||
class Dialog extends Component<any, any> {
|
|
||||||
static template = xml`
|
|
||||||
<span>
|
|
||||||
<t t-slot="header">default content</t>
|
|
||||||
</span>`;
|
|
||||||
}
|
|
||||||
class Parent extends Component<any, any> {
|
|
||||||
static template = xml`<div><Dialog/></div>`;
|
|
||||||
static components = { Dialog };
|
|
||||||
}
|
|
||||||
const parent = new Parent();
|
|
||||||
await parent.mount(fixture);
|
|
||||||
|
|
||||||
expect(fixture.innerHTML).toBe("<div><span>default content</span></div>");
|
|
||||||
expect(env.qweb.templates[Dialog.template].fn.toString()).toMatchSnapshot();
|
|
||||||
});
|
|
||||||
|
|
||||||
test("dafault slots can define a default content", async () => {
|
|
||||||
class Dialog extends Component<any, any> {
|
|
||||||
static template = xml`
|
|
||||||
<span>
|
|
||||||
<t t-slot="default">default content</t>
|
|
||||||
</span>`;
|
|
||||||
}
|
|
||||||
class Parent extends Component<any, any> {
|
|
||||||
static template = xml`<div><Dialog/></div>`;
|
|
||||||
static components = { Dialog };
|
|
||||||
}
|
|
||||||
const parent = new Parent();
|
|
||||||
await parent.mount(fixture);
|
|
||||||
|
|
||||||
expect(fixture.innerHTML).toBe("<div><span>default content</span></div>");
|
|
||||||
expect(env.qweb.templates[Dialog.template].fn.toString()).toMatchSnapshot();
|
|
||||||
});
|
|
||||||
|
|
||||||
test("default content is not rendered if slot is provided", async () => {
|
|
||||||
class Dialog extends Component<any, any> {
|
|
||||||
static template = xml`
|
|
||||||
<span>
|
|
||||||
<t t-slot="default">default content</t>
|
|
||||||
</span>`;
|
|
||||||
}
|
|
||||||
class Parent extends Component<any, any> {
|
|
||||||
static template = xml`<div><Dialog>hey</Dialog></div>`;
|
|
||||||
static components = { Dialog };
|
|
||||||
}
|
|
||||||
const parent = new Parent();
|
|
||||||
await parent.mount(fixture);
|
|
||||||
|
|
||||||
expect(fixture.innerHTML).toBe("<div><span>hey</span></div>");
|
|
||||||
});
|
|
||||||
|
|
||||||
test("default content is not rendered if named slot is provided", async () => {
|
|
||||||
class Dialog extends Component<any, any> {
|
|
||||||
static template = xml`
|
|
||||||
<span>
|
|
||||||
<t t-slot="header">default content</t>
|
|
||||||
</span>`;
|
|
||||||
}
|
|
||||||
class Parent extends Component<any, any> {
|
|
||||||
static template = xml`<div><Dialog><t t-set="header">hey</t></Dialog></div>`;
|
|
||||||
static components = { Dialog };
|
|
||||||
}
|
|
||||||
const parent = new Parent();
|
|
||||||
await parent.mount(fixture);
|
|
||||||
|
|
||||||
expect(fixture.innerHTML).toBe("<div><span>hey</span></div>");
|
|
||||||
});
|
|
||||||
|
|
||||||
test("slots are rendered with proper context", async () => {
|
|
||||||
env.qweb.addTemplates(`
|
|
||||||
<templates>
|
|
||||||
<div t-name="Parent">
|
|
||||||
<span class="counter"><t t-esc="state.val"/></span>
|
|
||||||
<Dialog>
|
|
||||||
<t t-set="footer"><button t-on-click="doSomething">do something</button></t>
|
|
||||||
</Dialog>
|
|
||||||
</div>
|
|
||||||
<span t-name="Dialog"><t t-slot="footer"/></span>
|
|
||||||
</templates>
|
|
||||||
`);
|
|
||||||
class Dialog extends Widget {}
|
|
||||||
class Parent extends Widget {
|
|
||||||
static components = { Dialog };
|
|
||||||
state = useState({ val: 0 });
|
|
||||||
doSomething() {
|
|
||||||
this.state.val++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const parent = new Parent();
|
|
||||||
await parent.mount(fixture);
|
|
||||||
|
|
||||||
expect(fixture.innerHTML).toBe(
|
|
||||||
'<div><span class="counter">0</span><span><button>do something</button></span></div>'
|
|
||||||
);
|
|
||||||
|
|
||||||
fixture.querySelector("button")!.click();
|
|
||||||
await nextTick();
|
|
||||||
|
|
||||||
expect(fixture.innerHTML).toBe(
|
|
||||||
'<div><span class="counter">1</span><span><button>do something</button></span></div>'
|
|
||||||
);
|
|
||||||
expect(QWeb.slots["1_footer"].toString()).toMatchSnapshot();
|
|
||||||
});
|
|
||||||
|
|
||||||
test("slots are rendered with proper context, part 2", async () => {
|
|
||||||
env.qweb.addTemplates(`
|
|
||||||
<templates>
|
|
||||||
<a t-name="Link" t-att-href="props.to">
|
|
||||||
<t t-slot="default"/>
|
|
||||||
</a>
|
|
||||||
<div t-name="App">
|
|
||||||
<u><li t-foreach="state.users" t-as="user" t-key="user.id">
|
|
||||||
<Link to="'/user/' + user.id">User <t t-esc="user.name"/></Link>
|
|
||||||
</li></u>
|
|
||||||
</div>
|
|
||||||
</templates>
|
|
||||||
`);
|
|
||||||
class Link extends Widget {}
|
|
||||||
|
|
||||||
class App extends Widget {
|
|
||||||
state = useState({
|
|
||||||
users: [
|
|
||||||
{ id: 1, name: "Aaron" },
|
|
||||||
{ id: 2, name: "David" }
|
|
||||||
]
|
|
||||||
});
|
|
||||||
static components = { Link };
|
|
||||||
}
|
|
||||||
|
|
||||||
const app = new App();
|
|
||||||
await app.mount(fixture);
|
|
||||||
|
|
||||||
expect(fixture.innerHTML).toBe(
|
|
||||||
'<div><u><li><a href="/user/1">User Aaron</a></li><li><a href="/user/2">User David</a></li></u></div>'
|
|
||||||
);
|
|
||||||
expect(env.qweb.templates.Link.fn.toString()).toMatchSnapshot();
|
|
||||||
expect(env.qweb.templates.App.fn.toString()).toMatchSnapshot();
|
|
||||||
|
|
||||||
// test updateprops here
|
|
||||||
app.state.users[1].name = "Mathieu";
|
|
||||||
await nextTick();
|
|
||||||
expect(fixture.innerHTML).toBe(
|
|
||||||
'<div><u><li><a href="/user/1">User Aaron</a></li><li><a href="/user/2">User Mathieu</a></li></u></div>'
|
|
||||||
);
|
|
||||||
expect(QWeb.slots["1_default"].toString()).toMatchSnapshot();
|
|
||||||
});
|
|
||||||
|
|
||||||
test("slots are rendered with proper context, part 3", async () => {
|
|
||||||
env.qweb.addTemplates(`
|
|
||||||
<templates>
|
|
||||||
<a t-name="Link" t-att-href="props.to">
|
|
||||||
<t t-slot="default"/>
|
|
||||||
</a>
|
|
||||||
<div t-name="App">
|
|
||||||
<u><li t-foreach="state.users" t-as="user" t-key="user.id" >
|
|
||||||
<t t-set="userdescr" t-value="'User ' + user.name"/>
|
|
||||||
<Link to="'/user/' + user.id"><t t-esc="userdescr"/></Link>
|
|
||||||
</li></u>
|
|
||||||
</div>
|
|
||||||
</templates>
|
|
||||||
`);
|
|
||||||
class Link extends Widget {}
|
|
||||||
|
|
||||||
class App extends Widget {
|
|
||||||
state = useState({
|
|
||||||
users: [
|
|
||||||
{ id: 1, name: "Aaron" },
|
|
||||||
{ id: 2, name: "David" }
|
|
||||||
]
|
|
||||||
});
|
|
||||||
static components = { Link };
|
|
||||||
}
|
|
||||||
|
|
||||||
const app = new App();
|
|
||||||
await app.mount(fixture);
|
|
||||||
|
|
||||||
expect(fixture.innerHTML).toBe(
|
|
||||||
'<div><u><li><a href="/user/1">User Aaron</a></li><li><a href="/user/2">User David</a></li></u></div>'
|
|
||||||
);
|
|
||||||
expect(env.qweb.templates.Link.fn.toString()).toMatchSnapshot();
|
|
||||||
expect(env.qweb.templates.App.fn.toString()).toMatchSnapshot();
|
|
||||||
|
|
||||||
// test updateprops here
|
|
||||||
app.state.users[1].name = "Mathieu";
|
|
||||||
await nextTick();
|
|
||||||
expect(fixture.innerHTML).toBe(
|
|
||||||
'<div><u><li><a href="/user/1">User Aaron</a></li><li><a href="/user/2">User Mathieu</a></li></u></div>'
|
|
||||||
);
|
|
||||||
expect(QWeb.slots["1_default"].toString()).toMatchSnapshot();
|
|
||||||
});
|
|
||||||
|
|
||||||
test("slots are rendered with proper context, part 4", async () => {
|
|
||||||
env.qweb.addTemplates(`
|
|
||||||
<templates>
|
|
||||||
<a t-name="Link" t-att-href="props.to">
|
|
||||||
<t t-slot="default"/>
|
|
||||||
</a>
|
|
||||||
<div t-name="App">
|
|
||||||
<t t-set="userdescr" t-value="'User ' + state.user.name"/>
|
|
||||||
<Link to="'/user/' + state.user.id"><t t-esc="userdescr"/></Link>
|
|
||||||
</div>
|
|
||||||
</templates>
|
|
||||||
`);
|
|
||||||
class Link extends Widget {}
|
|
||||||
|
|
||||||
class App extends Widget {
|
|
||||||
state = useState({ user: { id: 1, name: "Aaron" } });
|
|
||||||
static components = { Link };
|
|
||||||
}
|
|
||||||
|
|
||||||
const app = new App();
|
|
||||||
await app.mount(fixture);
|
|
||||||
|
|
||||||
expect(fixture.innerHTML).toBe('<div><a href="/user/1">User Aaron</a></div>');
|
|
||||||
|
|
||||||
expect(env.qweb.templates.App.fn.toString()).toMatchSnapshot();
|
|
||||||
|
|
||||||
// test updateprops here
|
|
||||||
app.state.user.name = "David";
|
|
||||||
await nextTick();
|
|
||||||
expect(fixture.innerHTML).toBe('<div><a href="/user/1">User David</a></div>');
|
|
||||||
expect(QWeb.slots["1_default"].toString()).toMatchSnapshot();
|
|
||||||
});
|
|
||||||
|
|
||||||
test("refs are properly bound in slots", async () => {
|
|
||||||
class Dialog extends Widget {
|
|
||||||
static template = xml`<span><t t-slot="footer"/></span>`;
|
|
||||||
}
|
|
||||||
class Parent extends Widget {
|
|
||||||
static template = xml`
|
|
||||||
<div>
|
|
||||||
<span class="counter"><t t-esc="state.val"/></span>
|
|
||||||
<Dialog>
|
|
||||||
<t t-set="footer"><button t-ref="myButton" t-on-click="doSomething">do something</button></t>
|
|
||||||
</Dialog>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
static components = { Dialog };
|
|
||||||
state = useState({ val: 0 });
|
|
||||||
button = useRef("myButton");
|
|
||||||
doSomething() {
|
|
||||||
this.state.val++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const parent = new Parent();
|
|
||||||
await parent.mount(fixture);
|
|
||||||
|
|
||||||
expect(fixture.innerHTML).toBe(
|
|
||||||
'<div><span class="counter">0</span><span><button>do something</button></span></div>'
|
|
||||||
);
|
|
||||||
|
|
||||||
parent.button.el!.click();
|
|
||||||
await nextTick();
|
|
||||||
|
|
||||||
expect(fixture.innerHTML).toBe(
|
|
||||||
'<div><span class="counter">1</span><span><button>do something</button></span></div>'
|
|
||||||
);
|
|
||||||
expect(QWeb.slots["1_footer"].toString()).toMatchSnapshot();
|
|
||||||
});
|
|
||||||
|
|
||||||
test("content is the default slot", async () => {
|
|
||||||
env.qweb.addTemplates(`
|
|
||||||
<templates>
|
|
||||||
<div t-name="Parent">
|
|
||||||
<Dialog>
|
|
||||||
<span>sts rocks</span>
|
|
||||||
</Dialog>
|
|
||||||
</div>
|
|
||||||
<div t-name="Dialog"><t t-slot="default"/></div>
|
|
||||||
</templates>
|
|
||||||
`);
|
|
||||||
class Dialog extends Widget {}
|
|
||||||
class Parent extends Widget {
|
|
||||||
static components = { Dialog };
|
|
||||||
}
|
|
||||||
const parent = new Parent();
|
|
||||||
await parent.mount(fixture);
|
|
||||||
|
|
||||||
expect(fixture.innerHTML).toBe("<div><div><span>sts rocks</span></div></div>");
|
|
||||||
expect(QWeb.slots["1_default"].toString()).toMatchSnapshot();
|
|
||||||
});
|
|
||||||
|
|
||||||
test("default slot work with text nodes", async () => {
|
|
||||||
env.qweb.addTemplates(`
|
|
||||||
<templates>
|
|
||||||
<div t-name="Parent">
|
|
||||||
<Dialog>sts rocks</Dialog>
|
|
||||||
</div>
|
|
||||||
<div t-name="Dialog"><t t-slot="default"/></div>
|
|
||||||
</templates>
|
|
||||||
`);
|
|
||||||
class Dialog extends Widget {}
|
|
||||||
class Parent extends Widget {
|
|
||||||
static components = { Dialog };
|
|
||||||
}
|
|
||||||
const parent = new Parent();
|
|
||||||
await parent.mount(fixture);
|
|
||||||
|
|
||||||
expect(fixture.innerHTML).toBe("<div><div>sts rocks</div></div>");
|
|
||||||
expect(QWeb.slots["1_default"].toString()).toMatchSnapshot();
|
|
||||||
});
|
|
||||||
|
|
||||||
test("multiple roots are allowed in a named slot", async () => {
|
|
||||||
env.qweb.addTemplates(`
|
|
||||||
<templates>
|
|
||||||
<div t-name="Parent">
|
|
||||||
<Dialog>
|
|
||||||
<t t-set="content">
|
|
||||||
<span>sts</span>
|
|
||||||
<span>rocks</span>
|
|
||||||
</t>
|
|
||||||
</Dialog>
|
|
||||||
</div>
|
|
||||||
<div t-name="Dialog"><t t-slot="content"/></div>
|
|
||||||
</templates>
|
|
||||||
`);
|
|
||||||
class Dialog extends Widget {}
|
|
||||||
class Parent extends Widget {
|
|
||||||
static components = { Dialog };
|
|
||||||
}
|
|
||||||
const parent = new Parent();
|
|
||||||
await parent.mount(fixture);
|
|
||||||
|
|
||||||
expect(fixture.innerHTML).toBe("<div><div><span>sts</span><span>rocks</span></div></div>");
|
|
||||||
expect(QWeb.slots["1_content"].toString()).toMatchSnapshot();
|
|
||||||
});
|
|
||||||
|
|
||||||
test("multiple roots are allowed in a default slot", async () => {
|
|
||||||
env.qweb.addTemplates(`
|
|
||||||
<templates>
|
|
||||||
<div t-name="Parent">
|
|
||||||
<Dialog>
|
|
||||||
<span>sts</span>
|
|
||||||
<span>rocks</span>
|
|
||||||
</Dialog>
|
|
||||||
</div>
|
|
||||||
<div t-name="Dialog"><t t-slot="default"/></div>
|
|
||||||
</templates>
|
|
||||||
`);
|
|
||||||
class Dialog extends Widget {}
|
|
||||||
class Parent extends Widget {
|
|
||||||
static components = { Dialog };
|
|
||||||
}
|
|
||||||
const parent = new Parent();
|
|
||||||
await parent.mount(fixture);
|
|
||||||
|
|
||||||
expect(fixture.innerHTML).toBe("<div><div><span>sts</span><span>rocks</span></div></div>");
|
|
||||||
expect(QWeb.slots["1_default"].toString()).toMatchSnapshot();
|
|
||||||
});
|
|
||||||
|
|
||||||
test("missing slots are ignored", async () => {
|
|
||||||
env.qweb.addTemplates(`
|
|
||||||
<templates>
|
|
||||||
<div t-name="Parent">
|
|
||||||
<Dialog/>
|
|
||||||
</div>
|
|
||||||
<span t-name="Dialog">
|
|
||||||
<t t-slot="default"/>
|
|
||||||
<span>some content</span>
|
|
||||||
<t t-slot="footer"/>
|
|
||||||
</span>
|
|
||||||
</templates>
|
|
||||||
`);
|
|
||||||
class Dialog extends Widget {}
|
|
||||||
class Parent extends Widget {
|
|
||||||
static components = { Dialog };
|
|
||||||
}
|
|
||||||
const parent = new Parent();
|
|
||||||
await parent.mount(fixture);
|
|
||||||
|
|
||||||
expect(fixture.innerHTML).toBe("<div><span><span>some content</span></span></div>");
|
|
||||||
});
|
|
||||||
|
|
||||||
test("t-debug on a t-set (defining a slot)", async () => {
|
|
||||||
const consoleLog = console.log;
|
|
||||||
console.log = jest.fn();
|
|
||||||
|
|
||||||
env.qweb.addTemplates(`
|
|
||||||
<templates>
|
|
||||||
<div t-name="Parent">
|
|
||||||
<Dialog><t t-set="content" t-debug="">abc</t></Dialog>
|
|
||||||
</div>
|
|
||||||
<span t-name="Dialog">
|
|
||||||
<t t-slot="content"/>
|
|
||||||
</span>
|
|
||||||
</templates>
|
|
||||||
`);
|
|
||||||
class Dialog extends Widget {}
|
|
||||||
class Parent extends Widget {
|
|
||||||
static components = { Dialog };
|
|
||||||
}
|
|
||||||
const parent = new Parent();
|
|
||||||
await parent.mount(fixture);
|
|
||||||
expect(console.log).toHaveBeenCalledTimes(0);
|
|
||||||
console.log = consoleLog;
|
|
||||||
});
|
|
||||||
|
|
||||||
test("slot preserves properly parented relationship", async () => {
|
|
||||||
env.qweb.addTemplates(`
|
|
||||||
<templates>
|
|
||||||
<div t-name="Parent">
|
|
||||||
<Child>
|
|
||||||
<GrandChild/>
|
|
||||||
</Child>
|
|
||||||
</div>
|
|
||||||
<div t-name="Child"><t t-slot="default"/></div>
|
|
||||||
<div t-name="GrandChild">Grand Child</div>
|
|
||||||
</templates>
|
|
||||||
`);
|
|
||||||
class Child extends Widget {}
|
|
||||||
class GrandChild extends Widget {}
|
|
||||||
class Parent extends Widget {
|
|
||||||
static components = { Child, GrandChild };
|
|
||||||
}
|
|
||||||
const parent = new Parent();
|
|
||||||
await parent.mount(fixture);
|
|
||||||
|
|
||||||
expect(fixture.innerHTML).toBe("<div><div><div>Grand Child</div></div></div>");
|
|
||||||
|
|
||||||
const parentChildren = children(parent);
|
|
||||||
expect(parentChildren.length).toBe(1);
|
|
||||||
expect(parentChildren[0]).toBeInstanceOf(Child);
|
|
||||||
|
|
||||||
const childrenChildren = children(parentChildren[0]);
|
|
||||||
expect(childrenChildren.length).toBe(1);
|
|
||||||
expect(childrenChildren[0]).toBeInstanceOf(GrandChild);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("nested slots: evaluation context and parented relationship", async () => {
|
|
||||||
let slot;
|
|
||||||
class Slot extends Component<any, any> {
|
|
||||||
static template = xml`<span t-esc="props.val"/>`;
|
|
||||||
constructor(parent, props) {
|
|
||||||
super(parent, props);
|
|
||||||
slot = this;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
class GrandChild extends Component<any, any> {
|
|
||||||
static template = xml`<div><t t-slot="default"/></div>`;
|
|
||||||
}
|
|
||||||
class Child extends Component<any, any> {
|
|
||||||
static components = { GrandChild };
|
|
||||||
static template = xml`
|
|
||||||
<GrandChild>
|
|
||||||
<t t-slot="default"/>
|
|
||||||
</GrandChild>`;
|
|
||||||
}
|
|
||||||
class Parent extends Component<any, any> {
|
|
||||||
static components = { Child, Slot };
|
|
||||||
static template = xml`<Child><Slot val="state.val"/></Child>`;
|
|
||||||
state = useState({ val: 3 });
|
|
||||||
}
|
|
||||||
const parent = new Parent();
|
|
||||||
await parent.mount(fixture);
|
|
||||||
|
|
||||||
expect(fixture.innerHTML).toBe("<div><span>3</span></div>");
|
|
||||||
expect(slot.__owl__.parent).toBeInstanceOf(GrandChild);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("slot are properly rendered if inner props are changed", async () => {
|
|
||||||
env.qweb.addTemplates(`
|
|
||||||
<templates>
|
|
||||||
<div t-name="SomeComponent">
|
|
||||||
SC:<t t-esc="props.val"/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<div t-name="GenericComponent">
|
|
||||||
<t t-slot="default" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div t-name="App">
|
|
||||||
<button t-on-click="inc">Inc[<t t-esc="state.val"/>]</button>
|
|
||||||
<GenericComponent>
|
|
||||||
<SomeComponent val="state.val"/>
|
|
||||||
</GenericComponent>
|
|
||||||
</div>
|
|
||||||
</templates>
|
|
||||||
`);
|
|
||||||
class SomeComponent extends Widget {}
|
|
||||||
class GenericComponent extends Widget {}
|
|
||||||
class App extends Widget {
|
|
||||||
static components = { GenericComponent, SomeComponent };
|
|
||||||
state = useState({ val: 4 });
|
|
||||||
|
|
||||||
inc() {
|
|
||||||
this.state.val++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const app = new App();
|
|
||||||
await app.mount(fixture);
|
|
||||||
|
|
||||||
expect(fixture.innerHTML).toBe("<div><button>Inc[4]</button><div><div> SC:4</div></div></div>");
|
|
||||||
(<any>fixture.querySelector("button")).click();
|
|
||||||
await nextTick();
|
|
||||||
expect(fixture.innerHTML).toBe("<div><button>Inc[5]</button><div><div> SC:5</div></div></div>");
|
|
||||||
});
|
|
||||||
|
|
||||||
test("slots and wrapper components", async () => {
|
|
||||||
class Link extends Component<any, any> {
|
|
||||||
static template = xml`
|
|
||||||
<a href="abc">
|
|
||||||
<t t-slot="default"/>
|
|
||||||
</a>`;
|
|
||||||
}
|
|
||||||
|
|
||||||
class A extends Component<any, any> {
|
|
||||||
static template = xml`<Link>hey</Link>`;
|
|
||||||
static components = { Link: Link };
|
|
||||||
}
|
|
||||||
|
|
||||||
const a = new A();
|
|
||||||
await a.mount(fixture);
|
|
||||||
|
|
||||||
expect(fixture.innerHTML).toBe(`<a href="abc">hey</a>`);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("template can just return a slot", async () => {
|
|
||||||
class Child extends Widget {
|
|
||||||
static template = xml`<span><t t-esc="props.value"/></span>`;
|
|
||||||
}
|
|
||||||
class SlotComponent extends Widget {
|
|
||||||
static template = xml`<t t-slot="default"/>`;
|
|
||||||
}
|
|
||||||
|
|
||||||
class Parent extends Widget {
|
|
||||||
static template = xml`
|
|
||||||
<div>
|
|
||||||
<SlotComponent><Child value="state.value"/></SlotComponent>
|
|
||||||
</div>`;
|
|
||||||
static components = { SlotComponent, Child };
|
|
||||||
state = useState({ value: 3 });
|
|
||||||
}
|
|
||||||
const parent = new Parent();
|
|
||||||
await parent.mount(fixture);
|
|
||||||
expect(fixture.innerHTML).toBe("<div><span>3</span></div>");
|
|
||||||
|
|
||||||
expect(QWeb.TEMPLATES[SlotComponent.template].fn.toString()).toMatchSnapshot();
|
|
||||||
|
|
||||||
parent.state.value = 5;
|
|
||||||
await nextTick();
|
|
||||||
expect(fixture.innerHTML).toBe("<div><span>5</span></div>");
|
|
||||||
});
|
|
||||||
|
|
||||||
test("multiple slots containing components", async () => {
|
|
||||||
class C extends Component<any, any> {
|
|
||||||
static template = xml`<span><t t-esc="props.val"/></span>`;
|
|
||||||
}
|
|
||||||
class B extends Component<any, any> {
|
|
||||||
static template = xml`<div><t t-slot="s1"/><t t-slot="s2"/></div>`;
|
|
||||||
}
|
|
||||||
class A extends Component<any, any> {
|
|
||||||
static template = xml`
|
|
||||||
<B>
|
|
||||||
<t t-set="s1"><C val="1"/></t>
|
|
||||||
<t t-set="s2"><C val="2"/></t>
|
|
||||||
</B>`;
|
|
||||||
static components = { B, C };
|
|
||||||
}
|
|
||||||
|
|
||||||
const a = new A();
|
|
||||||
await a.mount(fixture);
|
|
||||||
|
|
||||||
expect(fixture.innerHTML).toBe(`<div><span>1</span><span>2</span></div>`);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("slots in t-foreach and re-rendering", async () => {
|
|
||||||
class Child extends Widget {
|
|
||||||
static template = xml`<span><t t-esc="state.val"/><t t-slot="default"/></span>`;
|
|
||||||
state = useState({ val: "A" });
|
|
||||||
mounted() {
|
|
||||||
this.state.val = "B";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
class Parent extends Widget {
|
|
||||||
static components = { Child };
|
|
||||||
static template = xml`
|
|
||||||
<div>
|
|
||||||
<t t-foreach="Array(2)" t-as="n" t-key="n_index">
|
|
||||||
<Child><t t-esc="n_index"/></Child>
|
|
||||||
</t>
|
|
||||||
</div>`;
|
|
||||||
}
|
|
||||||
const parent = new Parent();
|
|
||||||
await parent.mount(fixture);
|
|
||||||
expect(fixture.innerHTML).toBe("<div><span>A0</span><span>A1</span></div>");
|
|
||||||
|
|
||||||
await nextTick(); // wait for the changes triggered in mounted to be applied
|
|
||||||
expect(fixture.innerHTML).toBe("<div><span>B0</span><span>B1</span></div>");
|
|
||||||
});
|
|
||||||
|
|
||||||
test("slots in t-foreach with t-set and re-rendering", async () => {
|
|
||||||
class Child extends Widget {
|
|
||||||
static template = xml`
|
|
||||||
<span>
|
|
||||||
<t t-esc="state.val"/>
|
|
||||||
<t t-slot="default"/>
|
|
||||||
</span>`;
|
|
||||||
state = useState({ val: "A" });
|
|
||||||
mounted() {
|
|
||||||
this.state.val = "B";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
class ParentWidget extends Widget {
|
|
||||||
static components = { Child };
|
|
||||||
static template = xml`
|
|
||||||
<div>
|
|
||||||
<t t-foreach="Array(2)" t-as="n" t-key="n_index">
|
|
||||||
<t t-set="dummy" t-value="n_index"/>
|
|
||||||
<Child><t t-esc="dummy"/></Child>
|
|
||||||
</t>
|
|
||||||
</div>`;
|
|
||||||
}
|
|
||||||
|
|
||||||
const widget = new ParentWidget();
|
|
||||||
await widget.mount(fixture);
|
|
||||||
expect(fixture.innerHTML).toBe("<div><span>A0</span><span>A1</span></div>");
|
|
||||||
|
|
||||||
await nextTick(); // wait for changes triggered in mounted to be applied
|
|
||||||
expect(fixture.innerHTML).toBe("<div><span>B0</span><span>B1</span></div>");
|
|
||||||
});
|
|
||||||
|
|
||||||
test("nested slots in same template", async () => {
|
|
||||||
let child, child2, child3;
|
|
||||||
class Child extends Widget {
|
|
||||||
static template = xml`
|
|
||||||
<span id="c1">
|
|
||||||
<div>
|
|
||||||
<t t-slot="default"/>
|
|
||||||
</div>
|
|
||||||
</span>`;
|
|
||||||
constructor(parent, props) {
|
|
||||||
super(parent, props);
|
|
||||||
child = this;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
class Child2 extends Widget {
|
|
||||||
static template = xml`
|
|
||||||
<span id="c2">
|
|
||||||
<t t-slot="default"/>
|
|
||||||
</span>`;
|
|
||||||
constructor(parent, props) {
|
|
||||||
super(parent, props);
|
|
||||||
child2 = this;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
class Child3 extends Widget {
|
|
||||||
static template = xml`
|
|
||||||
<span>Child 3</span>`;
|
|
||||||
constructor(parent, props) {
|
|
||||||
super(parent, props);
|
|
||||||
child3 = this;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
class Parent extends Widget {
|
|
||||||
static components = { Child, Child2, Child3 };
|
|
||||||
static template = xml`
|
|
||||||
<span id="parent">
|
|
||||||
<Child>
|
|
||||||
<Child2>
|
|
||||||
<Child3/>
|
|
||||||
</Child2>
|
|
||||||
</Child>
|
|
||||||
</span>`;
|
|
||||||
}
|
|
||||||
|
|
||||||
const widget = new Parent();
|
|
||||||
await widget.mount(fixture);
|
|
||||||
expect(fixture.innerHTML).toBe(
|
|
||||||
'<span id="parent"><span id="c1"><div><span id="c2"><span>Child 3</span></span></div></span></span>'
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(child3.__owl__.parent).toStrictEqual(child2);
|
|
||||||
expect(child2.__owl__.parent).toStrictEqual(child);
|
|
||||||
expect(child.__owl__.parent).toStrictEqual(widget);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("t-slot nested within another slot", async () => {
|
|
||||||
let portal, modal, child3;
|
|
||||||
class Child3 extends Widget {
|
|
||||||
static template = xml`
|
|
||||||
<span>Child 3</span>`;
|
|
||||||
constructor(parent, props) {
|
|
||||||
super(parent, props);
|
|
||||||
child3 = this;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
class Modal extends Widget {
|
|
||||||
static template = xml`
|
|
||||||
<span id="modal">
|
|
||||||
<t t-slot="default"/>
|
|
||||||
</span>`;
|
|
||||||
constructor(parent, props) {
|
|
||||||
super(parent, props);
|
|
||||||
modal = this;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
class Portal extends Widget {
|
|
||||||
static template = xml`
|
|
||||||
<span id="portal">
|
|
||||||
<t t-slot="default"/>
|
|
||||||
</span>`;
|
|
||||||
constructor(parent, props) {
|
|
||||||
super(parent, props);
|
|
||||||
portal = this;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
class Dialog extends Widget {
|
|
||||||
static components = { Modal, Portal };
|
|
||||||
static template = xml`
|
|
||||||
<span id="c2">
|
|
||||||
<Modal>
|
|
||||||
<Portal>
|
|
||||||
<t t-slot="default"/>
|
|
||||||
</Portal>
|
|
||||||
</Modal>
|
|
||||||
</span>`;
|
|
||||||
}
|
|
||||||
class Parent extends Widget {
|
|
||||||
static components = { Child3, Dialog };
|
|
||||||
static template = xml`
|
|
||||||
<span id="c1">
|
|
||||||
<Dialog>
|
|
||||||
<Child3/>
|
|
||||||
</Dialog>
|
|
||||||
</span>`;
|
|
||||||
}
|
|
||||||
|
|
||||||
const widget = new Parent();
|
|
||||||
await widget.mount(fixture);
|
|
||||||
expect(fixture.innerHTML).toBe(
|
|
||||||
'<span id="c1"><span id="c2"><span id="modal"><span id="portal"><span>Child 3</span></span></span></span></span>'
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(child3.__owl__.parent).toStrictEqual(portal);
|
|
||||||
expect(portal.__owl__.parent).toStrictEqual(modal);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("t-slot supports many instances", async () => {
|
|
||||||
let child3;
|
|
||||||
class Child3 extends Widget {
|
|
||||||
static template = xml`
|
|
||||||
<span>Child 3</span>`;
|
|
||||||
constructor(parent, props) {
|
|
||||||
super(parent, props);
|
|
||||||
child3 = this;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
class Dialog extends Widget {
|
|
||||||
static template = xml`
|
|
||||||
<span id="c2">
|
|
||||||
<t t-slot="default"/>
|
|
||||||
</span>`;
|
|
||||||
}
|
|
||||||
class Parent extends Widget {
|
|
||||||
static components = { Child3, Dialog };
|
|
||||||
static template = xml`
|
|
||||||
<span id="c1">
|
|
||||||
<Dialog>
|
|
||||||
<Child3 val="state.lol"/>
|
|
||||||
</Dialog>
|
|
||||||
</span>`;
|
|
||||||
state = { lol: "k" };
|
|
||||||
}
|
|
||||||
|
|
||||||
const widget = new Parent();
|
|
||||||
await widget.mount(fixture);
|
|
||||||
expect(child3.props.val).toBe("k");
|
|
||||||
|
|
||||||
const widget_1 = new Parent();
|
|
||||||
widget_1.state.lol = "m";
|
|
||||||
await widget_1.mount(fixture);
|
|
||||||
expect(child3.props.val).toBe("m");
|
|
||||||
});
|
|
||||||
|
|
||||||
test("slots in slots, with vars", async () => {
|
|
||||||
class B extends Component<any, any> {
|
|
||||||
static template = xml`<span><t t-slot="default"/></span>`;
|
|
||||||
}
|
|
||||||
class A extends Component<any, any> {
|
|
||||||
static template = xml`
|
|
||||||
<div>
|
|
||||||
<B>
|
|
||||||
<t t-slot="default"/>
|
|
||||||
</B>
|
|
||||||
</div>`;
|
|
||||||
static components = { B };
|
|
||||||
}
|
|
||||||
class Parent extends Component<any, any> {
|
|
||||||
static template = xml`
|
|
||||||
<div>
|
|
||||||
<t t-set="test" t-value="state.name"/>
|
|
||||||
<A>
|
|
||||||
<p>hey<t t-esc="test"/></p>
|
|
||||||
</A>
|
|
||||||
</div>`;
|
|
||||||
static components = { A };
|
|
||||||
state = { name: "aaron" };
|
|
||||||
}
|
|
||||||
|
|
||||||
const parent = new Parent();
|
|
||||||
await parent.mount(fixture);
|
|
||||||
expect(fixture.innerHTML).toBe("<div><div><span><p>heyaaron</p></span></div></div>");
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("t-model directive", () => {
|
describe("t-model directive", () => {
|
||||||
test("basic use, on an input", async () => {
|
test("basic use, on an input", async () => {
|
||||||
|
|||||||
@@ -0,0 +1,887 @@
|
|||||||
|
import { Component, Env } from "../../src/component/component";
|
||||||
|
import { QWeb } from "../../src/qweb/qweb";
|
||||||
|
import { xml } from "../../src/tags";
|
||||||
|
import { useState, useRef } from "../../src/hooks";
|
||||||
|
import {
|
||||||
|
makeTestFixture,
|
||||||
|
makeTestEnv,
|
||||||
|
nextTick,
|
||||||
|
} from "../helpers";
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// Setup and helpers
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// We create before each test:
|
||||||
|
// - fixture: a div, appended to the DOM, intended to be the target of dom
|
||||||
|
// manipulations. Note that it is removed after each test.
|
||||||
|
// - env: a WEnv, necessary to create new components
|
||||||
|
|
||||||
|
let fixture: HTMLElement;
|
||||||
|
let env: Env;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = makeTestFixture();
|
||||||
|
env = makeTestEnv();
|
||||||
|
env.qweb.addTemplate("Component<any,any>", "<div></div>");
|
||||||
|
env.qweb.addTemplate(
|
||||||
|
"Counter",
|
||||||
|
`<div><t t-esc="state.counter"/><button t-on-click="inc">Inc</button></div>`
|
||||||
|
);
|
||||||
|
env.qweb.addTemplate("WidgetA", `<div>Hello<t t-component="b"/></div>`);
|
||||||
|
env.qweb.addTemplate("WidgetB", `<div>world</div>`);
|
||||||
|
Component.env = env;
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
fixture.remove();
|
||||||
|
});
|
||||||
|
|
||||||
|
function children(w: Component<any,any>): Component<any,any>[] {
|
||||||
|
const childrenMap = w.__owl__.children;
|
||||||
|
return Object.keys(childrenMap).map(id => childrenMap[id]);
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// Tests
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
describe("t-slot directive", () => {
|
||||||
|
test("can define and call slots", async () => {
|
||||||
|
env.qweb.addTemplates(`
|
||||||
|
<templates>
|
||||||
|
<div t-name="Parent">
|
||||||
|
<Dialog>
|
||||||
|
<t t-set="header"><span>header</span></t>
|
||||||
|
<t t-set="footer"><span>footer</span></t>
|
||||||
|
</Dialog>
|
||||||
|
</div>
|
||||||
|
<div t-name="Dialog">
|
||||||
|
<div><t t-slot="header"/></div>
|
||||||
|
<div><t t-slot="footer"/></div>
|
||||||
|
</div>
|
||||||
|
</templates>
|
||||||
|
`);
|
||||||
|
class Dialog extends Component<any,any> {}
|
||||||
|
class Parent extends Component<any,any> {
|
||||||
|
static components = { Dialog };
|
||||||
|
}
|
||||||
|
const parent = new Parent();
|
||||||
|
await parent.mount(fixture);
|
||||||
|
|
||||||
|
expect(fixture.innerHTML).toBe(
|
||||||
|
"<div><div><div><span>header</span></div><div><span>footer</span></div></div></div>"
|
||||||
|
);
|
||||||
|
expect(env.qweb.templates.Parent.fn.toString()).toMatchSnapshot();
|
||||||
|
expect(env.qweb.templates.Dialog.fn.toString()).toMatchSnapshot();
|
||||||
|
expect(QWeb.slots["1_header"].toString()).toMatchSnapshot();
|
||||||
|
expect(QWeb.slots["1_footer"].toString()).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("named slots can define a default content", async () => {
|
||||||
|
class Dialog extends Component<any, any> {
|
||||||
|
static template = xml`
|
||||||
|
<span>
|
||||||
|
<t t-slot="header">default content</t>
|
||||||
|
</span>`;
|
||||||
|
}
|
||||||
|
class Parent extends Component<any, any> {
|
||||||
|
static template = xml`<div><Dialog/></div>`;
|
||||||
|
static components = { Dialog };
|
||||||
|
}
|
||||||
|
const parent = new Parent();
|
||||||
|
await parent.mount(fixture);
|
||||||
|
|
||||||
|
expect(fixture.innerHTML).toBe("<div><span>default content</span></div>");
|
||||||
|
expect(env.qweb.templates[Dialog.template].fn.toString()).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("dafault slots can define a default content", async () => {
|
||||||
|
class Dialog extends Component<any, any> {
|
||||||
|
static template = xml`
|
||||||
|
<span>
|
||||||
|
<t t-slot="default">default content</t>
|
||||||
|
</span>`;
|
||||||
|
}
|
||||||
|
class Parent extends Component<any, any> {
|
||||||
|
static template = xml`<div><Dialog/></div>`;
|
||||||
|
static components = { Dialog };
|
||||||
|
}
|
||||||
|
const parent = new Parent();
|
||||||
|
await parent.mount(fixture);
|
||||||
|
|
||||||
|
expect(fixture.innerHTML).toBe("<div><span>default content</span></div>");
|
||||||
|
expect(env.qweb.templates[Dialog.template].fn.toString()).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("default content is not rendered if slot is provided", async () => {
|
||||||
|
class Dialog extends Component<any, any> {
|
||||||
|
static template = xml`
|
||||||
|
<span>
|
||||||
|
<t t-slot="default">default content</t>
|
||||||
|
</span>`;
|
||||||
|
}
|
||||||
|
class Parent extends Component<any, any> {
|
||||||
|
static template = xml`<div><Dialog>hey</Dialog></div>`;
|
||||||
|
static components = { Dialog };
|
||||||
|
}
|
||||||
|
const parent = new Parent();
|
||||||
|
await parent.mount(fixture);
|
||||||
|
|
||||||
|
expect(fixture.innerHTML).toBe("<div><span>hey</span></div>");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("default content is not rendered if named slot is provided", async () => {
|
||||||
|
class Dialog extends Component<any, any> {
|
||||||
|
static template = xml`
|
||||||
|
<span>
|
||||||
|
<t t-slot="header">default content</t>
|
||||||
|
</span>`;
|
||||||
|
}
|
||||||
|
class Parent extends Component<any, any> {
|
||||||
|
static template = xml`<div><Dialog><t t-set="header">hey</t></Dialog></div>`;
|
||||||
|
static components = { Dialog };
|
||||||
|
}
|
||||||
|
const parent = new Parent();
|
||||||
|
await parent.mount(fixture);
|
||||||
|
|
||||||
|
expect(fixture.innerHTML).toBe("<div><span>hey</span></div>");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("slots are rendered with proper context", async () => {
|
||||||
|
env.qweb.addTemplates(`
|
||||||
|
<templates>
|
||||||
|
<div t-name="Parent">
|
||||||
|
<span class="counter"><t t-esc="state.val"/></span>
|
||||||
|
<Dialog>
|
||||||
|
<t t-set="footer"><button t-on-click="doSomething">do something</button></t>
|
||||||
|
</Dialog>
|
||||||
|
</div>
|
||||||
|
<span t-name="Dialog"><t t-slot="footer"/></span>
|
||||||
|
</templates>
|
||||||
|
`);
|
||||||
|
class Dialog extends Component<any,any> {}
|
||||||
|
class Parent extends Component<any,any> {
|
||||||
|
static components = { Dialog };
|
||||||
|
state = useState({ val: 0 });
|
||||||
|
doSomething() {
|
||||||
|
this.state.val++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const parent = new Parent();
|
||||||
|
await parent.mount(fixture);
|
||||||
|
|
||||||
|
expect(fixture.innerHTML).toBe(
|
||||||
|
'<div><span class="counter">0</span><span><button>do something</button></span></div>'
|
||||||
|
);
|
||||||
|
|
||||||
|
fixture.querySelector("button")!.click();
|
||||||
|
await nextTick();
|
||||||
|
|
||||||
|
expect(fixture.innerHTML).toBe(
|
||||||
|
'<div><span class="counter">1</span><span><button>do something</button></span></div>'
|
||||||
|
);
|
||||||
|
expect(QWeb.slots["1_footer"].toString()).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("slots are rendered with proper context, part 2", async () => {
|
||||||
|
env.qweb.addTemplates(`
|
||||||
|
<templates>
|
||||||
|
<a t-name="Link" t-att-href="props.to">
|
||||||
|
<t t-slot="default"/>
|
||||||
|
</a>
|
||||||
|
<div t-name="App">
|
||||||
|
<u><li t-foreach="state.users" t-as="user" t-key="user.id">
|
||||||
|
<Link to="'/user/' + user.id">User <t t-esc="user.name"/></Link>
|
||||||
|
</li></u>
|
||||||
|
</div>
|
||||||
|
</templates>
|
||||||
|
`);
|
||||||
|
class Link extends Component<any,any> {}
|
||||||
|
|
||||||
|
class App extends Component<any,any> {
|
||||||
|
state = useState({
|
||||||
|
users: [
|
||||||
|
{ id: 1, name: "Aaron" },
|
||||||
|
{ id: 2, name: "David" }
|
||||||
|
]
|
||||||
|
});
|
||||||
|
static components = { Link };
|
||||||
|
}
|
||||||
|
|
||||||
|
const app = new App();
|
||||||
|
await app.mount(fixture);
|
||||||
|
|
||||||
|
expect(fixture.innerHTML).toBe(
|
||||||
|
'<div><u><li><a href="/user/1">User Aaron</a></li><li><a href="/user/2">User David</a></li></u></div>'
|
||||||
|
);
|
||||||
|
expect(env.qweb.templates.Link.fn.toString()).toMatchSnapshot();
|
||||||
|
expect(env.qweb.templates.App.fn.toString()).toMatchSnapshot();
|
||||||
|
|
||||||
|
// test updateprops here
|
||||||
|
app.state.users[1].name = "Mathieu";
|
||||||
|
await nextTick();
|
||||||
|
expect(fixture.innerHTML).toBe(
|
||||||
|
'<div><u><li><a href="/user/1">User Aaron</a></li><li><a href="/user/2">User Mathieu</a></li></u></div>'
|
||||||
|
);
|
||||||
|
expect(QWeb.slots["1_default"].toString()).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("slots are rendered with proper context, part 3", async () => {
|
||||||
|
env.qweb.addTemplates(`
|
||||||
|
<templates>
|
||||||
|
<a t-name="Link" t-att-href="props.to">
|
||||||
|
<t t-slot="default"/>
|
||||||
|
</a>
|
||||||
|
<div t-name="App">
|
||||||
|
<u><li t-foreach="state.users" t-as="user" t-key="user.id" >
|
||||||
|
<t t-set="userdescr" t-value="'User ' + user.name"/>
|
||||||
|
<Link to="'/user/' + user.id"><t t-esc="userdescr"/></Link>
|
||||||
|
</li></u>
|
||||||
|
</div>
|
||||||
|
</templates>
|
||||||
|
`);
|
||||||
|
class Link extends Component<any,any> {}
|
||||||
|
|
||||||
|
class App extends Component<any,any> {
|
||||||
|
state = useState({
|
||||||
|
users: [
|
||||||
|
{ id: 1, name: "Aaron" },
|
||||||
|
{ id: 2, name: "David" }
|
||||||
|
]
|
||||||
|
});
|
||||||
|
static components = { Link };
|
||||||
|
}
|
||||||
|
|
||||||
|
const app = new App();
|
||||||
|
await app.mount(fixture);
|
||||||
|
|
||||||
|
expect(fixture.innerHTML).toBe(
|
||||||
|
'<div><u><li><a href="/user/1">User Aaron</a></li><li><a href="/user/2">User David</a></li></u></div>'
|
||||||
|
);
|
||||||
|
expect(env.qweb.templates.Link.fn.toString()).toMatchSnapshot();
|
||||||
|
expect(env.qweb.templates.App.fn.toString()).toMatchSnapshot();
|
||||||
|
|
||||||
|
// test updateprops here
|
||||||
|
app.state.users[1].name = "Mathieu";
|
||||||
|
await nextTick();
|
||||||
|
expect(fixture.innerHTML).toBe(
|
||||||
|
'<div><u><li><a href="/user/1">User Aaron</a></li><li><a href="/user/2">User Mathieu</a></li></u></div>'
|
||||||
|
);
|
||||||
|
expect(QWeb.slots["1_default"].toString()).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("slots are rendered with proper context, part 4", async () => {
|
||||||
|
env.qweb.addTemplates(`
|
||||||
|
<templates>
|
||||||
|
<a t-name="Link" t-att-href="props.to">
|
||||||
|
<t t-slot="default"/>
|
||||||
|
</a>
|
||||||
|
<div t-name="App">
|
||||||
|
<t t-set="userdescr" t-value="'User ' + state.user.name"/>
|
||||||
|
<Link to="'/user/' + state.user.id"><t t-esc="userdescr"/></Link>
|
||||||
|
</div>
|
||||||
|
</templates>
|
||||||
|
`);
|
||||||
|
class Link extends Component<any,any> {}
|
||||||
|
|
||||||
|
class App extends Component<any,any> {
|
||||||
|
state = useState({ user: { id: 1, name: "Aaron" } });
|
||||||
|
static components = { Link };
|
||||||
|
}
|
||||||
|
|
||||||
|
const app = new App();
|
||||||
|
await app.mount(fixture);
|
||||||
|
|
||||||
|
expect(fixture.innerHTML).toBe('<div><a href="/user/1">User Aaron</a></div>');
|
||||||
|
|
||||||
|
expect(env.qweb.templates.App.fn.toString()).toMatchSnapshot();
|
||||||
|
|
||||||
|
// test updateprops here
|
||||||
|
app.state.user.name = "David";
|
||||||
|
await nextTick();
|
||||||
|
expect(fixture.innerHTML).toBe('<div><a href="/user/1">User David</a></div>');
|
||||||
|
expect(QWeb.slots["1_default"].toString()).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("refs are properly bound in slots", async () => {
|
||||||
|
class Dialog extends Component<any,any> {
|
||||||
|
static template = xml`<span><t t-slot="footer"/></span>`;
|
||||||
|
}
|
||||||
|
class Parent extends Component<any,any> {
|
||||||
|
static template = xml`
|
||||||
|
<div>
|
||||||
|
<span class="counter"><t t-esc="state.val"/></span>
|
||||||
|
<Dialog>
|
||||||
|
<t t-set="footer"><button t-ref="myButton" t-on-click="doSomething">do something</button></t>
|
||||||
|
</Dialog>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
static components = { Dialog };
|
||||||
|
state = useState({ val: 0 });
|
||||||
|
button = useRef("myButton");
|
||||||
|
doSomething() {
|
||||||
|
this.state.val++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const parent = new Parent();
|
||||||
|
await parent.mount(fixture);
|
||||||
|
|
||||||
|
expect(fixture.innerHTML).toBe(
|
||||||
|
'<div><span class="counter">0</span><span><button>do something</button></span></div>'
|
||||||
|
);
|
||||||
|
|
||||||
|
parent.button.el!.click();
|
||||||
|
await nextTick();
|
||||||
|
|
||||||
|
expect(fixture.innerHTML).toBe(
|
||||||
|
'<div><span class="counter">1</span><span><button>do something</button></span></div>'
|
||||||
|
);
|
||||||
|
expect(QWeb.slots["1_footer"].toString()).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("content is the default slot", async () => {
|
||||||
|
env.qweb.addTemplates(`
|
||||||
|
<templates>
|
||||||
|
<div t-name="Parent">
|
||||||
|
<Dialog>
|
||||||
|
<span>sts rocks</span>
|
||||||
|
</Dialog>
|
||||||
|
</div>
|
||||||
|
<div t-name="Dialog"><t t-slot="default"/></div>
|
||||||
|
</templates>
|
||||||
|
`);
|
||||||
|
class Dialog extends Component<any,any> {}
|
||||||
|
class Parent extends Component<any,any> {
|
||||||
|
static components = { Dialog };
|
||||||
|
}
|
||||||
|
const parent = new Parent();
|
||||||
|
await parent.mount(fixture);
|
||||||
|
|
||||||
|
expect(fixture.innerHTML).toBe("<div><div><span>sts rocks</span></div></div>");
|
||||||
|
expect(QWeb.slots["1_default"].toString()).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("default slot work with text nodes", async () => {
|
||||||
|
env.qweb.addTemplates(`
|
||||||
|
<templates>
|
||||||
|
<div t-name="Parent">
|
||||||
|
<Dialog>sts rocks</Dialog>
|
||||||
|
</div>
|
||||||
|
<div t-name="Dialog"><t t-slot="default"/></div>
|
||||||
|
</templates>
|
||||||
|
`);
|
||||||
|
class Dialog extends Component<any,any> {}
|
||||||
|
class Parent extends Component<any,any> {
|
||||||
|
static components = { Dialog };
|
||||||
|
}
|
||||||
|
const parent = new Parent();
|
||||||
|
await parent.mount(fixture);
|
||||||
|
|
||||||
|
expect(fixture.innerHTML).toBe("<div><div>sts rocks</div></div>");
|
||||||
|
expect(QWeb.slots["1_default"].toString()).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("multiple roots are allowed in a named slot", async () => {
|
||||||
|
env.qweb.addTemplates(`
|
||||||
|
<templates>
|
||||||
|
<div t-name="Parent">
|
||||||
|
<Dialog>
|
||||||
|
<t t-set="content">
|
||||||
|
<span>sts</span>
|
||||||
|
<span>rocks</span>
|
||||||
|
</t>
|
||||||
|
</Dialog>
|
||||||
|
</div>
|
||||||
|
<div t-name="Dialog"><t t-slot="content"/></div>
|
||||||
|
</templates>
|
||||||
|
`);
|
||||||
|
class Dialog extends Component<any,any> {}
|
||||||
|
class Parent extends Component<any,any> {
|
||||||
|
static components = { Dialog };
|
||||||
|
}
|
||||||
|
const parent = new Parent();
|
||||||
|
await parent.mount(fixture);
|
||||||
|
|
||||||
|
expect(fixture.innerHTML).toBe("<div><div><span>sts</span><span>rocks</span></div></div>");
|
||||||
|
expect(QWeb.slots["1_content"].toString()).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("multiple roots are allowed in a default slot", async () => {
|
||||||
|
env.qweb.addTemplates(`
|
||||||
|
<templates>
|
||||||
|
<div t-name="Parent">
|
||||||
|
<Dialog>
|
||||||
|
<span>sts</span>
|
||||||
|
<span>rocks</span>
|
||||||
|
</Dialog>
|
||||||
|
</div>
|
||||||
|
<div t-name="Dialog"><t t-slot="default"/></div>
|
||||||
|
</templates>
|
||||||
|
`);
|
||||||
|
class Dialog extends Component<any,any> {}
|
||||||
|
class Parent extends Component<any,any> {
|
||||||
|
static components = { Dialog };
|
||||||
|
}
|
||||||
|
const parent = new Parent();
|
||||||
|
await parent.mount(fixture);
|
||||||
|
|
||||||
|
expect(fixture.innerHTML).toBe("<div><div><span>sts</span><span>rocks</span></div></div>");
|
||||||
|
expect(QWeb.slots["1_default"].toString()).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("missing slots are ignored", async () => {
|
||||||
|
env.qweb.addTemplates(`
|
||||||
|
<templates>
|
||||||
|
<div t-name="Parent">
|
||||||
|
<Dialog/>
|
||||||
|
</div>
|
||||||
|
<span t-name="Dialog">
|
||||||
|
<t t-slot="default"/>
|
||||||
|
<span>some content</span>
|
||||||
|
<t t-slot="footer"/>
|
||||||
|
</span>
|
||||||
|
</templates>
|
||||||
|
`);
|
||||||
|
class Dialog extends Component<any,any> {}
|
||||||
|
class Parent extends Component<any,any> {
|
||||||
|
static components = { Dialog };
|
||||||
|
}
|
||||||
|
const parent = new Parent();
|
||||||
|
await parent.mount(fixture);
|
||||||
|
|
||||||
|
expect(fixture.innerHTML).toBe("<div><span><span>some content</span></span></div>");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("t-debug on a t-set (defining a slot)", async () => {
|
||||||
|
const consoleLog = console.log;
|
||||||
|
console.log = jest.fn();
|
||||||
|
|
||||||
|
env.qweb.addTemplates(`
|
||||||
|
<templates>
|
||||||
|
<div t-name="Parent">
|
||||||
|
<Dialog><t t-set="content" t-debug="">abc</t></Dialog>
|
||||||
|
</div>
|
||||||
|
<span t-name="Dialog">
|
||||||
|
<t t-slot="content"/>
|
||||||
|
</span>
|
||||||
|
</templates>
|
||||||
|
`);
|
||||||
|
class Dialog extends Component<any,any> {}
|
||||||
|
class Parent extends Component<any,any> {
|
||||||
|
static components = { Dialog };
|
||||||
|
}
|
||||||
|
const parent = new Parent();
|
||||||
|
await parent.mount(fixture);
|
||||||
|
expect(console.log).toHaveBeenCalledTimes(0);
|
||||||
|
console.log = consoleLog;
|
||||||
|
});
|
||||||
|
|
||||||
|
test("slot preserves properly parented relationship", async () => {
|
||||||
|
env.qweb.addTemplates(`
|
||||||
|
<templates>
|
||||||
|
<div t-name="Parent">
|
||||||
|
<Child>
|
||||||
|
<GrandChild/>
|
||||||
|
</Child>
|
||||||
|
</div>
|
||||||
|
<div t-name="Child"><t t-slot="default"/></div>
|
||||||
|
<div t-name="GrandChild">Grand Child</div>
|
||||||
|
</templates>
|
||||||
|
`);
|
||||||
|
class Child extends Component<any,any> {}
|
||||||
|
class GrandChild extends Component<any,any> {}
|
||||||
|
class Parent extends Component<any,any> {
|
||||||
|
static components = { Child, GrandChild };
|
||||||
|
}
|
||||||
|
const parent = new Parent();
|
||||||
|
await parent.mount(fixture);
|
||||||
|
|
||||||
|
expect(fixture.innerHTML).toBe("<div><div><div>Grand Child</div></div></div>");
|
||||||
|
|
||||||
|
const parentChildren = children(parent);
|
||||||
|
expect(parentChildren.length).toBe(1);
|
||||||
|
expect(parentChildren[0]).toBeInstanceOf(Child);
|
||||||
|
|
||||||
|
const childrenChildren = children(parentChildren[0]);
|
||||||
|
expect(childrenChildren.length).toBe(1);
|
||||||
|
expect(childrenChildren[0]).toBeInstanceOf(GrandChild);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("nested slots: evaluation context and parented relationship", async () => {
|
||||||
|
let slot;
|
||||||
|
class Slot extends Component<any, any> {
|
||||||
|
static template = xml`<span t-esc="props.val"/>`;
|
||||||
|
constructor(parent, props) {
|
||||||
|
super(parent, props);
|
||||||
|
slot = this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
class GrandChild extends Component<any, any> {
|
||||||
|
static template = xml`<div><t t-slot="default"/></div>`;
|
||||||
|
}
|
||||||
|
class Child extends Component<any, any> {
|
||||||
|
static components = { GrandChild };
|
||||||
|
static template = xml`
|
||||||
|
<GrandChild>
|
||||||
|
<t t-slot="default"/>
|
||||||
|
</GrandChild>`;
|
||||||
|
}
|
||||||
|
class Parent extends Component<any, any> {
|
||||||
|
static components = { Child, Slot };
|
||||||
|
static template = xml`<Child><Slot val="state.val"/></Child>`;
|
||||||
|
state = useState({ val: 3 });
|
||||||
|
}
|
||||||
|
const parent = new Parent();
|
||||||
|
await parent.mount(fixture);
|
||||||
|
|
||||||
|
expect(fixture.innerHTML).toBe("<div><span>3</span></div>");
|
||||||
|
expect(slot.__owl__.parent).toBeInstanceOf(GrandChild);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("slot are properly rendered if inner props are changed", async () => {
|
||||||
|
env.qweb.addTemplates(`
|
||||||
|
<templates>
|
||||||
|
<div t-name="SomeComponent">
|
||||||
|
SC:<t t-esc="props.val"/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div t-name="GenericComponent">
|
||||||
|
<t t-slot="default" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div t-name="App">
|
||||||
|
<button t-on-click="inc">Inc[<t t-esc="state.val"/>]</button>
|
||||||
|
<GenericComponent>
|
||||||
|
<SomeComponent val="state.val"/>
|
||||||
|
</GenericComponent>
|
||||||
|
</div>
|
||||||
|
</templates>
|
||||||
|
`);
|
||||||
|
class SomeComponent extends Component<any,any> {}
|
||||||
|
class GenericComponent extends Component<any,any> {}
|
||||||
|
class App extends Component<any,any> {
|
||||||
|
static components = { GenericComponent, SomeComponent };
|
||||||
|
state = useState({ val: 4 });
|
||||||
|
|
||||||
|
inc() {
|
||||||
|
this.state.val++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const app = new App();
|
||||||
|
await app.mount(fixture);
|
||||||
|
|
||||||
|
expect(fixture.innerHTML).toBe("<div><button>Inc[4]</button><div><div> SC:4</div></div></div>");
|
||||||
|
(<any>fixture.querySelector("button")).click();
|
||||||
|
await nextTick();
|
||||||
|
expect(fixture.innerHTML).toBe("<div><button>Inc[5]</button><div><div> SC:5</div></div></div>");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("slots and wrapper components", async () => {
|
||||||
|
class Link extends Component<any, any> {
|
||||||
|
static template = xml`
|
||||||
|
<a href="abc">
|
||||||
|
<t t-slot="default"/>
|
||||||
|
</a>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
class A extends Component<any, any> {
|
||||||
|
static template = xml`<Link>hey</Link>`;
|
||||||
|
static components = { Link: Link };
|
||||||
|
}
|
||||||
|
|
||||||
|
const a = new A();
|
||||||
|
await a.mount(fixture);
|
||||||
|
|
||||||
|
expect(fixture.innerHTML).toBe(`<a href="abc">hey</a>`);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("template can just return a slot", async () => {
|
||||||
|
class Child extends Component<any,any> {
|
||||||
|
static template = xml`<span><t t-esc="props.value"/></span>`;
|
||||||
|
}
|
||||||
|
class SlotComponent extends Component<any,any> {
|
||||||
|
static template = xml`<t t-slot="default"/>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
class Parent extends Component<any,any> {
|
||||||
|
static template = xml`
|
||||||
|
<div>
|
||||||
|
<SlotComponent><Child value="state.value"/></SlotComponent>
|
||||||
|
</div>`;
|
||||||
|
static components = { SlotComponent, Child };
|
||||||
|
state = useState({ value: 3 });
|
||||||
|
}
|
||||||
|
const parent = new Parent();
|
||||||
|
await parent.mount(fixture);
|
||||||
|
expect(fixture.innerHTML).toBe("<div><span>3</span></div>");
|
||||||
|
|
||||||
|
expect(QWeb.TEMPLATES[SlotComponent.template].fn.toString()).toMatchSnapshot();
|
||||||
|
|
||||||
|
parent.state.value = 5;
|
||||||
|
await nextTick();
|
||||||
|
expect(fixture.innerHTML).toBe("<div><span>5</span></div>");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("multiple slots containing components", async () => {
|
||||||
|
class C extends Component<any, any> {
|
||||||
|
static template = xml`<span><t t-esc="props.val"/></span>`;
|
||||||
|
}
|
||||||
|
class B extends Component<any, any> {
|
||||||
|
static template = xml`<div><t t-slot="s1"/><t t-slot="s2"/></div>`;
|
||||||
|
}
|
||||||
|
class A extends Component<any, any> {
|
||||||
|
static template = xml`
|
||||||
|
<B>
|
||||||
|
<t t-set="s1"><C val="1"/></t>
|
||||||
|
<t t-set="s2"><C val="2"/></t>
|
||||||
|
</B>`;
|
||||||
|
static components = { B, C };
|
||||||
|
}
|
||||||
|
|
||||||
|
const a = new A();
|
||||||
|
await a.mount(fixture);
|
||||||
|
|
||||||
|
expect(fixture.innerHTML).toBe(`<div><span>1</span><span>2</span></div>`);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("slots in t-foreach and re-rendering", async () => {
|
||||||
|
class Child extends Component<any,any> {
|
||||||
|
static template = xml`<span><t t-esc="state.val"/><t t-slot="default"/></span>`;
|
||||||
|
state = useState({ val: "A" });
|
||||||
|
mounted() {
|
||||||
|
this.state.val = "B";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
class Parent extends Component<any,any> {
|
||||||
|
static components = { Child };
|
||||||
|
static template = xml`
|
||||||
|
<div>
|
||||||
|
<t t-foreach="Array(2)" t-as="n" t-key="n_index">
|
||||||
|
<Child><t t-esc="n_index"/></Child>
|
||||||
|
</t>
|
||||||
|
</div>`;
|
||||||
|
}
|
||||||
|
const parent = new Parent();
|
||||||
|
await parent.mount(fixture);
|
||||||
|
expect(fixture.innerHTML).toBe("<div><span>A0</span><span>A1</span></div>");
|
||||||
|
|
||||||
|
await nextTick(); // wait for the changes triggered in mounted to be applied
|
||||||
|
expect(fixture.innerHTML).toBe("<div><span>B0</span><span>B1</span></div>");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("slots in t-foreach with t-set and re-rendering", async () => {
|
||||||
|
class Child extends Component<any,any> {
|
||||||
|
static template = xml`
|
||||||
|
<span>
|
||||||
|
<t t-esc="state.val"/>
|
||||||
|
<t t-slot="default"/>
|
||||||
|
</span>`;
|
||||||
|
state = useState({ val: "A" });
|
||||||
|
mounted() {
|
||||||
|
this.state.val = "B";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
class ParentWidget extends Component<any,any> {
|
||||||
|
static components = { Child };
|
||||||
|
static template = xml`
|
||||||
|
<div>
|
||||||
|
<t t-foreach="Array(2)" t-as="n" t-key="n_index">
|
||||||
|
<t t-set="dummy" t-value="n_index"/>
|
||||||
|
<Child><t t-esc="dummy"/></Child>
|
||||||
|
</t>
|
||||||
|
</div>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const widget = new ParentWidget();
|
||||||
|
await widget.mount(fixture);
|
||||||
|
expect(fixture.innerHTML).toBe("<div><span>A0</span><span>A1</span></div>");
|
||||||
|
|
||||||
|
await nextTick(); // wait for changes triggered in mounted to be applied
|
||||||
|
expect(fixture.innerHTML).toBe("<div><span>B0</span><span>B1</span></div>");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("nested slots in same template", async () => {
|
||||||
|
let child, child2, child3;
|
||||||
|
class Child extends Component<any,any> {
|
||||||
|
static template = xml`
|
||||||
|
<span id="c1">
|
||||||
|
<div>
|
||||||
|
<t t-slot="default"/>
|
||||||
|
</div>
|
||||||
|
</span>`;
|
||||||
|
constructor(parent, props) {
|
||||||
|
super(parent, props);
|
||||||
|
child = this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
class Child2 extends Component<any,any> {
|
||||||
|
static template = xml`
|
||||||
|
<span id="c2">
|
||||||
|
<t t-slot="default"/>
|
||||||
|
</span>`;
|
||||||
|
constructor(parent, props) {
|
||||||
|
super(parent, props);
|
||||||
|
child2 = this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
class Child3 extends Component<any,any> {
|
||||||
|
static template = xml`
|
||||||
|
<span>Child 3</span>`;
|
||||||
|
constructor(parent, props) {
|
||||||
|
super(parent, props);
|
||||||
|
child3 = this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
class Parent extends Component<any,any> {
|
||||||
|
static components = { Child, Child2, Child3 };
|
||||||
|
static template = xml`
|
||||||
|
<span id="parent">
|
||||||
|
<Child>
|
||||||
|
<Child2>
|
||||||
|
<Child3/>
|
||||||
|
</Child2>
|
||||||
|
</Child>
|
||||||
|
</span>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const widget = new Parent();
|
||||||
|
await widget.mount(fixture);
|
||||||
|
expect(fixture.innerHTML).toBe(
|
||||||
|
'<span id="parent"><span id="c1"><div><span id="c2"><span>Child 3</span></span></div></span></span>'
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(child3.__owl__.parent).toStrictEqual(child2);
|
||||||
|
expect(child2.__owl__.parent).toStrictEqual(child);
|
||||||
|
expect(child.__owl__.parent).toStrictEqual(widget);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("t-slot nested within another slot", async () => {
|
||||||
|
let portal, modal, child3;
|
||||||
|
class Child3 extends Component<any,any> {
|
||||||
|
static template = xml`
|
||||||
|
<span>Child 3</span>`;
|
||||||
|
constructor(parent, props) {
|
||||||
|
super(parent, props);
|
||||||
|
child3 = this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
class Modal extends Component<any,any> {
|
||||||
|
static template = xml`
|
||||||
|
<span id="modal">
|
||||||
|
<t t-slot="default"/>
|
||||||
|
</span>`;
|
||||||
|
constructor(parent, props) {
|
||||||
|
super(parent, props);
|
||||||
|
modal = this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
class Portal extends Component<any,any> {
|
||||||
|
static template = xml`
|
||||||
|
<span id="portal">
|
||||||
|
<t t-slot="default"/>
|
||||||
|
</span>`;
|
||||||
|
constructor(parent, props) {
|
||||||
|
super(parent, props);
|
||||||
|
portal = this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
class Dialog extends Component<any,any> {
|
||||||
|
static components = { Modal, Portal };
|
||||||
|
static template = xml`
|
||||||
|
<span id="c2">
|
||||||
|
<Modal>
|
||||||
|
<Portal>
|
||||||
|
<t t-slot="default"/>
|
||||||
|
</Portal>
|
||||||
|
</Modal>
|
||||||
|
</span>`;
|
||||||
|
}
|
||||||
|
class Parent extends Component<any,any> {
|
||||||
|
static components = { Child3, Dialog };
|
||||||
|
static template = xml`
|
||||||
|
<span id="c1">
|
||||||
|
<Dialog>
|
||||||
|
<Child3/>
|
||||||
|
</Dialog>
|
||||||
|
</span>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const widget = new Parent();
|
||||||
|
await widget.mount(fixture);
|
||||||
|
expect(fixture.innerHTML).toBe(
|
||||||
|
'<span id="c1"><span id="c2"><span id="modal"><span id="portal"><span>Child 3</span></span></span></span></span>'
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(child3.__owl__.parent).toStrictEqual(portal);
|
||||||
|
expect(portal.__owl__.parent).toStrictEqual(modal);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("t-slot supports many instances", async () => {
|
||||||
|
let child3;
|
||||||
|
class Child3 extends Component<any,any> {
|
||||||
|
static template = xml`
|
||||||
|
<span>Child 3</span>`;
|
||||||
|
constructor(parent, props) {
|
||||||
|
super(parent, props);
|
||||||
|
child3 = this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
class Dialog extends Component<any,any> {
|
||||||
|
static template = xml`
|
||||||
|
<span id="c2">
|
||||||
|
<t t-slot="default"/>
|
||||||
|
</span>`;
|
||||||
|
}
|
||||||
|
class Parent extends Component<any,any> {
|
||||||
|
static components = { Child3, Dialog };
|
||||||
|
static template = xml`
|
||||||
|
<span id="c1">
|
||||||
|
<Dialog>
|
||||||
|
<Child3 val="state.lol"/>
|
||||||
|
</Dialog>
|
||||||
|
</span>`;
|
||||||
|
state = { lol: "k" };
|
||||||
|
}
|
||||||
|
|
||||||
|
const widget = new Parent();
|
||||||
|
await widget.mount(fixture);
|
||||||
|
expect(child3.props.val).toBe("k");
|
||||||
|
|
||||||
|
const widget_1 = new Parent();
|
||||||
|
widget_1.state.lol = "m";
|
||||||
|
await widget_1.mount(fixture);
|
||||||
|
expect(child3.props.val).toBe("m");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("slots in slots, with vars", async () => {
|
||||||
|
class B extends Component<any, any> {
|
||||||
|
static template = xml`<span><t t-slot="default"/></span>`;
|
||||||
|
}
|
||||||
|
class A extends Component<any, any> {
|
||||||
|
static template = xml`
|
||||||
|
<div>
|
||||||
|
<B>
|
||||||
|
<t t-slot="default"/>
|
||||||
|
</B>
|
||||||
|
</div>`;
|
||||||
|
static components = { B };
|
||||||
|
}
|
||||||
|
class Parent extends Component<any, any> {
|
||||||
|
static template = xml`
|
||||||
|
<div>
|
||||||
|
<t t-set="test" t-value="state.name"/>
|
||||||
|
<A>
|
||||||
|
<p>hey<t t-esc="test"/></p>
|
||||||
|
</A>
|
||||||
|
</div>`;
|
||||||
|
static components = { A };
|
||||||
|
state = { name: "aaron" };
|
||||||
|
}
|
||||||
|
|
||||||
|
const parent = new Parent();
|
||||||
|
await parent.mount(fixture);
|
||||||
|
expect(fixture.innerHTML).toBe("<div><div><span><p>heyaaron</p></span></div></div>");
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user