Skip to content

Commit

Permalink
Merge pull request #5714 from gaearon/passthrough-svg-attributes
Browse files Browse the repository at this point in the history
Pass all SVG attributes through
  • Loading branch information
gaearon committed Dec 25, 2015
2 parents 82fe64a + 98a7100 commit 53dabe7
Show file tree
Hide file tree
Showing 8 changed files with 332 additions and 38 deletions.
39 changes: 39 additions & 0 deletions src/renderers/dom/client/__tests__/ReactDOMSVG-test.js
Expand Up @@ -21,6 +21,45 @@ describe('ReactDOMSVG', function() {
ReactDOMServer = require('ReactDOMServer');
});

it('creates initial markup for known hyphenated attributes', function() {
var markup = ReactDOMServer.renderToString(
<svg clip-path="url(#starlet)" />
);
expect(markup).toContain('clip-path="url(#starlet)"');
});

it('creates initial markup for camel case attributes', function() {
var markup = ReactDOMServer.renderToString(
<svg viewBox="0 0 100 100" />
);
expect(markup).toContain('viewBox="0 0 100 100"');
});

it('deprecates camel casing of hyphenated attributes', function() {
spyOn(console, 'error');
var markup = ReactDOMServer.renderToString(
<svg clipPath="url(#starlet)" />
);
expect(markup).toContain('clip-path="url(#starlet)"');
expect(console.error.argsForCall.length).toBe(1);
expect(console.error.argsForCall[0][0]).toContain('clipPath');
expect(console.error.argsForCall[0][0]).toContain('clip-path');
});

it('creates initial markup for unknown hyphenated attributes', function() {
var markup = ReactDOMServer.renderToString(
<svg the-word="the-bird" />
);
expect(markup).toContain('the-word="the-bird"');
});

it('creates initial markup for unknown camel case attributes', function() {
var markup = ReactDOMServer.renderToString(
<svg theWord="theBird" />
);
expect(markup).toContain('theWord="theBird"');
});

