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
});