diff --git a/src/qweb/qweb.ts b/src/qweb/qweb.ts
index 9ec5ce2c..2da41059 100644
--- a/src/qweb/qweb.ts
+++ b/src/qweb/qweb.ts
@@ -96,8 +96,8 @@ const UTILS: Utils = {
},
shallowEqual,
addNameSpace(vnode) {
- addNS(vnode.data, vnode.children, vnode.sel);
- },
+ addNS(vnode.data, vnode.children, vnode.sel);
+ }
};
function parseXML(xml: string): Document {
@@ -541,9 +541,16 @@ export class QWeb extends EventBus {
}
this._compileChildren(node, ctx);
- if (node.nodeName === 'svg') {
- ctx.rootContext.shouldDefineUtils = true;
- ctx.addLine(`utils.addNameSpace(vn${ctx.parentNode});`);
+ // svg support
+ // we hadd svg namespace if it is a svg or if it is a g, but only if it is
+ // 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) {
diff --git a/src/vdom/vdom.ts b/src/vdom/vdom.ts
index 38fe34f5..6d45521b 100644
--- a/src/vdom/vdom.ts
+++ b/src/vdom/vdom.ts
@@ -568,9 +568,13 @@ export function addNS(data: any, children: VNodes | undefined, sel: string | und
data.ns = "http://www.w3.org/2000/svg";
if (sel !== "foreignObject" && children !== undefined) {
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) {
- addNS(childData, (children[i] as VNode).children as VNodes, children[i].sel);
+ addNS(childData, (child as VNode).children as VNodes, child.sel);
}
}
}
diff --git a/tests/component/component.test.ts b/tests/component/component.test.ts
index 10614583..b9ac27d3 100644
--- a/tests/component/component.test.ts
+++ b/tests/component/component.test.ts
@@ -4418,3 +4418,28 @@ describe("dynamic t-props", () => {
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`
+
+
+ `;
+ }
+
+ class Svg extends Widget {
+ static template = xml`
+ `;
+ static components = { GComp };
+ }
+ const widget = new Svg(env);
+ await widget.mount(fixture);
+
+ expect(fixture.innerHTML).toBe(
+ ''
+ );
+ });
+});
diff --git a/tests/qweb/__snapshots__/qweb.test.ts.snap b/tests/qweb/__snapshots__/qweb.test.ts.snap
index 7de37f63..f7182df7 100644
--- a/tests/qweb/__snapshots__/qweb.test.ts.snap
+++ b/tests/qweb/__snapshots__/qweb.test.ts.snap
@@ -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`] = `
"function anonymous(context,extra
) {
diff --git a/tests/qweb/qweb.test.ts b/tests/qweb/qweb.test.ts
index 2016ad7e..61043e1f 100644
--- a/tests/qweb/qweb.test.ts
+++ b/tests/qweb/qweb.test.ts
@@ -1315,7 +1315,23 @@ describe("global template registration", () => {
describe("properly support svg", () => {
test("add proper namespace to svg", () => {
- qweb.addTemplate("test", ``);
- expect(renderToString(qweb, "test")).toBe(``);
+ qweb.addTemplate(
+ "test",
+ ``
+ );
+ expect(renderToString(qweb, "test")).toBe(
+ ``
+ );
+ });
+
+ test("add proper namespace to g tags", () => {
+ // this is necessary if one wants to use components in a svg
+ qweb.addTemplate(
+ "test",
+ ` `
+ );
+ expect(renderToString(qweb, "test")).toBe(
+ ` `
+ );
});
});
diff --git a/tests/vdom.test.ts b/tests/vdom.test.ts
index ec52b503..c9fb15e8 100644
--- a/tests/vdom.test.ts
+++ b/tests/vdom.test.ts
@@ -1,5 +1,5 @@
import { h, patch } from "../src/vdom";
-import { init } from "../src/vdom/vdom";
+import { init, addNS } from "../src/vdom/vdom";
function map(list, fn) {
var ret: any[] = [];
@@ -206,19 +206,17 @@ describe("snabbdom", function() {
expect(elm.firstChild.namespaceURI).toBe(SVGNamespace);
// 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;
expect(elm.namespaceURI).toBe(SVGNamespace);
expect(elm.firstChild.namespaceURI).toBe(SVGNamespace);
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() {