it('creates initial namespaced markup', function() {
var markup = ReactDOMServer.renderToString(
<svg>
Expand Down
47 changes: 47 additions & 0 deletions src/renderers/dom/shared/DOMPropertyOperations.js
Expand Up @@ -124,6 +124,31 @@ var DOMPropertyOperations = {
return name + '=' + quoteAttributeValueForBrowser(value);
},

/**
* Creates markup for an SVG property.
*
* @param {string} name
* @param {*} value
* @return {string} Markup string, or empty string if the property was invalid.
*/
createMarkupForSVGAttribute: function(name, value) {
if (__DEV__) {
ReactDOMInstrumentation.debugTool.onCreateMarkupForSVGAttribute(name, value);
}
if (!isAttributeNameSafe(name) || value == null) {
return '';
}
var propertyInfo = DOMProperty.properties.hasOwnProperty(name) ?
DOMProperty.properties[name] : null;
if (propertyInfo) {
// Migration path for deprecated camelCase aliases for SVG attributes
var { attributeName } = propertyInfo;
return attributeName + '=' + quoteAttributeValueForBrowser(value);
} else {
return name + '=' + quoteAttributeValueForBrowser(value);
}
},

/**
* Sets the value for a property on a node.
*
Expand Down Expand Up @@ -183,6 +208,18 @@ var DOMPropertyOperations = {
}
},

setValueForSVGAttribute: function(node, name, value) {
if (__DEV__) {
ReactDOMInstrumentation.debugTool.onSetValueForSVGAttribute(node, name, value);
}
if (DOMProperty.properties.hasOwnProperty(name)) {
// Migration path for deprecated camelCase aliases for SVG attributes
DOMPropertyOperations.setValueForProperty(node, name, value);
} else {
DOMPropertyOperations.setValueForAttribute(node, name, value);
}
},

/**
* Deletes the value for a property on a node.
*
Expand Down Expand Up @@ -217,6 +254,16 @@ var DOMPropertyOperations = {
}
},

deleteValueForSVGAttribute: function(node, name) {
var propertyInfo = DOMProperty.properties.hasOwnProperty(name) ?
DOMProperty.properties[name] : null;
if (propertyInfo) {
DOMPropertyOperations.deleteValueForProperty(node, name);
} else {
node.removeAttribute(name);
}
},

};

ReactPerf.measureMethods(DOMPropertyOperations, 'DOMPropertyOperations', {
Expand Down
13 changes: 13 additions & 0 deletions src/renderers/dom/shared/ReactDOMComponent.js
Expand Up @@ -647,6 +647,8 @@ ReactDOMComponent.Mixin = {
if (propKey !== CHILDREN) {
markup = DOMPropertyOperations.createMarkupForCustomAttribute(propKey, propValue);
}
} else if (this._namespaceURI === DOMNamespaces.svg) {
markup = DOMPropertyOperations.createMarkupForSVGAttribute(propKey, propValue);
} else {
markup = DOMPropertyOperations.createMarkupForProperty(propKey, propValue);
}
Expand Down Expand Up @@ -858,6 +860,11 @@ ReactDOMComponent.Mixin = {
// listener (e.g., onClick={null})
deleteListener(this, propKey);
}
} else if (this._namespaceURI === DOMNamespaces.svg) {
DOMPropertyOperations.deleteValueForSVGAttribute(
getNode(this),
propKey
);
} else if (
DOMProperty.properties[propKey] ||
DOMProperty.isCustomAttribute(propKey)) {
Expand Down Expand Up @@ -924,6 +931,12 @@ ReactDOMComponent.Mixin = {
propKey,
nextProp
);
} else if (this._namespaceURI === DOMNamespaces.svg) {
DOMPropertyOperations.setValueForSVGAttribute(
getNode(this),
propKey,
nextProp
);
} else if (
DOMProperty.properties[propKey] ||
DOMProperty.isCustomAttribute(propKey)) {
Expand Down
9 changes: 9 additions & 0 deletions src/renderers/dom/shared/ReactDOMDebugTool.js
Expand Up @@ -12,6 +12,8 @@
'use strict';

var ReactDOMUnknownPropertyDevtool = require('ReactDOMUnknownPropertyDevtool');
var ReactDOMSVGDeprecatedAttributeDevtool =
require('ReactDOMSVGDeprecatedAttributeDevtool');

var warning = require('warning');

Expand Down Expand Up @@ -53,14 +55,21 @@ var ReactDOMDebugTool = {
onCreateMarkupForProperty(name, value) {
emitEvent('onCreateMarkupForProperty', name, value);
},
onCreateMarkupForSVGAttribute(name, value) {
emitEvent('onCreateMarkupForSVGAttribute', name, value);
},
onSetValueForProperty(node, name, value) {
emitEvent('onSetValueForProperty', node, name, value);
},
onSetValueForSVGAttribute(node, name, value) {
emitEvent('onSetValueForSVGAttribute', node, name, value);
},
onDeleteValueForProperty(node, name) {
emitEvent('onDeleteValueForProperty', node, name);
},
};

ReactDOMDebugTool.addDevtool(ReactDOMUnknownPropertyDevtool);
ReactDOMDebugTool.addDevtool(ReactDOMSVGDeprecatedAttributeDevtool);

module.exports = ReactDOMDebugTool;
37 changes: 0 additions & 37 deletions src/renderers/dom/shared/SVGDOMPropertyConfig.js
Expand Up @@ -23,46 +23,19 @@ var NS = {
var SVGDOMPropertyConfig = {
Properties: {
clipPath: MUST_USE_ATTRIBUTE,
cx: MUST_USE_ATTRIBUTE,
cy: MUST_USE_ATTRIBUTE,
d: MUST_USE_ATTRIBUTE,
dx: MUST_USE_ATTRIBUTE,
dy: MUST_USE_ATTRIBUTE,
fill: MUST_USE_ATTRIBUTE,
fillOpacity: MUST_USE_ATTRIBUTE,
fontFamily: MUST_USE_ATTRIBUTE,
fontSize: MUST_USE_ATTRIBUTE,
fx: MUST_USE_ATTRIBUTE,
fy: MUST_USE_ATTRIBUTE,
gradientTransform: MUST_USE_ATTRIBUTE,
gradientUnits: MUST_USE_ATTRIBUTE,
markerEnd: MUST_USE_ATTRIBUTE,
markerMid: MUST_USE_ATTRIBUTE,
markerStart: MUST_USE_ATTRIBUTE,
offset: MUST_USE_ATTRIBUTE,
opacity: MUST_USE_ATTRIBUTE,
patternContentUnits: MUST_USE_ATTRIBUTE,
patternUnits: MUST_USE_ATTRIBUTE,
points: MUST_USE_ATTRIBUTE,
preserveAspectRatio: MUST_USE_ATTRIBUTE,
r: MUST_USE_ATTRIBUTE,
rx: MUST_USE_ATTRIBUTE,
ry: MUST_USE_ATTRIBUTE,
spreadMethod: MUST_USE_ATTRIBUTE,
stopColor: MUST_USE_ATTRIBUTE,
stopOpacity: MUST_USE_ATTRIBUTE,
stroke: MUST_USE_ATTRIBUTE,
strokeDasharray: MUST_USE_ATTRIBUTE,
strokeLinecap: MUST_USE_ATTRIBUTE,
strokeOpacity: MUST_USE_ATTRIBUTE,
strokeWidth: MUST_USE_ATTRIBUTE,
textAnchor: MUST_USE_ATTRIBUTE,
transform: MUST_USE_ATTRIBUTE,
version: MUST_USE_ATTRIBUTE,
viewBox: MUST_USE_ATTRIBUTE,
x1: MUST_USE_ATTRIBUTE,
x2: MUST_USE_ATTRIBUTE,
x: MUST_USE_ATTRIBUTE,
xlinkActuate: MUST_USE_ATTRIBUTE,
xlinkArcrole: MUST_USE_ATTRIBUTE,
xlinkHref: MUST_USE_ATTRIBUTE,
Expand All @@ -73,9 +46,6 @@ var SVGDOMPropertyConfig = {
xmlBase: MUST_USE_ATTRIBUTE,
xmlLang: MUST_USE_ATTRIBUTE,
xmlSpace: MUST_USE_ATTRIBUTE,
y1: MUST_USE_ATTRIBUTE,
y2: MUST_USE_ATTRIBUTE,
y: MUST_USE_ATTRIBUTE,
},
DOMAttributeNamespaces: {
xlinkActuate: NS.xlink,
Expand All @@ -94,23 +64,16 @@ var SVGDOMPropertyConfig = {
fillOpacity: 'fill-opacity',
fontFamily: 'font-family',
fontSize: 'font-size',
gradientTransform: 'gradientTransform',
gradientUnits: 'gradientUnits',
markerEnd: 'marker-end',
markerMid: 'marker-mid',
markerStart: 'marker-start',
patternContentUnits: 'patternContentUnits',
patternUnits: 'patternUnits',
preserveAspectRatio: 'preserveAspectRatio',
spreadMethod: 'spreadMethod',
stopColor: 'stop-color',
stopOpacity: 'stop-opacity',
strokeDasharray: 'stroke-dasharray',
strokeLinecap: 'stroke-linecap',
strokeOpacity: 'stroke-opacity',
strokeWidth: 'stroke-width',
textAnchor: 'text-anchor',
viewBox: 'viewBox',
xlinkActuate: 'xlink:actuate',
xlinkArcrole: 'xlink:arcrole',
xlinkHref: 'xlink:href',
Expand Down

0 comments on commit 53dabe7

Please sign in to comment.