Warn about unknown property values
Fixes #255.
Ben Alpert committed
Aug 15, 2013 at 00:43 UTC
a4c23d328c7080dbb9cd6996ea227146e942f658
6 files changed
+75
-2
src/addons/transitions/ReactTransitionGroup.js
+6
-1
@@ -102,7 +102,12 @@ var ReactTransitionGroup = React.createClass({
102
render: function() {
103
return this.transferPropsTo(
104
this.props.component(
105
- null,
105
+ {
106
+ transitionName: null,
107
+ transitionEnter: null,
108
+ transitionLeave: null,
109
+ component: null
110
+ },
111
this.renderTransitionableChildren(this.props.children)
112
)
113
);
src/dom/DOMProperty.js
+11
-1
@@ -82,8 +82,11 @@ var DOMPropertyInjection = {
82
83
DOMProperty.isStandardName[propName] = true;
84
85
+ var lowerCased = propName.toLowerCase();
86
+ DOMProperty.getPossibleStandardName[lowerCased] = propName;
87
+
88
DOMProperty.getAttributeName[propName] =
86
- DOMAttributeNames[propName] || propName.toLowerCase();
89
+ DOMAttributeNames[propName] || lowerCased;
90
91
DOMProperty.getPropertyName[propName] =
92
DOMPropertyNames[propName] || propName;
@@ -141,6 +144,13 @@ var DOMProperty = {
144
*/
145
isStandardName: {},
146
147
+ /**
148
+ * Mapping from lowercase property names to the properly cased version, used
149
+ * to warn in the case of missing properties.
150
+ * @type {Object}
151
+ */
152
+ getPossibleStandardName: {},
153
+
154
/**
155
* Mapping from normalized names to attribute names that differ. Attribute
156
* names are used when rendering markup or with `*Attribute()`.
src/dom/DOMPropertyOperations.js
+39
@@ -28,6 +28,36 @@ var processAttributeNameAndPrefix = memoizeStringOnly(function(name) {
28
return escapeTextForBrowser(name) + '="';
29
});
30
31
+if (__DEV__) {
32
+ var reactProps = {
33
+ '{owner}': true,
34
+ children: true,
35
+ dangerouslySetInnerHTML: true,
36
+ key: true,
37
+ ref: true
38
+ };
39
+ var warnedProperties = {};
40
+
41
+ var warnUnknownProperty = function(name) {
42
+ if (reactProps[name] || warnedProperties[name]) {
43
+ return;
44
+ }
45
+
46
+ warnedProperties[name] = true;
47
+ var message = 'Unknown DOM property ' + name + '.';
48
+ var lowerCasedName = name.toLowerCase();
49
+
50
+ // data-* attributes should be lowercase; suggest the lowercase version
51
+ var standardName = DOMProperty.isCustomAttribute(lowerCasedName) ?
52
+ lowerCasedName : DOMProperty.getPossibleStandardName[lowerCasedName];
53
+ if (standardName != null) {
54
+ message += ' Did you mean ' + standardName + '?';
55
+ }
56
+
57
+ console.warn(message);
58
+ };
59
+}
60
+
61
/**
62
* Operations for dealing with DOM properties.
63
*/
@@ -55,6 +85,9 @@ var DOMPropertyOperations = {
85
return processAttributeNameAndPrefix(name) +
86
escapeTextForBrowser(value) + '"';
87
} else {
88
+ if (__DEV__) {
89
+ warnUnknownProperty(name);
90
+ }
91
return null;
92
}
93
},
@@ -85,6 +118,10 @@ var DOMPropertyOperations = {
118
}
119
} else if (DOMProperty.isCustomAttribute(name)) {
120
node.setAttribute(name, value);
121
+ } else {
122
+ if (__DEV__) {
123
+ warnUnknownProperty(name);
124
+ }
125
}
126
},
127
@@ -110,6 +147,8 @@ var DOMPropertyOperations = {
147
}
148
} else if (DOMProperty.isCustomAttribute(name)) {
149
node.removeAttribute(name);
150
+ } else if (__DEV__) {
151
+ warnUnknownProperty(name);
152
}
153
}
154
src/dom/__tests__/DOMPropertyOperations-test.js
+16
@@ -58,6 +58,16 @@ describe('DOMPropertyOperations', function() {
58
)).toBe('id="simple"');
59
});
60
61
+ it('should warn about incorrect casing', function() {
62
+ spyOn(console, 'warn');
63
+ expect(DOMPropertyOperations.createMarkupForProperty(
64
+ 'tabindex',
65
+ '1'
66
+ )).toBe(null);
67
+ expect(console.warn.argsForCall.length).toBe(1);
68
+ expect(console.warn.argsForCall[0][0]).toContain('tabIndex');
69
+ });
70
+
71
it('should create markup for boolean properties', function() {
72
expect(DOMPropertyOperations.createMarkupForProperty(
73
'checked',
@@ -141,18 +151,24 @@ describe('DOMPropertyOperations', function() {
151
152
describe('injectDOMPropertyConfig', function() {
153
it('should support custom attributes', function() {
154
+ spyOn(console, 'warn');
155
+
156
// foobar does not exist yet
157
expect(DOMPropertyOperations.createMarkupForProperty(
158
'foobar',
159
'simple'
160
)).toBe(null);
161
162
+ expect(console.warn.argsForCall.length).toBe(1);
163
+
164
// foo-* does not exist yet
165
expect(DOMPropertyOperations.createMarkupForProperty(
166
'foo-xyz',
167
'simple'
168
)).toBe(null);
169
170
+ expect(console.warn.argsForCall.length).toBe(2);
171
+
172
// inject foobar DOM property
173
DOMProperty.injection.injectDOMPropertyConfig({
174
isCustomAttribute: function(name) {
src/dom/components/ReactDOMInput.js
+2
@@ -61,6 +61,8 @@ var ReactDOMInput = ReactCompositeComponent.createClass({
61
// Clone `this.props` so we don't mutate the input.
62
var props = merge(this.props);
63
64
+ props.defaultChecked = null;
65
+ props.defaultValue = null;
66
props.checked =
67
this.props.checked != null ? this.props.checked : this.state.checked;
68
// Cast `this.props.value` to a string so equality checks pass.
src/dom/components/ReactDOMTextarea.js
+1
@@ -102,6 +102,7 @@ var ReactDOMTextarea = ReactCompositeComponent.createClass({
102
'`dangerouslySetInnerHTML` does not make sense on <textarea>.'
103
);
104
105
+ props.defaultValue = null;
106
props.value =
107
this.props.value != null ? this.props.value : this.state.value;
108
props.onChange = this._handleChange;