mirror of
https://github.com/odoo/owl.git
synced 2025-10-06 19:59:41 +07:00
[IMP] svg namespace support
This commit is contained in:
@@ -116,6 +116,7 @@ interface IntermediateTree {
|
||||
forceRef?: boolean;
|
||||
refIdx?: number;
|
||||
refN: number;
|
||||
currentNS: string | null;
|
||||
}
|
||||
|
||||
function buildTree(
|
||||
@@ -124,9 +125,10 @@ function buildTree(
|
||||
domParentTree: IntermediateTree | null = null
|
||||
): IntermediateTree {
|
||||
switch (node.nodeType) {
|
||||
case 1: {
|
||||
case Node.ELEMENT_NODE: {
|
||||
// HTMLElement
|
||||
let isActive = false;
|
||||
let currentNS = parent && parent.currentNS;
|
||||
const tagName = (node as Element).tagName;
|
||||
let el: Node | undefined = undefined;
|
||||
const info: DynamicInfo[] = [];
|
||||
@@ -143,11 +145,18 @@ function buildTree(
|
||||
el = document.createTextNode("");
|
||||
isActive = true;
|
||||
}
|
||||
if (!el) {
|
||||
el = document.createElement(tagName);
|
||||
const attrs = (node as Element).attributes;
|
||||
const ns = attrs.getNamedItem("block-ns");
|
||||
if (ns) {
|
||||
attrs.removeNamedItem("block-ns");
|
||||
currentNS = ns.value;
|
||||
}
|
||||
if (el instanceof HTMLElement) {
|
||||
const attrs = (node as Element).attributes;
|
||||
if (!el) {
|
||||
el = currentNS
|
||||
? document.createElementNS(currentNS, tagName)
|
||||
: document.createElement(tagName);
|
||||
}
|
||||
if (el instanceof Element) {
|
||||
for (let i = 0; i < attrs.length; i++) {
|
||||
const attrName = attrs[i].name;
|
||||
const attrValue = attrs[i].value;
|
||||
@@ -193,13 +202,14 @@ function buildTree(
|
||||
el,
|
||||
info,
|
||||
refN: isActive ? 1 : 0,
|
||||
currentNS,
|
||||
};
|
||||
|
||||
if (node.firstChild) {
|
||||
const childNode = node.childNodes[0];
|
||||
if (
|
||||
node.childNodes.length === 1 &&
|
||||
childNode.nodeType === 1 &&
|
||||
childNode.nodeType === Node.ELEMENT_NODE &&
|
||||
(childNode as Element).tagName.startsWith("block-child-")
|
||||
) {
|
||||
const tagName = (childNode as Element).tagName;
|
||||
@@ -227,11 +237,11 @@ function buildTree(
|
||||
}
|
||||
return tree;
|
||||
}
|
||||
case 3:
|
||||
case 8: {
|
||||
case Node.TEXT_NODE:
|
||||
case Node.COMMENT_NODE: {
|
||||
// text node or comment node
|
||||
const el =
|
||||
node.nodeType === 3
|
||||
node.nodeType === Node.TEXT_NODE
|
||||
? document.createTextNode(node.textContent!)
|
||||
: document.createComment(node.textContent!);
|
||||
return {
|
||||
@@ -241,6 +251,7 @@ function buildTree(
|
||||
el,
|
||||
info: [],
|
||||
refN: 0,
|
||||
currentNS: null,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -497,6 +497,10 @@ export class CodeGenerator {
|
||||
}
|
||||
// attributes
|
||||
const attrs: { [key: string]: string } = {};
|
||||
if (ast.ns) {
|
||||
// specific namespace uri
|
||||
attrs["block-ns"] = ast.ns;
|
||||
}
|
||||
for (let key in ast.attrs) {
|
||||
if (key.startsWith("t-attf")) {
|
||||
let expr = interpolate(ast.attrs[key]);
|
||||
|
||||
+12
-9
@@ -49,6 +49,7 @@ export interface ASTDomNode {
|
||||
shouldTrim: boolean;
|
||||
shouldNumberize: boolean;
|
||||
} | null;
|
||||
ns: string | null;
|
||||
}
|
||||
|
||||
export interface ASTMulti {
|
||||
@@ -171,11 +172,12 @@ export type AST =
|
||||
// -----------------------------------------------------------------------------
|
||||
interface ParsingContext {
|
||||
inPreTag: boolean;
|
||||
inSVG: boolean;
|
||||
}
|
||||
|
||||
export function parse(xml: string | Node): AST {
|
||||
const node = xml instanceof Element ? xml : parseXML(`<t>${xml}</t>`).firstChild!;
|
||||
const ctx = { inPreTag: false };
|
||||
const ctx = { inPreTag: false, inSVG: false };
|
||||
const ast = parseNode(node, ctx);
|
||||
if (!ast) {
|
||||
return { type: ASTType.Text, value: "" };
|
||||
@@ -240,7 +242,7 @@ const lineBreakRE = /[\r\n]/;
|
||||
const whitespaceRE = /\s+/g;
|
||||
|
||||
function parseTextCommentNode(node: ChildNode, ctx: ParsingContext): AST | null {
|
||||
if (node.nodeType === 3) {
|
||||
if (node.nodeType === Node.TEXT_NODE) {
|
||||
let value = node.textContent || "";
|
||||
if (!ctx.inPreTag) {
|
||||
if (lineBreakRE.test(value) && !value.trim()) {
|
||||
@@ -250,7 +252,7 @@ function parseTextCommentNode(node: ChildNode, ctx: ParsingContext): AST | null
|
||||
}
|
||||
|
||||
return { type: ASTType.Text, value };
|
||||
} else if (node.nodeType === 8) {
|
||||
} else if (node.nodeType === Node.COMMENT_NODE) {
|
||||
return { type: ASTType.Comment, value: node.textContent || "" };
|
||||
}
|
||||
return null;
|
||||
@@ -289,18 +291,18 @@ const hasBracketsAtTheEnd = /\[[^\[]+\]\s*$/;
|
||||
|
||||
function parseDOMNode(node: Element, ctx: ParsingContext): AST | null {
|
||||
const { tagName } = node;
|
||||
let dynamicTag = null;
|
||||
if (node.hasAttribute("t-tag")) {
|
||||
dynamicTag = node.getAttribute("t-tag");
|
||||
node.removeAttribute("t-tag");
|
||||
}
|
||||
const dynamicTag = node.getAttribute("t-tag");
|
||||
node.removeAttribute("t-tag");
|
||||
if (tagName === "t" && !dynamicTag) {
|
||||
return null;
|
||||
}
|
||||
const children: AST[] = [];
|
||||
if (tagName === "pre") {
|
||||
ctx = { inPreTag: true };
|
||||
ctx.inPreTag = true;
|
||||
}
|
||||
const shouldAddSVGNS = tagName === "svg" || (tagName === "g" && !ctx.inSVG);
|
||||
ctx.inSVG = ctx.inSVG || shouldAddSVGNS;
|
||||
const ns = shouldAddSVGNS ? "http://www.w3.org/2000/svg" : null;
|
||||
const ref = node.getAttribute("t-ref");
|
||||
node.removeAttribute("t-ref");
|
||||
|
||||
@@ -381,6 +383,7 @@ function parseDOMNode(node: Element, ctx: ParsingContext): AST | null {
|
||||
ref,
|
||||
content: children,
|
||||
model,
|
||||
ns,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
import { createBlock, mount } from "../../src/blockdom";
|
||||
import { makeTestFixture } from "./helpers";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Setup and helpers
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
const XHTML_URI = "http://www.w3.org/1999/xhtml";
|
||||
const SVG_URI = "http://www.w3.org/2000/svg";
|
||||
let fixture: HTMLElement;
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = makeTestFixture();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
fixture.remove();
|
||||
});
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Tests
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
describe("namespace", () => {
|
||||
test("default namespace is xhtml", () => {
|
||||
const block = createBlock(`<tag/>`);
|
||||
const tree = block();
|
||||
mount(tree, fixture);
|
||||
expect(fixture.innerHTML).toBe("<tag></tag>");
|
||||
expect(fixture.firstElementChild!.namespaceURI).toBe(XHTML_URI);
|
||||
});
|
||||
|
||||
test("namespace can be changed with block-ns", () => {
|
||||
const block = createBlock(`<tag block-ns="${SVG_URI}"/>`);
|
||||
const tree = block();
|
||||
mount(tree, fixture);
|
||||
expect(fixture.innerHTML).toBe("<tag></tag>");
|
||||
expect(fixture.firstElementChild!.namespaceURI).toBe(SVG_URI);
|
||||
});
|
||||
|
||||
test("namespace is kept for children", () => {
|
||||
const block = createBlock(
|
||||
`<parent block-ns="${SVG_URI}"><child><subchild/></child><child/></parent>`
|
||||
);
|
||||
const tree = block();
|
||||
mount(tree, fixture);
|
||||
expect(fixture.innerHTML).toBe(
|
||||
"<parent><child><subchild></subchild></child><child></child></parent>"
|
||||
);
|
||||
const parent = fixture.firstElementChild!;
|
||||
const child1 = parent.firstElementChild!;
|
||||
const subchild = child1.firstElementChild!;
|
||||
const child2 = child1.nextElementSibling!;
|
||||
expect(parent.namespaceURI).toBe(SVG_URI);
|
||||
expect(child1.namespaceURI).toBe(SVG_URI);
|
||||
expect(child2.namespaceURI).toBe(SVG_URI);
|
||||
expect(subchild.namespaceURI).toBe(SVG_URI);
|
||||
});
|
||||
|
||||
test("various namespaces in same block", () => {
|
||||
const block = createBlock(`<none><one block-ns="one"/><two block-ns="two"/></none>`);
|
||||
const tree = block();
|
||||
mount(tree, fixture);
|
||||
expect(fixture.innerHTML).toBe("<none><one></one><two></two></none>");
|
||||
const none = fixture.firstElementChild!;
|
||||
const one = none.firstElementChild!;
|
||||
const two = one.nextElementSibling!;
|
||||
expect(none.namespaceURI).toBe(XHTML_URI);
|
||||
expect(one.namespaceURI).toBe("one");
|
||||
expect(two.namespaceURI).toBe("two");
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,57 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`properly support svg add proper namespace to g tags 1`] = `
|
||||
"function anonymous(bdom, helpers
|
||||
) {
|
||||
let { text, createBlock, list, multi, html, toggler, component } = bdom;
|
||||
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber, safeOutput } = helpers;
|
||||
|
||||
let block1 = createBlock(\`<g block-ns=\\"http://www.w3.org/2000/svg\\"><circle cx=\\"50\\" cy=\\"50\\" r=\\"4\\" stroke=\\"green\\" stroke-width=\\"1\\" fill=\\"yellow\\"/> </g>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
return block1();
|
||||
}
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`properly support svg add proper namespace to svg 1`] = `
|
||||
"function anonymous(bdom, helpers
|
||||
) {
|
||||
let { text, createBlock, list, multi, html, toggler, component } = bdom;
|
||||
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber, safeOutput } = helpers;
|
||||
|
||||
let block1 = createBlock(\`<svg block-ns=\\"http://www.w3.org/2000/svg\\" width=\\"100px\\" height=\\"90px\\"><circle cx=\\"50\\" cy=\\"50\\" r=\\"4\\" stroke=\\"green\\" stroke-width=\\"1\\" fill=\\"yellow\\"/> </svg>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
return block1();
|
||||
}
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`properly support svg namespace to g tags not added if already in svg namespace 1`] = `
|
||||
"function anonymous(bdom, helpers
|
||||
) {
|
||||
let { text, createBlock, list, multi, html, toggler, component } = bdom;
|
||||
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber, safeOutput } = helpers;
|
||||
|
||||
let block1 = createBlock(\`<svg block-ns=\\"http://www.w3.org/2000/svg\\"><g/></svg>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
return block1();
|
||||
}
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`properly support svg namespace to svg tags added even if already in svg namespace 1`] = `
|
||||
"function anonymous(bdom, helpers
|
||||
) {
|
||||
let { text, createBlock, list, multi, html, toggler, component } = bdom;
|
||||
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber, safeOutput } = helpers;
|
||||
|
||||
let block1 = createBlock(\`<svg block-ns=\\"http://www.w3.org/2000/svg\\"><svg block-ns=\\"http://www.w3.org/2000/svg\\"/></svg>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
return block1();
|
||||
}
|
||||
}"
|
||||
`;
|
||||
@@ -46,6 +46,7 @@ describe("qweb parser", () => {
|
||||
on: {},
|
||||
ref: null,
|
||||
model: null,
|
||||
ns: null,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -73,6 +74,7 @@ describe("qweb parser", () => {
|
||||
ref: null,
|
||||
model: null,
|
||||
content: [],
|
||||
ns: null,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -86,6 +88,7 @@ describe("qweb parser", () => {
|
||||
ref: null,
|
||||
model: null,
|
||||
content: [{ type: ASTType.Text, value: "some text" }],
|
||||
ns: null,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -98,6 +101,7 @@ describe("qweb parser", () => {
|
||||
on: {},
|
||||
ref: null,
|
||||
model: null,
|
||||
ns: null,
|
||||
content: [
|
||||
{ type: ASTType.Text, value: "some text" },
|
||||
{
|
||||
@@ -108,6 +112,7 @@ describe("qweb parser", () => {
|
||||
on: {},
|
||||
ref: null,
|
||||
model: null,
|
||||
ns: null,
|
||||
content: [{ type: ASTType.Text, value: "inside" }],
|
||||
},
|
||||
],
|
||||
@@ -126,6 +131,7 @@ describe("qweb parser", () => {
|
||||
on: {},
|
||||
ref: null,
|
||||
model: null,
|
||||
ns: null,
|
||||
content: [],
|
||||
},
|
||||
{
|
||||
@@ -136,6 +142,7 @@ describe("qweb parser", () => {
|
||||
on: {},
|
||||
ref: null,
|
||||
model: null,
|
||||
ns: null,
|
||||
content: [],
|
||||
},
|
||||
],
|
||||
@@ -155,6 +162,7 @@ describe("qweb parser", () => {
|
||||
on: {},
|
||||
ref: null,
|
||||
model: null,
|
||||
ns: null,
|
||||
content: [],
|
||||
},
|
||||
],
|
||||
@@ -170,10 +178,83 @@ describe("qweb parser", () => {
|
||||
on: {},
|
||||
ref: null,
|
||||
model: null,
|
||||
ns: null,
|
||||
content: [{ type: ASTType.Text, value: "foo" }],
|
||||
});
|
||||
});
|
||||
|
||||
test("svg dom node", async () => {
|
||||
expect(
|
||||
parse(
|
||||
`<svg width="100px" height="90px"><circle cx="50" cy="50" r="4" stroke="green" stroke-width="1" fill="yellow"/></svg>`
|
||||
)
|
||||
).toEqual({
|
||||
attrs: {
|
||||
height: "90px",
|
||||
width: "100px",
|
||||
},
|
||||
content: [
|
||||
{
|
||||
attrs: {
|
||||
cx: "50",
|
||||
cy: "50",
|
||||
fill: "yellow",
|
||||
r: "4",
|
||||
stroke: "green",
|
||||
"stroke-width": "1",
|
||||
},
|
||||
content: [],
|
||||
dynamicTag: null,
|
||||
model: null,
|
||||
ns: null,
|
||||
on: {},
|
||||
ref: null,
|
||||
tag: "circle",
|
||||
type: 2,
|
||||
},
|
||||
],
|
||||
dynamicTag: null,
|
||||
model: null,
|
||||
ns: "http://www.w3.org/2000/svg",
|
||||
on: {},
|
||||
ref: null,
|
||||
tag: "svg",
|
||||
type: 2,
|
||||
});
|
||||
expect(
|
||||
parse(`<g><circle cx="50" cy="50" r="4" stroke="green" stroke-width="1" fill="yellow"/></g>`)
|
||||
).toEqual({
|
||||
attrs: {},
|
||||
content: [
|
||||
{
|
||||
attrs: {
|
||||
cx: "50",
|
||||
cy: "50",
|
||||
fill: "yellow",
|
||||
r: "4",
|
||||
stroke: "green",
|
||||
"stroke-width": "1",
|
||||
},
|
||||
content: [],
|
||||
dynamicTag: null,
|
||||
model: null,
|
||||
ns: null,
|
||||
on: {},
|
||||
ref: null,
|
||||
tag: "circle",
|
||||
type: 2,
|
||||
},
|
||||
],
|
||||
dynamicTag: null,
|
||||
model: null,
|
||||
ns: "http://www.w3.org/2000/svg",
|
||||
on: {},
|
||||
ref: null,
|
||||
tag: "g",
|
||||
type: 2,
|
||||
});
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// t-esc
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -200,6 +281,7 @@ describe("qweb parser", () => {
|
||||
on: {},
|
||||
ref: null,
|
||||
model: null,
|
||||
ns: null,
|
||||
content: [{ type: ASTType.TEsc, expr: "text", defaultValue: "" }],
|
||||
});
|
||||
});
|
||||
@@ -221,6 +303,7 @@ describe("qweb parser", () => {
|
||||
on: {},
|
||||
ref: null,
|
||||
model: null,
|
||||
ns: null,
|
||||
content: [{ type: ASTType.TEsc, expr: "text", defaultValue: "hey" }],
|
||||
});
|
||||
});
|
||||
@@ -262,6 +345,7 @@ describe("qweb parser", () => {
|
||||
on: {},
|
||||
ref: null,
|
||||
model: null,
|
||||
ns: null,
|
||||
content: [{ type: ASTType.TOut, expr: "text", body: null }],
|
||||
});
|
||||
});
|
||||
@@ -275,6 +359,7 @@ describe("qweb parser", () => {
|
||||
on: {},
|
||||
ref: null,
|
||||
model: null,
|
||||
ns: null,
|
||||
content: [
|
||||
{ type: ASTType.TOut, expr: "text", body: [{ type: ASTType.Text, value: "body" }] },
|
||||
],
|
||||
@@ -294,6 +379,7 @@ describe("qweb parser", () => {
|
||||
on: {},
|
||||
ref: null,
|
||||
model: null,
|
||||
ns: null,
|
||||
content: [
|
||||
{
|
||||
type: ASTType.TIf,
|
||||
@@ -321,6 +407,7 @@ describe("qweb parser", () => {
|
||||
on: {},
|
||||
ref: null,
|
||||
model: null,
|
||||
ns: null,
|
||||
content: [{ type: ASTType.Text, value: "hey" }],
|
||||
},
|
||||
tElif: null,
|
||||
@@ -397,6 +484,7 @@ describe("qweb parser", () => {
|
||||
on: {},
|
||||
ref: null,
|
||||
model: null,
|
||||
ns: null,
|
||||
content: [
|
||||
{
|
||||
type: ASTType.Text,
|
||||
@@ -415,6 +503,7 @@ describe("qweb parser", () => {
|
||||
on: {},
|
||||
ref: null,
|
||||
model: null,
|
||||
ns: null,
|
||||
content: [{ type: ASTType.Text, value: "elif" }],
|
||||
},
|
||||
},
|
||||
@@ -427,6 +516,7 @@ describe("qweb parser", () => {
|
||||
on: {},
|
||||
ref: null,
|
||||
model: null,
|
||||
ns: null,
|
||||
content: [
|
||||
{
|
||||
type: ASTType.Text,
|
||||
@@ -474,6 +564,7 @@ describe("qweb parser", () => {
|
||||
dynamicTag: null,
|
||||
ref: null,
|
||||
model: null,
|
||||
ns: null,
|
||||
content: [{ type: ASTType.Text, value: "ok" }],
|
||||
},
|
||||
],
|
||||
@@ -493,6 +584,7 @@ describe("qweb parser", () => {
|
||||
dynamicTag: null,
|
||||
ref: null,
|
||||
model: null,
|
||||
ns: null,
|
||||
content: [{ type: ASTType.Text, value: "ok" }],
|
||||
},
|
||||
{ type: ASTType.Text, value: "abc" },
|
||||
@@ -527,6 +619,7 @@ describe("qweb parser", () => {
|
||||
on: {},
|
||||
ref: null,
|
||||
model: null,
|
||||
ns: null,
|
||||
content: [
|
||||
{
|
||||
type: ASTType.TIf,
|
||||
@@ -601,6 +694,7 @@ describe("qweb parser", () => {
|
||||
on: {},
|
||||
ref: null,
|
||||
model: null,
|
||||
ns: null,
|
||||
content: [{ type: ASTType.TEsc, expr: "item", defaultValue: "" }],
|
||||
},
|
||||
memo: "",
|
||||
@@ -646,6 +740,7 @@ describe("qweb parser", () => {
|
||||
on: {},
|
||||
ref: null,
|
||||
model: null,
|
||||
ns: null,
|
||||
content: [{ type: ASTType.TEsc, expr: "item", defaultValue: "" }],
|
||||
},
|
||||
memo: "",
|
||||
@@ -681,6 +776,7 @@ describe("qweb parser", () => {
|
||||
on: {},
|
||||
ref: null,
|
||||
model: null,
|
||||
ns: null,
|
||||
content: [{ type: ASTType.TEsc, expr: "item", defaultValue: "" }],
|
||||
},
|
||||
},
|
||||
@@ -715,6 +811,7 @@ describe("qweb parser", () => {
|
||||
on: {},
|
||||
ref: null,
|
||||
model: null,
|
||||
ns: null,
|
||||
content: [{ type: ASTType.TEsc, expr: "category.name", defaultValue: "" }],
|
||||
},
|
||||
memo: "",
|
||||
@@ -736,6 +833,7 @@ describe("qweb parser", () => {
|
||||
model: null,
|
||||
tag: "div",
|
||||
dynamicTag: null,
|
||||
ns: null,
|
||||
content: [
|
||||
{
|
||||
type: ASTType.TForEach,
|
||||
@@ -781,6 +879,7 @@ describe("qweb parser", () => {
|
||||
ref: null,
|
||||
model: null,
|
||||
attrs: {},
|
||||
ns: null,
|
||||
content: [{ type: ASTType.TEsc, expr: "item", defaultValue: "" }],
|
||||
},
|
||||
memo: "",
|
||||
@@ -888,6 +987,7 @@ describe("qweb parser", () => {
|
||||
on: {},
|
||||
ref: null,
|
||||
model: null,
|
||||
ns: null,
|
||||
content: [
|
||||
{
|
||||
type: ASTType.TCall,
|
||||
@@ -925,6 +1025,7 @@ describe("qweb parser", () => {
|
||||
on: { click: "add" },
|
||||
ref: null,
|
||||
model: null,
|
||||
ns: null,
|
||||
content: [{ type: ASTType.Text, value: "Click" }],
|
||||
});
|
||||
});
|
||||
@@ -1003,6 +1104,7 @@ describe("qweb parser", () => {
|
||||
ref: null,
|
||||
model: null,
|
||||
on: {},
|
||||
ns: null,
|
||||
},
|
||||
{
|
||||
type: ASTType.DomNode,
|
||||
@@ -1013,6 +1115,7 @@ describe("qweb parser", () => {
|
||||
ref: null,
|
||||
model: null,
|
||||
on: {},
|
||||
ns: null,
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -1203,6 +1306,7 @@ describe("qweb parser", () => {
|
||||
on: {},
|
||||
ref: null,
|
||||
model: null,
|
||||
ns: null,
|
||||
content: [{ type: ASTType.Text, value: "hey" }],
|
||||
},
|
||||
});
|
||||
@@ -1220,6 +1324,7 @@ describe("qweb parser", () => {
|
||||
on: {},
|
||||
ref: null,
|
||||
model: null,
|
||||
ns: null,
|
||||
content: [{ type: ASTType.Text, value: "hey" }],
|
||||
},
|
||||
});
|
||||
@@ -1238,6 +1343,7 @@ describe("qweb parser", () => {
|
||||
on: {},
|
||||
ref: "name",
|
||||
model: null,
|
||||
ns: null,
|
||||
content: [{ type: ASTType.Text, value: "hey" }],
|
||||
});
|
||||
});
|
||||
@@ -1251,6 +1357,7 @@ describe("qweb parser", () => {
|
||||
on: {},
|
||||
ref: "name",
|
||||
model: null,
|
||||
ns: null,
|
||||
content: [
|
||||
{ type: ASTType.TOut, expr: "text", body: [{ type: ASTType.Text, value: "body" }] },
|
||||
],
|
||||
@@ -1266,6 +1373,7 @@ describe("qweb parser", () => {
|
||||
on: {},
|
||||
ref: "name",
|
||||
model: null,
|
||||
ns: null,
|
||||
content: [{ type: ASTType.TEsc, expr: "text", defaultValue: "body" }],
|
||||
});
|
||||
});
|
||||
@@ -1309,7 +1417,8 @@ describe("qweb parser", () => {
|
||||
model: null,
|
||||
tag: "div",
|
||||
dynamicTag: null,
|
||||
type: 2,
|
||||
type: ASTType.DomNode,
|
||||
ns: null,
|
||||
},
|
||||
type: 16,
|
||||
},
|
||||
@@ -1339,6 +1448,7 @@ describe("qweb parser", () => {
|
||||
ref: null,
|
||||
tag: "input",
|
||||
dynamicTag: null,
|
||||
ns: null,
|
||||
model: {
|
||||
baseExpr: "state",
|
||||
expr: "'stuff'",
|
||||
@@ -1357,6 +1467,7 @@ describe("qweb parser", () => {
|
||||
ref: null,
|
||||
tag: "input",
|
||||
dynamicTag: null,
|
||||
ns: null,
|
||||
model: {
|
||||
baseExpr: "state",
|
||||
expr: "'stuff'",
|
||||
@@ -1375,6 +1486,7 @@ describe("qweb parser", () => {
|
||||
ref: null,
|
||||
tag: "input",
|
||||
dynamicTag: null,
|
||||
ns: null,
|
||||
model: {
|
||||
baseExpr: "state",
|
||||
expr: "'stuff'",
|
||||
@@ -1394,6 +1506,7 @@ describe("qweb parser", () => {
|
||||
ref: null,
|
||||
tag: "textarea",
|
||||
dynamicTag: null,
|
||||
ns: null,
|
||||
model: {
|
||||
baseExpr: "state",
|
||||
expr: "'stuff'",
|
||||
@@ -1412,6 +1525,7 @@ describe("qweb parser", () => {
|
||||
ref: null,
|
||||
tag: "input",
|
||||
dynamicTag: null,
|
||||
ns: null,
|
||||
model: {
|
||||
baseExpr: "state",
|
||||
expr: "'stuff'",
|
||||
@@ -1430,6 +1544,7 @@ describe("qweb parser", () => {
|
||||
ref: null,
|
||||
tag: "input",
|
||||
dynamicTag: null,
|
||||
ns: null,
|
||||
model: {
|
||||
baseExpr: "state",
|
||||
expr: "'stuff'",
|
||||
@@ -1448,6 +1563,7 @@ describe("qweb parser", () => {
|
||||
ref: null,
|
||||
tag: "input",
|
||||
dynamicTag: null,
|
||||
ns: null,
|
||||
model: {
|
||||
baseExpr: "state",
|
||||
expr: "'stuff'",
|
||||
@@ -1472,6 +1588,7 @@ describe("qweb parser", () => {
|
||||
tag: "div",
|
||||
dynamicTag: "theTag",
|
||||
model: null,
|
||||
ns: null,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
+25
-17
@@ -1,22 +1,30 @@
|
||||
import { renderToString, snapshotEverything } from "../helpers";
|
||||
|
||||
// NB: check the snapshots to see where the SVG namespaces are added
|
||||
snapshotEverything();
|
||||
|
||||
describe("properly support svg", () => {
|
||||
test.skip("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>`
|
||||
// );
|
||||
// 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 svg", () => {
|
||||
const template = `<svg width="100px" height="90px"><circle cx="50" cy="50" r="4" stroke="green" stroke-width="1" fill="yellow"/> </svg>`;
|
||||
expect(renderToString(template)).toBe(
|
||||
`<svg width=\"100px\" height=\"90px\"><circle cx=\"50\" cy=\"50\" r=\"4\" stroke=\"green\" stroke-width=\"1\" fill=\"yellow\"></circle> </svg>`
|
||||
);
|
||||
});
|
||||
|
||||
test.skip("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>`
|
||||
// );
|
||||
test("add proper namespace to g tags", () => {
|
||||
const template = `<g><circle cx="50" cy="50" r="4" stroke="green" stroke-width="1" fill="yellow"/> </g>`;
|
||||
expect(renderToString(template)).toBe(
|
||||
`<g><circle cx=\"50\" cy=\"50\" r=\"4\" stroke=\"green\" stroke-width=\"1\" fill=\"yellow\"></circle> </g>`
|
||||
);
|
||||
});
|
||||
|
||||
test("namespace to g tags not added if already in svg namespace", () => {
|
||||
const template = `<svg><g/></svg>`;
|
||||
expect(renderToString(template)).toBe(`<svg><g></g></svg>`);
|
||||
});
|
||||
|
||||
test("namespace to svg tags added even if already in svg namespace", () => {
|
||||
const template = `<svg><svg/></svg>`;
|
||||
expect(renderToString(template)).toBe(`<svg><svg></svg></svg>`);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1192,7 +1192,7 @@ exports[`support svg components add proper namespace to svg 1`] = `
|
||||
let { text, createBlock, list, multi, html, toggler, component } = bdom;
|
||||
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber, safeOutput } = helpers;
|
||||
|
||||
let block1 = createBlock(\`<g><circle cx=\\"50\\" cy=\\"50\\" r=\\"4\\" stroke=\\"green\\" stroke-width=\\"1\\" fill=\\"yellow\\"/></g>\`);
|
||||
let block1 = createBlock(\`<g block-ns=\\"http://www.w3.org/2000/svg\\"><circle cx=\\"50\\" cy=\\"50\\" r=\\"4\\" stroke=\\"green\\" stroke-width=\\"1\\" fill=\\"yellow\\"/></g>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
return block1();
|
||||
@@ -1206,7 +1206,7 @@ exports[`support svg components add proper namespace to svg 2`] = `
|
||||
let { text, createBlock, list, multi, html, toggler, component } = bdom;
|
||||
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber, safeOutput } = helpers;
|
||||
|
||||
let block1 = createBlock(\`<svg><block-child-0/></svg>\`);
|
||||
let block1 = createBlock(\`<svg block-ns=\\"http://www.w3.org/2000/svg\\"><block-child-0/></svg>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
let b2 = component(\`GComp\`, {}, key + \`__1\`, node, ctx);
|
||||
|
||||
Reference in New Issue
Block a user