Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add PropTypes.component to demand a single React component. #857

Closed
wants to merge 1 commit into from
Closed
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
22 changes: 21 additions & 1 deletion src/core/ReactPropTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,8 @@ var Props = {

instanceOf: createInstanceTypeChecker,

renderable: createRenderableTypeChecker()
renderable: createRenderableTypeChecker(),
component: createComponentTypeChecker()

};

Expand Down Expand Up @@ -200,6 +201,25 @@ function createRenderableTypeChecker() {
return createChainableTypeChecker(validateRenderableType);
}

function createComponentTypeChecker() {
function validateComponentType(
shouldThrow, propValue, propName, componentName, location
) {
var isValid = ReactComponent.isValidComponent(propValue);
if (!shouldThrow) {
return isValid;
}
invariant(
isValid,
'Invalid %s `%s` supplied to `%s`, expected a React component.',
ReactPropTypeLocationNames[location],
propName,
componentName
);
}
return createChainableTypeChecker(validateComponentType);
}

function createChainableTypeChecker(validate) {
function checkType(
isRequired, shouldThrow, props, propName, componentName, location
Expand Down
54 changes: 54 additions & 0 deletions src/core/__tests__/ReactPropTypes-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@

"use strict";

var ReactTestUtils;

var Props = require('ReactPropTypes');
var React = require('React');
var ReactPropTypeLocations = require('ReactPropTypeLocations');
Expand Down Expand Up @@ -46,6 +48,8 @@ var MyComponent = React.createClass({
describe('Primitive Types', function() {
beforeEach(function() {
require('mock-modules').dumpCache();

ReactTestUtils = require('ReactTestUtils');
});

it("should throw for invalid strings", function() {
Expand Down Expand Up @@ -170,6 +174,56 @@ describe('Instance Types', function() {
});
});

describe('Component Type', function() {
it('should support components', () => {
expect(typeCheck(Props.component, <div />)).not.toThrow();
});

it('should not support multiple components or scalar values', () => {
[[<div />, <div />], 123, 'foo', false].forEach((value) => {
expect(typeCheck(Props.component, value)).toThrow();
});
});

var Component = React.createClass({
propTypes: {
children: Props.component.isRequired
},

render: function() {
return <div>{this.props.children}</div>;
}
});

it('should be able to define a single child as children', () => {
expect(() => {
var instance =
<Component>
<div />
</Component>;
ReactTestUtils.renderIntoDocument(instance);
}).not.toThrow();
});

it('should throw when passing more than one child', () => {
expect(() => {
var instance =
<Component>
<div />
<div />
</Component>;
ReactTestUtils.renderIntoDocument(instance);
}).toThrow();
});

it('should throw when passing no children and isRequired is set', () => {
expect(() => {
var instance = <Component />;
ReactTestUtils.renderIntoDocument(instance);
}).toThrow();
});
});

describe('Union Types', function() {
beforeEach(function() {
require('mock-modules').dumpCache();
Expand Down