Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
127 changes: 127 additions & 0 deletions src/jsx-runtime.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1033,6 +1033,133 @@ describe('automatic runtime', () => {
});
});

test('nested SVG', () => {
expect(
<svg id="foo" className="wrapper" viewBox="25 25 50 50">
<circle id="baz" className="circle" cx="50" cy="50" r="20" />
<svg id="bar" className="inner" viewBox="1 2 3 4">
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the first case that’s fixed by my PR: an svg within an svg. Formerly, the inner svg was having its attributes erased due to double conversion.

<circle id="garply" className="round" cx="10" cy="20" r="30" />
</svg>
<svg viewBox="5 6 7 8">
<circle id="ding" className="ball" cx="40" cy="50" r="60" />
</svg>
<foreignObject id="xyzzy" className="abc">
<a href="quux"/>
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the second case that I’m fixing: an HTML tag within a foreign object was also having its props erased.

</foreignObject>
</svg>
).toStrictEqual({
children: [
{
children: undefined,
data: {
attrs: {
cx: '50',
cy: '50',
r: '20',
},
ns: 'http://www.w3.org/2000/svg',
},
elm: undefined,
key: undefined,
sel: 'circle#baz.circle',
text: undefined,
},
{
children: [
{
children: undefined,
data: {
attrs: {
cx: '10',
cy: '20',
r: '30',
},
ns: 'http://www.w3.org/2000/svg',
},
elm: undefined,
key: undefined,
sel: 'circle#garply.round',
text: undefined,
}
],
data: {
attrs: {
viewBox: '1 2 3 4',
},
ns: 'http://www.w3.org/2000/svg',
},
elm: undefined,
key: undefined,
sel: 'svg#bar.inner',
text: undefined,
},
{
children: [
{
children: undefined,
data: {
attrs: {
cx: '40',
cy: '50',
r: '60',
},
ns: 'http://www.w3.org/2000/svg',
},
elm: undefined,
key: undefined,
sel: 'circle#ding.ball',
text: undefined,
}
],
data: {
attrs: {
viewBox: '5 6 7 8',
},
ns: 'http://www.w3.org/2000/svg',
},
elm: undefined,
key: undefined,
sel: 'svg',
text: undefined,
},
{
children: [
{
children: undefined,
data: {
props: {
href: 'quux',
}
},
elm: undefined,
key: undefined,
sel: 'a',
text: undefined,
}
],
data: {
attrs: {},
ns: 'http://www.w3.org/2000/svg',
},
elm: undefined,
key: undefined,
sel: 'foreignObject#xyzzy.abc',
text: undefined,
},
],
data: {
attrs: {
viewBox: '25 25 50 50',
},
ns: 'http://www.w3.org/2000/svg',
},
elm: undefined,
key: undefined,
sel: 'svg#foo.wrapper',
text: undefined,
});
});

test('popover API', () => {
expect(
<>
Expand Down
14 changes: 13 additions & 1 deletion src/jsx-runtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1205,6 +1205,14 @@ const canonicalizeVNodeData = (orig: VNodeData): VNodeData => {
return data;
};

const tagFromSel = (sel: String | undefined): String | undefined => {
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added this utility method so that considerSVG can easily determine what its child tags are. I couldn’t think of a simpler way to do this, but I’m open to suggestions.

if (!sel) {
return undefined;
} else {
return sel.split(/[.#]/)[0];
}
}

const considerSVG = (vnode: VNode): VNode => {
const { props: { className = undefined, ...attrs } = {}, ...data } = vnode.data ?? {};

Expand All @@ -1215,7 +1223,11 @@ const considerSVG = (vnode: VNode): VNode => {
attrs,
ns: 'http://www.w3.org/2000/svg',
},
children: vnode.children?.map((child) => (typeof child === 'string' ? child : considerSVG(child))),
children: vnode.children?.map((child) =>
// Process children, but don't double-process nested svg elements that have already been processed,
// and don't process contents of foreignObject (which are not svg themselves).
(typeof child === 'string' || tagFromSel(child.sel) === 'svg' || tagFromSel(vnode.sel) === 'foreignObject'
? child : considerSVG(child))),
};
};

Expand Down