Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 30 additions & 4 deletions src/isomorphic/classic/types/ReactPropTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ var ReactFragment = require('ReactFragment');
var ReactPropTypeLocationNames = require('ReactPropTypeLocationNames');

var emptyFunction = require('emptyFunction');
var getIteratorFn = require('getIteratorFn');

/**
* Collection of methods that allow declaration and validation of props that are
Expand Down Expand Up @@ -345,12 +346,37 @@ function isNode(propValue) {
if (propValue === null || ReactElement.isValidElement(propValue)) {
return true;
}
propValue = ReactFragment.extractIfFragment(propValue);
for (var k in propValue) {
if (!isNode(propValue[k])) {
return false;

var iteratorFn = getIteratorFn(propValue);
if (iteratorFn) {
var iterator = iteratorFn.call(propValue);
var step;
if (iteratorFn !== propValue.entries) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We also need to check the values when iteratorFn === propValue.entries.

while (!(step = iterator.next()).done) {
if (!isNode(step.value)) {
return false;
}
}
} else {
// Iterator will provide entry [k,v] tuples rather than values.
while (!(step = iterator.next()).done) {
var entry = step.value;
if (entry) {
if (!isNode(entry[1])) {
return false;
}
}
}
}
} else {
propValue = ReactFragment.extractIfFragment(propValue);
for (var k in propValue) {
if (!isNode(propValue[k])) {
return false;
}
}
}

return true;
default:
return false;
Expand Down
33 changes: 33 additions & 0 deletions src/isomorphic/classic/types/__tests__/ReactPropTypes-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -405,6 +405,39 @@ describe('ReactPropTypes', function() {
});
});

it('should not warn for iterables', function() {
var iterable = {
'@@iterator': function() {
var i = 0;
return {
next: function() {
var done = ++i > 2;
return {value: done ? undefined : <MyComponent />, done: done};
},
};
},
};

typeCheckPass(PropTypes.node, iterable);
});

it('should not warn for entry iterables', function() {
var iterable = {
'@@iterator': function() {
var i = 0;
return {
next: function() {
var done = ++i > 2;
return {value: done ? undefined : ['#' + i, <MyComponent />], done: done};
},
};
},
};
iterable.entries = iterable['@@iterator'];

typeCheckPass(PropTypes.node, iterable);
});

it('should not warn for null/undefined if not required', function() {
typeCheckPass(PropTypes.node, null);
typeCheckPass(PropTypes.node, undefined);
Expand Down