diff --git a/src/renderers/dom/fiber/ReactDOMFiberComponent.js b/src/renderers/dom/fiber/ReactDOMFiberComponent.js index 9b46875c92c8e..5df978dde49ef 100644 --- a/src/renderers/dom/fiber/ReactDOMFiberComponent.js +++ b/src/renderers/dom/fiber/ReactDOMFiberComponent.js @@ -922,8 +922,7 @@ var ReactDOMFiberComponent = { var extraAttributeNames: Set = new Set(); var attributes = domElement.attributes; for (var i = 0; i < attributes.length; i++) { - // TODO: Do we need to lower case this to get case insensitive matches? - var name = attributes[i].name; + var name = attributes[i].name.toLowerCase(); switch (name) { // Built-in attributes are whitelisted // TODO: Once these are gone from the server renderer, we don't need diff --git a/src/renderers/dom/shared/DOMProperty.js b/src/renderers/dom/shared/DOMProperty.js index a3641c932dfdd..1edc7261d1fd0 100644 --- a/src/renderers/dom/shared/DOMProperty.js +++ b/src/renderers/dom/shared/DOMProperty.js @@ -119,7 +119,11 @@ var DOMPropertyInjection = { if (DOMAttributeNames.hasOwnProperty(propName)) { var attributeName = DOMAttributeNames[propName]; - DOMProperty.aliases[attributeName] = true; + DOMProperty.aliases[attributeName.toLowerCase()] = true; + + if (lowerCased !== attributeName) { + DOMProperty.aliases[lowerCased] = true; + } propertyInfo.attributeName = attributeName; if (__DEV__) { @@ -238,10 +242,6 @@ var DOMProperty = { return true; } - if (name.toLowerCase() !== name) { - return false; - } - switch (typeof value) { case 'undefined': case 'boolean': diff --git a/src/renderers/dom/shared/__tests__/ReactDOMComponent-test.js b/src/renderers/dom/shared/__tests__/ReactDOMComponent-test.js index 74a9f1eef50bc..9cdb405109944 100644 --- a/src/renderers/dom/shared/__tests__/ReactDOMComponent-test.js +++ b/src/renderers/dom/shared/__tests__/ReactDOMComponent-test.js @@ -1992,30 +1992,14 @@ describe('ReactDOMComponent', () => { expect(el.getAttribute('ajaxify')).toBe('ajaxy'); }); - it('only allows lower-case data attributes', function() { - spyOn(console, 'error'); - + it('allows cased data attributes', function() { var el = ReactTestUtils.renderIntoDocument(
); - - expect(el.hasAttribute('data-foobar')).toBe(false); - - expectDev(console.error.calls.argsFor(0)[0]).toContain( - 'Warning: Invalid DOM property `data-fooBar`. Custom attributes ' + - 'and data attributes must be lower case. Instead use `data-foobar`', - ); + expect(el.getAttribute('data-foobar')).toBe('true'); }); - it('only allows lower-case custom attributes', function() { - spyOn(console, 'error'); - + it('allows cased custom attributes', function() { var el = ReactTestUtils.renderIntoDocument(
); - - expect(el.hasAttribute('foobar')).toBe(false); - - expectDev(console.error.calls.argsFor(0)[0]).toContain( - 'Warning: Invalid DOM property `fooBar`. Custom attributes ' + - 'and data attributes must be lower case. Instead use `foobar`', - ); + expect(el.getAttribute('foobar')).toBe('true'); }); }); }); diff --git a/src/renderers/dom/shared/__tests__/ReactDOMServerIntegration-test.js b/src/renderers/dom/shared/__tests__/ReactDOMServerIntegration-test.js index 8080371bb3605..8c7af5c04fe9e 100644 --- a/src/renderers/dom/shared/__tests__/ReactDOMServerIntegration-test.js +++ b/src/renderers/dom/shared/__tests__/ReactDOMServerIntegration-test.js @@ -615,6 +615,12 @@ describe('ReactDOMServerIntegration', () => { expect(e.hasAttribute('className')).toBe(false); }); + itRenders('no classname prop', async render => { + const e = await render(
, 1); + expect(e.hasAttribute('class')).toBe(false); + expect(e.hasAttribute('classname')).toBe(false); + }); + itRenders('no className prop when given the alias', async render => { const e = await render(
, 1); expect(e.className).toBe(''); @@ -793,9 +799,32 @@ describe('ReactDOMServerIntegration', () => { }); }); + describe('cased attributes', function() { + itRenders('no badly cased aliased HTML attribute', async render => { + const e = await render(, 1); + expect(e.hasAttribute('http-equiv')).toBe(false); + expect(e.hasAttribute('httpequiv')).toBe(false); + }); + + itRenders('no badly cased SVG attribute', async render => { + const e = await render(, 1); + expect(e.hasAttribute('textLength')).toBe(false); + }); + + itRenders('no badly cased aliased SVG attribute', async render => { + const e = await render(, 1); + expect(e.hasAttribute('strokedasharray')).toBe(false); + }); + + itRenders('no badly cased original SVG attribute that is aliased', async render => { + const e = await render(, 1); + expect(e.hasAttribute('stroke-dasharray')).toBe(false); + }); + }); + describe('unknown attributes', function() { itRenders('unknown attributes', async render => { - const e = await render(
, 0); + const e = await render(
); expect(e.getAttribute('foo')).toBe('bar'); }); @@ -809,13 +838,21 @@ describe('ReactDOMServerIntegration', () => { expect(e.hasAttribute('data-foo')).toBe(false); }); - itRenders('no unknown data- attributes with casing', async render => { - const e = await render(
, 1); - expect(e.hasAttribute('data-foobar')).toBe(false); + itRenders('unknown data- attributes with casing', async render => { + const e = await render(
); + expect(e.getAttribute('data-fooBar')).toBe('true'); }); + itRenders( + 'no unknown data- attributes with casing and null value', + async render => { + const e = await render(
); + expect(e.hasAttribute('data-fooBar')).toBe(false); + }, + ); + itRenders('custom attributes for non-standard elements', async render => { - const e = await render(, 0); + const e = await render(); expect(e.getAttribute('foo')).toBe('bar'); }); @@ -848,9 +885,9 @@ describe('ReactDOMServerIntegration', () => { }, ); - itRenders('no cased custom attributes', async render => { - const e = await render(
, 1); - expect(e.hasAttribute('fooBar')).toBe(false); + itRenders('cased custom attributes', async render => { + const e = await render(
); + expect(e.getAttribute('fooBar')).toBe('test'); }); }); diff --git a/src/renderers/dom/shared/hooks/ReactDOMUnknownPropertyHook.js b/src/renderers/dom/shared/hooks/ReactDOMUnknownPropertyHook.js index f71af11a2e80c..11da5af3c315d 100644 --- a/src/renderers/dom/shared/hooks/ReactDOMUnknownPropertyHook.js +++ b/src/renderers/dom/shared/hooks/ReactDOMUnknownPropertyHook.js @@ -101,20 +101,6 @@ if (__DEV__) { return true; } - // Otherwise, we have a custom attribute. Custom attributes should always - // be lowercase. - if (lowerCasedName !== name) { - warning( - false, - 'Invalid DOM property `%s`. Custom attributes and data attributes ' + - 'must be lower case. Instead use `%s`.%s', - name, - lowerCasedName, - getStackAddendum(debugID), - ); - return true; - } - return DOMProperty.shouldSetAttribute(name, value); }; }