diff --git a/src/renderers/dom/shared/DOMMarkupOperations.js b/src/renderers/dom/shared/DOMMarkupOperations.js index d2470493053..cfa05f56d90 100644 --- a/src/renderers/dom/shared/DOMMarkupOperations.js +++ b/src/renderers/dom/shared/DOMMarkupOperations.js @@ -10,6 +10,7 @@ 'use strict'; var DOMProperty = require('DOMProperty'); +var {Namespaces} = require('DOMNamespaces'); var quoteAttributeValueForBrowser = require('quoteAttributeValueForBrowser'); @@ -17,6 +18,8 @@ if (__DEV__) { var warning = require('fbjs/lib/warning'); } +var HTML_NAMESPACE = Namespaces.html; + // isAttributeNameSafe() is currently duplicated in DOMPropertyOperations. // TODO: Find a better place for this. var VALID_ATTRIBUTE_NAME_REGEX = new RegExp( @@ -85,13 +88,16 @@ var DOMMarkupOperations = { * @param {*} value * @return {?string} Markup string, or null if the property was invalid. */ - createMarkupForProperty: function(name, value) { + createMarkupForProperty: function(name, value, namespace) { var propertyInfo = DOMProperty.getPropertyInfo(name); if (propertyInfo) { if (shouldIgnoreValue(propertyInfo, value)) { return ''; } var attributeName = propertyInfo.attributeName; + if (namespace === HTML_NAMESPACE) { + attributeName = attributeName.toLowerCase(); + } if ( propertyInfo.hasBooleanValue || (propertyInfo.hasOverloadedBooleanValue && value === true) @@ -107,6 +113,9 @@ var DOMMarkupOperations = { if (value == null) { return ''; } + if (namespace === HTML_NAMESPACE) { + name = name.toLowerCase(); + } return name + '=' + quoteAttributeValueForBrowser(value); } return null; diff --git a/src/renderers/dom/shared/__tests__/ReactServerRendering-test.js b/src/renderers/dom/shared/__tests__/ReactServerRendering-test.js index 632f7306c14..ff7c8901947 100644 --- a/src/renderers/dom/shared/__tests__/ReactServerRendering-test.js +++ b/src/renderers/dom/shared/__tests__/ReactServerRendering-test.js @@ -695,4 +695,33 @@ describe('ReactDOMServer', () => { 'HTML tags in React.', ); }); + + it('uses lowercase tags in HTML output', () => { + expect(ReactDOMServer.renderToString(
)).toContain( + 'tabindex=', + ); + }); + + it('keeps tracks of attribute case sensitivity based on the namespace', () => { + const html = ReactDOMServer.renderToString( +
+ + + + + + + +
, + ); + // SVG is case sensitive + expect(html).toContain('text-rendering='); + expect(html).toContain('baseProfile='); + expect(html).toContain('tabindex='); + expect(html).toContain('viewBox='); + // HTML is not, but people expect lowercase output + expect(html).toContain('itemprop='); + expect(html).toContain('srcset='); + expect(html).toContain('minlength='); + }); }); diff --git a/src/renderers/shared/server/ReactPartialRenderer.js b/src/renderers/shared/server/ReactPartialRenderer.js index 5d128fe84da..d67d1df9d26 100644 --- a/src/renderers/shared/server/ReactPartialRenderer.js +++ b/src/renderers/shared/server/ReactPartialRenderer.js @@ -284,7 +284,11 @@ function createOpenTagMarkup( ); } } else { - markup = DOMMarkupOperations.createMarkupForProperty(propKey, propValue); + markup = DOMMarkupOperations.createMarkupForProperty( + propKey, + propValue, + namespace, + ); } if (markup) { ret += ' ' + markup;