├── .gitignore ├── .npmignore ├── README.md ├── babelhook.js ├── package.json ├── src └── ValidatorPropTypes.js └── test ├── ValidatorPropTypes.js └── mocha.opts /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | lib 3 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | src 2 | test 3 | babelhook.js 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # react-validator-prop-types 2 | [React PropType](https://facebook.github.io/react/docs/reusable-components.html#prop-validation) wrappers around [validator.js functions](https://github.com/chriso/validator.js#validators). 3 | 4 | ## What problem does this solve 5 | 6 | Great validation libraries already exist, but the default set of React PropTypes is pretty sparse by comparison. This just wraps the popular validator.js library into prop types that support the `isRequired` convention. 7 | 8 | ## Usage 9 | ```js 10 | var ValidatorPropTypes = require('react-validator-prop-types'); 11 | React.createClass({ 12 | propTypes: { 13 | background: ValidatorPropTypes.hexColor, 14 | email: ValidatorPropTypes.email.isRequired, 15 | username: ValidatorPropTypes.lowercase.isRequired, 16 | } 17 | ... 18 | }); 19 | ``` 20 | The following prop types are available: 21 | - email 22 | - url 23 | - fqdn 24 | - ip 25 | - alpha 26 | - numeric 27 | - alphanumeric 28 | - base64 29 | - hexadecimal 30 | - hexColor 31 | - lowercase 32 | - uppercase 33 | - int 34 | - float 35 | - uuid 36 | - date 37 | - creditCard 38 | - json 39 | - multibyte 40 | - ascii 41 | - fullWidth 42 | - halfWidth 43 | - variableWidth 44 | - surrogatePair 45 | - mongoId 46 | - currency 47 | 48 | ## Missing functions ? 49 | 50 | There's no easy way to wrap validator functions that take multiple, or optional arguments. This is why functions like `isMobilePhone` aren't supported (because of the `locale` argument). Suggestions for an api for those are encouraged. 51 | 52 | ## Acknowledgements 53 | 54 | Thanks to [Chris O'Hara](https://github.com/chriso) for [validator.js](https://github.com/chriso/validator.js). 55 | -------------------------------------------------------------------------------- /babelhook.js: -------------------------------------------------------------------------------- 1 | require('babel/register')({ 2 | experimental: true 3 | }); 4 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-validator-prop-types", 3 | "version": "1.1.0", 4 | "description": "", 5 | "main": "lib/ValidatorPropTypes", 6 | "scripts": { 7 | "test": "mocha", 8 | "prepublish": "npm run compile", 9 | "compile": "babel -d lib/ src/" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "git+https://github.com/pwmckenna/react-validator-prop-types.git" 14 | }, 15 | "author": "Patrick Williams ", 16 | "license": "ISC", 17 | "bugs": { 18 | "url": "https://github.com/pwmckenna/react-validator-prop-types/issues" 19 | }, 20 | "keywords": [ 21 | "react-component", 22 | "react-native", 23 | "prop-types", 24 | "proptypes", 25 | "validator", 26 | "react" 27 | ], 28 | "homepage": "https://github.com/pwmckenna/react-validator-prop-types#readme", 29 | "devDependencies": { 30 | "babel": "^5.8.23", 31 | "lodash": "^3.8.0", 32 | "react": "^0.13.3" 33 | }, 34 | "dependencies": { 35 | "validator": "^3.39.0" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/ValidatorPropTypes.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var validator = require('validator'); 4 | 5 | var checkType = function (fn, isRequired, props, propName, componentName) { 6 | if (props[propName] == null) { 7 | if (isRequired) { 8 | return new Error(`Required \`${propName}\` was not specified in \`${componentName}\`.`); 9 | } 10 | return; 11 | } 12 | 13 | if (!fn(props[propName])) { 14 | return new Error(`Invalid \`${propName}\` was specified in \`${componentName}\`.`); 15 | } 16 | }; 17 | 18 | var createPropType = function (validatorFn) { 19 | var propType = checkType.bind(null, validatorFn, false); 20 | propType.isRequired = checkType.bind(null, validatorFn, true); 21 | return propType; 22 | }; 23 | 24 | var map = { 25 | email: 'isEmail', 26 | url: 'isURL', 27 | fqdn: 'isFQDN', 28 | ip: 'isIP', 29 | alpha: 'isAlpha', 30 | numeric: 'isNumeric', 31 | alphanumeric: 'isAlphanumeric', 32 | base64: 'isBase64', 33 | hexadecimal: 'isHexadecimal', 34 | hexColor: 'isHexColor', 35 | lowercase: 'isLowercase', 36 | uppercase: 'isUppercase', 37 | int: 'isInt', 38 | float: 'isFloat', 39 | uuid: 'isUUID', 40 | date: 'isDate', 41 | creditCard: 'isCreditCard', 42 | json: 'isJSON', 43 | multibyte: 'isMultibyte', 44 | ascii: 'isAscii', 45 | fullWidth: 'isFullWidth', 46 | halfWidth: 'isHalfWidth', 47 | variableWidth: 'isVariableWidth', 48 | surrogatePair: 'isSurrogatePair', 49 | mongoId: 'isMongoId', 50 | currency: 'isCurrency' 51 | }; 52 | 53 | for (var key in map) { 54 | var functionName = map[key]; 55 | if (!validator.hasOwnProperty(functionName)) { 56 | throw new Error('validator.' + functionName + ' is not a function'); 57 | } 58 | module.exports[key] = createPropType(validator[functionName]); 59 | } 60 | -------------------------------------------------------------------------------- /test/ValidatorPropTypes.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var React = require('react'); 4 | var PropTypes = require('../src/ValidatorPropTypes'); 5 | var _ = require('lodash'); 6 | 7 | 8 | var errorPrefix = 'Warning: Failed propType: '; 9 | 10 | var getRequiredPropTypeError = function (propName, componentName) { 11 | return errorPrefix + `Required \`${propName}\` was not specified in \`${componentName}\`.`; 12 | }; 13 | 14 | var getPropTypeError = function (propName, componentName) { 15 | return errorPrefix + `Invalid \`${propName}\` was specified in \`${componentName}\`.` 16 | }; 17 | 18 | var propTypes = { 19 | email: PropTypes.email, 20 | url: PropTypes.url, 21 | fqdn: PropTypes.fqdn, 22 | ip: PropTypes.ip, 23 | alpha: PropTypes.alpha, 24 | numeric: PropTypes.numeric, 25 | alphanumeric: PropTypes.alphanumeric, 26 | base64: PropTypes.base64, 27 | hexadecimal: PropTypes.hexadecimal, 28 | hexColor: PropTypes.hexColor, 29 | lowercase: PropTypes.lowercase, 30 | uppercase: PropTypes.uppercase, 31 | int: PropTypes.int, 32 | float: PropTypes.float, 33 | uuid: PropTypes.uuid, 34 | date: PropTypes.date, 35 | creditCard: PropTypes.creditCard, 36 | json: PropTypes.json, 37 | multibyte: PropTypes.multibyte, 38 | ascii: PropTypes.ascii, 39 | fullWidth: PropTypes.fullWidth, 40 | halfWidth: PropTypes.halfWidth, 41 | variableWidth: PropTypes.variableWidth, 42 | surrogatePair: PropTypes.surrogatePair, 43 | mongoId: PropTypes.mongoId, 44 | currency: PropTypes.currency 45 | }; 46 | 47 | var propTypesRequired = {}; 48 | for (var k in propTypes) { 49 | propTypesRequired[k] = propTypes[k].isRequired; 50 | } 51 | 52 | var Component = React.createClass({ 53 | propTypes: propTypes, 54 | render: function () { 55 | return null; 56 | } 57 | }); 58 | 59 | var RequiredComponent = React.createClass({ 60 | propTypes: propTypesRequired, 61 | render: function () { 62 | return null; 63 | } 64 | }); 65 | 66 | describe('propTypes', function () { 67 | beforeEach(function () { 68 | this.warnings = []; 69 | this.warn = console.warn; 70 | console.warn = function (msg) { 71 | this.warnings.push(msg); 72 | }.bind(this); 73 | }); 74 | afterEach(function () { 75 | console.warn = this.warn; 76 | }); 77 | 78 | it('makes sure all propTypes can pass', function () { 79 | var props = { 80 | email: 'pwmckenna@gmail.com', 81 | url: 'http://pwmckenna.com/', 82 | fqdn: 'pwmckenna.com', 83 | ip: '4.2.2.2', 84 | alpha: 'alpha', 85 | numeric: '4222', 86 | alphanumeric: 'alpha4222', 87 | hexColor: '#ffffff', 88 | lowercase: 'lower', 89 | uppercase: 'UPPER', 90 | int: 4222, 91 | float: 4222.4222 92 | }; 93 | React.renderToString( 94 | 95 | ); 96 | if (this.warnings.length) { 97 | throw new Error('did not expected warnings for valid proptypes'); 98 | } 99 | }); 100 | 101 | it('makes sure all propTypes can fail', function () { 102 | var props = { 103 | email: NaN, 104 | url: NaN, 105 | fqdn: NaN, 106 | ip: NaN, 107 | alpha: NaN, 108 | numeric: NaN, 109 | alphanumeric: NaN, 110 | hexColor: NaN, 111 | lowercase: 'NaN', 112 | uppercase: 'nan', 113 | int: NaN, 114 | float: NaN 115 | }; 116 | React.renderToString( 117 | 118 | ); 119 | _.each(props, function (value, key) { 120 | if (!_.contains(this.warnings, getPropTypeError(key, 'Component'))) { 121 | throw new Error('expected to see prop type warnings for ' + key + ' printed to console.warn'); 122 | } 123 | }, this); 124 | }); 125 | it('tests required propTypes', function () { 126 | React.renderToString( 127 | // make sure to set a prop to null to ensure that null does not satisfy isRequired 128 | 129 | ); 130 | _.each(propTypes, function (value, key) { 131 | if (!_.contains(this.warnings, getRequiredPropTypeError(key, 'RequiredComponent'))) { 132 | throw new Error('expected to see required prop type warnings for ' + key + ' printed to console.warn'); 133 | } 134 | }, this); 135 | }) 136 | }); 137 | -------------------------------------------------------------------------------- /test/mocha.opts: -------------------------------------------------------------------------------- 1 | --require babelhook 2 | --------------------------------------------------------------------------------