@samitouri / QOS-React-2 / commits / 4852c30d62

Added 'objectOf' PropType validator to iterate on object literals and validate properties.

Added 'objectOf' PropType validator to iterate on object literals and validate properties.

Mathieu M-Gosselin committed May 27, 2014 at 10:53 UTC 4852c30d627082dc655b147494317beb518affcf
2 files changed +121 -2
src/core/ReactPropTypes.js
+24
@@ -83,6 +83,7 @@ var ReactPropTypes = {
83 arrayOf: createArrayOfTypeChecker,
84 component: createComponentTypeChecker(),
85 instanceOf: createInstanceTypeChecker,
86 + objectOf: createObjectOfTypeChecker,
87 oneOf: createEnumTypeChecker,
88 oneOfType: createUnionTypeChecker,
89 renderable: createRenderableTypeChecker(),
@@ -202,6 +203,29 @@ function createEnumTypeChecker(expectedValues) {
203 return createChainableTypeChecker(validate);
204 }
205
206 +function createObjectOfTypeChecker(typeChecker) {
207 + function validate(props, propName, componentName, location) {
208 + var propValue = props[propName];
209 + var propType = getPropType(propValue);
210 + if (propType !== 'object') {
211 + var locationName = ReactPropTypeLocationNames[location];
212 + return new Error(
213 + `Invalid ${locationName} \`${propName}\` of type ` +
214 + `\`${propType}\` supplied to \`${componentName}\`, expected an object.`
215 + );
216 + }
217 + for (var key in propValue) {
218 + if (propValue.hasOwnProperty(key)) {
219 + var error = typeChecker(propValue, key, componentName, location);
220 + if (error instanceof Error) {
221 + return error;
222 + }
223 + }
224 + }
225 + }
226 + return createChainableTypeChecker(validate);
227 +}
228 +
229 function createUnionTypeChecker(arrayOfTypeCheckers) {
230 function validate(props, propName, componentName, location) {
231 for (var i = 0; i < arrayOfTypeCheckers.length; i++) {
src/core/__tests__/ReactPropTypes-test.js
+97 -2
@@ -403,6 +403,97 @@ describe('React Component Types', function() {
403 });
404 });
405
406 +describe('ObjectOf Type', function() {
407 + it('should support the objectOf propTypes', function() {
408 + typeCheckPass(PropTypes.objectOf(PropTypes.number), {a: 1, b: 2, c: 3});
409 + typeCheckPass(
410 + PropTypes.objectOf(PropTypes.string),
411 + {a: 'a', b: 'b', c: 'c'}
412 + );
413 + typeCheckPass(
414 + PropTypes.objectOf(PropTypes.oneOf(['a', 'b'])),
415 + {a: 'a', b: 'b'}
416 + );
417 + });
418 +
419 + it('should support objectOf with complex types', function() {
420 + typeCheckPass(
421 + PropTypes.objectOf(PropTypes.shape({a: PropTypes.number.isRequired})),
422 + {a: {a: 1}, b: {a: 2}}
423 + );
424 +
425 + function Thing() {}
426 + typeCheckPass(
427 + PropTypes.objectOf(PropTypes.instanceOf(Thing)),
428 + {a: new Thing(), b: new Thing()}
429 + );
430 + });
431 +
432 + it('should warn with invalid items in the object', function() {
433 + typeCheckFail(
434 + PropTypes.objectOf(PropTypes.number),
435 + {a: 1, b: 2, c: 'b'},
436 + 'Invalid prop `c` of type `string` supplied to `testComponent`, ' +
437 + 'expected `number`.'
438 + );
439 + });
440 +
441 + it('should warn with invalid complex types', function() {
442 + function Thing() {}
443 + var name = Thing.name || '<<anonymous>>';
444 +
445 + typeCheckFail(
446 + PropTypes.objectOf(PropTypes.instanceOf(Thing)),
447 + {a: new Thing(), b: 'xyz'},
448 + 'Invalid prop `b` supplied to `testComponent`, expected instance of `' +
449 + name + '`.'
450 + );
451 + });
452 +
453 + it('should warn when passed something other than an object', function() {
454 + typeCheckFail(
455 + PropTypes.objectOf(PropTypes.number),
456 + [1, 2],
457 + 'Invalid prop `testProp` of type `array` supplied to `testComponent`, ' +
458 + 'expected an object.'
459 + );
460 + typeCheckFail(
461 + PropTypes.objectOf(PropTypes.number),
462 + 123,
463 + 'Invalid prop `testProp` of type `number` supplied to `testComponent`, ' +
464 + 'expected an object.'
465 + );
466 + typeCheckFail(
467 + PropTypes.objectOf(PropTypes.number),
468 + 'string',
469 + 'Invalid prop `testProp` of type `string` supplied to `testComponent`, ' +
470 + 'expected an object.'
471 + );
472 + });
473 +
474 + it('should not warn when passing an empty object', function() {
475 + typeCheckPass(PropTypes.objectOf(PropTypes.number), {});
476 + });
477 +
478 + it("should be implicitly optional and not warn without values", function() {
479 + typeCheckPass(PropTypes.objectOf(PropTypes.number), null);
480 + typeCheckPass(PropTypes.objectOf(PropTypes.number), undefined);
481 + });
482 +
483 + it("should warn for missing required values", function() {
484 + typeCheckFail(
485 + PropTypes.objectOf(PropTypes.number).isRequired,
486 + null,
487 + requiredMessage
488 + );
489 + typeCheckFail(
490 + PropTypes.objectOf(PropTypes.number).isRequired,
491 + undefined,
492 + requiredMessage
493 + );
494 + });
495 +});
496 +
497 describe('OneOf Types', function() {
498 it("should warn for invalid strings", function() {
499 typeCheckFail(
@@ -443,10 +534,14 @@ describe('OneOf Types', function() {
534
535 it("should warn for missing required values", function() {
536 typeCheckFail(
446 - PropTypes.oneOf(['red', 'blue']).isRequired, null, requiredMessage
537 + PropTypes.oneOf(['red', 'blue']).isRequired,
538 + null,
539 + requiredMessage
540 );
541 typeCheckFail(
449 - PropTypes.oneOf(['red', 'blue']).isRequired, undefined, requiredMessage
542 + PropTypes.oneOf(['red', 'blue']).isRequired,
543 + undefined,
544 + requiredMessage
545 );
546 });
547 });