Skip to content

Commit 024a6e6

Browse files
wadackelDaniel15
authored andcommitted
Fix bug that tag name of SVG would be lowercase (#138)
1 parent fab1517 commit 024a6e6

File tree

2 files changed

+127
-6
lines changed

2 files changed

+127
-6
lines changed

src/htmltojsx.js

Lines changed: 121 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,104 @@ var ELEMENT_ATTRIBUTE_MAPPING = {
3535
}
3636
};
3737

38+
// Reference: https://developer.mozilla.org/en-US/docs/Web/SVG/Element#SVG_elements
39+
var ELEMENT_TAG_NAME_MAPPING = {
40+
a: 'a',
41+
altglyph: 'altGlyph',
42+
altglyphdef: 'altGlyphDef',
43+
altglyphitem: 'altGlyphItem',
44+
animate: 'animate',
45+
animatecolor: 'animateColor',
46+
animatemotion: 'animateMotion',
47+
animatetransform: 'animateTransform',
48+
audio: 'audio',
49+
canvas: 'canvas',
50+
circle: 'circle',
51+
clippath: 'clipPath',
52+
color-profile: 'color',
53+
cursor: 'cursor',
54+
defs: 'defs',
55+
desc: 'desc',
56+
discard: 'discard',
57+
ellipse: 'ellipse',
58+
feblend: 'feBlend',
59+
fecolormatrix: 'feColorMatrix',
60+
fecomponenttransfer: 'feComponentTransfer',
61+
fecomposite: 'feComposite',
62+
feconvolvematrix: 'feConvolveMatrix',
63+
fediffuselighting: 'feDiffuseLighting',
64+
fedisplacementmap: 'feDisplacementMap',
65+
fedistantlight: 'feDistantLight',
66+
fedropshadow: 'feDropShadow',
67+
feflood: 'feFlood',
68+
fefunca: 'feFuncA',
69+
fefuncb: 'feFuncB',
70+
fefuncg: 'feFuncG',
71+
fefuncr: 'feFuncR',
72+
fegaussianblur: 'feGaussianBlur',
73+
feimage: 'feImage',
74+
femerge: 'feMerge',
75+
femergenode: 'feMergeNode',
76+
femorphology: 'feMorphology',
77+
feoffset: 'feOffset',
78+
fepointlight: 'fePointLight',
79+
fespecularlighting: 'feSpecularLighting',
80+
fespotlight: 'feSpotLight',
81+
fetile: 'feTile',
82+
feturbulence: 'feTurbulence',
83+
filter: 'filter',
84+
font: 'font',
85+
font-face: 'font',
86+
font-face-format: 'font',
87+
font-face-name: 'font',
88+
font-face-src: 'font',
89+
font-face-uri: 'font',
90+
foreignobject: 'foreignObject',
91+
g: 'g',
92+
glyph: 'glyph',
93+
glyphref: 'glyphRef',
94+
hatch: 'hatch',
95+
hatchpath: 'hatchpath',
96+
hkern: 'hkern',
97+
iframe: 'iframe',
98+
image: 'image',
99+
line: 'line',
100+
lineargradient: 'linearGradient',
101+
marker: 'marker',
102+
mask: 'mask',
103+
mesh: 'mesh',
104+
meshgradient: 'meshgradient',
105+
meshpatch: 'meshpatch',
106+
meshrow: 'meshrow',
107+
metadata: 'metadata',
108+
missing-glyph: 'missing',
109+
mpath: 'mpath',
110+
path: 'path',
111+
pattern: 'pattern',
112+
polygon: 'polygon',
113+
polyline: 'polyline',
114+
radialgradient: 'radialGradient',
115+
rect: 'rect',
116+
script: 'script',
117+
set: 'set',
118+
solidcolor: 'solidcolor',
119+
stop: 'stop',
120+
style: 'style',
121+
svg: 'svg',
122+
switch: 'switch',
123+
symbol: 'symbol',
124+
text: 'text',
125+
textpath: 'textPath',
126+
title: 'title',
127+
tref: 'tref',
128+
tspan: 'tspan',
129+
unknown: 'unknown',
130+
use: 'use',
131+
video: 'video',
132+
view: 'view',
133+
vkern: 'vkern'
134+
};
135+
38136
var HTMLDOMPropertyConfig = require('react-dom/lib/HTMLDOMPropertyConfig');
39137
var SVGDOMPropertyConfig = require('react-dom/lib/SVGDOMPropertyConfig');
40138

