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

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;