Skip to content

Commit 40d1369

Browse files
Merge pull request #388 from remarkablemark/fix/uncontrolled-components
fix(attributes-to-props): convert attribute to uncontrolled component prop
2 parents dcf4718 + a5231c7 commit 40d1369

File tree

6 files changed

+47
-4
lines changed

6 files changed

+47
-4
lines changed

lib/attributes-to-props.js

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,19 @@ module.exports = function attributesToProps(attributes) {
2828

2929
// convert HTML/SVG attribute to React prop
3030
attributeNameLowerCased = attributeName.toLowerCase();
31-
propName = reactProperty.possibleStandardNames[attributeNameLowerCased];
31+
propName = getPropName(attributeNameLowerCased);
3232

3333
if (propName) {
34-
props[propName] = attributeValue;
3534
propertyInfo = reactProperty.getPropertyInfo(propName);
35+
36+
// convert attribute to uncontrolled component prop (e.g., `value` to `defaultValue`)
37+
// https://reactjs.org/docs/uncontrolled-components.html
38+
if (propName === 'checked' || propName === 'value') {
39+
propName = getPropName('default' + attributeNameLowerCased);
40+
}
41+
42+
props[propName] = attributeValue;
43+
3644
switch (propertyInfo && propertyInfo.type) {
3745
case reactProperty.BOOLEAN:
3846
props[propName] = true;
@@ -57,3 +65,13 @@ module.exports = function attributesToProps(attributes) {
5765

5866
return props;
5967
};
68+
69+
/**
70+
* Gets prop name from lowercased attribute name.
71+
*
72+
* @param {string} attributeName - Lowercased attribute name.
73+
* @return {string}
74+
*/
75+
function getPropName(attributeName) {
76+
return reactProperty.possibleStandardNames[attributeName];
77+
}

test/__snapshots__/attributes-to-props.test.js.snap

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,9 @@ Object {
3131
"async": true,
3232
"autoFocus": true,
3333
"autoPlay": true,
34-
"checked": true,
3534
"controls": true,
3635
"default": true,
36+
"defaultChecked": true,
3737
"disabled": true,
3838
"draggable": "false",
3939
"formNoValidate": true,

test/__snapshots__/index.test.js.snap

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,14 @@ exports[`HTMLReactParser parses empty <script> 1`] = `<script />`;
110110
111111
exports[`HTMLReactParser parses empty <style> 1`] = `<style />`;
112112
113+
exports[`HTMLReactParser parses form 1`] = `
114+
<input
115+
defaultChecked={true}
116+
defaultValue="foo"
117+
type="text"
118+
/>
119+
`;
120+
113121
exports[`HTMLReactParser parses multiple HTML elements 1`] = `
114122
Array [
115123
<p>

test/attributes-to-props.test.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,18 @@ describe('attributesToProps with HTML attribute', () => {
103103
])('converts overloaded boolean attribute: %p', (attributes, props) => {
104104
expect(attributesToProps(attributes)).toEqual(props);
105105
});
106+
107+
it.each([
108+
[{ checked: '' }, { defaultChecked: true }],
109+
[{ checked: 'checked' }, { defaultChecked: true }],
110+
[{ value: '' }, { defaultValue: '' }],
111+
[{ value: 'foo' }, { defaultValue: 'foo' }]
112+
])(
113+
'converts form attribute to uncontrolled component property',
114+
(attributes, props) => {
115+
expect(attributesToProps(attributes)).toEqual(props);
116+
}
117+
);
106118
});
107119

108120
describe('attributesToProps with SVG attribute', () => {

test/data/html.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,6 @@ module.exports = {
1515
doctype: '<!DOCTYPE html>',
1616
title: '<title><em>text</em></title>',
1717
customElement:
18-
'<custom-element class="myClass" custom-attribute="value" style="-o-transition: all .5s; line-height: 1;"></custom-element>'
18+
'<custom-element class="myClass" custom-attribute="value" style="-o-transition: all .5s; line-height: 1;"></custom-element>',
19+
form: '<input type="text" value="foo" checked="checked">'
1920
};

test/index.test.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,10 @@ describe('HTMLReactParser', () => {
6565
expect(parse('<style></style>')).toMatchSnapshot();
6666
});
6767

68+
it('parses form', () => {
69+
expect(parse(html.form)).toMatchSnapshot();
70+
});
71+
6872
it('parses SVG', () => {
6973
expect(parse(svg.complex)).toMatchSnapshot();
7074
});

0 commit comments

Comments
 (0)