@samitouri / QOS-React-2 / commits / a88b655ccd

Added support for namespace attributes

Federico Rampazzo committed May 5, 2015 at 16:08 UTC a88b655ccd6f6519fb25fb8e81161f80274b9b02
6 files changed +162 -3
src/browser/__tests__/ReactDOMSVG-test.js new
+90
@@ -0,0 +1,90 @@
1 +/**
2 + * Copyright 2013-2015, Facebook, Inc.
3 + * All rights reserved.
4 + *
5 + * This source code is licensed under the BSD-style license found in the
6 + * LICENSE file in the root directory of this source tree. An additional grant
7 + * of patent rights can be found in the PATENTS file in the same directory.
8 + *
9 + * @emails react-core
10 + */
11 +
12 +/*jslint evil: true */
13 +
14 +'use strict';
15 +
16 +var React = require('React');
17 +var ReactTestUtils = require('ReactTestUtils');
18 +
19 +var SVGDOMNamespaces = {
20 + xlink: 'http://www.w3.org/1999/xlink',
21 + xml: 'http://www.w3.org/XML/1998/namespace',
22 + xmlns: 'http://www.w3.org/2000/xmlns/'
23 +};
24 +
25 +describe('ReactDOMSVG', function() {
26 + it("allows a SVG element", function() {
27 + var element = React.createElement('svg', {xmlnsXlink: 'http://www.w3.org/1999/xlink'});
28 + var instance = ReactTestUtils.renderIntoDocument(element);
29 + var svg = React.findDOMNode(instance);
30 + expect(svg.tagName).toBe('svg');
31 + var xlink = svg.getAttributeNS(SVGDOMNamespaces.xmlns, 'xlink');
32 + expect(xlink).toBe('http://www.w3.org/1999/xlink');
33 + });
34 +
35 + it("allows a SVG element with an image node", function() {
36 + var instance = ReactTestUtils.renderIntoDocument(
37 + <svg xmlnsXlink='http://www.w3.org/1999/xlink'>
38 + <image xlinkHref="http://i.imgur.com/w7GCRPb.png" />
39 + </svg>
40 + );
41 + var svg = React.findDOMNode(instance);
42 + var image = svg.childNodes[0];
43 + expect(image.tagName).toBe('image');
44 + var href = image.getAttributeNS(SVGDOMNamespaces.xlink, 'href');
45 + expect(href).toBe('http://i.imgur.com/w7GCRPb.png');
46 + });
47 +
48 + it("allows a SVG element with a link", function() {
49 + var instance = ReactTestUtils.renderIntoDocument(
50 + <svg xmlnsXlink='http://www.w3.org/1999/xlink'>
51 + <g>
52 + <a
53 + xlinkHref="http://facebook.github.io/react/"
54 + xlinkActuate="onRequest"
55 + xlinkShow="new"
56 + xlinkArcrole="http://example.com/iri-arcrole-reference.svg"
57 + xlinkRole="http://example.com/iri-role-reference.svg"
58 + xlinkTitle="React"
59 + xlinkType="simple"
60 + >
61 + <text xmlLang="en-US" xmlSpace="preserve" x="0" y="15" fill="black">React</text>
62 + </a>
63 + </g>
64 + </svg>
65 + );
66 + var svg = React.findDOMNode(instance);
67 + var link = svg.childNodes[0].childNodes[0];
68 + expect(link.tagName).toBe('a');
69 + var href = link.getAttributeNS(SVGDOMNamespaces.xlink, 'href');
70 + expect(href).toBe('http://facebook.github.io/react/');
71 + var actuate = link.getAttributeNS(SVGDOMNamespaces.xlink, 'actuate');
72 + expect(actuate).toBe('onRequest');
73 + var show = link.getAttributeNS(SVGDOMNamespaces.xlink, 'show');
74 + expect(show).toBe('new');
75 + var arcrole = link.getAttributeNS(SVGDOMNamespaces.xlink, 'arcrole');
76 + expect(arcrole).toBe('http://example.com/iri-arcrole-reference.svg');
77 + var role = link.getAttributeNS(SVGDOMNamespaces.xlink, 'role');
78 + expect(role).toBe('http://example.com/iri-role-reference.svg');
79 + var title = link.getAttributeNS(SVGDOMNamespaces.xlink, 'title');
80 + expect(title).toBe('React');
81 + var type = link.getAttributeNS(SVGDOMNamespaces.xlink, 'type');
82 + expect(type).toBe('simple');
83 + var text = link.childNodes[0];
84 + var lang = text.getAttributeNS(SVGDOMNamespaces.xml, 'lang');
85 + expect(lang).toBe('en-US');
86 + var space = text.getAttributeNS(SVGDOMNamespaces.xml, 'space');
87 + expect(space).toBe('preserve');
88 + });
89 +
90 +});
\ No newline at end of file
src/browser/ui/dom/DOMProperty.js
+12
@@ -60,6 +60,7 @@ var DOMPropertyInjection = {
60 */
61 injectDOMPropertyConfig: function(domPropertyConfig) {
62 var Properties = domPropertyConfig.Properties || {};
63 + var PropertyNamespaces = domPropertyConfig.PropertyNamespaces || {};
64 var DOMAttributeNames = domPropertyConfig.DOMAttributeNames || {};
65 var DOMPropertyNames = domPropertyConfig.DOMPropertyNames || {};
66 var DOMMutationMethods = domPropertyConfig.DOMMutationMethods || {};
@@ -93,6 +94,11 @@ var DOMPropertyInjection = {
94 DOMProperty.getAttributeName[propName] = lowerCased;
95 }
96
97 + if (PropertyNamespaces.hasOwnProperty(propName)) {
98 + DOMProperty.getAttributeNamespace[propName] =
99 + PropertyNamespaces[propName];
100 + }
101 +
102 DOMProperty.getPropertyName[propName] =
103 DOMPropertyNames.hasOwnProperty(propName) ?
104 DOMPropertyNames[propName] :
@@ -182,6 +188,12 @@ var DOMProperty = {
188 */
189 getAttributeName: {},
190
191 + /**
192 + * Mapping from normalized names to namespaces.
193 + * @type {Object}
194 + */
195 + getAttributeNamespace: {},
196 +
197 /**
198 * Mapping from normalized names to properties on DOM node instances.
199 * (This includes properties that mutate due to external factors.)
src/browser/ui/dom/DOMPropertyOperations.js
+7 -1
@@ -126,9 +126,15 @@ var DOMPropertyOperations = {
126 } else if (shouldIgnoreValue(name, value)) {
127 this.deleteValueForProperty(node, name);
128 } else if (DOMProperty.mustUseAttribute[name]) {
129 + var attributeName = DOMProperty.getAttributeName[name];
130 + var namespace = DOMProperty.getAttributeNamespace[name];
131 // `setAttribute` with objects becomes only `[object]` in IE8/9,
132 // ('' + value) makes it output the correct toString()-value.
131 - node.setAttribute(DOMProperty.getAttributeName[name], '' + value);
133 + if (namespace) {
134 + node.setAttributeNS(namespace, attributeName, '' + value);
135 + } else {
136 + node.setAttribute(attributeName, '' + value);
137 + }
138 } else {
139 var propName = DOMProperty.getPropertyName[name];
140 // Must explicitly cast values for HAS_SIDE_EFFECTS-properties to the
src/browser/ui/dom/HTMLDOMPropertyConfig.js
+4 -1
@@ -39,7 +39,6 @@ if (ExecutionEnvironment.canUseDOM) {
39 );
40 }
41
42 -
42 var HTMLDOMPropertyConfig = {
43 isCustomAttribute: RegExp.prototype.test.bind(
44 /^(data|aria)-[a-z_][a-z\d_.\-]*$/
@@ -160,6 +159,7 @@ var HTMLDOMPropertyConfig = {
159 value: MUST_USE_PROPERTY | HAS_SIDE_EFFECTS,
160 width: MUST_USE_ATTRIBUTE,
161 wmode: MUST_USE_ATTRIBUTE,
162 + xmlns: MUST_USE_ATTRIBUTE,
163
164 /**
165 * Non-standard Properties
@@ -183,6 +183,9 @@ var HTMLDOMPropertyConfig = {
183 // IE-only attribute that controls focus behavior
184 unselectable: MUST_USE_ATTRIBUTE
185 },
186 + PropertyNamespaces: {
187 + xmlns: 'http://www.w3.org/2000/xmlns/'
188 + },
189 DOMAttributeNames: {
190 acceptCharset: 'accept-charset',
191 className: 'class',
src/browser/ui/dom/SVGDOMPropertyConfig.js
+42 -1
@@ -17,6 +17,12 @@ var DOMProperty = require('DOMProperty');
17
18 var MUST_USE_ATTRIBUTE = DOMProperty.injection.MUST_USE_ATTRIBUTE;
19
20 +var SVGDOMNamespaces = {
21 + xlink: 'http://www.w3.org/1999/xlink',
22 + xml: 'http://www.w3.org/XML/1998/namespace',
23 + xmlns: 'http://www.w3.org/2000/xmlns/'
24 +};
25 +
26 var SVGDOMPropertyConfig = {
27 Properties: {
28 clipPath: MUST_USE_ATTRIBUTE,
@@ -60,10 +66,34 @@ var SVGDOMPropertyConfig = {
66 x1: MUST_USE_ATTRIBUTE,
67 x2: MUST_USE_ATTRIBUTE,
68 x: MUST_USE_ATTRIBUTE,
69 + xlinkActuate: MUST_USE_ATTRIBUTE,
70 + xlinkArcrole: MUST_USE_ATTRIBUTE,
71 + xlinkHref: MUST_USE_ATTRIBUTE,
72 + xlinkRole: MUST_USE_ATTRIBUTE,
73 + xlinkShow: MUST_USE_ATTRIBUTE,
74 + xlinkTitle: MUST_USE_ATTRIBUTE,
75 + xlinkType: MUST_USE_ATTRIBUTE,
76 + xmlBase: MUST_USE_ATTRIBUTE,
77 + xmlLang: MUST_USE_ATTRIBUTE,
78 + xmlSpace: MUST_USE_ATTRIBUTE,
79 + xmlnsXlink: MUST_USE_ATTRIBUTE,
80 y1: MUST_USE_ATTRIBUTE,
81 y2: MUST_USE_ATTRIBUTE,
82 y: MUST_USE_ATTRIBUTE
83 },
84 + PropertyNamespaces: {
85 + xlinkActuate: SVGDOMNamespaces.xlink,
86 + xlinkArcrole: SVGDOMNamespaces.xlink,
87 + xlinkHref: SVGDOMNamespaces.xlink,
88 + xlinkRole: SVGDOMNamespaces.xlink,
89 + xlinkShow: SVGDOMNamespaces.xlink,
90 + xlinkTitle: SVGDOMNamespaces.xlink,
91 + xlinkType: SVGDOMNamespaces.xlink,
92 + xmlBase: SVGDOMNamespaces.xml,
93 + xmlLang: SVGDOMNamespaces.xml,
94 + xmlSpace: SVGDOMNamespaces.xml,
95 + xmlnsXlink: SVGDOMNamespaces.xmlns
96 + },
97 DOMAttributeNames: {
98 clipPath: 'clip-path',
99 fillOpacity: 'fill-opacity',
@@ -85,7 +115,18 @@ var SVGDOMPropertyConfig = {
115 strokeOpacity: 'stroke-opacity',
116 strokeWidth: 'stroke-width',
117 textAnchor: 'text-anchor',
88 - viewBox: 'viewBox'
118 + viewBox: 'viewBox',
119 + xlinkActuate: 'xlink:actuate',
120 + xlinkArcrole: 'xlink:arcrole',
121 + xlinkHref: 'xlink:href',
122 + xlinkRole: 'xlink:role',
123 + xlinkShow: 'xlink:show',
124 + xlinkTitle: 'xlink:title',
125 + xlinkType: 'xlink:type',
126 + xmlBase: 'xml:base',
127 + xmlLang: 'xml:lang',
128 + xmlSpace: 'xml:space',
129 + xmlnsXlink: 'xmlns:xlink'
130 }
131 };
132
src/browser/ui/dom/__tests__/DOMPropertyOperations-test.js
+7
@@ -181,9 +181,11 @@ describe('DOMPropertyOperations', function() {
181
182 describe('setValueForProperty', function() {
183 var stubNode;
184 + var stubSVGNode;
185
186 beforeEach(function() {
187 stubNode = document.createElement('div');
188 + stubSVGNode = document.createElement('svg');
189 });
190
191 it('should set values as properties by default', function() {
@@ -196,6 +198,11 @@ describe('DOMPropertyOperations', function() {
198 expect(stubNode.getAttribute('role')).toBe('#');
199 expect(stubNode.role).toBeUndefined();
200 });
201 +
202 + it('should set values as namespace attributes if necessary', function() {
203 + DOMPropertyOperations.setValueForProperty(stubSVGNode, 'xmlnsXlink', 'http://www.w3.org/1999/xlink');
204 + expect(stubSVGNode.getAttribute('xmlns:xlink')).toBe('http://www.w3.org/1999/xlink');
205 + });
206
207 it('should convert attribute values to string first', function() {
208 // Browsers default to this behavior, but some test environments do not.