Merge pull request #267 from spicyj/warn-props

Warn about unknown property values
This commit is contained in:
Paul O’Shannessy
2013-09-07 13:06:41 -07:00
6 changed files with 75 additions and 2 deletions
@@ -102,7 +102,12 @@ var ReactTransitionGroup = React.createClass({
render: function() {
return this.transferPropsTo(
this.props.component(
null,
{
transitionName: null,
transitionEnter: null,
transitionLeave: null,
component: null
},
this.renderTransitionableChildren(this.props.children)
)
);
+11 -1
View File
@@ -82,8 +82,11 @@ var DOMPropertyInjection = {
DOMProperty.isStandardName[propName] = true;
var lowerCased = propName.toLowerCase();
DOMProperty.getPossibleStandardName[lowerCased] = propName;
DOMProperty.getAttributeName[propName] =
DOMAttributeNames[propName] || propName.toLowerCase();
DOMAttributeNames[propName] || lowerCased;
DOMProperty.getPropertyName[propName] =
DOMPropertyNames[propName] || propName;
@@ -141,6 +144,13 @@ var DOMProperty = {
*/
isStandardName: {},
/**
* Mapping from lowercase property names to the properly cased version, used
* to warn in the case of missing properties.
* @type {Object}
*/
getPossibleStandardName: {},
/**
* Mapping from normalized names to attribute names that differ. Attribute
* names are used when rendering markup or with `*Attribute()`.
+39
View File
@@ -28,6 +28,36 @@ var processAttributeNameAndPrefix = memoizeStringOnly(function(name) {
return escapeTextForBrowser(name) + '="';
});
if (__DEV__) {
var reactProps = {
'{owner}': true,
children: true,
dangerouslySetInnerHTML: true,
key: true,
ref: true
};
var warnedProperties = {};
var warnUnknownProperty = function(name) {
if (reactProps[name] || warnedProperties[name]) {
return;
}
warnedProperties[name] = true;
var message = 'Unknown DOM property ' + name + '.';
var lowerCasedName = name.toLowerCase();
// data-* attributes should be lowercase; suggest the lowercase version
var standardName = DOMProperty.isCustomAttribute(lowerCasedName) ?
lowerCasedName : DOMProperty.getPossibleStandardName[lowerCasedName];
if (standardName != null) {
message += ' Did you mean ' + standardName + '?';
}
console.warn(message);
};
}
/**
* Operations for dealing with DOM properties.
*/
@@ -55,6 +85,9 @@ var DOMPropertyOperations = {
return processAttributeNameAndPrefix(name) +
escapeTextForBrowser(value) + '"';
} else {
if (__DEV__) {
warnUnknownProperty(name);
}
return null;
}
},
@@ -85,6 +118,10 @@ var DOMPropertyOperations = {
}
} else if (DOMProperty.isCustomAttribute(name)) {
node.setAttribute(name, value);
} else {
if (__DEV__) {
warnUnknownProperty(name);
}
}
},
@@ -110,6 +147,8 @@ var DOMPropertyOperations = {
}
} else if (DOMProperty.isCustomAttribute(name)) {
node.removeAttribute(name);
} else if (__DEV__) {
warnUnknownProperty(name);
}
}
@@ -58,6 +58,16 @@ describe('DOMPropertyOperations', function() {
)).toBe('id="simple"');
});
it('should warn about incorrect casing', function() {
spyOn(console, 'warn');
expect(DOMPropertyOperations.createMarkupForProperty(
'tabindex',
'1'
)).toBe(null);
expect(console.warn.argsForCall.length).toBe(1);
expect(console.warn.argsForCall[0][0]).toContain('tabIndex');
});
it('should create markup for boolean properties', function() {
expect(DOMPropertyOperations.createMarkupForProperty(
'checked',
@@ -141,18 +151,24 @@ describe('DOMPropertyOperations', function() {
describe('injectDOMPropertyConfig', function() {
it('should support custom attributes', function() {
spyOn(console, 'warn');
// foobar does not exist yet
expect(DOMPropertyOperations.createMarkupForProperty(
'foobar',
'simple'
)).toBe(null);
expect(console.warn.argsForCall.length).toBe(1);
// foo-* does not exist yet
expect(DOMPropertyOperations.createMarkupForProperty(
'foo-xyz',
'simple'
)).toBe(null);
expect(console.warn.argsForCall.length).toBe(2);
// inject foobar DOM property
DOMProperty.injection.injectDOMPropertyConfig({
isCustomAttribute: function(name) {
+2
View File
@@ -61,6 +61,8 @@ var ReactDOMInput = ReactCompositeComponent.createClass({
// Clone `this.props` so we don't mutate the input.
var props = merge(this.props);
props.defaultChecked = null;
props.defaultValue = null;
props.checked =
this.props.checked != null ? this.props.checked : this.state.checked;
// Cast `this.props.value` to a string so equality checks pass.
+1
View File
@@ -102,6 +102,7 @@ var ReactDOMTextarea = ReactCompositeComponent.createClass({
'`dangerouslySetInnerHTML` does not make sense on <textarea>.'
);
props.defaultValue = null;
props.value =
this.props.value != null ? this.props.value : this.state.value;
props.onChange = this._handleChange;