mirror of
https://github.com/odoo/owl.git
synced 2025-10-06 19:59:41 +07:00
[FIX] qweb: improved svg support for components
If we want to generate dynamically svg components, it is useful to be able to define components with a <g> tag as root, and then getting the correct namespace. closes #302
This commit is contained in:
+12
-5
@@ -96,8 +96,8 @@ const UTILS: Utils = {
|
|||||||
},
|
},
|
||||||
shallowEqual,
|
shallowEqual,
|
||||||
addNameSpace(vnode) {
|
addNameSpace(vnode) {
|
||||||
addNS(vnode.data, vnode.children, vnode.sel);
|
addNS(vnode.data, vnode.children, vnode.sel);
|
||||||
},
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
function parseXML(xml: string): Document {
|
function parseXML(xml: string): Document {
|
||||||
@@ -541,9 +541,16 @@ export class QWeb extends EventBus {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this._compileChildren(node, ctx);
|
this._compileChildren(node, ctx);
|
||||||
if (node.nodeName === 'svg') {
|
// svg support
|
||||||
ctx.rootContext.shouldDefineUtils = true;
|
// we hadd svg namespace if it is a svg or if it is a g, but only if it is
|
||||||
ctx.addLine(`utils.addNameSpace(vn${ctx.parentNode});`);
|
// the root node. This is the easiest way to support svg sub components:
|
||||||
|
// they need to have a g tag as root. Otherwise, we would need a complete
|
||||||
|
// list of allowed svg tags.
|
||||||
|
const shouldAddNS =
|
||||||
|
node.nodeName === "svg" || (node.nodeName === "g" && ctx.rootNode === ctx.parentNode);
|
||||||
|
if (shouldAddNS) {
|
||||||
|
ctx.rootContext.shouldDefineUtils = true;
|
||||||
|
ctx.addLine(`utils.addNameSpace(vn${ctx.parentNode});`);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let { directive, value, fullName } of validDirectives) {
|
for (let { directive, value, fullName } of validDirectives) {
|
||||||
|
|||||||
+6
-2
@@ -568,9 +568,13 @@ export function addNS(data: any, children: VNodes | undefined, sel: string | und
|
|||||||
data.ns = "http://www.w3.org/2000/svg";
|
data.ns = "http://www.w3.org/2000/svg";
|
||||||
if (sel !== "foreignObject" && children !== undefined) {
|
if (sel !== "foreignObject" && children !== undefined) {
|
||||||
for (let i = 0, iLen = children.length; i < iLen; ++i) {
|
for (let i = 0, iLen = children.length; i < iLen; ++i) {
|
||||||
let childData = children[i].data;
|
const child = children[i];
|
||||||
|
if (child === null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let childData = child.data;
|
||||||
if (childData !== undefined) {
|
if (childData !== undefined) {
|
||||||
addNS(childData, (children[i] as VNode).children as VNodes, children[i].sel);
|
addNS(childData, (child as VNode).children as VNodes, child.sel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4418,3 +4418,28 @@ describe("dynamic t-props", () => {
|
|||||||
expect(env.qweb.templates[Parent.template].fn.toString()).toMatchSnapshot();
|
expect(env.qweb.templates[Parent.template].fn.toString()).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("support svg components", () => {
|
||||||
|
test("add proper namespace to svg", async () => {
|
||||||
|
class GComp extends Widget {
|
||||||
|
static template = xml`
|
||||||
|
<g>
|
||||||
|
<circle cx="50" cy="50" r="4" stroke="green" stroke-width="1" fill="yellow"/>
|
||||||
|
</g>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
class Svg extends Widget {
|
||||||
|
static template = xml`
|
||||||
|
<svg>
|
||||||
|
<GComp/>
|
||||||
|
</svg>`;
|
||||||
|
static components = { GComp };
|
||||||
|
}
|
||||||
|
const widget = new Svg(env);
|
||||||
|
await widget.mount(fixture);
|
||||||
|
|
||||||
|
expect(fixture.innerHTML).toBe(
|
||||||
|
'<svg><g><circle cx="50" cy="50" r="4" stroke="green" stroke-width="1" fill="yellow"></circle></g></svg>'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
@@ -676,6 +676,29 @@ exports[`misc global 1`] = `
|
|||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`properly support svg add proper namespace to g tags 1`] = `
|
||||||
|
"function anonymous(context,extra
|
||||||
|
) {
|
||||||
|
let utils = this.constructor.utils;
|
||||||
|
var h = this.h;
|
||||||
|
let c1 = [], p1 = {key:1};
|
||||||
|
var vn1 = h('g', p1, c1);
|
||||||
|
result = vn1;
|
||||||
|
var _2 = '50';
|
||||||
|
var _3 = '50';
|
||||||
|
var _4 = '4';
|
||||||
|
var _5 = 'green';
|
||||||
|
var _6 = '1';
|
||||||
|
var _7 = 'yellow';
|
||||||
|
let c8 = [], p8 = {key:8,attrs:{cx: _2,cy: _3,r: _4,stroke: _5,\\"stroke-width\\": _6,fill: _7}};
|
||||||
|
var vn8 = h('circle', p8, c8);
|
||||||
|
c1.push(vn8);
|
||||||
|
c1.push({text: \` \`});
|
||||||
|
utils.addNameSpace(vn1);
|
||||||
|
return vn1;
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`properly support svg add proper namespace to svg 1`] = `
|
exports[`properly support svg add proper namespace to svg 1`] = `
|
||||||
"function anonymous(context,extra
|
"function anonymous(context,extra
|
||||||
) {
|
) {
|
||||||
|
|||||||
+18
-2
@@ -1315,7 +1315,23 @@ describe("global template registration", () => {
|
|||||||
|
|
||||||
describe("properly support svg", () => {
|
describe("properly support svg", () => {
|
||||||
test("add proper namespace to svg", () => {
|
test("add proper namespace to svg", () => {
|
||||||
qweb.addTemplate("test", `<svg width="100px" height="90px"><circle cx="50" cy="50" r="4" stroke="green" stroke-width="1" fill="yellow"/> </svg>`);
|
qweb.addTemplate(
|
||||||
expect(renderToString(qweb, "test")).toBe(`<svg width=\"100px\" height=\"90px\"><circle cx=\"50\" cy=\"50\" r=\"4\" stroke=\"green\" stroke-width=\"1\" fill=\"yellow\"></circle> </svg>`);
|
"test",
|
||||||
|
`<svg width="100px" height="90px"><circle cx="50" cy="50" r="4" stroke="green" stroke-width="1" fill="yellow"/> </svg>`
|
||||||
|
);
|
||||||
|
expect(renderToString(qweb, "test")).toBe(
|
||||||
|
`<svg width=\"100px\" height=\"90px\"><circle cx=\"50\" cy=\"50\" r=\"4\" stroke=\"green\" stroke-width=\"1\" fill=\"yellow\"></circle> </svg>`
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("add proper namespace to g tags", () => {
|
||||||
|
// this is necessary if one wants to use components in a svg
|
||||||
|
qweb.addTemplate(
|
||||||
|
"test",
|
||||||
|
`<g><circle cx="50" cy="50" r="4" stroke="green" stroke-width="1" fill="yellow"/> </g>`
|
||||||
|
);
|
||||||
|
expect(renderToString(qweb, "test")).toBe(
|
||||||
|
`<g><circle cx=\"50\" cy=\"50\" r=\"4\" stroke=\"green\" stroke-width=\"1\" fill=\"yellow\"></circle> </g>`
|
||||||
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
+7
-9
@@ -1,5 +1,5 @@
|
|||||||
import { h, patch } from "../src/vdom";
|
import { h, patch } from "../src/vdom";
|
||||||
import { init } from "../src/vdom/vdom";
|
import { init, addNS } from "../src/vdom/vdom";
|
||||||
|
|
||||||
function map(list, fn) {
|
function map(list, fn) {
|
||||||
var ret: any[] = [];
|
var ret: any[] = [];
|
||||||
@@ -206,19 +206,17 @@ describe("snabbdom", function() {
|
|||||||
expect(elm.firstChild.namespaceURI).toBe(SVGNamespace);
|
expect(elm.firstChild.namespaceURI).toBe(SVGNamespace);
|
||||||
|
|
||||||
// verify that svg tag automatically gets svg namespace
|
// verify that svg tag automatically gets svg namespace
|
||||||
elm = patch(vnode0, h("svg", [h("foreignObject", [h("div", ["I am HTML embedded in SVG"])])]))
|
const vnode = h("svg", [h("foreignObject", [h("div", ["I am HTML embedded in SVG"])])]);
|
||||||
|
// need to add namespace manually. it is usually done by the template
|
||||||
|
// compiler
|
||||||
|
addNS(vnode.data,(vnode as any).children, vnode.sel);
|
||||||
|
|
||||||
|
elm = patch(vnode0, vnode)
|
||||||
.elm;
|
.elm;
|
||||||
expect(elm.namespaceURI).toBe(SVGNamespace);
|
expect(elm.namespaceURI).toBe(SVGNamespace);
|
||||||
expect(elm.firstChild.namespaceURI).toBe(SVGNamespace);
|
expect(elm.firstChild.namespaceURI).toBe(SVGNamespace);
|
||||||
expect(elm.firstChild.firstChild.namespaceURI).toBe(XHTMLNamespace);
|
expect(elm.firstChild.firstChild.namespaceURI).toBe(XHTMLNamespace);
|
||||||
|
|
||||||
// verify that svg tag with extra selectors gets svg namespace
|
|
||||||
elm = patch(vnode0, h("svg#some-id")).elm;
|
|
||||||
expect(elm.namespaceURI).toBe(SVGNamespace);
|
|
||||||
|
|
||||||
// verify that non-svg tag beginning with 'svg' does NOT get namespace
|
|
||||||
elm = patch(vnode0, h("svg-custom-el")).elm;
|
|
||||||
expect(elm.namespaceURI).not.toBe(SVGNamespace);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test("receives classes in selector", function() {
|
test("receives classes in selector", function() {
|
||||||
|
|||||||
Reference in New Issue
Block a user