diff --git a/package.json b/package.json
index e152a4aab2..eaa5b7655d 100644
--- a/package.json
+++ b/package.json
@@ -95,6 +95,7 @@
"react": "^0.13.3",
"react-component-metadata": "^1.3.0",
"react-hot-loader": "^1.2.8",
+ "react-prop-types": "^0.2.2",
"react-router": "^0.13.3",
"rimraf": "^2.4.2",
"semver": "^5.0.1",
diff --git a/src/BootstrapMixin.js b/src/BootstrapMixin.js
index 98c05bd068..35f9ecb90e 100644
--- a/src/BootstrapMixin.js
+++ b/src/BootstrapMixin.js
@@ -1,6 +1,6 @@
import React from 'react';
import styleMaps from './styleMaps';
-import CustomPropTypes from './utils/CustomPropTypes';
+import { keyOf } from 'react-prop-types';
const BootstrapMixin = {
propTypes: {
@@ -8,7 +8,7 @@ const BootstrapMixin = {
* bootstrap className
* @private
*/
- bsClass: CustomPropTypes.keyOf(styleMaps.CLASSES),
+ bsClass: keyOf(styleMaps.CLASSES),
/**
* Style variants
* @type {("default"|"primary"|"success"|"info"|"warning"|"danger"|"link")}
@@ -18,7 +18,7 @@ const BootstrapMixin = {
* Size variants
* @type {("xsmall"|"small"|"medium"|"large"|"xs"|"sm"|"md"|"lg")}
*/
- bsSize: CustomPropTypes.keyOf(styleMaps.SIZES)
+ bsSize: keyOf(styleMaps.SIZES)
},
getBsClassSet() {
diff --git a/src/Button.js b/src/Button.js
index 9dd80faf8f..e2270ebe2d 100644
--- a/src/Button.js
+++ b/src/Button.js
@@ -1,7 +1,7 @@
import React from 'react';
import classNames from 'classnames';
import BootstrapMixin from './BootstrapMixin';
-import CustomPropTypes from './utils/CustomPropTypes';
+import { elementType } from 'react-prop-types';
import ButtonInput from './ButtonInput';
const Button = React.createClass({
@@ -16,7 +16,7 @@ const Button = React.createClass({
/**
* You can use a custom element for this component
*/
- componentClass: CustomPropTypes.elementType,
+ componentClass: elementType,
href: React.PropTypes.string,
target: React.PropTypes.string,
/**
diff --git a/src/ButtonGroup.js b/src/ButtonGroup.js
index 303a61eadb..c32b7fa435 100644
--- a/src/ButtonGroup.js
+++ b/src/ButtonGroup.js
@@ -1,7 +1,7 @@
import React from 'react';
import classNames from 'classnames';
import BootstrapMixin from './BootstrapMixin';
-import CustomPropTypes from './utils/CustomPropTypes';
+import { all } from 'react-prop-types';
const ButtonGroup = React.createClass({
mixins: [BootstrapMixin],
@@ -13,7 +13,7 @@ const ButtonGroup = React.createClass({
* Display block buttons, only useful when used with the "vertical" prop.
* @type {bool}
*/
- block: CustomPropTypes.all([
+ block: all([
React.PropTypes.bool,
function(props, propName, componentName) {
if (props.block && !props.vertical) {
diff --git a/src/Col.js b/src/Col.js
index f945fe10b5..83e6dc462d 100644
--- a/src/Col.js
+++ b/src/Col.js
@@ -1,7 +1,7 @@
import React from 'react';
import classNames from 'classnames';
import styleMaps from './styleMaps';
-import CustomPropTypes from './utils/CustomPropTypes';
+import { elementType } from 'react-prop-types';
const Col = React.createClass({
propTypes: {
@@ -136,7 +136,7 @@ const Col = React.createClass({
/**
* You can use a custom element for this component
*/
- componentClass: CustomPropTypes.elementType
+ componentClass: elementType
},
getDefaultProps() {
diff --git a/src/Collapse.js b/src/Collapse.js
index c4a921679c..2b6152c8c4 100644
--- a/src/Collapse.js
+++ b/src/Collapse.js
@@ -1,7 +1,7 @@
import React from 'react';
import Transition from 'react-overlays/lib/Transition';
import domUtils from './utils/domUtils';
-import CustomPropTypes from './utils/CustomPropTypes';
+import { all } from 'react-prop-types';
import deprecationWarning from './utils/deprecationWarning';
import createChainedFunction from './utils/createChainedFunction';
@@ -145,7 +145,7 @@ Collapse.propTypes = {
* duration
* @private
*/
- duration: CustomPropTypes.all([
+ duration: all([
React.PropTypes.number,
(props)=> {
if (props.duration != null){
diff --git a/src/Dropdown.js b/src/Dropdown.js
index 3800bec484..a5b652af39 100644
--- a/src/Dropdown.js
+++ b/src/Dropdown.js
@@ -9,6 +9,7 @@ import CustomPropTypes from './utils/CustomPropTypes';
import createChainedFunction from './utils/createChainedFunction';
import find from 'lodash/collection/find';
import omit from 'lodash/object/omit';
+import { all, elementType, isRequiredForA11y } from 'react-prop-types';
const TOGGLE_REF = 'toggle-btn';
@@ -229,20 +230,20 @@ Dropdown.propTypes = {
* @type {string|number}
* @required
*/
- id: CustomPropTypes.isRequiredForA11y(
+ id: isRequiredForA11y(
React.PropTypes.oneOfType([
React.PropTypes.string,
React.PropTypes.number
])
),
- componentClass: CustomPropTypes.elementType,
+ componentClass: elementType,
/**
* The children of a Dropdown may be a `` or a ``.
* @type {node}
*/
- children: CustomPropTypes.all([
+ children: all([
CustomPropTypes.requiredRoles(TOGGLE_ROLE, MENU_ROLE),
CustomPropTypes.exclusiveRoles(MENU_ROLE)
]),
diff --git a/src/DropdownButton.js b/src/DropdownButton.js
index 0bd169e2c7..610c740b31 100644
--- a/src/DropdownButton.js
+++ b/src/DropdownButton.js
@@ -2,7 +2,7 @@ import React from 'react';
import BootstrapMixin from './BootstrapMixin';
import Dropdown from './Dropdown';
import NavDropdown from './NavDropdown';
-import CustomPropTypes from './utils/CustomPropTypes';
+import { all } from 'react-prop-types';
import deprecationWarning from './utils/deprecationWarning';
import omit from 'lodash/object/omit';
@@ -46,7 +46,7 @@ DropdownButton.propTypes = {
* @type {bool}
* @deprecated Use the `NavDropdown` instead.
*/
- navItem: CustomPropTypes.all([
+ navItem: all([
React.PropTypes.bool,
function(props, propName, componentName) {
if (props.navItem) {
diff --git a/src/DropdownToggle.js b/src/DropdownToggle.js
index 5d3b3145b7..c6e9c06ce4 100644
--- a/src/DropdownToggle.js
+++ b/src/DropdownToggle.js
@@ -1,7 +1,7 @@
import React from 'react';
import classNames from 'classnames';
import Button from './Button';
-import CustomPropTypes from './utils/CustomPropTypes';
+import { singlePropFrom } from 'react-prop-types';
import SafeAnchor from './SafeAnchor';
const CARET = ;
@@ -29,7 +29,7 @@ export default class DropdownToggle extends React.Component {
}
}
-const titleAndChildrenValidation = CustomPropTypes.singlePropFrom([
+const titleAndChildrenValidation = singlePropFrom([
'title',
'children'
]);
diff --git a/src/Fade.js b/src/Fade.js
index 62ce1b6798..ef8677c615 100644
--- a/src/Fade.js
+++ b/src/Fade.js
@@ -1,6 +1,6 @@
import React from 'react';
import Transition from 'react-overlays/lib/Transition';
-import CustomPropTypes from './utils/CustomPropTypes';
+import { all } from 'react-prop-types';
import deprecationWarning from './utils/deprecationWarning';
class Fade extends React.Component {
@@ -51,7 +51,7 @@ Fade.propTypes = {
* duration
* @private
*/
- duration: CustomPropTypes.all([
+ duration: all([
React.PropTypes.number,
(props)=> {
if (props.duration != null){
@@ -95,4 +95,3 @@ Fade.defaultProps = {
};
export default Fade;
-
diff --git a/src/Grid.js b/src/Grid.js
index b0102f3ce3..ec4679b19f 100644
--- a/src/Grid.js
+++ b/src/Grid.js
@@ -1,6 +1,6 @@
import React from 'react';
import classNames from 'classnames';
-import CustomPropTypes from './utils/CustomPropTypes';
+import { elementType } from 'react-prop-types';
const Grid = React.createClass({
propTypes: {
@@ -13,7 +13,7 @@ const Grid = React.createClass({
/**
* You can use a custom element for this component
*/
- componentClass: CustomPropTypes.elementType
+ componentClass: elementType
},
getDefaultProps() {
diff --git a/src/Jumbotron.js b/src/Jumbotron.js
index 5d5f69e658..135e6ca1db 100644
--- a/src/Jumbotron.js
+++ b/src/Jumbotron.js
@@ -1,13 +1,13 @@
import React from 'react';
import classNames from 'classnames';
-import CustomPropTypes from './utils/CustomPropTypes';
+import { elementType } from 'react-prop-types';
const Jumbotron = React.createClass({
propTypes: {
/**
* You can use a custom element for this component
*/
- componentClass: CustomPropTypes.elementType
+ componentClass: elementType
},
getDefaultProps() {
diff --git a/src/MenuItem.js b/src/MenuItem.js
index e0823b9186..f254ea6f66 100644
--- a/src/MenuItem.js
+++ b/src/MenuItem.js
@@ -1,6 +1,6 @@
import React from 'react';
import classnames from 'classnames';
-import CustomPropTypes from './utils/CustomPropTypes';
+import { all } from 'react-prop-types';
import SafeAnchor from './SafeAnchor';
export default class MenuItem extends React.Component {
@@ -62,7 +62,7 @@ export default class MenuItem extends React.Component {
MenuItem.propTypes = {
disabled: React.PropTypes.bool,
- divider: CustomPropTypes.all([
+ divider: all([
React.PropTypes.bool,
function(props, propName, componentName) {
if (props.divider && props.children) {
diff --git a/src/Modal.js b/src/Modal.js
index 6b8f18210e..1ffee7f709 100644
--- a/src/Modal.js
+++ b/src/Modal.js
@@ -5,7 +5,7 @@ import domUtils from './utils/domUtils';
import getScrollbarSize from 'dom-helpers/util/scrollbarSize';
import EventListener from './utils/EventListener';
import createChainedFunction from './utils/createChainedFunction';
-import CustomPropTypes from './utils/CustomPropTypes';
+import { elementType } from 'react-prop-types';
import Portal from 'react-overlays/lib/Portal';
import Fade from './Fade';
@@ -93,7 +93,7 @@ const Modal = React.createClass({
* A Component type that provides the modal content Markup. This is a useful prop when you want to use your own
* styles and markup to create a custom modal component.
*/
- dialogComponent: CustomPropTypes.elementType,
+ dialogComponent: elementType,
/**
* When `true` The modal will automatically shift focus to itself when it opens, and replace it to the last focused element when it closes.
diff --git a/src/Navbar.js b/src/Navbar.js
index 653db896d5..787e0a4b4a 100644
--- a/src/Navbar.js
+++ b/src/Navbar.js
@@ -4,7 +4,7 @@ import classNames from 'classnames';
import ValidComponentChildren from './utils/ValidComponentChildren';
import createChainedFunction from './utils/createChainedFunction';
-import CustomPropTypes from './utils/CustomPropTypes';
+import { elementType } from 'react-prop-types';
const Navbar = React.createClass({
mixins: [BootstrapMixin],
@@ -19,7 +19,7 @@ const Navbar = React.createClass({
/**
* You can use a custom element for this component
*/
- componentClass: CustomPropTypes.elementType,
+ componentClass: elementType,
brand: React.PropTypes.node,
toggleButton: React.PropTypes.node,
toggleNavKey: React.PropTypes.oneOfType([
diff --git a/src/Overlay.js b/src/Overlay.js
index 44e8f91d83..e0758adb68 100644
--- a/src/Overlay.js
+++ b/src/Overlay.js
@@ -3,7 +3,7 @@
import React, { cloneElement } from 'react';
import BaseOverlay from 'react-overlays/lib/Overlay';
-import CustomPropTypes from './utils/CustomPropTypes';
+import { elementType } from 'react-prop-types';
import Fade from './Fade';
import classNames from 'classnames';
@@ -57,7 +57,7 @@ Overlay.propTypes = {
*/
animation: React.PropTypes.oneOfType([
React.PropTypes.bool,
- CustomPropTypes.elementType
+ elementType
]),
/**
diff --git a/src/Pagination.js b/src/Pagination.js
index d9e2007fc3..164f59128b 100644
--- a/src/Pagination.js
+++ b/src/Pagination.js
@@ -2,7 +2,7 @@ import React from 'react';
import classNames from 'classnames';
import BootstrapMixin from './BootstrapMixin';
import PaginationButton from './PaginationButton';
-import CustomPropTypes from './utils/CustomPropTypes';
+import { elementType } from 'react-prop-types';
import SafeAnchor from './SafeAnchor';
const Pagination = React.createClass({
@@ -21,7 +21,7 @@ const Pagination = React.createClass({
/**
* You can use a custom element for the buttons
*/
- buttonComponentClass: CustomPropTypes.elementType
+ buttonComponentClass: elementType
},
getDefaultProps() {
diff --git a/src/PaginationButton.js b/src/PaginationButton.js
index 917ad79fdc..0aa3336f6c 100644
--- a/src/PaginationButton.js
+++ b/src/PaginationButton.js
@@ -2,7 +2,7 @@ import React from 'react';
import classNames from 'classnames';
import BootstrapMixin from './BootstrapMixin';
import createSelectedEvent from './utils/createSelectedEvent';
-import CustomPropTypes from './utils/CustomPropTypes';
+import { elementType } from 'react-prop-types';
const PaginationButton = React.createClass({
mixins: [BootstrapMixin],
@@ -19,7 +19,7 @@ const PaginationButton = React.createClass({
/**
* You can use a custom element for this component
*/
- buttonComponentClass: CustomPropTypes.elementType
+ buttonComponentClass: elementType
},
getDefaultProps() {
diff --git a/src/Popover.js b/src/Popover.js
index 7b8ed17f9a..a3a8b2a169 100644
--- a/src/Popover.js
+++ b/src/Popover.js
@@ -1,7 +1,7 @@
import React from 'react';
import classNames from 'classnames';
import BootstrapMixin from './BootstrapMixin';
-import CustomPropTypes from './utils/CustomPropTypes';
+import { isRequiredForA11y } from 'react-prop-types';
const Popover = React.createClass({
@@ -13,7 +13,7 @@ const Popover = React.createClass({
* @type {string}
* @required
*/
- id: CustomPropTypes.isRequiredForA11y(React.PropTypes.string),
+ id: isRequiredForA11y(React.PropTypes.string),
/**
* Sets the direction the Popover is positioned towards.
diff --git a/src/Portal.js b/src/Portal.js
index f27c32eb5c..4fc5b0ae56 100644
--- a/src/Portal.js
+++ b/src/Portal.js
@@ -8,4 +8,3 @@ export default deprecationWarning.wrapper(Portal, {
'http://react-bootstrap.github.io/react-overlays/examples/#portal and ' +
'https://github.com/react-bootstrap/react-bootstrap/issues/1084'
});
-
diff --git a/src/Row.js b/src/Row.js
index 90ccd145d2..53c35fc2bd 100644
--- a/src/Row.js
+++ b/src/Row.js
@@ -1,13 +1,13 @@
import React from 'react';
import classNames from 'classnames';
-import CustomPropTypes from './utils/CustomPropTypes';
+import { elementType } from 'react-prop-types';
const Row = React.createClass({
propTypes: {
/**
* You can use a custom element for this component
*/
- componentClass: CustomPropTypes.elementType
+ componentClass: elementType
},
getDefaultProps() {
diff --git a/src/Tooltip.js b/src/Tooltip.js
index 2691b14abf..ab8ca3ce28 100644
--- a/src/Tooltip.js
+++ b/src/Tooltip.js
@@ -1,7 +1,7 @@
import React from 'react';
import classNames from 'classnames';
import BootstrapMixin from './BootstrapMixin';
-import CustomPropTypes from './utils/CustomPropTypes';
+import { isRequiredForA11y } from 'react-prop-types';
const Tooltip = React.createClass({
mixins: [BootstrapMixin],
@@ -12,7 +12,7 @@ const Tooltip = React.createClass({
* @type {string}
* @required
*/
- id: CustomPropTypes.isRequiredForA11y(React.PropTypes.string),
+ id: isRequiredForA11y(React.PropTypes.string),
/**
* Sets the direction the Tooltip is positioned towards.
diff --git a/src/index.js b/src/index.js
index 72e728bf4f..d5019fc219 100644
--- a/src/index.js
+++ b/src/index.js
@@ -79,14 +79,12 @@ import domUtils from './utils/domUtils';
import childrenValueInputValidation from './utils/childrenValueInputValidation';
import createChainedFunction from './utils/createChainedFunction';
import ValidComponentChildren from './utils/ValidComponentChildren';
-import CustomPropTypes from './utils/CustomPropTypes';
export const utils = {
childrenValueInputValidation,
createChainedFunction,
ValidComponentChildren,
- CustomPropTypes,
- domUtils: createDeprecationWrapper(domUtils, 'utils/domUtils', 'npm install dom-helpers'),
+ domUtils: createDeprecationWrapper(domUtils, 'utils/domUtils', 'npm install dom-helpers')
};
function createDeprecationWrapper(obj, deprecated, instead, link){
diff --git a/src/utils/CustomPropTypes.js b/src/utils/CustomPropTypes.js
index caf3623f13..f5d7abb6e0 100644
--- a/src/utils/CustomPropTypes.js
+++ b/src/utils/CustomPropTypes.js
@@ -1,5 +1,3 @@
-import React from 'react';
-import warning from 'react/lib/warning';
import childrenToArray from './childrenToArray';
const ANONYMOUS = '<>';
@@ -30,30 +28,7 @@ function createChainableTypeChecker(validate) {
return chainedCheckType;
}
-const CustomPropTypes = {
-
- deprecated(propType, explanation){
- return function(props, propName, componentName){
- if (props[propName] != null) {
- warning(false, `"${propName}" property of "${componentName}" has been deprecated.\n${explanation}`);
- }
-
- return propType(props, propName, componentName);
- };
- },
-
- isRequiredForA11y(propType){
- return function(props, propName, componentName){
- if (props[propName] == null) {
- return new Error(
- 'The prop `' + propName + '` is required to make ' + componentName + ' accessible ' +
- 'for users using assistive technologies such as screen readers `'
- );
- }
-
- return propType(props, propName, componentName);
- };
- },
+export default {
requiredRoles(...roles) {
return createChainableTypeChecker(
@@ -101,151 +76,5 @@ const CustomPropTypes = {
}
});
- },
-
- /**
- * Checks whether a prop provides a DOM element
- *
- * The element can be provided in two forms:
- * - Directly passed
- * - Or passed an object that has a `render` method
- *
- * @param props
- * @param propName
- * @param componentName
- * @returns {Error|undefined}
- */
- mountable: createMountableChecker(),
-
- /**
- * Checks whether a prop provides a type of element.
- *
- * The type of element can be provided in two forms:
- * - tag name (string)
- * - a return value of React.createClass(...)
- *
- * @param props
- * @param propName
- * @param componentName
- * @returns {Error|undefined}
- */
- elementType: createElementTypeChecker(),
-
- /**
- * Checks whether a prop matches a key of an associated object
- *
- * @param props
- * @param propName
- * @param componentName
- * @returns {Error|undefined}
- */
- keyOf: createKeyOfChecker,
- /**
- * Checks if only one of the listed properties is in use. An error is given
- * if multiple have a value
- *
- * @param props
- * @param propName
- * @param componentName
- * @returns {Error|undefined}
- */
- singlePropFrom: createSinglePropFromChecker,
-
- all
-};
-
-function errMsg(props, propName, componentName, msgContinuation) {
- return `Invalid prop '${propName}' of value '${props[propName]}'` +
- ` supplied to '${componentName}'${msgContinuation}`;
-}
-
-function createMountableChecker() {
- function validate(props, propName, componentName) {
- if (typeof props[propName] !== 'object' ||
- typeof props[propName].render !== 'function' && props[propName].nodeType !== 1) {
- return new Error(
- errMsg(props, propName, componentName,
- ', expected a DOM element or an object that has a `render` method')
- );
- }
}
-
- return createChainableTypeChecker(validate);
-}
-
-function createKeyOfChecker(obj) {
- function validate(props, propName, componentName) {
- let propValue = props[propName];
- if (!obj.hasOwnProperty(propValue)) {
- let valuesString = JSON.stringify(Object.keys(obj));
- return new Error(
- errMsg(props, propName, componentName, `, expected one of ${valuesString}.`)
- );
- }
- }
- return createChainableTypeChecker(validate);
-}
-
-function createSinglePropFromChecker(arrOfProps) {
- function validate(props, propName, componentName) {
- const usedPropCount = arrOfProps
- .map(listedProp => props[listedProp])
- .reduce((acc, curr) => acc + (curr !== undefined ? 1 : 0), 0);
-
- if (usedPropCount > 1) {
- const [first, ...others] = arrOfProps;
- const message = `${others.join(', ')} and ${first}`;
- return new Error(
- `Invalid prop '${propName}', only one of the following ` +
- `may be provided: ${message}`
- );
- }
- }
- return validate;
-}
-
-function all(propTypes) {
- if (propTypes === undefined) {
- throw new Error('No validations provided');
- }
-
- if (!(propTypes instanceof Array)) {
- throw new Error('Invalid argument must be an array');
- }
-
- if (propTypes.length === 0) {
- throw new Error('No validations provided');
- }
-
- return function(props, propName, componentName) {
- for(let i = 0; i < propTypes.length; i++) {
- let result = propTypes[i](props, propName, componentName);
-
- if (result !== undefined && result !== null) {
- return result;
- }
- }
- };
-}
-
-function createElementTypeChecker() {
- function validate(props, propName, componentName) {
- let errBeginning = errMsg(props, propName, componentName,
- '. Expected an Element `type`');
-
- if (typeof props[propName] !== 'function') {
- if (React.isValidElement(props[propName])) {
- return new Error(errBeginning + ', not an actual Element');
- }
-
- if (typeof props[propName] !== 'string') {
- return new Error(errBeginning +
- ' such as a tag name or return value of React.createClass(...)');
- }
- }
- }
-
- return createChainableTypeChecker(validate);
-}
-
-export default CustomPropTypes;
+};
diff --git a/src/utils/childrenValueInputValidation.js b/src/utils/childrenValueInputValidation.js
index 075a4c42b0..37f888a3a3 100644
--- a/src/utils/childrenValueInputValidation.js
+++ b/src/utils/childrenValueInputValidation.js
@@ -1,5 +1,5 @@
import React from 'react';
-import { singlePropFrom } from './CustomPropTypes';
+import { singlePropFrom } from 'react-prop-types';
const propList = ['children', 'value'];
const typeList = [React.PropTypes.number, React.PropTypes.string];
diff --git a/src/utils/index.js b/src/utils/index.js
index e84c9f5b33..9c2a55d77c 100644
--- a/src/utils/index.js
+++ b/src/utils/index.js
@@ -7,7 +7,3 @@ deprecationWarning('utils/domUtils', 'npm install dom-helpers');
export domUtils from './domUtils';
export ValidComponentChildren from './ValidComponentChildren';
-
-deprecationWarning('utils/CustomPropTypes', 'npm install react-prop-types',
- 'https://github.com/react-bootstrap/react-bootstrap/issues/937');
-export CustomPropTypes from './CustomPropTypes';
diff --git a/test/utils/CustomPropTypesSpec.js b/test/utils/CustomPropTypesSpec.js
deleted file mode 100644
index 92e542b366..0000000000
--- a/test/utils/CustomPropTypesSpec.js
+++ /dev/null
@@ -1,207 +0,0 @@
-import React from 'react';
-import ReactTestUtils from 'react/lib/ReactTestUtils';
-import CustomPropTypes from '../../src/utils/CustomPropTypes';
-import {shouldWarn} from '../helpers';
-
-function isChainableAndUndefinedOK(validatorUnderTest) {
- it('Should validate OK with undefined or null values', function() {
- assert.isUndefined(validatorUnderTest({}, 'p', 'Component'));
- assert.isUndefined(validatorUnderTest({p: null}, 'p', 'Component'));
- });
-
- it('Should be able to chain', function() {
- let err = validatorUnderTest.isRequired({}, 'p', 'Component');
- assert.instanceOf(err, Error);
- assert.include(err.message, 'Required prop');
- assert.include(err.message, 'was not specified in');
- });
-}
-
-describe('CustomPropTypes', function() {
-
- describe('mountable', function () {
- function validate(prop) {
- return CustomPropTypes.mountable({p: prop}, 'p', 'Component');
- }
-
- isChainableAndUndefinedOK(CustomPropTypes.mountable);
-
- it('Should return error with non mountable values', function() {
- let err = validate({});
- assert.instanceOf(err, Error);
- assert.include(err.message, 'expected a DOM element or an object that has a `render` method');
- });
-
- it('Should return undefined with mountable values', function() {
- assert.isUndefined(validate(document.createElement('div')));
- assert.isUndefined(validate(document.body));
- assert.isUndefined(validate(ReactTestUtils.renderIntoDocument()));
- });
- });
-
- describe('elementType', function () {
- function validate(prop) {
- return CustomPropTypes.elementType({p: prop}, 'p', 'TestComponent');
- }
-
- isChainableAndUndefinedOK(CustomPropTypes.elementType);
-
- it('Should validate OK with elementType values', function() {
- assert.isUndefined(validate('span'));
- assert.isUndefined(validate(function(){}));
- });
-
- it('Should return error with not a string or function values', function() {
- let err = validate({});
- assert.instanceOf(err, Error);
- assert.include(err.message, 'Expected an Element `type` such as a tag name or return value of React.createClass(...)');
- });
-
- it('Should return error with react element', function() {
- let err = validate(React.createElement('span'));
- assert.instanceOf(err, Error);
- assert.include(err.message, 'Expected an Element `type`, not an actual Element');
- });
- });
-
- describe('keyOf', function () {
- let obj = {'foo': 1};
- function validate(prop) {
- return CustomPropTypes.keyOf(obj)({p: prop}, 'p', 'Component');
- }
-
- isChainableAndUndefinedOK(CustomPropTypes.keyOf(obj));
-
- it('Should return error with non-key values', function() {
- let err = validate('bar');
- assert.instanceOf(err, Error);
- assert.include(err.message, 'expected one of ["foo"]');
- });
-
- it('Should validate OK with key values', function() {
- assert.isUndefined(validate('foo'));
- obj.bar = 2;
- assert.isUndefined(validate('bar'));
- });
- });
-
- describe('singlePropFrom', function () {
- function validate(testProps) {
- const propList = ['children', 'value'];
-
- return CustomPropTypes.singlePropFrom(propList)(testProps, 'value', 'Component');
- }
-
- it('Should validate OK if only one listed prop in used', function () {
- const testProps = {value: 5};
-
- assert.isUndefined(validate(testProps));
- });
-
- it('Should return error if multiple of the listed properties have values', function () {
- let err = validate({value: 5, children: 5});
- assert.instanceOf(err, Error);
- assert.include(err.message, 'only one of the following may be provided: value and children');
- });
- });
-
- describe('all', function() {
- let validators;
- const props = {
- key: 'value'
- };
- const propName = 'key';
- const componentName = 'TestComponent';
-
- beforeEach(function() {
- validators = [
- sinon.stub(),
- sinon.stub(),
- sinon.stub()
- ];
- });
-
- it('with no arguments provided', function() {
- expect(() => {
- CustomPropTypes.all();
- }).to.throw(Error, /No validations provided/);
- });
-
- it('with no validations provided', function() {
- expect(() => {
- CustomPropTypes.all([]);
- }).to.throw(Error, /No validations provided/);
- });
-
- it('with invalid arguments provided', function() {
- expect(() => {
- CustomPropTypes.all(1);
- }).to.throw(Error, /Invalid argument must be an array/);
- });
-
- it('validates each validation', function() {
- const all = CustomPropTypes.all(validators);
-
- let result = all(props, propName, componentName);
- expect(result).to.equal(undefined);
-
- validators.forEach(x => {
- x.should.have.been.calledOnce
- .and.calledWith(props, propName, componentName);
- });
- });
-
- it('returns first validation failure', function() {
- let err = new Error('Failure');
- validators[1].returns(err);
- const all = CustomPropTypes.all(validators);
-
- let result = all(props, propName, componentName);
- expect(result).to.equal(err);
-
- validators[0].should.have.been.calledOnce
- .and.calledWith(props, propName, componentName);
-
- validators[1].should.have.been.calledOnce
- .and.calledWith(props, propName, componentName);
-
- validators[2].should.not.have.been.called;
- });
- });
-
- describe('isRequiredForA11y', function () {
- function validate(prop) {
- return CustomPropTypes.isRequiredForA11y(React.PropTypes.string)({p: prop}, 'p', 'Component');
- }
-
- it('Should validate OK when property is provided', function() {
- let err = validate('aria-tag');
- assert.notInstanceOf(err, Error);
- });
-
- it('Should return custom error message when property is not provided', function() {
- let err = validate(null);
- assert.instanceOf(err, Error);
- assert.include(err.message, 'accessible for users using assistive technologies such as screen readers');
- });
- });
-
- describe('deprecated', function () {
- function validate(prop) {
- return CustomPropTypes.deprecated(React.PropTypes.string, 'Read more at link')({pName: prop}, 'pName', 'ComponentName');
- }
-
- it('Should warn about deprecation and validate OK', function() {
- let err = validate('value');
- shouldWarn('"pName" property of "ComponentName" has been deprecated.\nRead more at link');
- assert.notInstanceOf(err, Error);
- });
-
- it('Should warn about deprecation and throw validation error when property value is not OK', function() {
- let err = validate({});
- shouldWarn('"pName" property of "ComponentName" has been deprecated.\nRead more at link');
- assert.instanceOf(err, Error);
- assert.include(err.message, 'Invalid undefined `pName` of type `object` supplied to `ComponentName`');
- });
- });
-});