@@ -67,6 +165,22 @@ function mappingAttributesFromReactConfig(config) {
67165
mappingAttributesFromReactConfig(HTMLDOMPropertyConfig);
68166
mappingAttributesFromReactConfig(SVGDOMPropertyConfig);
69167

168+
/**
169+
* Convert tag name to tag name suitable for JSX.
170+
*
171+
* @param {string} tagName String of tag name
172+
* @return {string}
173+
*/
174+
function jsxTagName(tagName) {
175+
var name = tagName.toLowerCase();
176+
177+
if (ELEMENT_TAG_NAME_MAPPING.hasOwnProperty(name)) {
178+
name = ELEMENT_TAG_NAME_MAPPING[name];
179+
}
180+
181+
return name;
182+
}
183+
70184
/**
71185
* Repeats a string a certain number of times.
72186
* Also: the future is bright and consists of native string repetition:
@@ -379,7 +493,7 @@ HTMLtoJSX.prototype = {
379493
* @param {DOMElement} node
380494
*/
381495
_beginVisitElement: function(node) {
382-
var tagName = node.tagName.toLowerCase();
496+
var tagName = jsxTagName(node.tagName);
383497
var attributes = [];
384498
for (var i = 0, count = node.attributes.length; i < count; i++) {
385499
attributes.push(this._getElementAttribute(node, node.attributes[i]));
@@ -412,14 +526,14 @@ HTMLtoJSX.prototype = {
412526
* @param {Node} node
413527
*/
414528
_endVisitElement: function(node) {
415-
var tagName = node.tagName.toLowerCase();
529+
var tagName = jsxTagName(node.tagName);
416530
// De-indent a bit
417531
// TODO: It's inefficient to do it this way :/
418532
this.output = trimEnd(this.output, this.config.indent);
419533
if (this._isSelfClosing(node)) {
420534
this.output += ' />';
421535
} else {
422-
this.output += '</' + node.tagName.toLowerCase() + '>';
536+
this.output += '</' + tagName + '>';
423537
}
424538

425539
if (tagName === 'pre') {
@@ -435,9 +549,10 @@ HTMLtoJSX.prototype = {
435549
* @return {boolean}
436550
*/
437551
_isSelfClosing: function(node) {
552+
var tagName = jsxTagName(node.tagName);
438553
// If it has children, it's not self-closing
439554
// Exception: All children of a textarea are moved to a "defaultValue" attribute, style attributes are dangerously set.
440-
return !node.firstChild || node.tagName.toLowerCase() === 'textarea' || node.tagName.toLowerCase() === 'style';
555+
return !node.firstChild || tagName === 'textarea' || tagName === 'style';
441556
},
442557

443558
/**
@@ -446,7 +561,7 @@ HTMLtoJSX.prototype = {
446561
* @param {TextNode} node
447562
*/
448563
_visitText: function(node) {
449-
var parentTag = node.parentNode && node.parentNode.tagName.toLowerCase();
564+
var parentTag = node.parentNode && jsxTagName(node.parentNode.tagName);
450565
if (parentTag === 'textarea' || parentTag === 'style') {
451566
// Ignore text content of textareas and styles, as it will have already been moved
452567
// to a "defaultValue" attribute and "dangerouslySetInnerHTML" attribute respectively.
@@ -499,7 +614,7 @@ HTMLtoJSX.prototype = {
499614
case 'style':
500615
return this._getStyleAttribute(attribute.value);
501616
default:
502-
var tagName = node.tagName.toLowerCase();
617+
var tagName = jsxTagName(node.tagName);
503618
var name =
504619
(ELEMENT_ATTRIBUTE_MAPPING[tagName] &&
505620
ELEMENT_ATTRIBUTE_MAPPING[tagName][attribute.name]) ||

test/htmltojsx-test.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,5 +267,11 @@ describe('htmltojsx', function() {
267267
expect(converter.convert('<style>\nh1 {\n background: url(\'http://foo.bar/img.jpg\';\n}\n</style>').trim())
268268
.toBe('<style dangerouslySetInnerHTML={{__html: "\\nh1 {\\n background: url(\'http://foo.bar/img.jpg\';\\n}\\n" }} />');
269269
});
270+
271+
it('should convert svg tag names', function() {
272+
var converter = new HTMLtoJSX({ createClass: false });
273+
expect(converter.convert('<svg><clipPath><feSpotLight><linearGradient></linearGradient></feSpotLight></clipPath></svg>').trim())
274+
.toBe('<svg><clipPath><feSpotLight><linearGradient /></feSpotLight></clipPath></svg>');
275+
});
270276
});
271277
});

0 commit comments

Comments
 (0)