Skip to content

Commit 53dabe7

Browse files
committed
Merge pull request facebook#5714 from gaearon/passthrough-svg-attributes
Pass all SVG attributes through
2 parents 82fe64a + 98a7100 commit 53dabe7

File tree

8 files changed

+332
-38
lines changed

8 files changed

+332
-38
lines changed

src/renderers/dom/client/__tests__/ReactDOMSVG-test.js

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,45 @@ describe('ReactDOMSVG', function() {
2121
ReactDOMServer = require('ReactDOMServer');
2222
});
2323

24+
it('creates initial markup for known hyphenated attributes', function() {
25+
var markup = ReactDOMServer.renderToString(
26+
<svg clip-path="url(#starlet)" />
27+
);
28+
expect(markup).toContain('clip-path="url(#starlet)"');
29+
});
30+
31+
it('creates initial markup for camel case attributes', function() {
32+
var markup = ReactDOMServer.renderToString(
33+
<svg viewBox="0 0 100 100" />
34+
);
35+
expect(markup).toContain('viewBox="0 0 100 100"');
36+
});
37+
38+
it('deprecates camel casing of hyphenated attributes', function() {
39+
spyOn(console, 'error');
40+
var markup = ReactDOMServer.renderToString(
41+
<svg clipPath="url(#starlet)" />
42+
);
43+
expect(markup).toContain('clip-path="url(#starlet)"');
44+
expect(console.error.argsForCall.length).toBe(1);
45+
expect(console.error.argsForCall[0][0]).toContain('clipPath');
46+
expect(console.error.argsForCall[0][0]).toContain('clip-path');
47+
});
48+
49+
it('creates initial markup for unknown hyphenated attributes', function() {
50+
var markup = ReactDOMServer.renderToString(
51+
<svg the-word="the-bird" />
52+
);
53+
expect(markup).toContain('the-word="the-bird"');
54+
});
55+
56+
it('creates initial markup for unknown camel case attributes', function() {
57+
var markup = ReactDOMServer.renderToString(
58+
<svg theWord="theBird" />
59+
);
60+
expect(markup).toContain('theWord="theBird"');
61+
});
62+
2463
it('creates initial namespaced markup', function() {
2564
var markup = ReactDOMServer.renderToString(
2665
<svg>

src/renderers/dom/shared/DOMPropertyOperations.js

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,31 @@ var DOMPropertyOperations = {
124124
return name + '=' + quoteAttributeValueForBrowser(value);
125125
},
126126

127+
/**
128+
* Creates markup for an SVG property.
129+
*
130+
* @param {string} name
131+
* @param {*} value
132+
* @return {string} Markup string, or empty string if the property was invalid.
133+
*/
134+
createMarkupForSVGAttribute: function(name, value) {
135+
if (__DEV__) {
136+
ReactDOMInstrumentation.debugTool.onCreateMarkupForSVGAttribute(name, value);
137+
}
138+
if (!isAttributeNameSafe(name) || value == null) {
139+
return '';
140+
}
141+
var propertyInfo = DOMProperty.properties.hasOwnProperty(name) ?
142+
DOMProperty.properties[name] : null;
143+
if (propertyInfo) {
144+
// Migration path for deprecated camelCase aliases for SVG attributes
145+
var { attributeName } = propertyInfo;
146+
return attributeName + '=' + quoteAttributeValueForBrowser(value);
147+
} else {
148+
return name + '=' + quoteAttributeValueForBrowser(value);
149+
}
150+
},
151+
127152
/**
128153
* Sets the value for a property on a node.
129154
*
@@ -183,6 +208,18 @@ var DOMPropertyOperations = {
183208
}
184209
},
185210

211+
setValueForSVGAttribute: function(node, name, value) {
212+
if (__DEV__) {
213+
ReactDOMInstrumentation.debugTool.onSetValueForSVGAttribute(node, name, value);
214+
}
215+
if (DOMProperty.properties.hasOwnProperty(name)) {
216+
// Migration path for deprecated camelCase aliases for SVG attributes
217+
DOMPropertyOperations.setValueForProperty(node, name, value);
218+
} else {
219+
DOMPropertyOperations.setValueForAttribute(node, name, value);
220+
}
221+
},
222+
186223
/**
187224
* Deletes the value for a property on a node.
188225
*
@@ -217,6 +254,16 @@ var DOMPropertyOperations = {
217254
}
218255
},
219256

257+
deleteValueForSVGAttribute: function(node, name) {
258+
var propertyInfo = DOMProperty.properties.hasOwnProperty(name) ?
259+
DOMProperty.properties[name] : null;
260+
if (propertyInfo) {
261+
DOMPropertyOperations.deleteValueForProperty(node, name);
262+
} else {
263+
node.removeAttribute(name);
264+
}
265+
},
266+
220267
};
221268

222269
ReactPerf.measureMethods(DOMPropertyOperations, 'DOMPropertyOperations', {

src/renderers/dom/shared/ReactDOMComponent.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -647,6 +647,8 @@ ReactDOMComponent.Mixin = {
647647
if (propKey !== CHILDREN) {
648648
markup = DOMPropertyOperations.createMarkupForCustomAttribute(propKey, propValue);
649649
}
650+
} else if (this._namespaceURI === DOMNamespaces.svg) {
651+
markup = DOMPropertyOperations.createMarkupForSVGAttribute(propKey, propValue);
650652
} else {
651653
markup = DOMPropertyOperations.createMarkupForProperty(propKey, propValue);
652654
}
@@ -858,6 +860,11 @@ ReactDOMComponent.Mixin = {
858860
// listener (e.g., onClick={null})
859861
deleteListener(this, propKey);
860862
}
863+
} else if (this._namespaceURI === DOMNamespaces.svg) {
864+
DOMPropertyOperations.deleteValueForSVGAttribute(
865+
getNode(this),
866+
propKey
867+
);
861868
} else if (
862869
DOMProperty.properties[propKey] ||
863870
DOMProperty.isCustomAttribute(propKey)) {
@@ -924,6 +931,12 @@ ReactDOMComponent.Mixin = {
924931
propKey,
925932
nextProp
926933
);
934+
} else if (this._namespaceURI === DOMNamespaces.svg) {
935+
DOMPropertyOperations.setValueForSVGAttribute(
936+
getNode(this),
937+
propKey,
938+
nextProp
939+
);
927940
} else if (
928941
DOMProperty.properties[propKey] ||
929942
DOMProperty.isCustomAttribute(propKey)) {

src/renderers/dom/shared/ReactDOMDebugTool.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
'use strict';
1313

1414
var ReactDOMUnknownPropertyDevtool = require('ReactDOMUnknownPropertyDevtool');
15+
var ReactDOMSVGDeprecatedAttributeDevtool =
16+
require('ReactDOMSVGDeprecatedAttributeDevtool');
1517

1618
var warning = require('warning');
1719

@@ -53,14 +55,21 @@ var ReactDOMDebugTool = {
5355
onCreateMarkupForProperty(name, value) {
5456
emitEvent('onCreateMarkupForProperty', name, value);
5557
},
58+
onCreateMarkupForSVGAttribute(name, value) {
59+
emitEvent('onCreateMarkupForSVGAttribute', name, value);
60+
},
5661
onSetValueForProperty(node, name, value) {
5762
emitEvent('onSetValueForProperty', node, name, value);
5863
},
64+
onSetValueForSVGAttribute(node, name, value) {
65+
emitEvent('onSetValueForSVGAttribute', node, name, value);
66+
},
5967
onDeleteValueForProperty(node, name) {
6068
emitEvent('onDeleteValueForProperty', node, name);
6169
},
6270
};
6371

6472
ReactDOMDebugTool.addDevtool(ReactDOMUnknownPropertyDevtool);
73+
ReactDOMDebugTool.addDevtool(ReactDOMSVGDeprecatedAttributeDevtool);
6574

6675
module.exports = ReactDOMDebugTool;

src/renderers/dom/shared/SVGDOMPropertyConfig.js

Lines changed: 0 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -23,46 +23,19 @@ var NS = {
2323
var SVGDOMPropertyConfig = {
2424
Properties: {
2525
clipPath: MUST_USE_ATTRIBUTE,
26-
cx: MUST_USE_ATTRIBUTE,
27-
cy: MUST_USE_ATTRIBUTE,
28-
d: MUST_USE_ATTRIBUTE,
29-
dx: MUST_USE_ATTRIBUTE,
30-
dy: MUST_USE_ATTRIBUTE,
31-
fill: MUST_USE_ATTRIBUTE,
3226
fillOpacity: MUST_USE_ATTRIBUTE,
3327
fontFamily: MUST_USE_ATTRIBUTE,
3428
fontSize: MUST_USE_ATTRIBUTE,
35-
fx: MUST_USE_ATTRIBUTE,
36-
fy: MUST_USE_ATTRIBUTE,
37-
gradientTransform: MUST_USE_ATTRIBUTE,
38-
gradientUnits: MUST_USE_ATTRIBUTE,
3929
markerEnd: MUST_USE_ATTRIBUTE,
4030
markerMid: MUST_USE_ATTRIBUTE,
4131
markerStart: MUST_USE_ATTRIBUTE,
42-
offset: MUST_USE_ATTRIBUTE,
43-
opacity: MUST_USE_ATTRIBUTE,
44-
patternContentUnits: MUST_USE_ATTRIBUTE,
45-
patternUnits: MUST_USE_ATTRIBUTE,
46-
points: MUST_USE_ATTRIBUTE,
47-
preserveAspectRatio: MUST_USE_ATTRIBUTE,
48-
r: MUST_USE_ATTRIBUTE,
49-
rx: MUST_USE_ATTRIBUTE,
50-
ry: MUST_USE_ATTRIBUTE,
51-
spreadMethod: MUST_USE_ATTRIBUTE,
5232
stopColor: MUST_USE_ATTRIBUTE,
5333
stopOpacity: MUST_USE_ATTRIBUTE,
54-
stroke: MUST_USE_ATTRIBUTE,
5534
strokeDasharray: MUST_USE_ATTRIBUTE,
5635
strokeLinecap: MUST_USE_ATTRIBUTE,
5736
strokeOpacity: MUST_USE_ATTRIBUTE,
5837
strokeWidth: MUST_USE_ATTRIBUTE,
5938
textAnchor: MUST_USE_ATTRIBUTE,
60-
transform: MUST_USE_ATTRIBUTE,
61-
version: MUST_USE_ATTRIBUTE,
62-
viewBox: MUST_USE_ATTRIBUTE,
63-
x1: MUST_USE_ATTRIBUTE,
64-
x2: MUST_USE_ATTRIBUTE,
65-
x: MUST_USE_ATTRIBUTE,
6639
xlinkActuate: MUST_USE_ATTRIBUTE,
6740
xlinkArcrole: MUST_USE_ATTRIBUTE,
6841
xlinkHref: MUST_USE_ATTRIBUTE,
@@ -73,9 +46,6 @@ var SVGDOMPropertyConfig = {
7346
xmlBase: MUST_USE_ATTRIBUTE,
7447
xmlLang: MUST_USE_ATTRIBUTE,
7548
xmlSpace: MUST_USE_ATTRIBUTE,
76-
y1: MUST_USE_ATTRIBUTE,
77-
y2: MUST_USE_ATTRIBUTE,
78-
y: MUST_USE_ATTRIBUTE,
7949
},
8050
DOMAttributeNamespaces: {
8151
xlinkActuate: NS.xlink,
@@ -94,23 +64,16 @@ var SVGDOMPropertyConfig = {
9464
fillOpacity: 'fill-opacity',
9565
fontFamily: 'font-family',
9666
fontSize: 'font-size',
97-
gradientTransform: 'gradientTransform',
98-
gradientUnits: 'gradientUnits',
9967
markerEnd: 'marker-end',
10068
markerMid: 'marker-mid',
10169
markerStart: 'marker-start',
102-
patternContentUnits: 'patternContentUnits',
103-
patternUnits: 'patternUnits',
104-
preserveAspectRatio: 'preserveAspectRatio',
105-
spreadMethod: 'spreadMethod',
10670
stopColor: 'stop-color',
10771
stopOpacity: 'stop-opacity',
10872
strokeDasharray: 'stroke-dasharray',
10973
strokeLinecap: 'stroke-linecap',
11074
strokeOpacity: 'stroke-opacity',
11175
strokeWidth: 'stroke-width',
11276
textAnchor: 'text-anchor',
113-
viewBox: 'viewBox',
11477
xlinkActuate: 'xlink:actuate',
11578
xlinkArcrole: 'xlink:arcrole',
11679
xlinkHref: 'xlink:href',

0 commit comments

Comments
 (0)