diff --git a/src/addons/link/__tests__/ReactLinkPropTypes-test.js b/src/addons/link/__tests__/ReactLinkPropTypes-test.js index f999be6baed88..8e4e177be8473 100644 --- a/src/addons/link/__tests__/ReactLinkPropTypes-test.js +++ b/src/addons/link/__tests__/ReactLinkPropTypes-test.js @@ -48,22 +48,22 @@ describe('ReactLink', function() { typeCheckFail( LinkPropTypes.link(React.PropTypes.any), {}, - 'Required prop `value` was not specified in `testComponent`.' + 'Required prop `testProp.value` was not specified in `testComponent`.' ); typeCheckFail( LinkPropTypes.link(React.PropTypes.any), {value: 123}, - 'Required prop `requestChange` was not specified in `testComponent`.' + 'Required prop `testProp.requestChange` was not specified in `testComponent`.' ); typeCheckFail( LinkPropTypes.link(React.PropTypes.any), {requestChange: emptyFunction}, - 'Required prop `value` was not specified in `testComponent`.' + 'Required prop `testProp.value` was not specified in `testComponent`.' ); typeCheckFail( LinkPropTypes.link(React.PropTypes.any), {value: null, requestChange: null}, - 'Required prop `value` was not specified in `testComponent`.' + 'Required prop `testProp.value` was not specified in `testComponent`.' ); }); @@ -104,7 +104,7 @@ describe('ReactLink', function() { typeCheckFail( LinkPropTypes.link(React.PropTypes.string), {value: 123, requestChange: emptyFunction}, - 'Invalid prop `value` of type `number` supplied to `testComponent`,' + + 'Invalid prop `testProp.value` of type `number` supplied to `testComponent`,' + ' expected `string`.' ); }); @@ -148,7 +148,7 @@ describe('ReactLink', function() { typeCheckFail( LinkPropTypes.link(React.PropTypes.oneOfType([React.PropTypes.number])), {value: 'imastring', requestChange: emptyFunction}, - 'Invalid prop `value` supplied to `testComponent`.' + 'Invalid prop `testProp.value` supplied to `testComponent`.' ); }); }); diff --git a/src/classic/types/ReactPropTypes.js b/src/classic/types/ReactPropTypes.js index dbf2e53153662..774322cae160c 100644 --- a/src/classic/types/ReactPropTypes.js +++ b/src/classic/types/ReactPropTypes.js @@ -86,19 +86,27 @@ var ReactPropTypes = { }; function createChainableTypeChecker(validate) { - function checkType(isRequired, props, propName, componentName, location) { + function checkType( + isRequired, + props, + propName, + componentName, + location, + propFullName + ) { componentName = componentName || ANONYMOUS; + propFullName = propFullName || propName; if (props[propName] == null) { var locationName = ReactPropTypeLocationNames[location]; if (isRequired) { return new Error( - `Required ${locationName} \`${propName}\` was not specified in ` + + `Required ${locationName} \`${propFullName}\` was not specified in ` + `\`${componentName}\`.` ); } return null; } else { - return validate(props, propName, componentName, location); + return validate(props, propName, componentName, location, propFullName); } } @@ -109,7 +117,7 @@ function createChainableTypeChecker(validate) { } function createPrimitiveTypeChecker(expectedType) { - function validate(props, propName, componentName, location) { + function validate(props, propName, componentName, location, propFullName) { var propValue = props[propName]; var propType = getPropType(propValue); if (propType !== expectedType) { @@ -120,8 +128,9 @@ function createPrimitiveTypeChecker(expectedType) { var preciseType = getPreciseType(propValue); return new Error( - `Invalid ${locationName} \`${propName}\` of type \`${preciseType}\` ` + - `supplied to \`${componentName}\`, expected \`${expectedType}\`.` + `Invalid ${locationName} \`${propFullName}\` of type ` + + `\`${preciseType}\` supplied to \`${componentName}\`, expected ` + + `\`${expectedType}\`.` ); } return null; @@ -134,18 +143,24 @@ function createAnyTypeChecker() { } function createArrayOfTypeChecker(typeChecker) { - function validate(props, propName, componentName, location) { + function validate(props, propName, componentName, location, propFullName) { var propValue = props[propName]; if (!Array.isArray(propValue)) { var locationName = ReactPropTypeLocationNames[location]; var propType = getPropType(propValue); return new Error( - `Invalid ${locationName} \`${propName}\` of type ` + + `Invalid ${locationName} \`${propFullName}\` of type ` + `\`${propType}\` supplied to \`${componentName}\`, expected an array.` ); } for (var i = 0; i < propValue.length; i++) { - var error = typeChecker(propValue, i, componentName, location); + var error = typeChecker( + propValue, + i, + componentName, + location, + `${propFullName}[${i}]` + ); if (error instanceof Error) { return error; } @@ -156,11 +171,11 @@ function createArrayOfTypeChecker(typeChecker) { } function createElementTypeChecker() { - function validate(props, propName, componentName, location) { + function validate(props, propName, componentName, location, propFullName) { if (!ReactElement.isValidElement(props[propName])) { var locationName = ReactPropTypeLocationNames[location]; return new Error( - `Invalid ${locationName} \`${propName}\` supplied to ` + + `Invalid ${locationName} \`${propFullName}\` supplied to ` + `\`${componentName}\`, expected a single ReactElement.` ); } @@ -170,12 +185,12 @@ function createElementTypeChecker() { } function createInstanceTypeChecker(expectedClass) { - function validate(props, propName, componentName, location) { + function validate(props, propName, componentName, location, propFullName) { if (!(props[propName] instanceof expectedClass)) { var locationName = ReactPropTypeLocationNames[location]; var expectedClassName = expectedClass.name || ANONYMOUS; return new Error( - `Invalid ${locationName} \`${propName}\` supplied to ` + + `Invalid ${locationName} \`${propFullName}\` supplied to ` + `\`${componentName}\`, expected instance of \`${expectedClassName}\`.` ); } @@ -185,7 +200,7 @@ function createInstanceTypeChecker(expectedClass) { } function createEnumTypeChecker(expectedValues) { - function validate(props, propName, componentName, location) { + function validate(props, propName, componentName, location, propFullName) { var propValue = props[propName]; for (var i = 0; i < expectedValues.length; i++) { if (propValue === expectedValues[i]) { @@ -196,7 +211,7 @@ function createEnumTypeChecker(expectedValues) { var locationName = ReactPropTypeLocationNames[location]; var valuesString = JSON.stringify(expectedValues); return new Error( - `Invalid ${locationName} \`${propName}\` of value \`${propValue}\` ` + + `Invalid ${locationName} \`${propFullName}\` of value \`${propValue}\` ` + `supplied to \`${componentName}\`, expected one of ${valuesString}.` ); } @@ -204,19 +219,25 @@ function createEnumTypeChecker(expectedValues) { } function createObjectOfTypeChecker(typeChecker) { - function validate(props, propName, componentName, location) { + function validate(props, propName, componentName, location, propFullName) { var propValue = props[propName]; var propType = getPropType(propValue); if (propType !== 'object') { var locationName = ReactPropTypeLocationNames[location]; return new Error( - `Invalid ${locationName} \`${propName}\` of type ` + + `Invalid ${locationName} \`${propFullName}\` of type ` + `\`${propType}\` supplied to \`${componentName}\`, expected an object.` ); } for (var key in propValue) { if (propValue.hasOwnProperty(key)) { - var error = typeChecker(propValue, key, componentName, location); + var error = typeChecker( + propValue, + key, + componentName, + location, + `${propFullName}.${key}` + ); if (error instanceof Error) { return error; } @@ -228,17 +249,19 @@ function createObjectOfTypeChecker(typeChecker) { } function createUnionTypeChecker(arrayOfTypeCheckers) { - function validate(props, propName, componentName, location) { + function validate(props, propName, componentName, location, propFullName) { for (var i = 0; i < arrayOfTypeCheckers.length; i++) { var checker = arrayOfTypeCheckers[i]; - if (checker(props, propName, componentName, location) == null) { + if ( + checker(props, propName, componentName, location, propFullName) == null + ) { return null; } } var locationName = ReactPropTypeLocationNames[location]; return new Error( - `Invalid ${locationName} \`${propName}\` supplied to ` + + `Invalid ${locationName} \`${propFullName}\` supplied to ` + `\`${componentName}\`.` ); } @@ -246,11 +269,11 @@ function createUnionTypeChecker(arrayOfTypeCheckers) { } function createNodeChecker() { - function validate(props, propName, componentName, location) { + function validate(props, propName, componentName, location, propFullName) { if (!isNode(props[propName])) { var locationName = ReactPropTypeLocationNames[location]; return new Error( - `Invalid ${locationName} \`${propName}\` supplied to ` + + `Invalid ${locationName} \`${propFullName}\` supplied to ` + `\`${componentName}\`, expected a ReactNode.` ); } @@ -260,13 +283,13 @@ function createNodeChecker() { } function createShapeTypeChecker(shapeTypes) { - function validate(props, propName, componentName, location) { + function validate(props, propName, componentName, location, propFullName) { var propValue = props[propName]; var propType = getPropType(propValue); if (propType !== 'object') { var locationName = ReactPropTypeLocationNames[location]; return new Error( - `Invalid ${locationName} \`${propName}\` of type \`${propType}\` ` + + `Invalid ${locationName} \`${propFullName}\` of type \`${propType}\` ` + `supplied to \`${componentName}\`, expected \`object\`.` ); } @@ -275,7 +298,13 @@ function createShapeTypeChecker(shapeTypes) { if (!checker) { continue; } - var error = checker(propValue, key, componentName, location); + var error = checker( + propValue, + key, + componentName, + location, + `${propFullName}.${key}` + ); if (error) { return error; } diff --git a/src/classic/types/__tests__/ReactPropTypes-test.js b/src/classic/types/__tests__/ReactPropTypes-test.js index 0508a8fe66a50..5e51800d892df 100644 --- a/src/classic/types/__tests__/ReactPropTypes-test.js +++ b/src/classic/types/__tests__/ReactPropTypes-test.js @@ -161,7 +161,7 @@ describe('ReactPropTypes', function() { typeCheckFail( PropTypes.arrayOf(PropTypes.number), [1, 2, 'b'], - 'Invalid prop `2` of type `string` supplied to `testComponent`, ' + + 'Invalid prop `testProp[2]` of type `string` supplied to `testComponent`, ' + 'expected `number`.' ); }); @@ -173,7 +173,7 @@ describe('ReactPropTypes', function() { typeCheckFail( PropTypes.arrayOf(PropTypes.instanceOf(Thing)), [new Thing(), 'xyz'], - 'Invalid prop `1` supplied to `testComponent`, expected instance of `' + + 'Invalid prop `testProp[1]` supplied to `testComponent`, expected instance of `' + name + '`.' ); }); @@ -458,7 +458,7 @@ describe('ReactPropTypes', function() { typeCheckFail( PropTypes.objectOf(PropTypes.number), {a: 1, b: 2, c: 'b'}, - 'Invalid prop `c` of type `string` supplied to `testComponent`, ' + + 'Invalid prop `testProp.c` of type `string` supplied to `testComponent`, ' + 'expected `number`.' ); }); @@ -470,7 +470,7 @@ describe('ReactPropTypes', function() { typeCheckFail( PropTypes.objectOf(PropTypes.instanceOf(Thing)), {a: new Thing(), b: 'xyz'}, - 'Invalid prop `b` supplied to `testComponent`, expected instance of `' + + 'Invalid prop `testProp.b` supplied to `testComponent`, expected instance of `' + name + '`.' ); }); @@ -668,7 +668,7 @@ describe('ReactPropTypes', function() { typeCheckFail( PropTypes.shape({key: PropTypes.number.isRequired}), {}, - 'Required prop `key` was not specified in `testComponent`.' + 'Required prop `testProp.key` was not specified in `testComponent`.' ); }); @@ -679,14 +679,14 @@ describe('ReactPropTypes', function() { secondKey: PropTypes.number.isRequired }), {}, - 'Required prop `key` was not specified in `testComponent`.' + 'Required prop `testProp.key` was not specified in `testComponent`.' ); }); it("should warn for invalid key types", function() { typeCheckFail(PropTypes.shape({key: PropTypes.number}), {key: 'abc'}, - 'Invalid prop `key` of type `string` supplied to `testComponent`, ' + + 'Invalid prop `testProp.key` of type `string` supplied to `testComponent`, ' + 'expected `number`.' ); });