diff --git a/.gitignore b/.gitignore index f03556e..15d5043 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ .DS_Store build node_modules +lib diff --git a/.travis.yml b/.travis.yml index e616d52..cc6db79 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,4 @@ language: node_js node_js: - - 'iojs' - '0.12' - - '0.11' - '0.10' diff --git a/API.md b/API.md index 4d2bc81..fa1a198 100644 --- a/API.md +++ b/API.md @@ -35,6 +35,8 @@ - [isFormSubmitted()](#isformsubmitted) - [validate](#validate) - [formNoValidate](#formnovalidate) +- [Formsy.HOC](#formsyhoc) +- [Formsy.Decorator](#formsydecorator) - [Formsy.addValidationRule](#formsyaddvalidationrule) - [Validators](#validators) @@ -109,7 +111,7 @@ var Form = React.createClass({ ```html ``` -Takes a function to run when the submit button has been clicked. +Takes a function to run when the submit button has been clicked. The first argument is the data of the form. The second argument will reset the form. The third argument will invalidate the form by taking an object that maps to inputs. This is useful for server side validation. E.g. `{email: "This email is taken"}`. Resetting or invalidating the form will cause **setState** to run on the form element component. @@ -226,12 +228,12 @@ The message that will show when the form input component is invalid. It will be #### validationErrors ```html -showRequired() ```javascript @@ -424,7 +426,7 @@ var MyInput = React.createClass({ } }); ``` -Lets you check if the form input component should indicate if it is a required field. This happens when the form input component value is empty and the required prop has been passed. +Lets you check if the form input component should indicate if it is a required field. This happens when the form input component value is empty and the required prop has been passed. #### showError() ```javascript @@ -539,6 +541,41 @@ var MyInput = React.createClass({ }); ``` +### Formsy.HOC +The same methods as the mixin are exposed to the HOC version of the element component, though through the `props`, not on the instance. +```js +import {HOC} from 'formsy-react'; + +class MyInput extends React.Component { + render() { + return ( +
+ this.props.setValue(e.target.value)}/> +
+ ); + } +}; +export default HOC(MyInput); +``` + +### Formsy.Decorator +The same methods as the mixin are exposed to the decorator version of the element component, though through the `props`, not on the instance. +```js +import {Decorator as FormsyElement} from 'formsy-react'; + +@FormsyElement() +class MyInput extends React.Component { + render() { + return ( +
+ this.props.setValue(e.target.value)}/> +
+ ); + } +}; +export default MyInput +``` + ### Formsy.addValidationRule(name, ruleFunc) An example: ```javascript @@ -622,18 +659,36 @@ Returns true if the value is the boolean true ``` Returns true if the value is the boolean false -**isNumeric** -```html - -``` -Returns true if string only contains numbers - **isAlpha** ```html ``` Returns true if string is only letters +**isNumeric** +```html + +``` +Returns true if string only contains numbers. Examples: 42; -3.14 + +**isAlphanumeric** +```html + +``` +Returns true if string only contains letters or numbers + +**isInt** +```html + +``` +Returns true if string represents integer value. Examples: 42; -12; 0 + +**isFloat** +```html + +``` +Returns true if string represents float value. Examples: 42; -3.14; 1e3 + **isWords** ```html diff --git a/README.md b/README.md index 962916b..87b3c08 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ A form input builder and validator for React JS ### From version 0.12.0 Formsy only supports React 0.13.1 and up ## Background -I wrote an article on forms and validation with React JS, [Nailing that validation with React JS](http://christianalfoni.github.io/javascript/2014/10/22/nailing-that-validation-with-reactjs.html), the result of that was this extension. +I wrote an article on forms and validation with React JS, [Nailing that validation with React JS](http://christianalfoni.github.io/javascript/2014/10/22/nailing-that-validation-with-reactjs.html), the result of that was this extension. The main concept is that forms, inputs and validation is done very differently across developers and projects. This extension to React JS aims to be that "sweet spot" between flexibility and reusability. @@ -21,12 +21,15 @@ The main concept is that forms, inputs and validation is done very differently a 2. Add validation rules and use them with simple syntax - 3. Use handlers for different states of your form. Ex. "onSubmit", "onError", "onValid" etc. + 3. Use handlers for different states of your form. Ex. "onSubmit", "onError", "onValid" etc. - 4. Server validation errors automatically binds to the correct form input component + 4. Pass external errors to the form to invalidate elements 5. You can dynamically add form elements to your form and they will register/unregister to the form +## Default elements +You can look at examples in this repo or use the [formsy-react-components](https://github.com/twisty/formsy-react-components) project to use bootstrap with formsy-react. + ## Install 1. Download from this REPO and use globally (Formsy) or with requirejs @@ -91,7 +94,7 @@ This code results in a form with a submit button that will run the `submit` meth // Add the Formsy Mixin mixins: [Formsy.Mixin], - // setValue() will set the value of the component, which in + // setValue() will set the value of the component, which in // turn will validate it and the rest of the form changeValue: function (event) { this.setValue(event.currentTarget.value); @@ -99,9 +102,9 @@ This code results in a form with a submit button that will run the `submit` meth render: function () { // Set a specific className based on the validation - // state of this component. showRequired() is true - // when the value is empty and the required prop is - // passed to the input. showError() is true when the + // state of this component. showRequired() is true + // when the value is empty and the required prop is + // passed to the input. showError() is true when the // value typed is invalid var className = this.showRequired() ? 'required' : this.showError() ? 'error' : null; diff --git a/bower.json b/bower.json index a3eeaac..2c5602d 100644 --- a/bower.json +++ b/bower.json @@ -1,6 +1,6 @@ { "name": "formsy-react", - "version": "0.14.1", + "version": "0.16.0", "description": "A form input builder and validator for React JS", "repository": { "type": "git", diff --git a/build/test.js b/build/test.js index e8908b0..39c8722 100644 --- a/build/test.js +++ b/build/test.js @@ -1,37 +1,55 @@ var React = require('react'); +var ReactDOM = require('react-dom'); var Formsy = require('./../src/main.js'); var Input = React.createClass({ - - mixins: [Formsy.Mixin], - + onChange: function (event) { + this.props.setValue(event.currentTarget.value); + }, render: function () { return (
- {this.showError()} - {this.getErrorMessage()} - + {this.props.showRequired() ? 'required' : ''} +
); } }); -var FormApp = React.createClass({ - componentDidMount: function () { - this.refs.form.updateInputsWithError({ - 'foo.bar': 'hmmm' +Input = Formsy.HOC(Input); + +var SomeComp = React.createClass({ + getInitialState: function () { + return { + isRequired: false + }; + }, + toggleRequired: function () { + this.setState({ + isRequired: !this.state.isRequired }); }, + render: function () { + return ( +
+ + +
+ ) + } +}); + +var FormApp = React.createClass({ onSubmit: function (model) { console.log('model', model); }, render: function () { return ( - - + + ); } }); -React.render(, document.getElementById('app')); \ No newline at end of file +ReactDOM.render(, document.getElementById('app')); diff --git a/examples/README.md b/examples/README.md index 1921954..691cc4f 100644 --- a/examples/README.md +++ b/examples/README.md @@ -29,4 +29,8 @@ If it is not helped try update your node.js and npm. 2. [**Custom Validation**](custom-validation) - One field with added validation rule (`Formsy.addValidationRule`) and one field with dynamically added validation and error messages. + One field with added validation rule (`Formsy.addValidationRule`) and one field with dynamically added validation and error messages. + +3. [**Reset Values**](reset-values) + + Reset text input, checkbox and select to their pristine values. diff --git a/examples/components/Input.js b/examples/components/Input.js new file mode 100644 index 0000000..a73e060 --- /dev/null +++ b/examples/components/Input.js @@ -0,0 +1,43 @@ +import React from 'react'; +import Formsy from 'formsy-react'; + +const MyInput = React.createClass({ + + // Add the Formsy Mixin + mixins: [Formsy.Mixin], + + // setValue() will set the value of the component, which in + // turn will validate it and the rest of the form + changeValue(event) { + this.setValue(event.currentTarget[this.props.type === 'checkbox' ? 'checked' : 'value']); + }, + render() { + + // Set a specific className based on the validation + // state of this component. showRequired() is true + // when the value is empty and the required prop is + // passed to the input. showError() is true when the + // value typed is invalid + const className = this.props.className + ' ' + (this.showRequired() ? 'required' : this.showError() ? 'error' : null); + + // An error message is returned ONLY if the component is invalid + // or the server has returned an error message + const errorMessage = this.getErrorMessage(); + + return ( +
+ + + {errorMessage} +
+ ); + } +}); + +export default MyInput; diff --git a/examples/components/Select.js b/examples/components/Select.js new file mode 100644 index 0000000..57a286d --- /dev/null +++ b/examples/components/Select.js @@ -0,0 +1,34 @@ +import React from 'react'; +import Formsy from 'formsy-react'; + +const MySelect = React.createClass({ + mixins: [Formsy.Mixin], + + changeValue(event) { + this.setValue(event.currentTarget.value); + }, + + render() { + const className = this.props.className + ' ' + (this.showRequired() ? 'required' : this.showError() ? 'error' : null); + const errorMessage = this.getErrorMessage(); + + const options = this.props.options.map((option, i) => ( + + )); + + return ( +
+ + + {errorMessage} +
+ ); + } + +}); + +export default MySelect; diff --git a/examples/custom-validation/app.js b/examples/custom-validation/app.js index bd5fd32..886e583 100644 --- a/examples/custom-validation/app.js +++ b/examples/custom-validation/app.js @@ -1,9 +1,12 @@ -var React = require('react'); -var Formsy = require('formsy-react'); +import React from 'react'; +import ReactDOM from 'react-dom'; +import Formsy from 'formsy-react'; -var currentYear = new Date().getFullYear(); +import MyInput from './../components/Input'; -var validators = { +const currentYear = new Date().getFullYear(); + +const validators = { time: { regexp: /^(([0-1]?[0-9])|([2][0-3])):([0-5]?[0-9])(:([0-5]?[0-9]))?$/, message: 'Not valid time' @@ -18,79 +21,56 @@ var validators = { } }; -Formsy.addValidationRule('isYearOfBirth', function (values, value) { +Formsy.addValidationRule('isYearOfBirth', (values, value) => { value = parseInt(value); - if (typeof value !== 'number' || value !== value) { + if (typeof value !== 'number') { return false; } return value < currentYear && value > currentYear - 130; }); -var App = React.createClass({ - submit: function (data) { +const App = React.createClass({ + submit(data) { alert(JSON.stringify(data, null, 4)); }, - render: function () { + render() { return ( - - + + ); } }); -var MyOwnInput = React.createClass({ +const DynamicInput = React.createClass({ mixins: [Formsy.Mixin], - changeValue: function (event) { + getInitialState() { + return { validationType: 'time' }; + }, + changeValue(event) { this.setValue(event.currentTarget.value); }, - render: function () { - var className = this.props.className + ' ' + (this.showError() ? 'error' : null); - var errorMessage = this.getErrorMessage(); - - return ( -
- - - {errorMessage} -
- ); - } -}); - -var DynamicInput = React.createClass({ - mixins: [Formsy.Mixin], - getInitialState: function() { - return { - validationType: 'time' - }; - }, - changeValue: function (event) { - this.setValue(event.currentTarget.value); - }, - changeValidation: function(validationType) { - this.setState({ - validationType: validationType - }); + changeValidation(validationType) { + this.setState({ validationType: validationType }); this.setValue(this.getValue()); }, - validate: function () { - var value = this.getValue(); - return value === '' ? true : validators[this.state.validationType].regexp.test(value); + validate() { + const value = this.getValue(); + return value !== '' ? validators[this.state.validationType].regexp.test(value) : true; }, - getCustomErrorMessage: function() { + getCustomErrorMessage() { return this.showError() ? validators[this.state.validationType].message : ''; }, - render: function () { - var className = this.props.className + ' ' + (this.showError() ? 'error' : null); - var errorMessage = this.getCustomErrorMessage(); + render() { + const className = this.props.className + ' ' + (this.showError() ? 'error' : null); + const errorMessage = this.getCustomErrorMessage(); return (
- + {errorMessage}
@@ -98,26 +78,27 @@ var DynamicInput = React.createClass({ } }); -var Validations = React.createClass({ - changeValidation: function(e) { +const Validations = React.createClass({ + changeValidation(e) { this.props.changeValidation(e.target.value); }, - render: function() { + render() { + const { validationType } = this.props; return (
Validation Type
- Time + Time
- Decimal + Decimal
- Binary + Binary
); } }); -React.render(, document.getElementById('example')); +ReactDOM.render(, document.getElementById('example')); diff --git a/examples/global.css b/examples/global.css index 15a2a75..ebdad7f 100644 --- a/examples/global.css +++ b/examples/global.css @@ -38,7 +38,8 @@ form { .form-group input[type='text'], .form-group input[type='email'], .form-group input[type='number'], -.form-group input[type='password'] { +.form-group input[type='password'], +.form-group select { display: block; width: 100%; height: 34px; @@ -62,4 +63,11 @@ form { button { padding: 10px 15px; border-radius: 4px; -} \ No newline at end of file +} + +.buttons button { + margin-left: 10px; +} +.buttons button:first-child { + margin-left: 0; +} diff --git a/examples/index.html b/examples/index.html index d813a18..175c07b 100644 --- a/examples/index.html +++ b/examples/index.html @@ -9,6 +9,7 @@ - \ No newline at end of file + diff --git a/examples/login/app.js b/examples/login/app.js index 24439c2..1de0d0a 100644 --- a/examples/login/app.js +++ b/examples/login/app.js @@ -1,65 +1,31 @@ -var React = require('react'); -var Formsy = require('formsy-react'); +import React from 'react'; +import ReactDOM from 'react-dom'; +import { Form } from 'formsy-react'; -var App = React.createClass({ - getInitialState: function() { +import MyInput from './../components/Input'; + +const App = React.createClass({ + getInitialState() { return { canSubmit: false }; }, - submit: function (data) { + submit(data) { alert(JSON.stringify(data, null, 4)); }, - enableButton: function () { - this.setState({ - canSubmit: true - }); + enableButton() { + this.setState({ canSubmit: true }); }, - disableButton: function () { - this.setState({ - canSubmit: false - }); + disableButton() { + this.setState({ canSubmit: false }); }, - render: function () { + render() { return ( - - - +
+ + - + ); } }); -var MyOwnInput = React.createClass({ - - // Add the Formsy Mixin - mixins: [Formsy.Mixin], - - // setValue() will set the value of the component, which in - // turn will validate it and the rest of the form - changeValue: function (event) { - this.setValue(event.currentTarget.value); - }, - render: function () { - - // Set a specific className based on the validation - // state of this component. showRequired() is true - // when the value is empty and the required prop is - // passed to the input. showError() is true when the - // value typed is invalid - var className = this.props.className + ' ' + (this.showRequired() ? 'required' : this.showError() ? 'error' : null); - - // An error message is returned ONLY if the component is invalid - // or the server has returned an error message - var errorMessage = this.getErrorMessage(); - - return ( -
- - - {errorMessage} -
- ); - } -}); - -React.render(, document.getElementById('example')); +ReactDOM.render(, document.getElementById('example')); diff --git a/examples/reset-values/app.css b/examples/reset-values/app.css new file mode 100644 index 0000000..e9e02dc --- /dev/null +++ b/examples/reset-values/app.css @@ -0,0 +1,4 @@ +.form { + width: 400px; + margin: 0 auto; +} diff --git a/examples/reset-values/app.js b/examples/reset-values/app.js new file mode 100644 index 0000000..d5deb8f --- /dev/null +++ b/examples/reset-values/app.js @@ -0,0 +1,44 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; +import { Form } from 'formsy-react'; + +import MyInput from './../components/Input'; +import MySelect from './../components/Select'; + +const user = { + name: 'Sam', + free: true, + hair: 'brown' +}; + +const App = React.createClass({ + submit(data) { + alert(JSON.stringify(data, null, 4)); + }, + resetForm() { + this.refs.form.reset(); + }, + render() { + return ( + + + + + +
+ + +
+
+ ); + } +}); + +ReactDOM.render(, document.getElementById('example')); diff --git a/examples/reset-values/index.html b/examples/reset-values/index.html new file mode 100644 index 0000000..036a23a --- /dev/null +++ b/examples/reset-values/index.html @@ -0,0 +1,14 @@ + + + + Reset Values + + + + +

Formsy React Examples / Reset Values

+
+ + + + diff --git a/examples/webpack.config.js b/examples/webpack.config.js index ca9992b..06bbc10 100644 --- a/examples/webpack.config.js +++ b/examples/webpack.config.js @@ -11,7 +11,7 @@ module.exports = { devtool: 'inline-source-map', entry: fs.readdirSync(__dirname).reduce(function (entries, dir) { - var isDraft = dir.charAt(0) === '_'; + var isDraft = dir.charAt(0) === '_' || dir.indexOf('components') >= 0; if (!isDraft && isDirectory(path.join(__dirname, dir))) { entries[dir] = path.join(__dirname, dir, 'app.js'); diff --git a/package.json b/package.json index 3ae5376..45a6e3c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "formsy-react", - "version": "0.14.1", + "version": "0.16.0", "description": "A form input builder and validator for React JS", "repository": { "type": "git", @@ -15,8 +15,9 @@ "scripts": { "start": "webpack-dev-server --content-base build", "deploy": "NODE_ENV=production webpack -p --config webpack.production.config.js", - "test": "node testrunner", - "examples": "webpack-dev-server --config examples/webpack.config.js --content-base examples" + "test": "babel-node testrunner", + "examples": "webpack-dev-server --config examples/webpack.config.js --content-base examples", + "prepublish": "babel ./src/ -d ./lib/" }, "author": "Christian Alfoni", "license": "MIT", @@ -27,17 +28,24 @@ "validation", "react-component" ], + "dependencies": { + "form-data-to-object": "^0.1.0" + }, "devDependencies": { "babel": "^5.6.4", "babel-core": "^5.1.11", "babel-loader": "^5.0.0", - "jasmine-node": "^1.14.5", - "jsdom": "^3.1.2", - "react": "^0.13.1", + "jsdom": "^6.5.1", + "lolex": "^1.3.2", + "nodeunit": "^0.9.1", + "react": "^0.14.0-rc1", + "react-addons-test-utils": "^0.14.0-rc1", + "react-dom": "^0.14.0-rc1", + "sinon": "^1.17.1", "webpack": "^1.7.3", "webpack-dev-server": "^1.7.0" }, "peerDependencies": { - "react": "0.13.x" + "react": "^0.14.0-rc1" } } diff --git a/release/formsy-react.js b/release/formsy-react.js index 82cfcc9..924b469 100644 --- a/release/formsy-react.js +++ b/release/formsy-react.js @@ -1,2 +1,2 @@ -!function(t,i){"object"==typeof exports&&"object"==typeof module?module.exports=i(require("react")):"function"==typeof define&&define.amd?define(["react"],i):"object"==typeof exports?exports.Formsy=i(require("react")):t.Formsy=i(t.react)}(this,function(t){return function(t){function i(r){if(e[r])return e[r].exports;var n=e[r]={exports:{},id:r,loaded:!1};return t[r].call(n.exports,n,n.exports,i),n.loaded=!0,n.exports}var e={};return i.m=t,i.c=e,i.p="",i(0)}([function(t,i,e){(function(i){"use strict";var r=Object.assign||function(t){for(var i=1;i1)throw new Error("Formsy does not support multiple args on string validations. Use object format of validations instead.");return t[r]=e.length?e[0]:!0,t},{}):t||{}};t.exports={getInitialState:function(){return{_value:this.props.value,_isRequired:!1,_isValid:!0,_isPristine:!0,_pristineValue:this.props.value,_validationError:"",_externalError:null,_formSubmitted:!1}},getDefaultProps:function(){return{validationError:"",validationErrors:{}}},componentWillMount:function(){var t=function(){this.setValidations(this.props.validations,this.props.required),this.props._attachToForm(this)}.bind(this);if(!this.props.name)throw new Error("Form Input requires a name property when used");return this.props._attachToForm?void t():setTimeout(function(){if(this.isMounted()){if(!this.props._attachToForm)throw new Error("Form Mixin requires component to be nested in a Form");t()}}.bind(this),0)},componentWillReceiveProps:function(t){this.setValidations(t.validations,t.required)},componentDidUpdate:function(t){r.isSame(this.props.value,t.value)||this.setValue(this.props.value)},componentWillUnmount:function(){this.props._detachFromForm(this)},setValidations:function(t,i){this._validations=n(t)||{},this._requiredValidations=i===!0?{isDefaultRequiredValue:!0}:n(i)},setValue:function(t){this.setState({_value:t,_isPristine:!1},function(){this.props._validate(this)}.bind(this))},resetValue:function(){this.setState({_value:this.state._pristineValue,_isPristine:!0},function(){this.props._validate(this)})},getValue:function(){return this.state._value},hasValue:function(){return""!==this.state._value},getErrorMessage:function(){return!this.isValid()||this.showRequired()?this.state._externalError||this.state._validationError:null},isFormDisabled:function(){return this.props._isFormDisabled()},isValid:function(){return this.state._isValid},isPristine:function(){return this.state._isPristine},isFormSubmitted:function(){return this.state._formSubmitted},isRequired:function(){return!!this.props.required},showRequired:function(){return this.state._isRequired},showError:function(){return!this.showRequired()&&!this.isValid()},isValidValue:function(t){return this.props._isValidValue.call(null,this,t)}}},function(t,i){"use strict";var e=function(t){return null!==t&&void 0!==t},r=function(t){return""===t},n={isDefaultRequiredValue:function(t,i){return void 0===i||""===i},isExisty:function(t,i){return e(i)},matchRegexp:function(t,i,n){return!e(i)||r(i)||n.test(i)},isUndefined:function(t,i){return void 0===i},isEmptyString:function(t,i){return r(i)},isEmail:function(t,i){return n.matchRegexp(t,i,/^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))$/i)},isUrl:function(t,i){return n.matchRegexp(t,i,/^(https?|s?ftp):\/\/(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$/i)},isTrue:function(t,i){return i===!0},isFalse:function(t,i){return i===!1},isNumeric:function(t,i){return"number"==typeof i?!0:n.matchRegexp(t,i,/^[-+]?(\d*[.])?\d+$/)},isAlpha:function(t,i){return n.matchRegexp(t,i,/^[a-zA-Z]+$/)},isWords:function(t,i){return n.matchRegexp(t,i,/^[a-zA-Z\s]+$/)},isSpecialWords:function(t,i){return n.matchRegexp(t,i,/^[a-zA-Z\s\u00C0-\u017F]+$/)},isLength:function(t,i,n){return!e(i)||r(i)||i.length===n},equals:function(t,i,n){return!e(i)||r(i)||i==n},equalsField:function(t,i,e){return i==t[e]},maxLength:function(t,i,r){return!e(i)||i.length<=r},minLength:function(t,i,n){return!e(i)||r(i)||i.length>=n}};t.exports=n},function(i,e){i.exports=t}])}); +!function(t,i){"object"==typeof exports&&"object"==typeof module?module.exports=i(require("react")):"function"==typeof define&&define.amd?define(["react"],i):"object"==typeof exports?exports.Formsy=i(require("react")):t.Formsy=i(t.react)}(this,function(t){return function(t){function i(r){if(e[r])return e[r].exports;var s=e[r]={exports:{},id:r,loaded:!1};return t[r].call(s.exports,s,s.exports,i),s.loaded=!0,s.exports}var e={};return i.m=t,i.c=e,i.p="",i(0)}([function(t,i,e){(function(i){"use strict";var r=Object.assign||function(t){for(var i=1;i1)throw new Error("Formsy does not support multiple args on string validations. Use object format of validations instead.");return t[r]=e.length?e[0]:!0,t},{}):t||{}};t.exports={getInitialState:function(){return{_value:this.props.value,_isRequired:!1,_isValid:!0,_isPristine:!0,_pristineValue:this.props.value,_validationError:[],_externalError:null,_formSubmitted:!1}},contextTypes:{formsy:s.PropTypes.object},getDefaultProps:function(){return{validationError:"",validationErrors:{}}},componentWillMount:function(){var t=function(){this.setValidations(this.props.validations,this.props.required),this.context.formsy.attachToForm(this)}.bind(this);if(!this.props.name)throw new Error("Form Input requires a name property when used");t()},componentWillReceiveProps:function(t){this.setValidations(t.validations,t.required)},componentDidUpdate:function(t){r.isSame(this.props.value,t.value)||this.setValue(this.props.value),r.isSame(this.props.validations,t.validations)&&r.isSame(this.props.required,t.required)||this.context.formsy.validate(this)},componentWillUnmount:function(){this.context.formsy.detachFromForm(this)},setValidations:function(t,i){this._validations=n(t)||{},this._requiredValidations=i===!0?{isDefaultRequiredValue:!0}:n(i)},setValue:function(t){this.setState({_value:t,_isPristine:!1},function(){this.context.formsy.validate(this)}.bind(this))},resetValue:function(){this.setState({_value:this.state._pristineValue,_isPristine:!0},function(){this.context.formsy.validate(this)})},getValue:function(){return this.state._value},hasValue:function(){return""!==this.state._value},getErrorMessage:function(){var t=this.getErrorMessages();return t.length?t[0]:null},getErrorMessages:function(){return!this.isValid()||this.showRequired()?this.state._externalError||this.state._validationError||[]:[]},isFormDisabled:function(){return this.context.formsy.isFormDisabled()},isValid:function(){return this.state._isValid},isPristine:function(){return this.state._isPristine},isFormSubmitted:function(){return this.state._formSubmitted},isRequired:function(){return!!this.props.required},showRequired:function(){return this.state._isRequired},showError:function(){return!this.showRequired()&&!this.isValid()},isValidValue:function(t){return this.context.formsy.isValidValue.call(null,this,t)}}}).call(i,function(){return this}())},function(t,i){"use strict";t.exports={arraysDiffer:function(t,i){var e=!1;return t.length!==i.length?e=!0:t.forEach(function(t,r){this.isSame(t,i[r])||(e=!0)},this),e},objectsDiffer:function(t,i){var e=!1;return Object.keys(t).length!==Object.keys(i).length?e=!0:Object.keys(t).forEach(function(r){this.isSame(t[r],i[r])||(e=!0)},this),e},isSame:function(t,i){return typeof t!=typeof i?!1:Array.isArray(t)?!this.arraysDiffer(t,i):"object"==typeof t&&null!==t&&null!==i?!this.objectsDiffer(t,i):t===i}}},function(t,i,e){(function(i){"use strict";var r=Object.assign||function(t){for(var i=1;i=s}};t.exports=s},function(t,i){t.exports=function(t){return Object.keys(t).reduce(function(i,e){var r=e.match(/[^\[]*/i),s=e.match(/\[.*?\]/g)||[];s=[r[0]].concat(s).map(function(t){return t.replace(/\[|\]/g,"")});for(var n=i;s.length;){var u=s.shift();u in n?n=n[u]:(n[u]=s.length?isNaN(s[0])?{}:[]:t[e],n=n[u])}return i},{})}}])}); //# sourceMappingURL=formsy-react.js.map \ No newline at end of file diff --git a/release/formsy-react.js.map b/release/formsy-react.js.map index 6ac7cb4..2d22050 100644 --- a/release/formsy-react.js.map +++ b/release/formsy-react.js.map @@ -1 +1 @@ -{"version":3,"sources":["webpack:///webpack/universalModuleDefinition","webpack:///formsy-react.js","webpack:///webpack/bootstrap 4bb4a803b94b7cb480f9","webpack:///./src/main.js","webpack:///./src/utils.js","webpack:///./src/Mixin.js","webpack:///./src/validationRules.js","webpack:///external \"react\""],"names":["root","factory","exports","module","require","define","amd","this","__WEBPACK_EXTERNAL_MODULE_4__","modules","__webpack_require__","moduleId","installedModules","id","loaded","call","m","c","p","global","_extends","Object","assign","target","i","arguments","length","source","key","prototype","hasOwnProperty","React","Formsy","validationRules","utils","Mixin","options","defaults","passedOptions","addValidationRule","name","func","Form","createClass","displayName","getInitialState","isValid","isSubmitting","canChange","getDefaultProps","onSuccess","onError","onSubmit","onValidSubmit","onInvalidSubmit","onSubmitted","onValid","onInvalid","onChange","validationErrors","preventExternalInvalidation","componentWillMount","inputs","model","componentDidMount","validateForm","componentWillUpdate","prevInputKeys","keys","componentDidUpdate","props","setInputValidationErrors","newInputKeys","arraysDiffer","reset","data","setFormPristine","resetModel","submit","event","preventDefault","updateModel","mapModel","updateInputsWithError","state","mapping","reduce","mappedModel","keyArray","split","base","currentKey","shift","bind","forEach","component","_value","setValue","resetValue","errors","index","args","_isValid","_validationError","setState","apply","isChanged","isSame","getPristineValues","getCurrentValues","value","Error","JSON","stringify","_externalError","traverseChildrenAndRegisterInputs","children","Children","map","child","cloneElement","_attachToForm","attachToForm","_detachFromForm","detachFromForm","_validate","validate","_isFormDisabled","isFormDisabled","_isValidValue","runValidation","disabled","isPristine","inputKeys","_formSubmitted","_isPristine","validation","_isRequired","isRequired","error","currentValues","validationError","validationResults","runRules","_validations","requiredResults","_requiredValidations","failed","success","validations","results","validationMethod","push","allIsValid","onValidationComplete","every","isMounted","render","createElement","a","b","isDifferent","item","objectsDiffer","Array","isArray","convertValidationsToObject","validateMethod","arg","parse","e","_pristineValue","configure","setValidations","required","setTimeout","componentWillReceiveProps","nextProps","prevProps","componentWillUnmount","isDefaultRequiredValue","getValue","hasValue","getErrorMessage","showRequired","isFormSubmitted","showError","isValidValue","_isExisty","undefined","isEmpty","values","isExisty","matchRegexp","regexp","test","isUndefined","isEmptyString","isEmail","isUrl","isTrue","isFalse","isNumeric","isAlpha","isWords","isSpecialWords","isLength","equals","eql","equalsField","field","maxLength","minLength"],"mappings":"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","file":"formsy-react.js","sourcesContent":["(function webpackUniversalModuleDefinition(root, factory) {\n\tif(typeof exports === 'object' && typeof module === 'object')\n\t\tmodule.exports = factory(require(\"react\"));\n\telse if(typeof define === 'function' && define.amd)\n\t\tdefine([\"react\"], factory);\n\telse if(typeof exports === 'object')\n\t\texports[\"Formsy\"] = factory(require(\"react\"));\n\telse\n\t\troot[\"Formsy\"] = factory(root[\"react\"]);\n})(this, function(__WEBPACK_EXTERNAL_MODULE_4__) {\nreturn \n\n\n/** WEBPACK FOOTER **\n ** webpack/universalModuleDefinition\n **/","(function webpackUniversalModuleDefinition(root, factory) {\n\tif(typeof exports === 'object' && typeof module === 'object')\n\t\tmodule.exports = factory(require(\"react\"));\n\telse if(typeof define === 'function' && define.amd)\n\t\tdefine([\"react\"], factory);\n\telse if(typeof exports === 'object')\n\t\texports[\"Formsy\"] = factory(require(\"react\"));\n\telse\n\t\troot[\"Formsy\"] = factory(root[\"react\"]);\n})(this, function(__WEBPACK_EXTERNAL_MODULE_4__) {\nreturn /******/ (function(modules) { // webpackBootstrap\n/******/ \t// The module cache\n/******/ \tvar installedModules = {};\n/******/\n/******/ \t// The require function\n/******/ \tfunction __webpack_require__(moduleId) {\n/******/\n/******/ \t\t// Check if module is in cache\n/******/ \t\tif(installedModules[moduleId])\n/******/ \t\t\treturn installedModules[moduleId].exports;\n/******/\n/******/ \t\t// Create a new module (and put it into the cache)\n/******/ \t\tvar module = installedModules[moduleId] = {\n/******/ \t\t\texports: {},\n/******/ \t\t\tid: moduleId,\n/******/ \t\t\tloaded: false\n/******/ \t\t};\n/******/\n/******/ \t\t// Execute the module function\n/******/ \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n/******/\n/******/ \t\t// Flag the module as loaded\n/******/ \t\tmodule.loaded = true;\n/******/\n/******/ \t\t// Return the exports of the module\n/******/ \t\treturn module.exports;\n/******/ \t}\n/******/\n/******/\n/******/ \t// expose the modules object (__webpack_modules__)\n/******/ \t__webpack_require__.m = modules;\n/******/\n/******/ \t// expose the module cache\n/******/ \t__webpack_require__.c = installedModules;\n/******/\n/******/ \t// __webpack_public_path__\n/******/ \t__webpack_require__.p = \"\";\n/******/\n/******/ \t// Load entry module and return exports\n/******/ \treturn __webpack_require__(0);\n/******/ })\n/************************************************************************/\n/******/ ([\n/* 0 */\n/***/ function(module, exports, __webpack_require__) {\n\n\t/* WEBPACK VAR INJECTION */(function(global) {'use strict';\n\t\n\tvar _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };\n\t\n\tvar React = global.React || __webpack_require__(4);\n\tvar Formsy = {};\n\tvar validationRules = __webpack_require__(3);\n\tvar utils = __webpack_require__(1);\n\tvar Mixin = __webpack_require__(2);\n\tvar options = {};\n\t\n\tFormsy.Mixin = Mixin;\n\t\n\tFormsy.defaults = function (passedOptions) {\n\t options = passedOptions;\n\t};\n\t\n\tFormsy.addValidationRule = function (name, func) {\n\t validationRules[name] = func;\n\t};\n\t\n\tFormsy.Form = React.createClass({\n\t displayName: 'Form',\n\t\n\t getInitialState: function getInitialState() {\n\t return {\n\t isValid: true,\n\t isSubmitting: false,\n\t canChange: false\n\t };\n\t },\n\t getDefaultProps: function getDefaultProps() {\n\t return {\n\t onSuccess: function onSuccess() {},\n\t onError: function onError() {},\n\t onSubmit: function onSubmit() {},\n\t onValidSubmit: function onValidSubmit() {},\n\t onInvalidSubmit: function onInvalidSubmit() {},\n\t onSubmitted: function onSubmitted() {},\n\t onValid: function onValid() {},\n\t onInvalid: function onInvalid() {},\n\t onChange: function onChange() {},\n\t validationErrors: null,\n\t preventExternalInvalidation: false\n\t };\n\t },\n\t\n\t // Add a map to store the inputs of the form, a model to store\n\t // the values of the form and register child inputs\n\t componentWillMount: function componentWillMount() {\n\t this.inputs = {};\n\t this.model = {};\n\t },\n\t\n\t componentDidMount: function componentDidMount() {\n\t this.validateForm();\n\t },\n\t\n\t componentWillUpdate: function componentWillUpdate() {\n\t\n\t // Keep a reference to input keys before form updates,\n\t // to check if inputs has changed after render\n\t this.prevInputKeys = Object.keys(this.inputs);\n\t },\n\t\n\t componentDidUpdate: function componentDidUpdate() {\n\t\n\t if (this.props.validationErrors) {\n\t this.setInputValidationErrors(this.props.validationErrors);\n\t }\n\t\n\t var newInputKeys = Object.keys(this.inputs);\n\t if (utils.arraysDiffer(this.prevInputKeys, newInputKeys)) {\n\t this.validateForm();\n\t }\n\t },\n\t\n\t // Allow resetting to specified data\n\t reset: function reset(data) {\n\t this.setFormPristine(true);\n\t this.resetModel(data);\n\t },\n\t\n\t // Update model, submit to url prop and send the model\n\t submit: function submit(event) {\n\t\n\t event && event.preventDefault();\n\t\n\t // Trigger form as not pristine.\n\t // If any inputs have not been touched yet this will make them dirty\n\t // so validation becomes visible (if based on isPristine)\n\t this.setFormPristine(false);\n\t this.updateModel();\n\t var model = this.mapModel();\n\t this.props.onSubmit(model, this.resetModel, this.updateInputsWithError);\n\t this.state.isValid ? this.props.onValidSubmit(model, this.resetModel, this.updateInputsWithError) : this.props.onInvalidSubmit(model, this.resetModel, this.updateInputsWithError);\n\t },\n\t\n\t mapModel: function mapModel() {\n\t if (this.props.mapping) {\n\t return this.props.mapping(this.model);\n\t } else {\n\t return Object.keys(this.model).reduce((function (mappedModel, key) {\n\t\n\t var keyArray = key.split('.');\n\t var base = mappedModel;\n\t while (keyArray.length) {\n\t var currentKey = keyArray.shift();\n\t base = base[currentKey] = keyArray.length ? base[currentKey] || {} : this.model[key];\n\t }\n\t\n\t return mappedModel;\n\t }).bind(this), {});\n\t }\n\t },\n\t\n\t // Goes through all registered components and\n\t // updates the model values\n\t updateModel: function updateModel() {\n\t Object.keys(this.inputs).forEach((function (name) {\n\t var component = this.inputs[name];\n\t this.model[name] = component.state._value;\n\t }).bind(this));\n\t },\n\t\n\t // Reset each key in the model to the original / initial / specified value\n\t resetModel: function resetModel(data) {\n\t Object.keys(this.inputs).forEach((function (name) {\n\t if (data && data[name]) {\n\t this.inputs[name].setValue(data[name]);\n\t } else {\n\t this.inputs[name].resetValue();\n\t }\n\t }).bind(this));\n\t this.validateForm();\n\t },\n\t\n\t setInputValidationErrors: function setInputValidationErrors(errors) {\n\t Object.keys(this.inputs).forEach((function (name, index) {\n\t var component = this.inputs[name];\n\t var args = [{\n\t _isValid: !(name in errors),\n\t _validationError: errors[name]\n\t }];\n\t component.setState.apply(component, args);\n\t }).bind(this));\n\t },\n\t\n\t // Checks if the values have changed from their initial value\n\t isChanged: function isChanged() {\n\t return !utils.isSame(this.getPristineValues(), this.getCurrentValues());\n\t },\n\t\n\t getPristineValues: function getPristineValues() {\n\t var inputs = this.inputs;\n\t return Object.keys(inputs).reduce(function (data, name) {\n\t var component = inputs[name];\n\t data[name] = component.props.value;\n\t return data;\n\t }, {});\n\t },\n\t\n\t // Go through errors from server and grab the components\n\t // stored in the inputs map. Change their state to invalid\n\t // and set the serverError message\n\t updateInputsWithError: function updateInputsWithError(errors) {\n\t Object.keys(errors).forEach((function (name, index) {\n\t var component = this.inputs[name];\n\t\n\t if (!component) {\n\t throw new Error('You are trying to update an input that does not exist. Verify errors object with input names. ' + JSON.stringify(errors));\n\t }\n\t var args = [{\n\t _isValid: this.props.preventExternalInvalidation || false,\n\t _externalError: errors[name]\n\t }];\n\t component.setState.apply(component, args);\n\t }).bind(this));\n\t },\n\t\n\t // Traverse the children and children of children to find\n\t // all inputs by checking the name prop. Maybe do a better\n\t // check here\n\t traverseChildrenAndRegisterInputs: function traverseChildrenAndRegisterInputs(children) {\n\t\n\t if (typeof children !== 'object' || children === null) {\n\t return children;\n\t }\n\t return React.Children.map(children, function (child) {\n\t\n\t if (typeof child !== 'object' || child === null) {\n\t return child;\n\t }\n\t\n\t if (child.props && child.props.name) {\n\t\n\t return React.cloneElement(child, {\n\t _attachToForm: this.attachToForm,\n\t _detachFromForm: this.detachFromForm,\n\t _validate: this.validate,\n\t _isFormDisabled: this.isFormDisabled,\n\t _isValidValue: (function (component, value) {\n\t return this.runValidation(component, value).isValid;\n\t }).bind(this)\n\t }, child.props && child.props.children);\n\t } else {\n\t return React.cloneElement(child, {}, this.traverseChildrenAndRegisterInputs(child.props && child.props.children));\n\t }\n\t }, this);\n\t },\n\t\n\t isFormDisabled: function isFormDisabled() {\n\t return this.props.disabled;\n\t },\n\t\n\t getCurrentValues: function getCurrentValues() {\n\t return Object.keys(this.inputs).reduce((function (data, name) {\n\t var component = this.inputs[name];\n\t data[name] = component.state._value;\n\t return data;\n\t }).bind(this), {});\n\t },\n\t\n\t setFormPristine: function setFormPristine(isPristine) {\n\t var inputs = this.inputs;\n\t var inputKeys = Object.keys(inputs);\n\t\n\t this.setState({\n\t _formSubmitted: !isPristine\n\t });\n\t\n\t // Iterate through each component and set it as pristine\n\t // or \"dirty\".\n\t inputKeys.forEach((function (name, index) {\n\t var component = inputs[name];\n\t component.setState({\n\t _formSubmitted: !isPristine,\n\t _isPristine: isPristine\n\t });\n\t }).bind(this));\n\t },\n\t\n\t // Use the binded values and the actual input value to\n\t // validate the input and set its state. Then check the\n\t // state of the form itself\n\t validate: function validate(component) {\n\t\n\t // Trigger onChange\n\t if (this.state.canChange) {\n\t this.props.onChange(this.getCurrentValues(), this.isChanged());\n\t }\n\t\n\t var validation = this.runValidation(component);\n\t // Run through the validations, split them up and call\n\t // the validator IF there is a value or it is required\n\t component.setState({\n\t _isValid: validation.isValid,\n\t _isRequired: validation.isRequired,\n\t _validationError: validation.error,\n\t _externalError: null\n\t }, this.validateForm);\n\t },\n\t\n\t // Checks validation on current value or a passed value\n\t runValidation: function runValidation(component, value) {\n\t\n\t var currentValues = this.getCurrentValues();\n\t var validationErrors = component.props.validationErrors;\n\t var validationError = component.props.validationError;\n\t value = arguments.length === 2 ? value : component.state._value;\n\t\n\t var validationResults = this.runRules(value, currentValues, component._validations);\n\t var requiredResults = this.runRules(value, currentValues, component._requiredValidations);\n\t\n\t // the component defines an explicit validate function\n\t if (typeof component.validate === \"function\") {\n\t validationResults.failed = component.validate() ? [] : ['failed'];\n\t }\n\t\n\t var isRequired = Object.keys(component._requiredValidations).length ? !!requiredResults.success.length : false;\n\t var isValid = !validationResults.failed.length && !(this.props.validationErrors && this.props.validationErrors[component.props.name]);\n\t\n\t return {\n\t isRequired: isRequired,\n\t isValid: isRequired ? false : isValid,\n\t error: (function () {\n\t\n\t if (isValid && !isRequired) {\n\t return '';\n\t }\n\t\n\t if (validationResults.errors.length) {\n\t return validationResults.errors[0];\n\t }\n\t\n\t if (this.props.validationErrors && this.props.validationErrors[component.props.name]) {\n\t return this.props.validationErrors[component.props.name];\n\t }\n\t\n\t if (isRequired) {\n\t return validationErrors[requiredResults.success[0]] || null;\n\t }\n\t\n\t if (!isValid) {\n\t return validationErrors[validationResults.failed[0]] || validationError;\n\t }\n\t }).call(this)\n\t };\n\t },\n\t\n\t runRules: function runRules(value, currentValues, validations) {\n\t\n\t var results = {\n\t errors: [],\n\t failed: [],\n\t success: []\n\t };\n\t if (Object.keys(validations).length) {\n\t Object.keys(validations).forEach(function (validationMethod) {\n\t\n\t if (validationRules[validationMethod] && typeof validations[validationMethod] === 'function') {\n\t throw new Error('Formsy does not allow you to override default validations: ' + validationMethod);\n\t }\n\t\n\t if (!validationRules[validationMethod] && typeof validations[validationMethod] !== 'function') {\n\t throw new Error('Formsy does not have the validation rule: ' + validationMethod);\n\t }\n\t\n\t if (typeof validations[validationMethod] === 'function') {\n\t var validation = validations[validationMethod](currentValues, value);\n\t if (typeof validation === 'string') {\n\t results.errors.push(validation);\n\t results.failed.push(validationMethod);\n\t } else if (!validation) {\n\t results.failed.push(validationMethod);\n\t }\n\t return;\n\t } else if (typeof validations[validationMethod] !== 'function') {\n\t var validation = validationRules[validationMethod](currentValues, value, validations[validationMethod]);\n\t if (typeof validation === 'string') {\n\t results.errors.push(validation);\n\t results.failed.push(validationMethod);\n\t } else if (!validation) {\n\t results.failed.push(validationMethod);\n\t } else {\n\t results.success.push(validationMethod);\n\t }\n\t return;\n\t }\n\t\n\t return results.success.push(validationMethod);\n\t });\n\t }\n\t\n\t return results;\n\t },\n\t\n\t // Validate the form by going through all child input components\n\t // and check their state\n\t validateForm: function validateForm() {\n\t var allIsValid;\n\t var inputs = this.inputs;\n\t var inputKeys = Object.keys(inputs);\n\t\n\t // We need a callback as we are validating all inputs again. This will\n\t // run when the last component has set its state\n\t var onValidationComplete = (function () {\n\t allIsValid = inputKeys.every((function (name) {\n\t return inputs[name].state._isValid;\n\t }).bind(this));\n\t\n\t this.setState({\n\t isValid: allIsValid\n\t });\n\t\n\t if (allIsValid) {\n\t this.props.onValid();\n\t } else {\n\t this.props.onInvalid();\n\t }\n\t\n\t // Tell the form that it can start to trigger change events\n\t this.setState({\n\t canChange: true\n\t });\n\t }).bind(this);\n\t\n\t // Run validation again in case affected by other inputs. The\n\t // last component validated will run the onValidationComplete callback\n\t inputKeys.forEach((function (name, index) {\n\t var component = inputs[name];\n\t var validation = this.runValidation(component);\n\t if (validation.isValid && component.state._externalError) {\n\t validation.isValid = false;\n\t }\n\t component.setState({\n\t _isValid: validation.isValid,\n\t _isRequired: validation.isRequired,\n\t _validationError: validation.error,\n\t _externalError: !validation.isValid && component.state._externalError ? component.state._externalError : null\n\t }, index === inputKeys.length - 1 ? onValidationComplete : null);\n\t }).bind(this));\n\t\n\t // If there are no inputs, set state where form is ready to trigger\n\t // change event. New inputs might be added later\n\t if (!inputKeys.length && this.isMounted()) {\n\t this.setState({\n\t canChange: true\n\t });\n\t }\n\t },\n\t\n\t // Method put on each input component to register\n\t // itself to the form\n\t attachToForm: function attachToForm(component) {\n\t this.inputs[component.props.name] = component;\n\t this.model[component.props.name] = component.state._value;\n\t this.validate(component);\n\t },\n\t\n\t // Method put on each input component to unregister\n\t // itself from the form\n\t detachFromForm: function detachFromForm(component) {\n\t delete this.inputs[component.props.name];\n\t delete this.model[component.props.name];\n\t this.validateForm();\n\t },\n\t render: function render() {\n\t\n\t return React.createElement(\n\t 'form',\n\t _extends({}, this.props, { onSubmit: this.submit }),\n\t this.traverseChildrenAndRegisterInputs(this.props.children)\n\t );\n\t }\n\t});\n\t\n\tif (!global.exports && !global.module && (!global.define || !global.define.amd)) {\n\t global.Formsy = Formsy;\n\t}\n\t\n\tmodule.exports = Formsy;\n\t/* WEBPACK VAR INJECTION */}.call(exports, (function() { return this; }())))\n\n/***/ },\n/* 1 */\n/***/ function(module, exports) {\n\n\t'use strict';\n\t\n\tmodule.exports = {\n\t arraysDiffer: function arraysDiffer(a, b) {\n\t var isDifferent = false;\n\t if (a.length !== b.length) {\n\t isDifferent = true;\n\t } else {\n\t a.forEach(function (item, index) {\n\t if (!this.isSame(item, b[index])) {\n\t isDifferent = true;\n\t }\n\t }, this);\n\t }\n\t return isDifferent;\n\t },\n\t\n\t objectsDiffer: function objectsDiffer(a, b) {\n\t var isDifferent = false;\n\t if (Object.keys(a).length !== Object.keys(b).length) {\n\t isDifferent = true;\n\t } else {\n\t Object.keys(a).forEach(function (key) {\n\t if (!this.isSame(a[key], b[key])) {\n\t isDifferent = true;\n\t }\n\t }, this);\n\t }\n\t return isDifferent;\n\t },\n\t\n\t isSame: function isSame(a, b) {\n\t if (typeof a !== typeof b) {\n\t return false;\n\t } else if (Array.isArray(a)) {\n\t return !this.arraysDiffer(a, b);\n\t } else if (typeof a === 'object' && a !== null && b !== null) {\n\t return !this.objectsDiffer(a, b);\n\t }\n\t\n\t return a === b;\n\t }\n\t};\n\n/***/ },\n/* 2 */\n/***/ function(module, exports, __webpack_require__) {\n\n\t'use strict';\n\t\n\tvar utils = __webpack_require__(1);\n\t\n\tvar convertValidationsToObject = function convertValidationsToObject(validations) {\n\t\n\t if (typeof validations === 'string') {\n\t\n\t return validations.split(/\\,(?![^{\\[]*[}\\]])/g).reduce(function (validations, validation) {\n\t var args = validation.split(':');\n\t var validateMethod = args.shift();\n\t\n\t args = args.map(function (arg) {\n\t try {\n\t return JSON.parse(arg);\n\t } catch (e) {\n\t return arg; // It is a string if it can not parse it\n\t }\n\t });\n\t\n\t if (args.length > 1) {\n\t throw new Error('Formsy does not support multiple args on string validations. Use object format of validations instead.');\n\t }\n\t\n\t validations[validateMethod] = args.length ? args[0] : true;\n\t return validations;\n\t }, {});\n\t }\n\t\n\t return validations || {};\n\t};\n\t\n\tmodule.exports = {\n\t getInitialState: function getInitialState() {\n\t return {\n\t _value: this.props.value,\n\t _isRequired: false,\n\t _isValid: true,\n\t _isPristine: true,\n\t _pristineValue: this.props.value,\n\t _validationError: '',\n\t _externalError: null,\n\t _formSubmitted: false\n\t };\n\t },\n\t getDefaultProps: function getDefaultProps() {\n\t return {\n\t validationError: '',\n\t validationErrors: {}\n\t };\n\t },\n\t\n\t componentWillMount: function componentWillMount() {\n\t var configure = (function () {\n\t this.setValidations(this.props.validations, this.props.required);\n\t this.props._attachToForm(this);\n\t }).bind(this);\n\t\n\t if (!this.props.name) {\n\t throw new Error('Form Input requires a name property when used');\n\t }\n\t\n\t if (!this.props._attachToForm) {\n\t return setTimeout((function () {\n\t if (!this.isMounted()) return;\n\t if (!this.props._attachToForm) {\n\t throw new Error('Form Mixin requires component to be nested in a Form');\n\t }\n\t configure();\n\t }).bind(this), 0);\n\t }\n\t configure();\n\t },\n\t\n\t // We have to make the validate method is kept when new props are added\n\t componentWillReceiveProps: function componentWillReceiveProps(nextProps) {\n\t this.setValidations(nextProps.validations, nextProps.required);\n\t },\n\t\n\t componentDidUpdate: function componentDidUpdate(prevProps) {\n\t\n\t // If the value passed has changed, set it. If value is not passed it will\n\t // internally update, and this will never run\n\t if (!utils.isSame(this.props.value, prevProps.value)) {\n\t this.setValue(this.props.value);\n\t }\n\t },\n\t\n\t // Detach it when component unmounts\n\t componentWillUnmount: function componentWillUnmount() {\n\t this.props._detachFromForm(this);\n\t },\n\t\n\t setValidations: function setValidations(validations, required) {\n\t\n\t // Add validations to the store itself as the props object can not be modified\n\t this._validations = convertValidationsToObject(validations) || {};\n\t this._requiredValidations = required === true ? { isDefaultRequiredValue: true } : convertValidationsToObject(required);\n\t },\n\t\n\t // We validate after the value has been set\n\t setValue: function setValue(value) {\n\t this.setState({\n\t _value: value,\n\t _isPristine: false\n\t }, (function () {\n\t this.props._validate(this);\n\t }).bind(this));\n\t },\n\t resetValue: function resetValue() {\n\t this.setState({\n\t _value: this.state._pristineValue,\n\t _isPristine: true\n\t }, function () {\n\t this.props._validate(this);\n\t });\n\t },\n\t getValue: function getValue() {\n\t return this.state._value;\n\t },\n\t hasValue: function hasValue() {\n\t return this.state._value !== '';\n\t },\n\t getErrorMessage: function getErrorMessage() {\n\t return !this.isValid() || this.showRequired() ? this.state._externalError || this.state._validationError : null;\n\t },\n\t isFormDisabled: function isFormDisabled() {\n\t return this.props._isFormDisabled();\n\t },\n\t isValid: function isValid() {\n\t return this.state._isValid;\n\t },\n\t isPristine: function isPristine() {\n\t return this.state._isPristine;\n\t },\n\t isFormSubmitted: function isFormSubmitted() {\n\t return this.state._formSubmitted;\n\t },\n\t isRequired: function isRequired() {\n\t return !!this.props.required;\n\t },\n\t showRequired: function showRequired() {\n\t return this.state._isRequired;\n\t },\n\t showError: function showError() {\n\t return !this.showRequired() && !this.isValid();\n\t },\n\t isValidValue: function isValidValue(value) {\n\t return this.props._isValidValue.call(null, this, value);\n\t }\n\t};\n\n/***/ },\n/* 3 */\n/***/ function(module, exports) {\n\n\t'use strict';\n\t\n\tvar _isExisty = function _isExisty(value) {\n\t return value !== null && value !== undefined;\n\t};\n\t\n\tvar isEmpty = function isEmpty(value) {\n\t return value === '';\n\t};\n\t\n\tvar validations = {\n\t isDefaultRequiredValue: function isDefaultRequiredValue(values, value) {\n\t return value === undefined || value === '';\n\t },\n\t isExisty: function isExisty(values, value) {\n\t return _isExisty(value);\n\t },\n\t matchRegexp: function matchRegexp(values, value, regexp) {\n\t return !_isExisty(value) || isEmpty(value) || regexp.test(value);\n\t },\n\t isUndefined: function isUndefined(values, value) {\n\t return value === undefined;\n\t },\n\t isEmptyString: function isEmptyString(values, value) {\n\t return isEmpty(value);\n\t },\n\t isEmail: function isEmail(values, value) {\n\t return validations.matchRegexp(values, value, /^((([a-z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])+(\\.([a-z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])+)*)|((\\x22)((((\\x20|\\x09)*(\\x0d\\x0a))?(\\x20|\\x09)+)?(([\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]|\\x21|[\\x23-\\x5b]|[\\x5d-\\x7e]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(\\\\([\\x01-\\x09\\x0b\\x0c\\x0d-\\x7f]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF]))))*(((\\x20|\\x09)*(\\x0d\\x0a))?(\\x20|\\x09)+)?(\\x22)))@((([a-z]|\\d|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(([a-z]|\\d|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])([a-z]|\\d|-|\\.|_|~|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])*([a-z]|\\d|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])))\\.)+(([a-z]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(([a-z]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])([a-z]|\\d|-|\\.|_|~|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])*([a-z]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])))$/i);\n\t },\n\t isUrl: function isUrl(values, value) {\n\t return validations.matchRegexp(values, value, /^(https?|s?ftp):\\/\\/(((([a-z]|\\d|-|\\.|_|~|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(%[\\da-f]{2})|[!\\$&'\\(\\)\\*\\+,;=]|:)*@)?(((\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5])\\.(\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5])\\.(\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5])\\.(\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5]))|((([a-z]|\\d|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(([a-z]|\\d|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])([a-z]|\\d|-|\\.|_|~|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])*([a-z]|\\d|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])))\\.)+(([a-z]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(([a-z]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])([a-z]|\\d|-|\\.|_|~|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])*([a-z]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])))\\.?)(:\\d*)?)(\\/((([a-z]|\\d|-|\\.|_|~|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(%[\\da-f]{2})|[!\\$&'\\(\\)\\*\\+,;=]|:|@)+(\\/(([a-z]|\\d|-|\\.|_|~|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(%[\\da-f]{2})|[!\\$&'\\(\\)\\*\\+,;=]|:|@)*)*)?)?(\\?((([a-z]|\\d|-|\\.|_|~|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(%[\\da-f]{2})|[!\\$&'\\(\\)\\*\\+,;=]|:|@)|[\\uE000-\\uF8FF]|\\/|\\?)*)?(#((([a-z]|\\d|-|\\.|_|~|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(%[\\da-f]{2})|[!\\$&'\\(\\)\\*\\+,;=]|:|@)|\\/|\\?)*)?$/i);\n\t },\n\t isTrue: function isTrue(values, value) {\n\t return value === true;\n\t },\n\t isFalse: function isFalse(values, value) {\n\t return value === false;\n\t },\n\t isNumeric: function isNumeric(values, value) {\n\t if (typeof value === 'number') {\n\t return true;\n\t }\n\t return validations.matchRegexp(values, value, /^[-+]?(\\d*[.])?\\d+$/);\n\t },\n\t isAlpha: function isAlpha(values, value) {\n\t return validations.matchRegexp(values, value, /^[a-zA-Z]+$/);\n\t },\n\t isWords: function isWords(values, value) {\n\t return validations.matchRegexp(values, value, /^[a-zA-Z\\s]+$/);\n\t },\n\t isSpecialWords: function isSpecialWords(values, value) {\n\t return validations.matchRegexp(values, value, /^[a-zA-Z\\s\\u00C0-\\u017F]+$/);\n\t },\n\t isLength: function isLength(values, value, length) {\n\t return !_isExisty(value) || isEmpty(value) || value.length === length;\n\t },\n\t equals: function equals(values, value, eql) {\n\t return !_isExisty(value) || isEmpty(value) || value == eql;\n\t },\n\t equalsField: function equalsField(values, value, field) {\n\t return value == values[field];\n\t },\n\t maxLength: function maxLength(values, value, length) {\n\t return !_isExisty(value) || value.length <= length;\n\t },\n\t minLength: function minLength(values, value, length) {\n\t return !_isExisty(value) || isEmpty(value) || value.length >= length;\n\t }\n\t};\n\t\n\tmodule.exports = validations;\n\n/***/ },\n/* 4 */\n/***/ function(module, exports) {\n\n\tmodule.exports = __WEBPACK_EXTERNAL_MODULE_4__;\n\n/***/ }\n/******/ ])\n});\n;\n\n\n/** WEBPACK FOOTER **\n ** formsy-react.js\n **/"," \t// The module cache\n \tvar installedModules = {};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId])\n \t\t\treturn installedModules[moduleId].exports;\n\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\texports: {},\n \t\t\tid: moduleId,\n \t\t\tloaded: false\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.loaded = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"\";\n\n \t// Load entry module and return exports\n \treturn __webpack_require__(0);\n\n\n\n/** WEBPACK FOOTER **\n ** webpack/bootstrap 4bb4a803b94b7cb480f9\n **/","var React = global.React || require('react');\nvar Formsy = {};\nvar validationRules = require('./validationRules.js');\nvar utils = require('./utils.js');\nvar Mixin = require('./Mixin.js');\nvar options = {};\n\nFormsy.Mixin = Mixin;\n\nFormsy.defaults = function (passedOptions) {\n options = passedOptions;\n};\n\nFormsy.addValidationRule = function (name, func) {\n validationRules[name] = func;\n};\n\nFormsy.Form = React.createClass({\n getInitialState: function () {\n return {\n isValid: true,\n isSubmitting: false,\n canChange: false\n };\n },\n getDefaultProps: function () {\n return {\n onSuccess: function () {},\n onError: function () {},\n onSubmit: function () {},\n onValidSubmit: function () {},\n onInvalidSubmit: function () {},\n onSubmitted: function () {},\n onValid: function () {},\n onInvalid: function () {},\n onChange: function () {},\n validationErrors: null,\n preventExternalInvalidation: false\n };\n },\n\n // Add a map to store the inputs of the form, a model to store\n // the values of the form and register child inputs\n componentWillMount: function () {\n this.inputs = {};\n this.model = {};\n },\n\n componentDidMount: function () {\n this.validateForm();\n },\n\n componentWillUpdate: function () {\n\n // Keep a reference to input keys before form updates,\n // to check if inputs has changed after render\n this.prevInputKeys = Object.keys(this.inputs);\n\n },\n\n componentDidUpdate: function () {\n\n if (this.props.validationErrors) {\n this.setInputValidationErrors(this.props.validationErrors);\n }\n\n var newInputKeys = Object.keys(this.inputs);\n if (utils.arraysDiffer(this.prevInputKeys, newInputKeys)) {\n this.validateForm();\n }\n\n },\n\n // Allow resetting to specified data\n reset: function (data) {\n this.setFormPristine(true);\n this.resetModel(data);\n },\n\n // Update model, submit to url prop and send the model\n submit: function (event) {\n\n event && event.preventDefault();\n\n // Trigger form as not pristine.\n // If any inputs have not been touched yet this will make them dirty\n // so validation becomes visible (if based on isPristine)\n this.setFormPristine(false);\n this.updateModel();\n var model = this.mapModel();\n this.props.onSubmit(model, this.resetModel, this.updateInputsWithError);\n this.state.isValid ? this.props.onValidSubmit(model, this.resetModel, this.updateInputsWithError) : this.props.onInvalidSubmit(model, this.resetModel, this.updateInputsWithError);\n\n },\n\n mapModel: function () {\n if (this.props.mapping) {\n return this.props.mapping(this.model)\n } else {\n return Object.keys(this.model).reduce(function (mappedModel, key) {\n\n var keyArray = key.split('.');\n var base = mappedModel;\n while (keyArray.length) {\n var currentKey = keyArray.shift();\n base = (base[currentKey] = keyArray.length ? base[currentKey] || {} : this.model[key]);\n }\n\n return mappedModel;\n\n }.bind(this), {});\n }\n },\n\n // Goes through all registered components and\n // updates the model values\n updateModel: function () {\n Object.keys(this.inputs).forEach(function (name) {\n var component = this.inputs[name];\n this.model[name] = component.state._value;\n }.bind(this));\n },\n\n // Reset each key in the model to the original / initial / specified value\n resetModel: function (data) {\n Object.keys(this.inputs).forEach(function (name) {\n if (data && data[name]) {\n this.inputs[name].setValue(data[name]);\n } else {\n this.inputs[name].resetValue();\n }\n }.bind(this));\n this.validateForm();\n },\n\n setInputValidationErrors: function (errors) {\n Object.keys(this.inputs).forEach(function (name, index) {\n var component = this.inputs[name];\n var args = [{\n _isValid: !(name in errors),\n _validationError: errors[name]\n }];\n component.setState.apply(component, args);\n }.bind(this));\n },\n\n // Checks if the values have changed from their initial value\n isChanged: function() {\n return !utils.isSame(this.getPristineValues(), this.getCurrentValues());\n },\n\n getPristineValues: function() {\n var inputs = this.inputs;\n return Object.keys(inputs).reduce(function (data, name) {\n var component = inputs[name];\n data[name] = component.props.value;\n return data;\n }, {});\n },\n\n // Go through errors from server and grab the components\n // stored in the inputs map. Change their state to invalid\n // and set the serverError message\n updateInputsWithError: function (errors) {\n Object.keys(errors).forEach(function (name, index) {\n var component = this.inputs[name];\n\n if (!component) {\n throw new Error('You are trying to update an input that does not exist. Verify errors object with input names. ' + JSON.stringify(errors));\n }\n var args = [{\n _isValid: this.props.preventExternalInvalidation || false,\n _externalError: errors[name]\n }];\n component.setState.apply(component, args);\n }.bind(this));\n },\n\n // Traverse the children and children of children to find\n // all inputs by checking the name prop. Maybe do a better\n // check here\n traverseChildrenAndRegisterInputs: function (children) {\n\n if (typeof children !== 'object' || children === null) {\n return children;\n }\n return React.Children.map(children, function (child) {\n\n if (typeof child !== 'object' || child === null) {\n return child;\n }\n\n if (child.props && child.props.name) {\n\n return React.cloneElement(child, {\n _attachToForm: this.attachToForm,\n _detachFromForm: this.detachFromForm,\n _validate: this.validate,\n _isFormDisabled: this.isFormDisabled,\n _isValidValue: function (component, value) {\n return this.runValidation(component, value).isValid;\n }.bind(this)\n }, child.props && child.props.children);\n } else {\n return React.cloneElement(child, {}, this.traverseChildrenAndRegisterInputs(child.props && child.props.children));\n }\n\n }, this);\n\n },\n\n isFormDisabled: function () {\n return this.props.disabled;\n },\n\n getCurrentValues: function () {\n return Object.keys(this.inputs).reduce(function (data, name) {\n var component = this.inputs[name];\n data[name] = component.state._value;\n return data;\n }.bind(this), {});\n },\n\n setFormPristine: function (isPristine) {\n var inputs = this.inputs;\n var inputKeys = Object.keys(inputs);\n\n this.setState({\n _formSubmitted: !isPristine\n })\n\n // Iterate through each component and set it as pristine\n // or \"dirty\".\n inputKeys.forEach(function (name, index) {\n var component = inputs[name];\n component.setState({\n _formSubmitted: !isPristine,\n _isPristine: isPristine\n });\n }.bind(this));\n },\n\n // Use the binded values and the actual input value to\n // validate the input and set its state. Then check the\n // state of the form itself\n validate: function (component) {\n\n // Trigger onChange\n if (this.state.canChange) {\n this.props.onChange(this.getCurrentValues(), this.isChanged());\n }\n\n var validation = this.runValidation(component);\n // Run through the validations, split them up and call\n // the validator IF there is a value or it is required\n component.setState({\n _isValid: validation.isValid,\n _isRequired: validation.isRequired,\n _validationError: validation.error,\n _externalError: null\n }, this.validateForm);\n\n },\n\n // Checks validation on current value or a passed value\n runValidation: function (component, value) {\n\n var currentValues = this.getCurrentValues();\n var validationErrors = component.props.validationErrors;\n var validationError = component.props.validationError;\n value = arguments.length === 2 ? value : component.state._value;\n\n var validationResults = this.runRules(value, currentValues, component._validations);\n var requiredResults = this.runRules(value, currentValues, component._requiredValidations);\n\n // the component defines an explicit validate function\n if (typeof component.validate === \"function\") {\n validationResults.failed = component.validate() ? [] : ['failed'];\n }\n\n var isRequired = Object.keys(component._requiredValidations).length ? !!requiredResults.success.length : false;\n var isValid = !validationResults.failed.length && !(this.props.validationErrors && this.props.validationErrors[component.props.name]);\n\n return {\n isRequired: isRequired,\n isValid: isRequired ? false : isValid,\n error: (function () {\n\n if (isValid && !isRequired) {\n return '';\n }\n\n if (validationResults.errors.length) {\n return validationResults.errors[0];\n }\n\n if (this.props.validationErrors && this.props.validationErrors[component.props.name]) {\n return this.props.validationErrors[component.props.name];\n }\n\n if (isRequired) {\n return validationErrors[requiredResults.success[0]] || null;\n }\n\n if (!isValid) {\n return validationErrors[validationResults.failed[0]] || validationError;\n }\n\n }.call(this))\n };\n\n },\n\n runRules: function (value, currentValues, validations) {\n\n var results = {\n errors: [],\n failed: [],\n success: []\n };\n if (Object.keys(validations).length) {\n Object.keys(validations).forEach(function (validationMethod) {\n\n if (validationRules[validationMethod] && typeof validations[validationMethod] === 'function') {\n throw new Error('Formsy does not allow you to override default validations: ' + validationMethod);\n }\n\n if (!validationRules[validationMethod] && typeof validations[validationMethod] !== 'function') {\n throw new Error('Formsy does not have the validation rule: ' + validationMethod);\n }\n\n if (typeof validations[validationMethod] === 'function') {\n var validation = validations[validationMethod](currentValues, value);\n if (typeof validation === 'string') {\n results.errors.push(validation);\n results.failed.push(validationMethod);\n } else if (!validation) {\n results.failed.push(validationMethod);\n }\n return;\n\n } else if (typeof validations[validationMethod] !== 'function') {\n var validation = validationRules[validationMethod](currentValues, value, validations[validationMethod]);\n if (typeof validation === 'string') {\n results.errors.push(validation);\n results.failed.push(validationMethod);\n } else if (!validation) {\n results.failed.push(validationMethod);\n } else {\n results.success.push(validationMethod);\n }\n return;\n\n }\n\n return results.success.push(validationMethod);\n\n });\n }\n\n return results;\n\n },\n\n // Validate the form by going through all child input components\n // and check their state\n validateForm: function () {\n var allIsValid;\n var inputs = this.inputs;\n var inputKeys = Object.keys(inputs);\n\n // We need a callback as we are validating all inputs again. This will\n // run when the last component has set its state\n var onValidationComplete = function () {\n allIsValid = inputKeys.every(function (name) {\n return inputs[name].state._isValid;\n }.bind(this));\n\n this.setState({\n isValid: allIsValid\n });\n\n if (allIsValid) {\n this.props.onValid();\n } else {\n this.props.onInvalid();\n }\n\n // Tell the form that it can start to trigger change events\n this.setState({\n canChange: true\n });\n\n }.bind(this);\n\n // Run validation again in case affected by other inputs. The\n // last component validated will run the onValidationComplete callback\n inputKeys.forEach(function (name, index) {\n var component = inputs[name];\n var validation = this.runValidation(component);\n if (validation.isValid && component.state._externalError) {\n validation.isValid = false;\n }\n component.setState({\n _isValid: validation.isValid,\n _isRequired: validation.isRequired,\n _validationError: validation.error,\n _externalError: !validation.isValid && component.state._externalError ? component.state._externalError : null\n }, index === inputKeys.length - 1 ? onValidationComplete : null);\n }.bind(this));\n\n // If there are no inputs, set state where form is ready to trigger\n // change event. New inputs might be added later\n if (!inputKeys.length && this.isMounted()) {\n this.setState({\n canChange: true\n });\n }\n },\n\n // Method put on each input component to register\n // itself to the form\n attachToForm: function (component) {\n this.inputs[component.props.name] = component;\n this.model[component.props.name] = component.state._value;\n this.validate(component);\n },\n\n // Method put on each input component to unregister\n // itself from the form\n detachFromForm: function (component) {\n delete this.inputs[component.props.name];\n delete this.model[component.props.name];\n this.validateForm();\n },\n render: function () {\n\n return (\n
\n {this.traverseChildrenAndRegisterInputs(this.props.children)}\n
\n );\n\n }\n});\n\nif (!global.exports && !global.module && (!global.define || !global.define.amd)) {\n global.Formsy = Formsy;\n}\n\nmodule.exports = Formsy;\n\n\n\n/** WEBPACK FOOTER **\n ** ./src/main.js\n **/","module.exports = {\n arraysDiffer: function (a, b) {\n var isDifferent = false;\n if (a.length !== b.length) {\n isDifferent = true;\n } else {\n a.forEach(function (item, index) {\n if (!this.isSame(item, b[index])) {\n isDifferent = true;\n }\n }, this);\n }\n return isDifferent;\n },\n\n objectsDiffer: function (a, b) {\n var isDifferent = false;\n if (Object.keys(a).length !== Object.keys(b).length) {\n isDifferent = true;\n } else {\n Object.keys(a).forEach(function (key) {\n if (!this.isSame(a[key], b[key])) {\n isDifferent = true;\n }\n }, this);\n }\n return isDifferent;\n },\n\n isSame: function (a, b) {\n if (typeof a !== typeof b) {\n return false;\n } else if (Array.isArray(a)) {\n return !this.arraysDiffer(a, b);\n } else if (typeof a === 'object' && a !== null && b !== null) {\n return !this.objectsDiffer(a, b);\n }\n\n return a === b;\n }\n};\n\n\n\n/** WEBPACK FOOTER **\n ** ./src/utils.js\n **/","var utils = require('./utils.js');\n\nvar convertValidationsToObject = function (validations) {\n\n if (typeof validations === 'string') {\n\n return validations.split(/\\,(?![^{\\[]*[}\\]])/g).reduce(function (validations, validation) {\n var args = validation.split(':');\n var validateMethod = args.shift();\n\n args = args.map(function (arg) {\n try {\n return JSON.parse(arg);\n } catch (e) {\n return arg; // It is a string if it can not parse it\n }\n });\n\n if (args.length > 1) {\n throw new Error('Formsy does not support multiple args on string validations. Use object format of validations instead.');\n }\n\n validations[validateMethod] = args.length ? args[0] : true;\n return validations;\n }, {});\n\n }\n\n return validations || {};\n};\n\nmodule.exports = {\n getInitialState: function () {\n return {\n _value: this.props.value,\n _isRequired: false,\n _isValid: true,\n _isPristine: true,\n _pristineValue: this.props.value,\n _validationError: '',\n _externalError: null,\n _formSubmitted: false\n };\n },\n getDefaultProps: function () {\n return {\n validationError: '',\n validationErrors: {}\n };\n },\n\n componentWillMount: function () {\n var configure = function () {\n this.setValidations(this.props.validations, this.props.required);\n this.props._attachToForm(this);\n }.bind(this);\n\n if (!this.props.name) {\n throw new Error('Form Input requires a name property when used');\n }\n\n if (!this.props._attachToForm) {\n return setTimeout(function () {\n if (!this.isMounted()) return;\n if (!this.props._attachToForm) {\n throw new Error('Form Mixin requires component to be nested in a Form');\n }\n configure();\n }.bind(this), 0);\n }\n configure();\n },\n\n // We have to make the validate method is kept when new props are added\n componentWillReceiveProps: function (nextProps) {\n this.setValidations(nextProps.validations, nextProps.required);\n },\n\n componentDidUpdate: function (prevProps) {\n\n // If the value passed has changed, set it. If value is not passed it will\n // internally update, and this will never run\n if (!utils.isSame(this.props.value, prevProps.value)) {\n this.setValue(this.props.value);\n }\n },\n\n // Detach it when component unmounts\n componentWillUnmount: function () {\n this.props._detachFromForm(this);\n },\n\n setValidations: function (validations, required) {\n\n // Add validations to the store itself as the props object can not be modified\n this._validations = convertValidationsToObject(validations) || {};\n this._requiredValidations = required === true ? {isDefaultRequiredValue: true} : convertValidationsToObject(required);\n\n },\n\n // We validate after the value has been set\n setValue: function (value) {\n this.setState({\n _value: value,\n _isPristine: false\n }, function () {\n this.props._validate(this);\n }.bind(this));\n },\n resetValue: function () {\n this.setState({\n _value: this.state._pristineValue,\n _isPristine: true\n }, function () {\n this.props._validate(this);\n });\n },\n getValue: function () {\n return this.state._value;\n },\n hasValue: function () {\n return this.state._value !== '';\n },\n getErrorMessage: function () {\n return !this.isValid() || this.showRequired() ? (this.state._externalError || this.state._validationError) : null;\n },\n isFormDisabled: function () {\n return this.props._isFormDisabled();\n },\n isValid: function () {\n return this.state._isValid;\n },\n isPristine: function () {\n return this.state._isPristine;\n },\n isFormSubmitted: function () {\n return this.state._formSubmitted;\n },\n isRequired: function () {\n return !!this.props.required;\n },\n showRequired: function () {\n return this.state._isRequired;\n },\n showError: function () {\n return !this.showRequired() && !this.isValid();\n },\n isValidValue: function (value) {\n return this.props._isValidValue.call(null, this, value);\n }\n};\n\n\n\n/** WEBPACK FOOTER **\n ** ./src/Mixin.js\n **/","var isExisty = function (value) {\n return value !== null && value !== undefined;\n};\n\nvar isEmpty = function (value) {\n return value === '';\n};\n\nvar validations = {\n isDefaultRequiredValue: function (values, value) {\n return value === undefined || value === '';\n },\n isExisty: function (values, value) {\n return isExisty(value);\n },\n matchRegexp: function (values, value, regexp) {\n return !isExisty(value) || isEmpty(value) || regexp.test(value);\n },\n isUndefined: function (values, value) {\n return value === undefined;\n },\n isEmptyString: function (values, value) {\n return isEmpty(value);\n },\n isEmail: function (values, value) {\n return validations.matchRegexp(values, value, /^((([a-z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])+(\\.([a-z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])+)*)|((\\x22)((((\\x20|\\x09)*(\\x0d\\x0a))?(\\x20|\\x09)+)?(([\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]|\\x21|[\\x23-\\x5b]|[\\x5d-\\x7e]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(\\\\([\\x01-\\x09\\x0b\\x0c\\x0d-\\x7f]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF]))))*(((\\x20|\\x09)*(\\x0d\\x0a))?(\\x20|\\x09)+)?(\\x22)))@((([a-z]|\\d|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(([a-z]|\\d|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])([a-z]|\\d|-|\\.|_|~|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])*([a-z]|\\d|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])))\\.)+(([a-z]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(([a-z]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])([a-z]|\\d|-|\\.|_|~|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])*([a-z]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])))$/i);\n },\n isUrl: function (values, value) {\n return validations.matchRegexp(values, value, /^(https?|s?ftp):\\/\\/(((([a-z]|\\d|-|\\.|_|~|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(%[\\da-f]{2})|[!\\$&'\\(\\)\\*\\+,;=]|:)*@)?(((\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5])\\.(\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5])\\.(\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5])\\.(\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5]))|((([a-z]|\\d|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(([a-z]|\\d|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])([a-z]|\\d|-|\\.|_|~|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])*([a-z]|\\d|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])))\\.)+(([a-z]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(([a-z]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])([a-z]|\\d|-|\\.|_|~|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])*([a-z]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])))\\.?)(:\\d*)?)(\\/((([a-z]|\\d|-|\\.|_|~|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(%[\\da-f]{2})|[!\\$&'\\(\\)\\*\\+,;=]|:|@)+(\\/(([a-z]|\\d|-|\\.|_|~|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(%[\\da-f]{2})|[!\\$&'\\(\\)\\*\\+,;=]|:|@)*)*)?)?(\\?((([a-z]|\\d|-|\\.|_|~|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(%[\\da-f]{2})|[!\\$&'\\(\\)\\*\\+,;=]|:|@)|[\\uE000-\\uF8FF]|\\/|\\?)*)?(#((([a-z]|\\d|-|\\.|_|~|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(%[\\da-f]{2})|[!\\$&'\\(\\)\\*\\+,;=]|:|@)|\\/|\\?)*)?$/i);\n },\n isTrue: function (values, value) {\n return value === true;\n },\n isFalse: function (values, value) {\n return value === false;\n },\n isNumeric: function (values, value) {\n if (typeof value === 'number') {\n return true;\n }\n return validations.matchRegexp(values, value, /^[-+]?(\\d*[.])?\\d+$/);\n },\n isAlpha: function (values, value) {\n return validations.matchRegexp(values, value, /^[a-zA-Z]+$/);\n },\n isWords: function (values, value) {\n return validations.matchRegexp(values, value, /^[a-zA-Z\\s]+$/);\n },\n isSpecialWords: function (values, value) {\n return validations.matchRegexp(values, value, /^[a-zA-Z\\s\\u00C0-\\u017F]+$/);\n },\n isLength: function (values, value, length) {\n return !isExisty(value) || isEmpty(value) || value.length === length;\n },\n equals: function (values, value, eql) {\n return !isExisty(value) || isEmpty(value) || value == eql;\n },\n equalsField: function (values, value, field) {\n return value == values[field];\n },\n maxLength: function (values, value, length) {\n return !isExisty(value) || value.length <= length;\n },\n minLength: function (values, value, length) {\n return !isExisty(value) || isEmpty(value) || value.length >= length;\n }\n};\n\nmodule.exports = validations;\n\n\n\n/** WEBPACK FOOTER **\n ** ./src/validationRules.js\n **/","module.exports = __WEBPACK_EXTERNAL_MODULE_4__;\n\n\n/*****************\n ** WEBPACK FOOTER\n ** external \"react\"\n ** module id = 4\n ** module chunks = 0\n **/"],"sourceRoot":""} \ No newline at end of file +{"version":3,"sources":["webpack:///webpack/universalModuleDefinition","webpack:///formsy-react.js","webpack:///webpack/bootstrap c2760db04162c861118c","webpack:///./src/main.js","webpack:///external \"react\"","webpack:///./src/Mixin.js","webpack:///./src/utils.js","webpack:///./src/Decorator.js","webpack:///./src/HOC.js","webpack:///./src/validationRules.js","webpack:///./~/form-data-to-object/index.js"],"names":["root","factory","exports","module","require","define","amd","this","__WEBPACK_EXTERNAL_MODULE_1__","modules","__webpack_require__","moduleId","installedModules","id","loaded","call","m","c","p","global","_extends","Object","assign","target","i","arguments","length","source","key","prototype","hasOwnProperty","React","Formsy","validationRules","formDataToObject","utils","Mixin","HOC","Decorator","options","defaults","passedOptions","addValidationRule","name","func","Form","createClass","displayName","getInitialState","isValid","isSubmitting","canChange","getDefaultProps","onSuccess","onError","onSubmit","onValidSubmit","onInvalidSubmit","onSubmitted","onValid","onInvalid","onChange","validationErrors","preventExternalInvalidation","childContextTypes","formsy","PropTypes","object","getChildContext","attachToForm","detachFromForm","validate","isFormDisabled","isValidValue","component","value","runValidation","bind","componentWillMount","inputs","model","componentDidMount","validateForm","componentWillUpdate","prevInputKeys","keys","componentDidUpdate","props","setInputValidationErrors","newInputKeys","arraysDiffer","reset","data","setFormPristine","resetModel","submit","event","preventDefault","updateModel","mapModel","updateInputsWithError","state","mapping","reduce","mappedModel","keyArray","split","base","currentKey","shift","forEach","_value","setValue","resetValue","errors","index","args","_isValid","_validationError","setState","apply","isChanged","isSame","getPristineValues","getCurrentValues","Error","JSON","stringify","_externalError","disabled","isPristine","inputKeys","_formSubmitted","_isPristine","validation","_isRequired","isRequired","error","currentValues","validationError","validationResults","runRules","_validations","requiredResults","_requiredValidations","failed","success","map","filter","x","pos","arr","indexOf","validations","results","validationMethod","push","allIsValid","onValidationComplete","every","isMounted","render","createElement","children","convertValidationsToObject","validateMethod","arg","parse","e","_pristineValue","contextTypes","configure","setValidations","required","context","componentWillReceiveProps","nextProps","prevProps","componentWillUnmount","isDefaultRequiredValue","getValue","hasValue","getErrorMessage","messages","getErrorMessages","showRequired","isFormSubmitted","showError","a","b","isDifferent","item","objectsDiffer","Array","isArray","Component","mixins","_isExisty","undefined","isEmpty","values","isExisty","matchRegexp","regexp","test","isUndefined","isEmptyString","isEmail","isUrl","isTrue","isFalse","isNumeric","isAlpha","isAlphanumeric","isInt","isFloat","isWords","isSpecialWords","isLength","equals","eql","equalsField","field","maxLength","minLength","output","parentKey","match","paths","concat","replace","currentPath","pathKey","isNaN"],"mappings":"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","file":"formsy-react.js","sourcesContent":["(function webpackUniversalModuleDefinition(root, factory) {\n\tif(typeof exports === 'object' && typeof module === 'object')\n\t\tmodule.exports = factory(require(\"react\"));\n\telse if(typeof define === 'function' && define.amd)\n\t\tdefine([\"react\"], factory);\n\telse if(typeof exports === 'object')\n\t\texports[\"Formsy\"] = factory(require(\"react\"));\n\telse\n\t\troot[\"Formsy\"] = factory(root[\"react\"]);\n})(this, function(__WEBPACK_EXTERNAL_MODULE_1__) {\nreturn \n\n\n/** WEBPACK FOOTER **\n ** webpack/universalModuleDefinition\n **/","(function webpackUniversalModuleDefinition(root, factory) {\n\tif(typeof exports === 'object' && typeof module === 'object')\n\t\tmodule.exports = factory(require(\"react\"));\n\telse if(typeof define === 'function' && define.amd)\n\t\tdefine([\"react\"], factory);\n\telse if(typeof exports === 'object')\n\t\texports[\"Formsy\"] = factory(require(\"react\"));\n\telse\n\t\troot[\"Formsy\"] = factory(root[\"react\"]);\n})(this, function(__WEBPACK_EXTERNAL_MODULE_1__) {\nreturn /******/ (function(modules) { // webpackBootstrap\n/******/ \t// The module cache\n/******/ \tvar installedModules = {};\n/******/\n/******/ \t// The require function\n/******/ \tfunction __webpack_require__(moduleId) {\n/******/\n/******/ \t\t// Check if module is in cache\n/******/ \t\tif(installedModules[moduleId])\n/******/ \t\t\treturn installedModules[moduleId].exports;\n/******/\n/******/ \t\t// Create a new module (and put it into the cache)\n/******/ \t\tvar module = installedModules[moduleId] = {\n/******/ \t\t\texports: {},\n/******/ \t\t\tid: moduleId,\n/******/ \t\t\tloaded: false\n/******/ \t\t};\n/******/\n/******/ \t\t// Execute the module function\n/******/ \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n/******/\n/******/ \t\t// Flag the module as loaded\n/******/ \t\tmodule.loaded = true;\n/******/\n/******/ \t\t// Return the exports of the module\n/******/ \t\treturn module.exports;\n/******/ \t}\n/******/\n/******/\n/******/ \t// expose the modules object (__webpack_modules__)\n/******/ \t__webpack_require__.m = modules;\n/******/\n/******/ \t// expose the module cache\n/******/ \t__webpack_require__.c = installedModules;\n/******/\n/******/ \t// __webpack_public_path__\n/******/ \t__webpack_require__.p = \"\";\n/******/\n/******/ \t// Load entry module and return exports\n/******/ \treturn __webpack_require__(0);\n/******/ })\n/************************************************************************/\n/******/ ([\n/* 0 */\n/***/ function(module, exports, __webpack_require__) {\n\n\t/* WEBPACK VAR INJECTION */(function(global) {'use strict';\n\t\n\tvar _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };\n\t\n\tvar React = global.React || __webpack_require__(1);\n\tvar Formsy = {};\n\tvar validationRules = __webpack_require__(6);\n\tvar formDataToObject = __webpack_require__(7);\n\tvar utils = __webpack_require__(3);\n\tvar Mixin = __webpack_require__(2);\n\tvar HOC = __webpack_require__(5);\n\tvar Decorator = __webpack_require__(4);\n\tvar options = {};\n\t\n\tFormsy.Mixin = Mixin;\n\tFormsy.HOC = HOC;\n\tFormsy.Decorator = Decorator;\n\t\n\tFormsy.defaults = function (passedOptions) {\n\t options = passedOptions;\n\t};\n\t\n\tFormsy.addValidationRule = function (name, func) {\n\t validationRules[name] = func;\n\t};\n\t\n\tFormsy.Form = React.createClass({\n\t displayName: 'Formsy',\n\t getInitialState: function getInitialState() {\n\t return {\n\t isValid: true,\n\t isSubmitting: false,\n\t canChange: false\n\t };\n\t },\n\t getDefaultProps: function getDefaultProps() {\n\t return {\n\t onSuccess: function onSuccess() {},\n\t onError: function onError() {},\n\t onSubmit: function onSubmit() {},\n\t onValidSubmit: function onValidSubmit() {},\n\t onInvalidSubmit: function onInvalidSubmit() {},\n\t onSubmitted: function onSubmitted() {},\n\t onValid: function onValid() {},\n\t onInvalid: function onInvalid() {},\n\t onChange: function onChange() {},\n\t validationErrors: null,\n\t preventExternalInvalidation: false\n\t };\n\t },\n\t\n\t childContextTypes: {\n\t formsy: React.PropTypes.object\n\t },\n\t getChildContext: function getChildContext() {\n\t return {\n\t formsy: {\n\t attachToForm: this.attachToForm,\n\t detachFromForm: this.detachFromForm,\n\t validate: this.validate,\n\t isFormDisabled: this.isFormDisabled,\n\t isValidValue: (function (component, value) {\n\t return this.runValidation(component, value).isValid;\n\t }).bind(this)\n\t }\n\t };\n\t },\n\t\n\t // Add a map to store the inputs of the form, a model to store\n\t // the values of the form and register child inputs\n\t componentWillMount: function componentWillMount() {\n\t this.inputs = {};\n\t this.model = {};\n\t },\n\t\n\t componentDidMount: function componentDidMount() {\n\t this.validateForm();\n\t },\n\t\n\t componentWillUpdate: function componentWillUpdate() {\n\t\n\t // Keep a reference to input keys before form updates,\n\t // to check if inputs has changed after render\n\t this.prevInputKeys = Object.keys(this.inputs);\n\t },\n\t\n\t componentDidUpdate: function componentDidUpdate() {\n\t\n\t if (this.props.validationErrors) {\n\t this.setInputValidationErrors(this.props.validationErrors);\n\t }\n\t\n\t var newInputKeys = Object.keys(this.inputs);\n\t if (utils.arraysDiffer(this.prevInputKeys, newInputKeys)) {\n\t this.validateForm();\n\t }\n\t },\n\t\n\t // Allow resetting to specified data\n\t reset: function reset(data) {\n\t this.setFormPristine(true);\n\t this.resetModel(data);\n\t },\n\t\n\t // Update model, submit to url prop and send the model\n\t submit: function submit(event) {\n\t\n\t event && event.preventDefault();\n\t\n\t // Trigger form as not pristine.\n\t // If any inputs have not been touched yet this will make them dirty\n\t // so validation becomes visible (if based on isPristine)\n\t this.setFormPristine(false);\n\t this.updateModel();\n\t var model = this.mapModel();\n\t this.props.onSubmit(model, this.resetModel, this.updateInputsWithError);\n\t this.state.isValid ? this.props.onValidSubmit(model, this.resetModel, this.updateInputsWithError) : this.props.onInvalidSubmit(model, this.resetModel, this.updateInputsWithError);\n\t },\n\t\n\t mapModel: function mapModel() {\n\t if (this.props.mapping) {\n\t return this.props.mapping(this.model);\n\t } else {\n\t return formDataToObject(Object.keys(this.model).reduce((function (mappedModel, key) {\n\t\n\t var keyArray = key.split('.');\n\t var base = mappedModel;\n\t while (keyArray.length) {\n\t var currentKey = keyArray.shift();\n\t base = base[currentKey] = keyArray.length ? base[currentKey] || {} : this.model[key];\n\t }\n\t\n\t return mappedModel;\n\t }).bind(this), {}));\n\t }\n\t },\n\t\n\t // Goes through all registered components and\n\t // updates the model values\n\t updateModel: function updateModel() {\n\t Object.keys(this.inputs).forEach((function (name) {\n\t var component = this.inputs[name];\n\t this.model[name] = component.state._value;\n\t }).bind(this));\n\t },\n\t\n\t // Reset each key in the model to the original / initial / specified value\n\t resetModel: function resetModel(data) {\n\t Object.keys(this.inputs).forEach((function (name) {\n\t if (data && data[name]) {\n\t this.inputs[name].setValue(data[name]);\n\t } else {\n\t this.inputs[name].resetValue();\n\t }\n\t }).bind(this));\n\t this.validateForm();\n\t },\n\t\n\t setInputValidationErrors: function setInputValidationErrors(errors) {\n\t Object.keys(this.inputs).forEach((function (name, index) {\n\t var component = this.inputs[name];\n\t var args = [{\n\t _isValid: !(name in errors),\n\t _validationError: typeof errors[name] === 'string' ? [errors[name]] : errors[name]\n\t }];\n\t component.setState.apply(component, args);\n\t }).bind(this));\n\t },\n\t\n\t // Checks if the values have changed from their initial value\n\t isChanged: function isChanged() {\n\t return !utils.isSame(this.getPristineValues(), this.getCurrentValues());\n\t },\n\t\n\t getPristineValues: function getPristineValues() {\n\t var inputs = this.inputs;\n\t return Object.keys(inputs).reduce(function (data, name) {\n\t var component = inputs[name];\n\t data[name] = component.props.value;\n\t return data;\n\t }, {});\n\t },\n\t\n\t // Go through errors from server and grab the components\n\t // stored in the inputs map. Change their state to invalid\n\t // and set the serverError message\n\t updateInputsWithError: function updateInputsWithError(errors) {\n\t Object.keys(errors).forEach((function (name, index) {\n\t var component = this.inputs[name];\n\t\n\t if (!component) {\n\t throw new Error('You are trying to update an input that does not exist. Verify errors object with input names. ' + JSON.stringify(errors));\n\t }\n\t var args = [{\n\t _isValid: this.props.preventExternalInvalidation || false,\n\t _externalError: typeof errors[name] === 'string' ? [errors[name]] : errors[name]\n\t }];\n\t component.setState.apply(component, args);\n\t }).bind(this));\n\t },\n\t\n\t isFormDisabled: function isFormDisabled() {\n\t return this.props.disabled;\n\t },\n\t\n\t getCurrentValues: function getCurrentValues() {\n\t return Object.keys(this.inputs).reduce((function (data, name) {\n\t var component = this.inputs[name];\n\t data[name] = component.state._value;\n\t return data;\n\t }).bind(this), {});\n\t },\n\t\n\t setFormPristine: function setFormPristine(isPristine) {\n\t var inputs = this.inputs;\n\t var inputKeys = Object.keys(inputs);\n\t\n\t this.setState({\n\t _formSubmitted: !isPristine\n\t });\n\t\n\t // Iterate through each component and set it as pristine\n\t // or \"dirty\".\n\t inputKeys.forEach((function (name, index) {\n\t var component = inputs[name];\n\t component.setState({\n\t _formSubmitted: !isPristine,\n\t _isPristine: isPristine\n\t });\n\t }).bind(this));\n\t },\n\t\n\t // Use the binded values and the actual input value to\n\t // validate the input and set its state. Then check the\n\t // state of the form itself\n\t validate: function validate(component) {\n\t\n\t // Trigger onChange\n\t if (this.state.canChange) {\n\t this.props.onChange(this.getCurrentValues(), this.isChanged());\n\t }\n\t\n\t var validation = this.runValidation(component);\n\t // Run through the validations, split them up and call\n\t // the validator IF there is a value or it is required\n\t component.setState({\n\t _isValid: validation.isValid,\n\t _isRequired: validation.isRequired,\n\t _validationError: validation.error,\n\t _externalError: null\n\t }, this.validateForm);\n\t },\n\t\n\t // Checks validation on current value or a passed value\n\t runValidation: function runValidation(component, value) {\n\t\n\t var currentValues = this.getCurrentValues();\n\t var validationErrors = component.props.validationErrors;\n\t var validationError = component.props.validationError;\n\t value = arguments.length === 2 ? value : component.state._value;\n\t\n\t var validationResults = this.runRules(value, currentValues, component._validations);\n\t var requiredResults = this.runRules(value, currentValues, component._requiredValidations);\n\t\n\t // the component defines an explicit validate function\n\t if (typeof component.validate === \"function\") {\n\t validationResults.failed = component.validate() ? [] : ['failed'];\n\t }\n\t\n\t var isRequired = Object.keys(component._requiredValidations).length ? !!requiredResults.success.length : false;\n\t var isValid = !validationResults.failed.length && !(this.props.validationErrors && this.props.validationErrors[component.props.name]);\n\t\n\t return {\n\t isRequired: isRequired,\n\t isValid: isRequired ? false : isValid,\n\t error: (function () {\n\t\n\t if (isValid && !isRequired) {\n\t return [];\n\t }\n\t\n\t if (validationResults.errors.length) {\n\t return validationResults.errors;\n\t }\n\t\n\t if (this.props.validationErrors && this.props.validationErrors[component.props.name]) {\n\t return typeof this.props.validationErrors[component.props.name] === 'string' ? [this.props.validationErrors[component.props.name]] : this.props.validationErrors[component.props.name];\n\t }\n\t\n\t if (isRequired) {\n\t var error = validationErrors[requiredResults.success[0]];\n\t return error ? [error] : null;\n\t }\n\t\n\t if (validationResults.failed.length) {\n\t return validationResults.failed.map(function (failed) {\n\t return validationErrors[failed] ? validationErrors[failed] : validationError;\n\t }).filter(function (x, pos, arr) {\n\t // Remove duplicates\n\t return arr.indexOf(x) === pos;\n\t });\n\t }\n\t }).call(this)\n\t };\n\t },\n\t\n\t runRules: function runRules(value, currentValues, validations) {\n\t\n\t var results = {\n\t errors: [],\n\t failed: [],\n\t success: []\n\t };\n\t if (Object.keys(validations).length) {\n\t Object.keys(validations).forEach(function (validationMethod) {\n\t\n\t if (validationRules[validationMethod] && typeof validations[validationMethod] === 'function') {\n\t throw new Error('Formsy does not allow you to override default validations: ' + validationMethod);\n\t }\n\t\n\t if (!validationRules[validationMethod] && typeof validations[validationMethod] !== 'function') {\n\t throw new Error('Formsy does not have the validation rule: ' + validationMethod);\n\t }\n\t\n\t if (typeof validations[validationMethod] === 'function') {\n\t var validation = validations[validationMethod](currentValues, value);\n\t if (typeof validation === 'string') {\n\t results.errors.push(validation);\n\t results.failed.push(validationMethod);\n\t } else if (!validation) {\n\t results.failed.push(validationMethod);\n\t }\n\t return;\n\t } else if (typeof validations[validationMethod] !== 'function') {\n\t var validation = validationRules[validationMethod](currentValues, value, validations[validationMethod]);\n\t if (typeof validation === 'string') {\n\t results.errors.push(validation);\n\t results.failed.push(validationMethod);\n\t } else if (!validation) {\n\t results.failed.push(validationMethod);\n\t } else {\n\t results.success.push(validationMethod);\n\t }\n\t return;\n\t }\n\t\n\t return results.success.push(validationMethod);\n\t });\n\t }\n\t\n\t return results;\n\t },\n\t\n\t // Validate the form by going through all child input components\n\t // and check their state\n\t validateForm: function validateForm() {\n\t var allIsValid;\n\t var inputs = this.inputs;\n\t var inputKeys = Object.keys(inputs);\n\t\n\t // We need a callback as we are validating all inputs again. This will\n\t // run when the last component has set its state\n\t var onValidationComplete = (function () {\n\t allIsValid = inputKeys.every((function (name) {\n\t return inputs[name].state._isValid;\n\t }).bind(this));\n\t\n\t this.setState({\n\t isValid: allIsValid\n\t });\n\t\n\t if (allIsValid) {\n\t this.props.onValid();\n\t } else {\n\t this.props.onInvalid();\n\t }\n\t\n\t // Tell the form that it can start to trigger change events\n\t this.setState({\n\t canChange: true\n\t });\n\t }).bind(this);\n\t\n\t // Run validation again in case affected by other inputs. The\n\t // last component validated will run the onValidationComplete callback\n\t inputKeys.forEach((function (name, index) {\n\t var component = inputs[name];\n\t var validation = this.runValidation(component);\n\t if (validation.isValid && component.state._externalError) {\n\t validation.isValid = false;\n\t }\n\t component.setState({\n\t _isValid: validation.isValid,\n\t _isRequired: validation.isRequired,\n\t _validationError: validation.error,\n\t _externalError: !validation.isValid && component.state._externalError ? component.state._externalError : null\n\t }, index === inputKeys.length - 1 ? onValidationComplete : null);\n\t }).bind(this));\n\t\n\t // If there are no inputs, set state where form is ready to trigger\n\t // change event. New inputs might be added later\n\t if (!inputKeys.length && this.isMounted()) {\n\t this.setState({\n\t canChange: true\n\t });\n\t }\n\t },\n\t\n\t // Method put on each input component to register\n\t // itself to the form\n\t attachToForm: function attachToForm(component) {\n\t this.inputs[component.props.name] = component;\n\t this.model[component.props.name] = component.state._value;\n\t this.validate(component);\n\t },\n\t\n\t // Method put on each input component to unregister\n\t // itself from the form\n\t detachFromForm: function detachFromForm(component) {\n\t delete this.inputs[component.props.name];\n\t delete this.model[component.props.name];\n\t this.validateForm();\n\t },\n\t render: function render() {\n\t\n\t return React.createElement(\n\t 'form',\n\t _extends({}, this.props, { onSubmit: this.submit }),\n\t this.props.children\n\t );\n\t }\n\t});\n\t\n\tif (!global.exports && !global.module && (!global.define || !global.define.amd)) {\n\t global.Formsy = Formsy;\n\t}\n\t\n\tmodule.exports = Formsy;\n\t/* WEBPACK VAR INJECTION */}.call(exports, (function() { return this; }())))\n\n/***/ },\n/* 1 */\n/***/ function(module, exports) {\n\n\tmodule.exports = __WEBPACK_EXTERNAL_MODULE_1__;\n\n/***/ },\n/* 2 */\n/***/ function(module, exports, __webpack_require__) {\n\n\t/* WEBPACK VAR INJECTION */(function(global) {'use strict';\n\t\n\tvar utils = __webpack_require__(3);\n\tvar React = global.React || __webpack_require__(1);\n\t\n\tvar convertValidationsToObject = function convertValidationsToObject(validations) {\n\t\n\t if (typeof validations === 'string') {\n\t\n\t return validations.split(/\\,(?![^{\\[]*[}\\]])/g).reduce(function (validations, validation) {\n\t var args = validation.split(':');\n\t var validateMethod = args.shift();\n\t\n\t args = args.map(function (arg) {\n\t try {\n\t return JSON.parse(arg);\n\t } catch (e) {\n\t return arg; // It is a string if it can not parse it\n\t }\n\t });\n\t\n\t if (args.length > 1) {\n\t throw new Error('Formsy does not support multiple args on string validations. Use object format of validations instead.');\n\t }\n\t\n\t validations[validateMethod] = args.length ? args[0] : true;\n\t return validations;\n\t }, {});\n\t }\n\t\n\t return validations || {};\n\t};\n\t\n\tmodule.exports = {\n\t getInitialState: function getInitialState() {\n\t return {\n\t _value: this.props.value,\n\t _isRequired: false,\n\t _isValid: true,\n\t _isPristine: true,\n\t _pristineValue: this.props.value,\n\t _validationError: [],\n\t _externalError: null,\n\t _formSubmitted: false\n\t };\n\t },\n\t contextTypes: {\n\t formsy: React.PropTypes.object // What about required?\n\t },\n\t getDefaultProps: function getDefaultProps() {\n\t return {\n\t validationError: '',\n\t validationErrors: {}\n\t };\n\t },\n\t\n\t componentWillMount: function componentWillMount() {\n\t var configure = (function () {\n\t this.setValidations(this.props.validations, this.props.required);\n\t\n\t // Pass a function instead?\n\t this.context.formsy.attachToForm(this);\n\t //this.props._attachToForm(this);\n\t }).bind(this);\n\t\n\t if (!this.props.name) {\n\t throw new Error('Form Input requires a name property when used');\n\t }\n\t\n\t /*\n\t if (!this.props._attachToForm) {\n\t return setTimeout(function () {\n\t if (!this.isMounted()) return;\n\t if (!this.props._attachToForm) {\n\t throw new Error('Form Mixin requires component to be nested in a Form');\n\t }\n\t configure();\n\t }.bind(this), 0);\n\t }\n\t */\n\t configure();\n\t },\n\t\n\t // We have to make the validate method is kept when new props are added\n\t componentWillReceiveProps: function componentWillReceiveProps(nextProps) {\n\t this.setValidations(nextProps.validations, nextProps.required);\n\t },\n\t\n\t componentDidUpdate: function componentDidUpdate(prevProps) {\n\t\n\t // If the value passed has changed, set it. If value is not passed it will\n\t // internally update, and this will never run\n\t if (!utils.isSame(this.props.value, prevProps.value)) {\n\t this.setValue(this.props.value);\n\t }\n\t\n\t // If validations or required is changed, run a new validation\n\t if (!utils.isSame(this.props.validations, prevProps.validations) || !utils.isSame(this.props.required, prevProps.required)) {\n\t this.context.formsy.validate(this);\n\t }\n\t },\n\t\n\t // Detach it when component unmounts\n\t componentWillUnmount: function componentWillUnmount() {\n\t this.context.formsy.detachFromForm(this);\n\t //this.props._detachFromForm(this);\n\t },\n\t\n\t setValidations: function setValidations(validations, required) {\n\t\n\t // Add validations to the store itself as the props object can not be modified\n\t this._validations = convertValidationsToObject(validations) || {};\n\t this._requiredValidations = required === true ? { isDefaultRequiredValue: true } : convertValidationsToObject(required);\n\t },\n\t\n\t // We validate after the value has been set\n\t setValue: function setValue(value) {\n\t this.setState({\n\t _value: value,\n\t _isPristine: false\n\t }, (function () {\n\t this.context.formsy.validate(this);\n\t //this.props._validate(this);\n\t }).bind(this));\n\t },\n\t resetValue: function resetValue() {\n\t this.setState({\n\t _value: this.state._pristineValue,\n\t _isPristine: true\n\t }, function () {\n\t this.context.formsy.validate(this);\n\t //this.props._validate(this);\n\t });\n\t },\n\t getValue: function getValue() {\n\t return this.state._value;\n\t },\n\t hasValue: function hasValue() {\n\t return this.state._value !== '';\n\t },\n\t getErrorMessage: function getErrorMessage() {\n\t var messages = this.getErrorMessages();\n\t return messages.length ? messages[0] : null;\n\t },\n\t getErrorMessages: function getErrorMessages() {\n\t return !this.isValid() || this.showRequired() ? this.state._externalError || this.state._validationError || [] : [];\n\t },\n\t isFormDisabled: function isFormDisabled() {\n\t return this.context.formsy.isFormDisabled();\n\t //return this.props._isFormDisabled();\n\t },\n\t isValid: function isValid() {\n\t return this.state._isValid;\n\t },\n\t isPristine: function isPristine() {\n\t return this.state._isPristine;\n\t },\n\t isFormSubmitted: function isFormSubmitted() {\n\t return this.state._formSubmitted;\n\t },\n\t isRequired: function isRequired() {\n\t return !!this.props.required;\n\t },\n\t showRequired: function showRequired() {\n\t return this.state._isRequired;\n\t },\n\t showError: function showError() {\n\t return !this.showRequired() && !this.isValid();\n\t },\n\t isValidValue: function isValidValue(value) {\n\t return this.context.formsy.isValidValue.call(null, this, value);\n\t //return this.props._isValidValue.call(null, this, value);\n\t }\n\t};\n\t/* WEBPACK VAR INJECTION */}.call(exports, (function() { return this; }())))\n\n/***/ },\n/* 3 */\n/***/ function(module, exports) {\n\n\t'use strict';\n\t\n\tmodule.exports = {\n\t arraysDiffer: function arraysDiffer(a, b) {\n\t var isDifferent = false;\n\t if (a.length !== b.length) {\n\t isDifferent = true;\n\t } else {\n\t a.forEach(function (item, index) {\n\t if (!this.isSame(item, b[index])) {\n\t isDifferent = true;\n\t }\n\t }, this);\n\t }\n\t return isDifferent;\n\t },\n\t\n\t objectsDiffer: function objectsDiffer(a, b) {\n\t var isDifferent = false;\n\t if (Object.keys(a).length !== Object.keys(b).length) {\n\t isDifferent = true;\n\t } else {\n\t Object.keys(a).forEach(function (key) {\n\t if (!this.isSame(a[key], b[key])) {\n\t isDifferent = true;\n\t }\n\t }, this);\n\t }\n\t return isDifferent;\n\t },\n\t\n\t isSame: function isSame(a, b) {\n\t if (typeof a !== typeof b) {\n\t return false;\n\t } else if (Array.isArray(a)) {\n\t return !this.arraysDiffer(a, b);\n\t } else if (typeof a === 'object' && a !== null && b !== null) {\n\t return !this.objectsDiffer(a, b);\n\t }\n\t\n\t return a === b;\n\t }\n\t};\n\n/***/ },\n/* 4 */\n/***/ function(module, exports, __webpack_require__) {\n\n\t/* WEBPACK VAR INJECTION */(function(global) {'use strict';\n\t\n\tvar _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };\n\t\n\tvar React = global.React || __webpack_require__(1);\n\tvar Mixin = __webpack_require__(2);\n\tmodule.exports = function () {\n\t return function (Component) {\n\t return React.createClass({\n\t mixins: [Mixin],\n\t render: function render() {\n\t return React.createElement(Component, _extends({\n\t setValidations: this.setValidations,\n\t setValue: this.setValue,\n\t resetValue: this.resetValue,\n\t getValue: this.getValue,\n\t hasValue: this.hasValue,\n\t getErrorMessage: this.getErrorMessage,\n\t getErrorMessages: this.getErrorMessages,\n\t isFormDisabled: this.isFormDisabled,\n\t isValid: this.isValid,\n\t isPristine: this.isPristine,\n\t isFormSubmitted: this.isFormSubmitted,\n\t isRequired: this.isRequired,\n\t showRequired: this.showRequired,\n\t showError: this.showError,\n\t isValidValue: this.isValidValue\n\t }, this.props));\n\t }\n\t });\n\t };\n\t};\n\t/* WEBPACK VAR INJECTION */}.call(exports, (function() { return this; }())))\n\n/***/ },\n/* 5 */\n/***/ function(module, exports, __webpack_require__) {\n\n\t/* WEBPACK VAR INJECTION */(function(global) {'use strict';\n\t\n\tvar React = global.React || __webpack_require__(1);\n\tvar Mixin = __webpack_require__(2);\n\tmodule.exports = function (Component) {\n\t return React.createClass({\n\t mixins: [Mixin],\n\t render: function render() {\n\t return React.createElement(Component, {\n\t setValidations: this.setValidations,\n\t setValue: this.setValue,\n\t resetValue: this.resetValue,\n\t getValue: this.getValue,\n\t hasValue: this.hasValue,\n\t getErrorMessage: this.getErrorMessage,\n\t getErrorMessages: this.getErrorMessages,\n\t isFormDisabled: this.isFormDisabled,\n\t isValid: this.isValid,\n\t isPristine: this.isPristine,\n\t isFormSubmitted: this.isFormSubmitted,\n\t isRequired: this.isRequired,\n\t showRequired: this.showRequired,\n\t showError: this.showError,\n\t isValidValue: this.isValidValue\n\t });\n\t }\n\t });\n\t};\n\t/* WEBPACK VAR INJECTION */}.call(exports, (function() { return this; }())))\n\n/***/ },\n/* 6 */\n/***/ function(module, exports) {\n\n\t'use strict';\n\t\n\tvar _isExisty = function _isExisty(value) {\n\t return value !== null && value !== undefined;\n\t};\n\t\n\tvar isEmpty = function isEmpty(value) {\n\t return value === '';\n\t};\n\t\n\tvar validations = {\n\t isDefaultRequiredValue: function isDefaultRequiredValue(values, value) {\n\t return value === undefined || value === '';\n\t },\n\t isExisty: function isExisty(values, value) {\n\t return _isExisty(value);\n\t },\n\t matchRegexp: function matchRegexp(values, value, regexp) {\n\t return !_isExisty(value) || isEmpty(value) || regexp.test(value);\n\t },\n\t isUndefined: function isUndefined(values, value) {\n\t return value === undefined;\n\t },\n\t isEmptyString: function isEmptyString(values, value) {\n\t return isEmpty(value);\n\t },\n\t isEmail: function isEmail(values, value) {\n\t return validations.matchRegexp(values, value, /^((([a-z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])+(\\.([a-z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])+)*)|((\\x22)((((\\x20|\\x09)*(\\x0d\\x0a))?(\\x20|\\x09)+)?(([\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]|\\x21|[\\x23-\\x5b]|[\\x5d-\\x7e]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(\\\\([\\x01-\\x09\\x0b\\x0c\\x0d-\\x7f]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF]))))*(((\\x20|\\x09)*(\\x0d\\x0a))?(\\x20|\\x09)+)?(\\x22)))@((([a-z]|\\d|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(([a-z]|\\d|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])([a-z]|\\d|-|\\.|_|~|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])*([a-z]|\\d|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])))\\.)+(([a-z]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(([a-z]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])([a-z]|\\d|-|\\.|_|~|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])*([a-z]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])))$/i);\n\t },\n\t isUrl: function isUrl(values, value) {\n\t return validations.matchRegexp(values, value, /^(https?|s?ftp):\\/\\/(((([a-z]|\\d|-|\\.|_|~|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(%[\\da-f]{2})|[!\\$&'\\(\\)\\*\\+,;=]|:)*@)?(((\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5])\\.(\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5])\\.(\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5])\\.(\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5]))|((([a-z]|\\d|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(([a-z]|\\d|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])([a-z]|\\d|-|\\.|_|~|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])*([a-z]|\\d|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])))\\.)+(([a-z]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(([a-z]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])([a-z]|\\d|-|\\.|_|~|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])*([a-z]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])))\\.?)(:\\d*)?)(\\/((([a-z]|\\d|-|\\.|_|~|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(%[\\da-f]{2})|[!\\$&'\\(\\)\\*\\+,;=]|:|@)+(\\/(([a-z]|\\d|-|\\.|_|~|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(%[\\da-f]{2})|[!\\$&'\\(\\)\\*\\+,;=]|:|@)*)*)?)?(\\?((([a-z]|\\d|-|\\.|_|~|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(%[\\da-f]{2})|[!\\$&'\\(\\)\\*\\+,;=]|:|@)|[\\uE000-\\uF8FF]|\\/|\\?)*)?(#((([a-z]|\\d|-|\\.|_|~|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(%[\\da-f]{2})|[!\\$&'\\(\\)\\*\\+,;=]|:|@)|\\/|\\?)*)?$/i);\n\t },\n\t isTrue: function isTrue(values, value) {\n\t return value === true;\n\t },\n\t isFalse: function isFalse(values, value) {\n\t return value === false;\n\t },\n\t isNumeric: function isNumeric(values, value) {\n\t if (typeof value === 'number') {\n\t return true;\n\t }\n\t return validations.matchRegexp(values, value, /^[-+]?(?:\\d*[.])?\\d+$/);\n\t },\n\t isAlpha: function isAlpha(values, value) {\n\t return validations.matchRegexp(values, value, /^[A-Z]+$/i);\n\t },\n\t isAlphanumeric: function isAlphanumeric(values, value) {\n\t return validations.matchRegexp(values, value, /^[0-9A-Z]+$/i);\n\t },\n\t isInt: function isInt(values, value) {\n\t return validations.matchRegexp(values, value, /^(?:[-+]?(?:0|[1-9]\\d*))$/);\n\t },\n\t isFloat: function isFloat(values, value) {\n\t return validations.matchRegexp(values, value, /^(?:[-+]?(?:\\d+))?(?:\\.\\d*)?(?:[eE][\\+\\-]?(?:\\d+))?$/);\n\t },\n\t isWords: function isWords(values, value) {\n\t return validations.matchRegexp(values, value, /^[A-Z\\s]+$/i);\n\t },\n\t isSpecialWords: function isSpecialWords(values, value) {\n\t return validations.matchRegexp(values, value, /^[A-Z\\s\\u00C0-\\u017F]+$/i);\n\t },\n\t isLength: function isLength(values, value, length) {\n\t return !_isExisty(value) || isEmpty(value) || value.length === length;\n\t },\n\t equals: function equals(values, value, eql) {\n\t return !_isExisty(value) || isEmpty(value) || value == eql;\n\t },\n\t equalsField: function equalsField(values, value, field) {\n\t return value == values[field];\n\t },\n\t maxLength: function maxLength(values, value, length) {\n\t return !_isExisty(value) || value.length <= length;\n\t },\n\t minLength: function minLength(values, value, length) {\n\t return !_isExisty(value) || isEmpty(value) || value.length >= length;\n\t }\n\t};\n\t\n\tmodule.exports = validations;\n\n/***/ },\n/* 7 */\n/***/ function(module, exports) {\n\n\tmodule.exports = function (source) {\n\t\n\t\n\t // \"foo[0]\"\n\t return Object.keys(source).reduce(function (output, key) {\n\t\n\t var parentKey = key.match(/[^\\[]*/i);\n\t var paths = key.match(/\\[.*?\\]/g) || [];\n\t paths = [parentKey[0]].concat(paths).map(function (key) {\n\t return key.replace(/\\[|\\]/g, '');\n\t });\n\t\n\t var currentPath = output;\n\t while (paths.length) {\n\t\n\t var pathKey = paths.shift();\n\t\n\t if (pathKey in currentPath) {\n\t currentPath = currentPath[pathKey];\n\t } else {\n\t currentPath[pathKey] = paths.length ? isNaN(paths[0]) ? {} : [] : source[key];\n\t currentPath = currentPath[pathKey];\n\t }\n\t\n\t }\n\t\n\t return output;\n\t\n\t }, {});\n\t\n\t};\n\n\n/***/ }\n/******/ ])\n});\n;\n\n\n/** WEBPACK FOOTER **\n ** formsy-react.js\n **/"," \t// The module cache\n \tvar installedModules = {};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId])\n \t\t\treturn installedModules[moduleId].exports;\n\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\texports: {},\n \t\t\tid: moduleId,\n \t\t\tloaded: false\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.loaded = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"\";\n\n \t// Load entry module and return exports\n \treturn __webpack_require__(0);\n\n\n\n/** WEBPACK FOOTER **\n ** webpack/bootstrap c2760db04162c861118c\n **/","var React = global.React || require('react');\nvar Formsy = {};\nvar validationRules = require('./validationRules.js');\nvar formDataToObject = require('form-data-to-object');\nvar utils = require('./utils.js');\nvar Mixin = require('./Mixin.js');\nvar HOC = require('./HOC.js');\nvar Decorator = require('./Decorator.js');\nvar options = {};\n\nFormsy.Mixin = Mixin;\nFormsy.HOC = HOC;\nFormsy.Decorator = Decorator;\n\nFormsy.defaults = function (passedOptions) {\n options = passedOptions;\n};\n\nFormsy.addValidationRule = function (name, func) {\n validationRules[name] = func;\n};\n\nFormsy.Form = React.createClass({\n displayName: 'Formsy',\n getInitialState: function () {\n return {\n isValid: true,\n isSubmitting: false,\n canChange: false\n };\n },\n getDefaultProps: function () {\n return {\n onSuccess: function () {},\n onError: function () {},\n onSubmit: function () {},\n onValidSubmit: function () {},\n onInvalidSubmit: function () {},\n onSubmitted: function () {},\n onValid: function () {},\n onInvalid: function () {},\n onChange: function () {},\n validationErrors: null,\n preventExternalInvalidation: false\n };\n },\n\n childContextTypes: {\n formsy: React.PropTypes.object\n },\n getChildContext: function () {\n return {\n formsy: {\n attachToForm: this.attachToForm,\n detachFromForm: this.detachFromForm,\n validate: this.validate,\n isFormDisabled: this.isFormDisabled,\n isValidValue: function (component, value) {\n return this.runValidation(component, value).isValid;\n }.bind(this)\n }\n }\n },\n\n // Add a map to store the inputs of the form, a model to store\n // the values of the form and register child inputs\n componentWillMount: function () {\n this.inputs = {};\n this.model = {};\n },\n\n componentDidMount: function () {\n this.validateForm();\n },\n\n componentWillUpdate: function () {\n\n // Keep a reference to input keys before form updates,\n // to check if inputs has changed after render\n this.prevInputKeys = Object.keys(this.inputs);\n\n },\n\n componentDidUpdate: function () {\n\n if (this.props.validationErrors) {\n this.setInputValidationErrors(this.props.validationErrors);\n }\n\n var newInputKeys = Object.keys(this.inputs);\n if (utils.arraysDiffer(this.prevInputKeys, newInputKeys)) {\n this.validateForm();\n }\n\n },\n\n // Allow resetting to specified data\n reset: function (data) {\n this.setFormPristine(true);\n this.resetModel(data);\n },\n\n // Update model, submit to url prop and send the model\n submit: function (event) {\n\n event && event.preventDefault();\n\n // Trigger form as not pristine.\n // If any inputs have not been touched yet this will make them dirty\n // so validation becomes visible (if based on isPristine)\n this.setFormPristine(false);\n this.updateModel();\n var model = this.mapModel();\n this.props.onSubmit(model, this.resetModel, this.updateInputsWithError);\n this.state.isValid ? this.props.onValidSubmit(model, this.resetModel, this.updateInputsWithError) : this.props.onInvalidSubmit(model, this.resetModel, this.updateInputsWithError);\n\n },\n\n mapModel: function () {\n if (this.props.mapping) {\n return this.props.mapping(this.model)\n } else {\n return formDataToObject(Object.keys(this.model).reduce(function (mappedModel, key) {\n\n var keyArray = key.split('.');\n var base = mappedModel;\n while (keyArray.length) {\n var currentKey = keyArray.shift();\n base = (base[currentKey] = keyArray.length ? base[currentKey] || {} : this.model[key]);\n }\n\n return mappedModel;\n\n }.bind(this), {}));\n }\n },\n\n // Goes through all registered components and\n // updates the model values\n updateModel: function () {\n Object.keys(this.inputs).forEach(function (name) {\n var component = this.inputs[name];\n this.model[name] = component.state._value;\n }.bind(this));\n },\n\n // Reset each key in the model to the original / initial / specified value\n resetModel: function (data) {\n Object.keys(this.inputs).forEach(function (name) {\n if (data && data[name]) {\n this.inputs[name].setValue(data[name]);\n } else {\n this.inputs[name].resetValue();\n }\n }.bind(this));\n this.validateForm();\n },\n\n setInputValidationErrors: function (errors) {\n Object.keys(this.inputs).forEach(function (name, index) {\n var component = this.inputs[name];\n var args = [{\n _isValid: !(name in errors),\n _validationError: typeof errors[name] === 'string' ? [errors[name]] : errors[name]\n }];\n component.setState.apply(component, args);\n }.bind(this));\n },\n\n // Checks if the values have changed from their initial value\n isChanged: function() {\n return !utils.isSame(this.getPristineValues(), this.getCurrentValues());\n },\n\n getPristineValues: function() {\n var inputs = this.inputs;\n return Object.keys(inputs).reduce(function (data, name) {\n var component = inputs[name];\n data[name] = component.props.value;\n return data;\n }, {});\n },\n\n // Go through errors from server and grab the components\n // stored in the inputs map. Change their state to invalid\n // and set the serverError message\n updateInputsWithError: function (errors) {\n Object.keys(errors).forEach(function (name, index) {\n var component = this.inputs[name];\n\n if (!component) {\n throw new Error('You are trying to update an input that does not exist. Verify errors object with input names. ' + JSON.stringify(errors));\n }\n var args = [{\n _isValid: this.props.preventExternalInvalidation || false,\n _externalError: typeof errors[name] === 'string' ? [errors[name]] : errors[name]\n }];\n component.setState.apply(component, args);\n }.bind(this));\n },\n\n isFormDisabled: function () {\n return this.props.disabled;\n },\n\n getCurrentValues: function () {\n return Object.keys(this.inputs).reduce(function (data, name) {\n var component = this.inputs[name];\n data[name] = component.state._value;\n return data;\n }.bind(this), {});\n },\n\n setFormPristine: function (isPristine) {\n var inputs = this.inputs;\n var inputKeys = Object.keys(inputs);\n\n this.setState({\n _formSubmitted: !isPristine\n })\n\n // Iterate through each component and set it as pristine\n // or \"dirty\".\n inputKeys.forEach(function (name, index) {\n var component = inputs[name];\n component.setState({\n _formSubmitted: !isPristine,\n _isPristine: isPristine\n });\n }.bind(this));\n },\n\n // Use the binded values and the actual input value to\n // validate the input and set its state. Then check the\n // state of the form itself\n validate: function (component) {\n\n // Trigger onChange\n if (this.state.canChange) {\n this.props.onChange(this.getCurrentValues(), this.isChanged());\n }\n\n var validation = this.runValidation(component);\n // Run through the validations, split them up and call\n // the validator IF there is a value or it is required\n component.setState({\n _isValid: validation.isValid,\n _isRequired: validation.isRequired,\n _validationError: validation.error,\n _externalError: null\n }, this.validateForm);\n\n },\n\n // Checks validation on current value or a passed value\n runValidation: function (component, value) {\n\n var currentValues = this.getCurrentValues();\n var validationErrors = component.props.validationErrors;\n var validationError = component.props.validationError;\n value = arguments.length === 2 ? value : component.state._value;\n\n var validationResults = this.runRules(value, currentValues, component._validations);\n var requiredResults = this.runRules(value, currentValues, component._requiredValidations);\n\n // the component defines an explicit validate function\n if (typeof component.validate === \"function\") {\n validationResults.failed = component.validate() ? [] : ['failed'];\n }\n\n var isRequired = Object.keys(component._requiredValidations).length ? !!requiredResults.success.length : false;\n var isValid = !validationResults.failed.length && !(this.props.validationErrors && this.props.validationErrors[component.props.name]);\n\n return {\n isRequired: isRequired,\n isValid: isRequired ? false : isValid,\n error: (function () {\n\n if (isValid && !isRequired) {\n return [];\n }\n\n if (validationResults.errors.length) {\n return validationResults.errors;\n }\n\n if (this.props.validationErrors && this.props.validationErrors[component.props.name]) {\n return typeof this.props.validationErrors[component.props.name] === 'string' ? [this.props.validationErrors[component.props.name]] : this.props.validationErrors[component.props.name];\n }\n\n if (isRequired) {\n var error = validationErrors[requiredResults.success[0]];\n return error ? [error] : null;\n }\n\n if (validationResults.failed.length) {\n return validationResults.failed.map(function(failed) {\n return validationErrors[failed] ? validationErrors[failed] : validationError;\n }).filter(function(x, pos, arr) {\n // Remove duplicates\n return arr.indexOf(x) === pos;\n });\n }\n\n }.call(this))\n };\n\n },\n\n runRules: function (value, currentValues, validations) {\n\n var results = {\n errors: [],\n failed: [],\n success: []\n };\n if (Object.keys(validations).length) {\n Object.keys(validations).forEach(function (validationMethod) {\n\n if (validationRules[validationMethod] && typeof validations[validationMethod] === 'function') {\n throw new Error('Formsy does not allow you to override default validations: ' + validationMethod);\n }\n\n if (!validationRules[validationMethod] && typeof validations[validationMethod] !== 'function') {\n throw new Error('Formsy does not have the validation rule: ' + validationMethod);\n }\n\n if (typeof validations[validationMethod] === 'function') {\n var validation = validations[validationMethod](currentValues, value);\n if (typeof validation === 'string') {\n results.errors.push(validation);\n results.failed.push(validationMethod);\n } else if (!validation) {\n results.failed.push(validationMethod);\n }\n return;\n\n } else if (typeof validations[validationMethod] !== 'function') {\n var validation = validationRules[validationMethod](currentValues, value, validations[validationMethod]);\n if (typeof validation === 'string') {\n results.errors.push(validation);\n results.failed.push(validationMethod);\n } else if (!validation) {\n results.failed.push(validationMethod);\n } else {\n results.success.push(validationMethod);\n }\n return;\n\n }\n\n return results.success.push(validationMethod);\n\n });\n }\n\n return results;\n\n },\n\n // Validate the form by going through all child input components\n // and check their state\n validateForm: function () {\n var allIsValid;\n var inputs = this.inputs;\n var inputKeys = Object.keys(inputs);\n\n // We need a callback as we are validating all inputs again. This will\n // run when the last component has set its state\n var onValidationComplete = function () {\n allIsValid = inputKeys.every(function (name) {\n return inputs[name].state._isValid;\n }.bind(this));\n\n this.setState({\n isValid: allIsValid\n });\n\n if (allIsValid) {\n this.props.onValid();\n } else {\n this.props.onInvalid();\n }\n\n // Tell the form that it can start to trigger change events\n this.setState({\n canChange: true\n });\n\n }.bind(this);\n\n // Run validation again in case affected by other inputs. The\n // last component validated will run the onValidationComplete callback\n inputKeys.forEach(function (name, index) {\n var component = inputs[name];\n var validation = this.runValidation(component);\n if (validation.isValid && component.state._externalError) {\n validation.isValid = false;\n }\n component.setState({\n _isValid: validation.isValid,\n _isRequired: validation.isRequired,\n _validationError: validation.error,\n _externalError: !validation.isValid && component.state._externalError ? component.state._externalError : null\n }, index === inputKeys.length - 1 ? onValidationComplete : null);\n }.bind(this));\n\n // If there are no inputs, set state where form is ready to trigger\n // change event. New inputs might be added later\n if (!inputKeys.length && this.isMounted()) {\n this.setState({\n canChange: true\n });\n }\n },\n\n // Method put on each input component to register\n // itself to the form\n attachToForm: function (component) {\n this.inputs[component.props.name] = component;\n this.model[component.props.name] = component.state._value;\n this.validate(component);\n },\n\n // Method put on each input component to unregister\n // itself from the form\n detachFromForm: function (component) {\n delete this.inputs[component.props.name];\n delete this.model[component.props.name];\n this.validateForm();\n },\n render: function () {\n\n return (\n
\n {this.props.children}\n
\n );\n\n }\n});\n\nif (!global.exports && !global.module && (!global.define || !global.define.amd)) {\n global.Formsy = Formsy;\n}\n\nmodule.exports = Formsy;\n\n\n\n/** WEBPACK FOOTER **\n ** ./src/main.js\n **/","module.exports = __WEBPACK_EXTERNAL_MODULE_1__;\n\n\n/*****************\n ** WEBPACK FOOTER\n ** external \"react\"\n ** module id = 1\n ** module chunks = 0\n **/","var utils = require('./utils.js');\nvar React = global.React || require('react');\n\nvar convertValidationsToObject = function (validations) {\n\n if (typeof validations === 'string') {\n\n return validations.split(/\\,(?![^{\\[]*[}\\]])/g).reduce(function (validations, validation) {\n var args = validation.split(':');\n var validateMethod = args.shift();\n\n args = args.map(function (arg) {\n try {\n return JSON.parse(arg);\n } catch (e) {\n return arg; // It is a string if it can not parse it\n }\n });\n\n if (args.length > 1) {\n throw new Error('Formsy does not support multiple args on string validations. Use object format of validations instead.');\n }\n\n validations[validateMethod] = args.length ? args[0] : true;\n return validations;\n }, {});\n\n }\n\n return validations || {};\n};\n\nmodule.exports = {\n getInitialState: function () {\n return {\n _value: this.props.value,\n _isRequired: false,\n _isValid: true,\n _isPristine: true,\n _pristineValue: this.props.value,\n _validationError: [],\n _externalError: null,\n _formSubmitted: false\n };\n },\n contextTypes: {\n formsy: React.PropTypes.object // What about required?\n },\n getDefaultProps: function () {\n return {\n validationError: '',\n validationErrors: {}\n };\n },\n\n componentWillMount: function () {\n var configure = function () {\n this.setValidations(this.props.validations, this.props.required);\n\n // Pass a function instead?\n this.context.formsy.attachToForm(this);\n //this.props._attachToForm(this);\n }.bind(this);\n\n if (!this.props.name) {\n throw new Error('Form Input requires a name property when used');\n }\n\n /*\n if (!this.props._attachToForm) {\n return setTimeout(function () {\n if (!this.isMounted()) return;\n if (!this.props._attachToForm) {\n throw new Error('Form Mixin requires component to be nested in a Form');\n }\n configure();\n }.bind(this), 0);\n }\n */\n configure();\n },\n\n // We have to make the validate method is kept when new props are added\n componentWillReceiveProps: function (nextProps) {\n this.setValidations(nextProps.validations, nextProps.required);\n\n },\n\n componentDidUpdate: function (prevProps) {\n\n // If the value passed has changed, set it. If value is not passed it will\n // internally update, and this will never run\n if (!utils.isSame(this.props.value, prevProps.value)) {\n this.setValue(this.props.value);\n }\n\n // If validations or required is changed, run a new validation\n if (!utils.isSame(this.props.validations, prevProps.validations) || !utils.isSame(this.props.required, prevProps.required)) {\n this.context.formsy.validate(this);\n }\n },\n\n // Detach it when component unmounts\n componentWillUnmount: function () {\n this.context.formsy.detachFromForm(this);\n //this.props._detachFromForm(this);\n },\n\n setValidations: function (validations, required) {\n\n // Add validations to the store itself as the props object can not be modified\n this._validations = convertValidationsToObject(validations) || {};\n this._requiredValidations = required === true ? {isDefaultRequiredValue: true} : convertValidationsToObject(required);\n\n },\n\n // We validate after the value has been set\n setValue: function (value) {\n this.setState({\n _value: value,\n _isPristine: false\n }, function () {\n this.context.formsy.validate(this);\n //this.props._validate(this);\n }.bind(this));\n },\n resetValue: function () {\n this.setState({\n _value: this.state._pristineValue,\n _isPristine: true\n }, function () {\n this.context.formsy.validate(this);\n //this.props._validate(this);\n });\n },\n getValue: function () {\n return this.state._value;\n },\n hasValue: function () {\n return this.state._value !== '';\n },\n getErrorMessage: function () {\n var messages = this.getErrorMessages();\n return messages.length ? messages[0] : null;\n },\n getErrorMessages: function () {\n return !this.isValid() || this.showRequired() ? (this.state._externalError || this.state._validationError || []) : [];\n },\n isFormDisabled: function () {\n return this.context.formsy.isFormDisabled();\n //return this.props._isFormDisabled();\n },\n isValid: function () {\n return this.state._isValid;\n },\n isPristine: function () {\n return this.state._isPristine;\n },\n isFormSubmitted: function () {\n return this.state._formSubmitted;\n },\n isRequired: function () {\n return !!this.props.required;\n },\n showRequired: function () {\n return this.state._isRequired;\n },\n showError: function () {\n return !this.showRequired() && !this.isValid();\n },\n isValidValue: function (value) {\n return this.context.formsy.isValidValue.call(null, this, value);\n //return this.props._isValidValue.call(null, this, value);\n }\n};\n\n\n\n/** WEBPACK FOOTER **\n ** ./src/Mixin.js\n **/","module.exports = {\n arraysDiffer: function (a, b) {\n var isDifferent = false;\n if (a.length !== b.length) {\n isDifferent = true;\n } else {\n a.forEach(function (item, index) {\n if (!this.isSame(item, b[index])) {\n isDifferent = true;\n }\n }, this);\n }\n return isDifferent;\n },\n\n objectsDiffer: function (a, b) {\n var isDifferent = false;\n if (Object.keys(a).length !== Object.keys(b).length) {\n isDifferent = true;\n } else {\n Object.keys(a).forEach(function (key) {\n if (!this.isSame(a[key], b[key])) {\n isDifferent = true;\n }\n }, this);\n }\n return isDifferent;\n },\n\n isSame: function (a, b) {\n if (typeof a !== typeof b) {\n return false;\n } else if (Array.isArray(a)) {\n return !this.arraysDiffer(a, b);\n } else if (typeof a === 'object' && a !== null && b !== null) {\n return !this.objectsDiffer(a, b);\n }\n\n return a === b;\n }\n};\n\n\n\n/** WEBPACK FOOTER **\n ** ./src/utils.js\n **/","var React = global.React || require('react');\nvar Mixin = require('./Mixin.js');\nmodule.exports = function () {\n return function (Component) {\n return React.createClass({\n mixins: [Mixin],\n render: function () {\n return React.createElement(Component, {\n setValidations: this.setValidations,\n setValue: this.setValue,\n resetValue: this.resetValue,\n getValue: this.getValue,\n hasValue: this.hasValue,\n getErrorMessage: this.getErrorMessage,\n getErrorMessages: this.getErrorMessages,\n isFormDisabled: this.isFormDisabled,\n isValid: this.isValid,\n isPristine: this.isPristine,\n isFormSubmitted: this.isFormSubmitted,\n isRequired: this.isRequired,\n showRequired: this.showRequired,\n showError: this.showError,\n isValidValue: this.isValidValue,\n ...this.props\n });\n }\n });\n };\n};\n\n\n\n/** WEBPACK FOOTER **\n ** ./src/Decorator.js\n **/","var React = global.React || require('react');\nvar Mixin = require('./Mixin.js');\nmodule.exports = function (Component) {\n return React.createClass({\n mixins: [Mixin],\n render: function () {\n return React.createElement(Component, {\n setValidations: this.setValidations,\n setValue: this.setValue,\n resetValue: this.resetValue,\n getValue: this.getValue,\n hasValue: this.hasValue,\n getErrorMessage: this.getErrorMessage,\n getErrorMessages: this.getErrorMessages,\n isFormDisabled: this.isFormDisabled,\n isValid: this.isValid,\n isPristine: this.isPristine,\n isFormSubmitted: this.isFormSubmitted,\n isRequired: this.isRequired,\n showRequired: this.showRequired,\n showError: this.showError,\n isValidValue: this.isValidValue\n });\n }\n });\n};\n\n\n\n/** WEBPACK FOOTER **\n ** ./src/HOC.js\n **/","var isExisty = function (value) {\n return value !== null && value !== undefined;\n};\n\nvar isEmpty = function (value) {\n return value === '';\n};\n\nvar validations = {\n isDefaultRequiredValue: function (values, value) {\n return value === undefined || value === '';\n },\n isExisty: function (values, value) {\n return isExisty(value);\n },\n matchRegexp: function (values, value, regexp) {\n return !isExisty(value) || isEmpty(value) || regexp.test(value);\n },\n isUndefined: function (values, value) {\n return value === undefined;\n },\n isEmptyString: function (values, value) {\n return isEmpty(value);\n },\n isEmail: function (values, value) {\n return validations.matchRegexp(values, value, /^((([a-z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])+(\\.([a-z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])+)*)|((\\x22)((((\\x20|\\x09)*(\\x0d\\x0a))?(\\x20|\\x09)+)?(([\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]|\\x21|[\\x23-\\x5b]|[\\x5d-\\x7e]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(\\\\([\\x01-\\x09\\x0b\\x0c\\x0d-\\x7f]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF]))))*(((\\x20|\\x09)*(\\x0d\\x0a))?(\\x20|\\x09)+)?(\\x22)))@((([a-z]|\\d|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(([a-z]|\\d|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])([a-z]|\\d|-|\\.|_|~|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])*([a-z]|\\d|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])))\\.)+(([a-z]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(([a-z]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])([a-z]|\\d|-|\\.|_|~|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])*([a-z]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])))$/i);\n },\n isUrl: function (values, value) {\n return validations.matchRegexp(values, value, /^(https?|s?ftp):\\/\\/(((([a-z]|\\d|-|\\.|_|~|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(%[\\da-f]{2})|[!\\$&'\\(\\)\\*\\+,;=]|:)*@)?(((\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5])\\.(\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5])\\.(\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5])\\.(\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5]))|((([a-z]|\\d|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(([a-z]|\\d|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])([a-z]|\\d|-|\\.|_|~|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])*([a-z]|\\d|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])))\\.)+(([a-z]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(([a-z]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])([a-z]|\\d|-|\\.|_|~|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])*([a-z]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])))\\.?)(:\\d*)?)(\\/((([a-z]|\\d|-|\\.|_|~|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(%[\\da-f]{2})|[!\\$&'\\(\\)\\*\\+,;=]|:|@)+(\\/(([a-z]|\\d|-|\\.|_|~|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(%[\\da-f]{2})|[!\\$&'\\(\\)\\*\\+,;=]|:|@)*)*)?)?(\\?((([a-z]|\\d|-|\\.|_|~|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(%[\\da-f]{2})|[!\\$&'\\(\\)\\*\\+,;=]|:|@)|[\\uE000-\\uF8FF]|\\/|\\?)*)?(#((([a-z]|\\d|-|\\.|_|~|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(%[\\da-f]{2})|[!\\$&'\\(\\)\\*\\+,;=]|:|@)|\\/|\\?)*)?$/i);\n },\n isTrue: function (values, value) {\n return value === true;\n },\n isFalse: function (values, value) {\n return value === false;\n },\n isNumeric: function (values, value) {\n if (typeof value === 'number') {\n return true;\n }\n return validations.matchRegexp(values, value, /^[-+]?(?:\\d*[.])?\\d+$/);\n },\n isAlpha: function (values, value) {\n return validations.matchRegexp(values, value, /^[A-Z]+$/i);\n },\n isAlphanumeric: function (values, value) {\n return validations.matchRegexp(values, value, /^[0-9A-Z]+$/i);\n },\n isInt: function (values, value) {\n return validations.matchRegexp(values, value, /^(?:[-+]?(?:0|[1-9]\\d*))$/);\n },\n isFloat: function (values, value) {\n return validations.matchRegexp(values, value, /^(?:[-+]?(?:\\d+))?(?:\\.\\d*)?(?:[eE][\\+\\-]?(?:\\d+))?$/);\n },\n isWords: function (values, value) {\n return validations.matchRegexp(values, value, /^[A-Z\\s]+$/i);\n },\n isSpecialWords: function (values, value) {\n return validations.matchRegexp(values, value, /^[A-Z\\s\\u00C0-\\u017F]+$/i);\n },\n isLength: function (values, value, length) {\n return !isExisty(value) || isEmpty(value) || value.length === length;\n },\n equals: function (values, value, eql) {\n return !isExisty(value) || isEmpty(value) || value == eql;\n },\n equalsField: function (values, value, field) {\n return value == values[field];\n },\n maxLength: function (values, value, length) {\n return !isExisty(value) || value.length <= length;\n },\n minLength: function (values, value, length) {\n return !isExisty(value) || isEmpty(value) || value.length >= length;\n }\n};\n\nmodule.exports = validations;\n\n\n\n/** WEBPACK FOOTER **\n ** ./src/validationRules.js\n **/","module.exports = function (source) {\n\n\n // \"foo[0]\"\n return Object.keys(source).reduce(function (output, key) {\n\n var parentKey = key.match(/[^\\[]*/i);\n var paths = key.match(/\\[.*?\\]/g) || [];\n paths = [parentKey[0]].concat(paths).map(function (key) {\n return key.replace(/\\[|\\]/g, '');\n });\n\n var currentPath = output;\n while (paths.length) {\n\n var pathKey = paths.shift();\n\n if (pathKey in currentPath) {\n currentPath = currentPath[pathKey];\n } else {\n currentPath[pathKey] = paths.length ? isNaN(paths[0]) ? {} : [] : source[key];\n currentPath = currentPath[pathKey];\n }\n\n }\n\n return output;\n\n }, {});\n\n};\n\n\n\n/*****************\n ** WEBPACK FOOTER\n ** ./~/form-data-to-object/index.js\n ** module id = 7\n ** module chunks = 0\n **/"],"sourceRoot":""} \ No newline at end of file diff --git a/specs/Element-spec.jsx b/specs/Element-spec.jsx deleted file mode 100644 index 20f3ea5..0000000 --- a/specs/Element-spec.jsx +++ /dev/null @@ -1,593 +0,0 @@ -var React = require('react/addons'); -var TestUtils = React.addons.TestUtils; -var Formsy = require('./../src/main.js'); - -describe('Element', function() { - - it('should return passed and setValue() value when using getValue()', function () { - - var TestInput = React.createClass({ - mixins: [Formsy.Mixin], - updateValue: function (event) { - this.setValue(event.target.value); - }, - render: function () { - return - } - }); - var form = TestUtils.renderIntoDocument( - - - - ); - - var input = TestUtils.findRenderedDOMComponentWithTag(form, 'INPUT'); - expect(input.getDOMNode().value).toBe('foo'); - TestUtils.Simulate.change(input, {target: {value: 'foobar'}}); - expect(input.getDOMNode().value).toBe('foobar'); - - }); - - it('should set back to pristine value when running reset', function () { - - var reset = null; - var TestInput = React.createClass({ - mixins: [Formsy.Mixin], - componentDidMount: function () { - reset = this.resetValue; - }, - updateValue: function (event) { - this.setValue(event.target.value); - }, - render: function () { - return - } - }); - var form = TestUtils.renderIntoDocument( - - - - ); - - var input = TestUtils.findRenderedDOMComponentWithTag(form, 'INPUT'); - TestUtils.Simulate.change(input, {target: {value: 'foobar'}}); - reset(); - expect(input.getDOMNode().value).toBe('foo'); - - }); - - it('should return error message passed when calling getErrorMessage()', function () { - - var getErrorMessage = null; - var TestInput = React.createClass({ - mixins: [Formsy.Mixin], - componentDidMount: function () { - getErrorMessage = this.getErrorMessage; - }, - updateValue: function (event) { - this.setValue(event.target.value); - }, - render: function () { - return - } - }); - var form = TestUtils.renderIntoDocument( - - - - ); - - expect(getErrorMessage()).toBe('Has to be email'); - - }); - - it('should return true or false when calling isValid() depending on valid state', function () { - - var isValid = null; - var TestInput = React.createClass({ - mixins: [Formsy.Mixin], - componentDidMount: function () { - isValid = this.isValid; - }, - updateValue: function (event) { - this.setValue(event.target.value); - }, - render: function () { - return - } - }); - var form = TestUtils.renderIntoDocument( - - - - ); - - expect(isValid()).toBe(false); - var input = TestUtils.findRenderedDOMComponentWithTag(form, 'INPUT'); - TestUtils.Simulate.change(input, {target: {value: 'foo@foo.com'}}); - expect(isValid()).toBe(true); - - }); - - it('should return true or false when calling isRequired() depending on passed required attribute', function () { - - var isRequireds = []; - var TestInput = React.createClass({ - mixins: [Formsy.Mixin], - componentDidMount: function () { - isRequireds.push(this.isRequired); - }, - updateValue: function (event) { - this.setValue(event.target.value); - }, - render: function () { - return - } - }); - var form = TestUtils.renderIntoDocument( - - - - - - ); - - expect(isRequireds[0]()).toBe(false); - expect(isRequireds[1]()).toBe(true); - expect(isRequireds[2]()).toBe(true); - - }); - - it('should return true or false when calling showRequired() depending on input being empty and required is passed, or not', function () { - - var showRequireds = []; - var TestInput = React.createClass({ - mixins: [Formsy.Mixin], - componentDidMount: function () { - showRequireds.push(this.showRequired); - }, - updateValue: function (event) { - this.setValue(event.target.value); - }, - render: function () { - return - } - }); - var form = TestUtils.renderIntoDocument( - - - - - - ); - - expect(showRequireds[0]()).toBe(false); - expect(showRequireds[1]()).toBe(true); - expect(showRequireds[2]()).toBe(false); - - }); - - it('should return true or false when calling isPristine() depending on input has been "touched" or not', function () { - - var isPristine = null; - var TestInput = React.createClass({ - mixins: [Formsy.Mixin], - componentDidMount: function () { - isPristine = this.isPristine; - }, - updateValue: function (event) { - this.setValue(event.target.value); - }, - render: function () { - return - } - }); - var form = TestUtils.renderIntoDocument( - - - - ); - - expect(isPristine()).toBe(true); - var input = TestUtils.findRenderedDOMComponentWithTag(form, 'INPUT'); - TestUtils.Simulate.change(input, {target: {value: 'foo'}}); - expect(isPristine()).toBe(false); - - }); - -it('should allow an undefined value to be updated to a value', function (done) { - var TestInput = React.createClass({ - mixins: [Formsy.Mixin], - changeValue: function (event) { - this.setValue(event.target.value); - }, - render: function () { - return - } - }); - var TestForm = React.createClass({ - getInitialState: function () { - return {value: undefined}; - }, - changeValue: function () { - this.setState({ - value: 'foo' - }); - }, - render: function () { - return ( - - - - ); - } - }); - var form = TestUtils.renderIntoDocument( - - ); - - form.changeValue(); - var input = TestUtils.findRenderedDOMComponentWithTag(form, 'INPUT'); - setTimeout(function () { - expect(input.getDOMNode().value).toBe('foo'); - done(); - }, 0); - }); - - it('should be able to test a values validity', function () { - - var isInvalid = false; - var TestInput = React.createClass({ - mixins: [Formsy.Mixin], - render: function () { - return - } - }); - var TestForm = React.createClass({ - render: function () { - return ( - - - - ); - } - }); - var form = TestUtils.renderIntoDocument( - - ); - - var input = TestUtils.findRenderedComponentWithType(form, TestInput); - expect(input.isValidValue('foo@bar.com')).toBe(true); - expect(input.isValidValue('foo@bar')).toBe(false); - - }); - - it('should be able to use an object as validations property', function () { - - var TestInput = React.createClass({ - mixins: [Formsy.Mixin], - render: function () { - return - } - }); - var TestForm = React.createClass({ - render: function () { - return ( - - - - ); - } - }); - var form = TestUtils.renderIntoDocument( - - ); - - var input = TestUtils.findRenderedComponentWithType(form, TestInput); - expect(input.isValidValue('foo@bar.com')).toBe(true); - expect(input.isValidValue('foo@bar')).toBe(false); - }); - - it('should be able to pass complex values to a validation rule', function () { - - var TestInput = React.createClass({ - mixins: [Formsy.Mixin], - changeValue: function (event) { - this.setValue(event.target.value); - }, - render: function () { - return - } - }); - var TestForm = React.createClass({ - render: function () { - return ( - - - - ); - } - }); - var form = TestUtils.renderIntoDocument( - - ); - - var inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); - expect(inputComponent.isValid()).toBe(true); - var input = TestUtils.findRenderedDOMComponentWithTag(form, 'INPUT'); - TestUtils.Simulate.change(input, {target: {value: 'bar'}}); - expect(inputComponent.isValid()).toBe(false); - }); - - it('should be able to run a function to validate', function () { - - var TestInput = React.createClass({ - mixins: [Formsy.Mixin], - changeValue: function (event) { - this.setValue(event.target.value); - }, - render: function () { - return - } - }); - var TestForm = React.createClass({ - customValidationA: function (values, value) { - return value === 'foo'; - }, - customValidationB: function (values, value) { - return value === 'foo' && values.A === 'foo'; - }, - render: function () { - return ( - - - - - ); - } - }); - var form = TestUtils.renderIntoDocument( - - ); - - var inputComponent = TestUtils.scryRenderedComponentsWithType(form, TestInput); - expect(inputComponent[0].isValid()).toBe(true); - expect(inputComponent[1].isValid()).toBe(true); - var input = TestUtils.scryRenderedDOMComponentsWithTag(form, 'INPUT'); - TestUtils.Simulate.change(input[0], {target: {value: 'bar'}}); - expect(inputComponent[0].isValid()).toBe(false); - expect(inputComponent[1].isValid()).toBe(false); - }); - - it('should override all error messages with error messages passed by form', function () { - var TestInput = React.createClass({ - mixins: [Formsy.Mixin], - render: function () { - return - } - }); - var TestForm = React.createClass({ - render: function () { - return ( - - - - ); - } - }); - var form = TestUtils.renderIntoDocument( - - ); - - var inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); - expect(inputComponent.getErrorMessage()).toBe('bar'); - }); - - it('should override validation rules with required rules', function () { - var TestInput = React.createClass({ - mixins: [Formsy.Mixin], - render: function () { - return - } - }); - var TestForm = React.createClass({ - render: function () { - return ( - - - - ); - } - }); - var form = TestUtils.renderIntoDocument( - - ); - - var inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); - expect(inputComponent.getErrorMessage()).toBe('bar3'); - }); - - it('should fall back to default error message when non exist in validationErrors map', function () { - var TestInput = React.createClass({ - mixins: [Formsy.Mixin], - render: function () { - return - } - }); - var TestForm = React.createClass({ - render: function () { - return ( - - - - ); - } - }); - var form = TestUtils.renderIntoDocument( - - ); - - var inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); - expect(inputComponent.getErrorMessage()).toBe('bar'); - }); - - it('should not be valid if it is required and required rule is true', function () { - var TestInput = React.createClass({ - mixins: [Formsy.Mixin], - render: function () { - return - } - }); - var TestForm = React.createClass({ - render: function () { - return ( - - - - ); - } - }); - var form = TestUtils.renderIntoDocument( - - ); - - var inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); - expect(inputComponent.isValid()).toBe(false); - }); - - it('should handle objects and arrays as values', function () { - - var TestInput = React.createClass({ - mixins: [Formsy.Mixin], - render: function () { - return
{JSON.stringify(this.getValue())}
- } - }); - var TestForm = React.createClass({ - getInitialState: function () { - return { - foo: {foo: 'bar'}, - bar: ['foo'] - }; - }, - render: function () { - return ( - - - - - ); - } - }); - var form = TestUtils.renderIntoDocument(); - - form.setState({ - foo: {foo: 'foo'}, - bar: ['bar'] - }); - - var inputs = TestUtils.scryRenderedComponentsWithType(form, TestInput); - expect(inputs[0].getValue()).toEqual({foo: 'foo'}); - expect(inputs[1].getValue()).toEqual(['bar']); - - }); - - it('should handle isFormDisabled with dynamic inputs', function () { - - var TestInput = React.createClass({ - mixins: [Formsy.Mixin], - render: function () { - return - } - }); - - var TestForm = React.createClass({ - getInitialState: function () { - return { - bool: true - }; - }, - flip: function () { - this.setState({ - bool: !this.state.bool - }); - }, - render: function () { - return ( - - {this.state.bool ? - : - - } - - ); - } - }); - - var form = TestUtils.renderIntoDocument(); - var input = TestUtils.findRenderedComponentWithType(form, TestInput); - expect(input.isFormDisabled()).toBe(true); - form.flip(); - expect(input.isFormDisabled()).toBe(false); - - }); - - it('should allow for dot notation in name which maps to a deep object', function () { - - var TestInput = React.createClass({ - mixins: [Formsy.Mixin], - render: function () { - return - } - }); - - var TestForm = React.createClass({ - onSubmit: function (model) { - expect(model).toEqual({foo: {bar: 'foo', test: 'test'}}); - }, - render: function () { - return ( - - - - - ); - } - }); - - var form = TestUtils.renderIntoDocument(); - var formEl = TestUtils.findRenderedDOMComponentWithTag(form, 'form'); - TestUtils.Simulate.submit(formEl); - - }); - -}); diff --git a/specs/Formsy-spec.jsx b/specs/Formsy-spec.jsx deleted file mode 100755 index 34d3250..0000000 --- a/specs/Formsy-spec.jsx +++ /dev/null @@ -1,692 +0,0 @@ -var React = require('react/addons'); -var TestUtils = React.addons.TestUtils; -var Formsy = require('./../src/main.js'); - -describe('Formsy', function () { - - describe('Setting up a form', function () { - - it('should render a form into the document', function () { - var form = TestUtils.renderIntoDocument( ); - expect(form.getDOMNode().tagName).toEqual('FORM'); - }); - - it('should set a class name if passed', function () { - var form = TestUtils.renderIntoDocument( ); - expect(form.getDOMNode().className).toEqual('foo'); - }); - - it('should allow for null/undefined children', function (done) { - var TestInput = React.createClass({ - mixins: [Formsy.Mixin], - changeValue: function (event) { - this.setValue(event.target.value); - }, - render: function () { - return - } - }); - - var model = null; - var TestForm = React.createClass({ - onSubmit: function (formModel) { - model = formModel; - }, - render: function () { - return ( - -

Test

- { null } - { undefined } - -
- ); - } - }); - - var form = TestUtils.renderIntoDocument(); - setTimeout(function () { - TestUtils.Simulate.submit(form.getDOMNode()); - expect(model).toEqual({name: 'foo'}); - done(); - }, 10); - }); - - it('should allow for inputs being added dynamically', function (done) { - - var inputs = []; - var forceUpdate = null; - var model = null; - var TestInput = React.createClass({ - mixins: [Formsy.Mixin], - render: function () { - return
- } - }); - var TestForm = React.createClass({ - componentWillMount: function () { - forceUpdate = this.forceUpdate.bind(this); - }, - onSubmit: function (formModel) { - model = formModel; - }, - render: function () { - return ( - - {inputs} - ); - } - }); - var form = TestUtils.renderIntoDocument( - - ); - - // Wait before adding the input - setTimeout(function () { - - inputs.push(); - - forceUpdate(function () { - // Wait for next event loop, as that does the form - setTimeout(function () { - TestUtils.Simulate.submit(form.getDOMNode()); - expect(model.test).toBeDefined(); - done(); - }, 0); - - }); - - }, 10); - - }); - - it('should allow dynamically added inputs to update the form-model', function (done) { - - var inputs = []; - var forceUpdate = null; - var model = null; - var TestInput = React.createClass({ - mixins: [Formsy.Mixin], - changeValue: function (event) { - this.setValue(event.target.value); - }, - render: function () { - return - } - }); - var TestForm = React.createClass({ - componentWillMount: function () { - forceUpdate = this.forceUpdate.bind(this); - }, - onSubmit: function (formModel) { - model = formModel; - }, - render: function () { - return ( - - {inputs} - ); - } - }); - var form = TestUtils.renderIntoDocument( - - ); - - // Wait before adding the input - setTimeout(function () { - - inputs.push(); - - forceUpdate(function () { - - // Wait for next event loop, as that does the form - setTimeout(function () { - TestUtils.Simulate.change(TestUtils.findRenderedDOMComponentWithTag(form, 'INPUT'), {target: {value: 'foo'}}); - TestUtils.Simulate.submit(form.getDOMNode()); - expect(model.test).toBe('foo'); - done(); - }, 0); - - }); - - }, 0); - - }); - - it('should allow a dynamically updated input to update the form-model', function (done) { - - var forceUpdate = null; - var model = null; - var TestInput = React.createClass({ - mixins: [Formsy.Mixin], - changeValue: function (event) { - this.setValue(event.target.value); - }, - render: function () { - return - } - }); - - var input; - var TestForm = React.createClass({ - componentWillMount: function () { - forceUpdate = this.forceUpdate.bind(this); - }, - onSubmit: function (formModel) { - model = formModel; - }, - render: function () { - input = ; - - return ( - - {input} - ); - } - }); - var form = TestUtils.renderIntoDocument(); - - // Wait before changing the input - setTimeout(function () { - form.setProps({value: 'bar'}); - - forceUpdate(function () { - // Wait for next event loop, as that does the form - setTimeout(function () { - TestUtils.Simulate.submit(form.getDOMNode()); - expect(model.test).toBe('bar'); - done(); - }, 0); - - }); - - }, 10); - - }); - - describe('validations', function() { - var CheckValid, onSubmit, OtherCheckValid; - var isValid; - - var TestInput = React.createClass({ - mixins: [Formsy.Mixin], - changeValue: function (event) { - this.setValue(event.target.value); - }, - render: function () { - return - } - }); - - var TestForm = React.createClass({ - getDefaultProps: function() { - return { - inputs: [], - }; - }, - render: function () { - var builtInputs = []; - var inputs = this.props.inputs; - for (var i=0; i < inputs.length; i++) { - var input = inputs[i]; - builtInputs.push(); - } - var _this = this; - return - { builtInputs } - ; - } - }); - - beforeEach(function() { - isValid = true; - CheckValid = jasmine.createSpy('CheckValid'); - Formsy.addValidationRule('CheckValid', CheckValid); - OtherCheckValid = jasmine.createSpy('CheckValid'); - Formsy.addValidationRule('OtherCheckValid', OtherCheckValid); - onSubmit = jasmine.createSpy('onSubmit'); - }); - - it('should run when the input changes', function() { - var form = TestUtils.renderIntoDocument(); - var input = TestUtils.findRenderedDOMComponentWithTag(form, 'input'); - TestUtils.Simulate.change(input.getDOMNode(), {target: {value: 'bar'}}); - expect(CheckValid).toHaveBeenCalledWith({one: 'bar'}, 'bar', true); - expect(OtherCheckValid).not.toHaveBeenCalled(); - }); - - it('should allow the validation to be changed', function() { - var form = TestUtils.renderIntoDocument(); - form.setProps({inputs: [{name: 'one', validations: 'OtherCheckValid', value: 'foo'}] }); - var input = TestUtils.findRenderedDOMComponentWithTag(form, 'input'); - TestUtils.Simulate.change(input.getDOMNode(), {target: {value: 'bar'}}); - expect(OtherCheckValid).toHaveBeenCalledWith({one: 'bar'}, 'bar', true); - }); - - it('should invalidate a form if dynamically inserted input is invalid', function(done) { - var form = TestUtils.renderIntoDocument(); - expect(isValid).toEqual(true); - form.setProps({inputs: [ - {name: 'one', validations: 'isEmail', value: 'foo@bar.com'}, - {name: 'two', validations: 'isEmail', value: 'foo@bar'}, - ]}, function() { - setTimeout(function() { - expect(isValid).toEqual(false); - done(); - }, 0); - }); - }); - - it('should validate a form when removing an invalid input', function(done) { - var form = TestUtils.renderIntoDocument(); - expect(isValid).toEqual(false); - form.setProps({inputs: [{name: 'one', validations: 'isEmail', value: 'foo@bar.com'}]}, function() { - setTimeout(function() { - expect(isValid).toEqual(true); - done(); - }, 0); - }); - }); - - it('runs multiple validations', function() { - var form = TestUtils.renderIntoDocument(); - var input = TestUtils.findRenderedDOMComponentWithTag(form, 'input'); - TestUtils.Simulate.change(input.getDOMNode(), {target: {value: 'bar'}}); - expect(CheckValid).toHaveBeenCalledWith({one: 'bar'}, 'bar', true); - expect(OtherCheckValid).toHaveBeenCalledWith({one: 'bar'}, 'bar', true); - }); - }); - - it('should not trigger onChange when form is mounted', function () { - var hasChanged = jasmine.createSpy('onChange'); - var TestForm = React.createClass({ - onChange: function () { - hasChanged(); - }, - render: function () { - return ; - } - }); - var form = TestUtils.renderIntoDocument(); - expect(hasChanged).not.toHaveBeenCalled(); - }); - - it('should trigger onChange when form element is changed', function () { - var hasChanged = jasmine.createSpy('onChange'); - var MyInput = React.createClass({ - mixins: [Formsy.Mixin], - onChange: function (event) { - this.setValue(event.target.value); - }, - render: function () { - return - } - }); - var TestForm = React.createClass({ - onChange: function () { - hasChanged(); - }, - render: function () { - return ( - - - - ); - } - }); - var form = TestUtils.renderIntoDocument(); - TestUtils.Simulate.change(TestUtils.findRenderedDOMComponentWithTag(form, 'INPUT'), {target: {value: 'bar'}}); - expect(hasChanged).toHaveBeenCalled(); - }); - - it('should trigger onChange when new input is added to form', function (done) { - var hasChanged = jasmine.createSpy('onChange'); - var inputs = []; - var forceUpdate = null; - var TestInput = React.createClass({ - mixins: [Formsy.Mixin], - changeValue: function (event) { - this.setValue(event.target.value); - }, - render: function () { - return - } - }); - var TestForm = React.createClass({ - componentWillMount: function () { - forceUpdate = this.forceUpdate.bind(this); - }, - onChange: function () { - hasChanged(); - }, - render: function () { - return ( - - {inputs} - ); - } - }); - var form = TestUtils.renderIntoDocument( - - ); - - // Wait before adding the input - inputs.push(); - - forceUpdate(function () { - - // Wait for next event loop, as that does the form - setTimeout(function () { - expect(hasChanged).toHaveBeenCalled(); - done(); - }, 0); - - }); - - }); - - }); - - describe('Update a form', function () { - - it('should allow elements to check if the form is disabled', function (done) { - - var TestInput = React.createClass({ - mixins: [Formsy.Mixin], - render: function () { - return - } - }); - var TestForm = React.createClass({ - getInitialState: function () { - return {disabled: true}; - }, - enableForm: function () { - this.setState({ - disabled: false - }); - }, - render: function () { - return ( - - - ); - } - }); - var form = TestUtils.renderIntoDocument( - - ); - - var input = TestUtils.findRenderedComponentWithType(form, TestInput); - expect(input.isFormDisabled()).toBe(true); - form.enableForm(); - setTimeout(function () { - expect(input.isFormDisabled()).toBe(false); - done(); - }, 0); - - }); - - it('should be possible to pass error state of elements by changing an errors attribute', function (done) { - - var TestInput = React.createClass({ - mixins: [Formsy.Mixin], - render: function () { - return ; - } - }); - var TestForm = React.createClass({ - getInitialState: function () { - return { - validationErrors: { - foo: 'bar' - } - }; - }, - onChange: function (values) { - if (values.foo) { - this.setState({ - validationErrors: {} - }); - } else { - this.setState({ - validationErrors: {foo: 'bar'} - }); - } - }, - render: function () { - return ( - - - ); - } - }); - var form = TestUtils.renderIntoDocument( - - ); - - // Wait for update - setTimeout(function () { - var input = TestUtils.findRenderedComponentWithType(form, TestInput); - expect(input.getErrorMessage()).toBe('bar'); - input.setValue('gotValue'); - - // Wait for update - setTimeout(function () { - expect(input.getErrorMessage()).toBe(null); - done(); - }, 0); - }, 0); - - }); - - - it('should trigger an onValidSubmit when submitting a valid form', function () { - - var isCalled = false; - var TestInput = React.createClass({ - mixins: [Formsy.Mixin], - render: function () { - return ; - } - }); - var TestForm = React.createClass({ - onValidSubmit: function () { - isCalled = true; - }, - render: function () { - return ( - - - ); - } - }); - var form = TestUtils.renderIntoDocument( - - ); - - var TestForm = TestUtils.findRenderedComponentWithType(form, TestForm); - TestUtils.Simulate.submit(TestForm.getDOMNode()); - expect(isCalled).toBe(true); - - }); - - it('should trigger an onInvalidSubmit when submitting an invalid form', function () { - - var isCalled = false; - var TestInput = React.createClass({ - mixins: [Formsy.Mixin], - render: function () { - return ; - } - }); - var TestForm = React.createClass({ - onInvalidSubmit: function () { - isCalled = true; - }, - render: function () { - return ( - - - ); - } - }); - var form = TestUtils.renderIntoDocument( - - ); - - var TestForm = TestUtils.findRenderedComponentWithType(form, TestForm); - TestUtils.Simulate.submit(TestForm.getDOMNode()); - expect(isCalled).toBe(true); - - }); - - }); - - describe("value === false", function() { - var onSubmit; - var TestInput = React.createClass({ - mixins: [Formsy.Mixin], - getDefaultProps: function() { - return { - type: "text", - }; - }, - changeValue: function() { - this.setValue(e.target[this.props.type === "checkbox" ? "checked" : "value"]); - }, - render: function () { - return - } - }); - - var TestForm = React.createClass({ - render: function () { - return ( - - - - - ); - } - }); - - beforeEach(function() { - onSubmit = jasmine.createSpy("onSubmit"); - }); - - it("should call onSubmit correctly", function() { - var form = TestUtils.renderIntoDocument(); - TestUtils.Simulate.submit(form.getDOMNode()); - expect(onSubmit).toHaveBeenCalledWith({foo: false}); - }); - - it("should allow dynamic changes to false", function() { - var form = TestUtils.renderIntoDocument(); - form.setProps({value: false}); - TestUtils.Simulate.submit(form.getDOMNode()); - expect(onSubmit).toHaveBeenCalledWith({foo: false}); - }); - - it("should say the form is submitted", function() { - var form = TestUtils.renderIntoDocument(); - var input = TestUtils.findRenderedComponentWithType(form, TestInput); - expect(input.isFormSubmitted()).toBe(false); - TestUtils.Simulate.submit(form.getDOMNode()); - expect(input.isFormSubmitted()).toBe(true); - }); - - it("should be able to reset the form to its pristine state", function() { - var form = TestUtils.renderIntoDocument(); - var input = TestUtils.findRenderedComponentWithType(form, TestInput); - var formsyForm = TestUtils.findRenderedComponentWithType(form, Formsy.Form); - expect(input.getValue()).toBe(true); - form.setProps({value: false}); - expect(input.getValue()).toBe(false); - formsyForm.reset(); - expect(input.getValue()).toBe(true); - }); - - it("should be able to reset the form using custom data", function() { - var form = TestUtils.renderIntoDocument(); - var input = TestUtils.findRenderedComponentWithType(form, TestInput); - var formsyForm = TestUtils.findRenderedComponentWithType(form, Formsy.Form); - expect(input.getValue()).toBe(true); - form.setProps({value: false}); - expect(input.getValue()).toBe(false); - formsyForm.reset({ - foo: 'bar' - }); - expect(input.getValue()).toBe('bar'); - }); - - }); - - describe('.isChanged()', function() { - var onChange; - var TestInput = React.createClass({ - mixins: [Formsy.Mixin], - changeValue: function (event) { - this.setValue(event.target.value); - }, - render: function () { - return - } - }); - - var TestForm = React.createClass({ - getDefaultProps: function() { - return { - inputs: [], - }; - }, - render: function () { - var builtInputs = []; - var inputs = this.props.inputs; - for (var i=0; i < inputs.length; i++) { - var input = inputs[i]; - builtInputs.push(); - } - return - { builtInputs } - { this.props.children } - ; - } - }); - - beforeEach(function() { - onChange = jasmine.createSpy("onChange"); - }); - - it('initially returns false', function() { - var form = TestUtils.renderIntoDocument(); - expect(form.refs.formsy.isChanged()).toEqual(false); - expect(onChange).not.toHaveBeenCalled(); - }); - - it('returns true when changed', function() { - var form = TestUtils.renderIntoDocument(); - var input = TestUtils.findRenderedDOMComponentWithTag(form, 'input'); - TestUtils.Simulate.change(input.getDOMNode(), {target: {value: 'bar'}}); - expect(form.refs.formsy.isChanged()).toEqual(true); - expect(onChange).toHaveBeenCalledWith({one: 'bar'}, true); - }); - - it('returns false if changes are undone', function() { - var form = TestUtils.renderIntoDocument(); - var input = TestUtils.findRenderedDOMComponentWithTag(form, 'input'); - TestUtils.Simulate.change(input.getDOMNode(), {target: {value: 'bar'}}); - expect(onChange).toHaveBeenCalledWith({one: 'bar'}, true); - TestUtils.Simulate.change(input.getDOMNode(), {target: {value: 'foo'}}); - expect(form.refs.formsy.isChanged()).toEqual(false); - expect(onChange).toHaveBeenCalledWith({one: 'foo'}, false); - }); - }); - -}); diff --git a/specs/Rules-equals-spec.jsx b/specs/Rules-equals-spec.jsx deleted file mode 100644 index a689b16..0000000 --- a/specs/Rules-equals-spec.jsx +++ /dev/null @@ -1,62 +0,0 @@ -var React = require('react/addons'); -var TestUtils = React.addons.TestUtils; -var Formsy = require('./../src/main.js'); - -describe('Rules: equals', function() { - var TestInput, isValid, form, input; - - function pass(value) { - return pass.length ? function () { - TestUtils.Simulate.change(input, {target: {value: value}}); - expect(isValid).toBe(true); - } : function () { expect(isValid).toBe(true); }; - } - - function fail(value) { - return fail.length ? function () { - TestUtils.Simulate.change(input, {target: {value: value}}); - expect(isValid).toBe(false); - } : function () { expect(isValid).toBe(false); }; - } - - beforeEach(function() { - TestInput = React.createClass({ - mixins: [Formsy.Mixin], - updateValue: function (event) { - this.setValue(event.target.value); - }, - render: function () { - isValid = this.isValid(); - return - } - }); - - form = TestUtils.renderIntoDocument( - - - - ); - - input = TestUtils.findRenderedDOMComponentWithTag(form, 'INPUT'); - - }); - - afterEach(function() { - TestInput = isValid = form = null; - }); - - it('should pass with a default value', pass()); - - it('should fail when the value is not equal', fail('foo')); - - it('should pass when the value is equal', pass('myValue')); - - it('should pass with an empty string', pass('')); - - it('should pass with an undefined', pass(undefined)); - - it('should pass with a null', pass(null)); - - it('should fail with a number', fail(42)); - -}); diff --git a/specs/Rules-isAlpha-spec.jsx b/specs/Rules-isAlpha-spec.jsx deleted file mode 100644 index 60b7a5e..0000000 --- a/specs/Rules-isAlpha-spec.jsx +++ /dev/null @@ -1,62 +0,0 @@ -var React = require('react/addons'); -var TestUtils = React.addons.TestUtils; -var Formsy = require('./../src/main.js'); - -describe('Rules: isAlpha', function() { - var TestInput, isValid, form, input; - - function pass(value) { - return pass.length ? function () { - TestUtils.Simulate.change(input, {target: {value: value}}); - expect(isValid).toBe(true); - } : function () { expect(isValid).toBe(true); }; - } - - function fail(value) { - return fail.length ? function () { - TestUtils.Simulate.change(input, {target: {value: value}}); - expect(isValid).toBe(false); - } : function () { expect(isValid).toBe(false); }; - } - - beforeEach(function() { - TestInput = React.createClass({ - mixins: [Formsy.Mixin], - updateValue: function (event) { - this.setValue(event.target.value); - }, - render: function () { - isValid = this.isValid(); - return - } - }); - - form = TestUtils.renderIntoDocument( - - - - ); - - input = TestUtils.findRenderedDOMComponentWithTag(form, 'INPUT'); - - }); - - afterEach(function() { - TestInput = isValid = form = null; - }); - - it('should pass with a default value', pass()); - - it('should pass with a string is only latin letters', pass('myValue')); - - it('should fail with a string with numbers', fail('myValue 42')); - - it('should pass with an undefined', pass(undefined)); - - it('should pass with a null', pass(null)); - - it('should pass with an empty string', pass('')); - - it('should fail with a number', fail(42)); - -}); diff --git a/specs/Rules-isEmail-spec.jsx b/specs/Rules-isEmail-spec.jsx deleted file mode 100644 index 29e5943..0000000 --- a/specs/Rules-isEmail-spec.jsx +++ /dev/null @@ -1,62 +0,0 @@ -var React = require('react/addons'); -var TestUtils = React.addons.TestUtils; -var Formsy = require('./../src/main.js'); - -describe('Rules: isEmail', function() { - var TestInput, isValid, form, input; - - function pass(value) { - return pass.length ? function () { - TestUtils.Simulate.change(input, {target: {value: value}}); - expect(isValid).toBe(true); - } : function () { expect(isValid).toBe(true); }; - } - - function fail(value) { - return fail.length ? function () { - TestUtils.Simulate.change(input, {target: {value: value}}); - expect(isValid).toBe(false); - } : function () { expect(isValid).toBe(false); }; - } - - beforeEach(function() { - TestInput = React.createClass({ - mixins: [Formsy.Mixin], - updateValue: function (event) { - this.setValue(event.target.value); - }, - render: function () { - isValid = this.isValid(); - return - } - }); - - form = TestUtils.renderIntoDocument( - - - - ); - - input = TestUtils.findRenderedDOMComponentWithTag(form, 'INPUT'); - - }); - - afterEach(function() { - TestInput = isValid = form = null; - }); - - it('should pass with a default value', pass()); - - it('should fail with "foo"', fail('foo')); - - it('should pass with "foo@foo.com"', pass('foo@foo.com')); - - it('should pass with an undefined', pass(undefined)); - - it('should pass with a null', pass(null)); - - it('should pass with an empty string', pass('')); - - it('should fail with a number', fail(42)); - -}); diff --git a/specs/Rules-isEmptyString-spec.jsx b/specs/Rules-isEmptyString-spec.jsx deleted file mode 100644 index 7fdad83..0000000 --- a/specs/Rules-isEmptyString-spec.jsx +++ /dev/null @@ -1,62 +0,0 @@ -var React = require('react/addons'); -var TestUtils = React.addons.TestUtils; -var Formsy = require('./../src/main.js'); - -describe('Rules: isEmptyString', function() { - var TestInput, isValid, form, input; - - function pass(value) { - return pass.length ? function () { - TestUtils.Simulate.change(input, {target: {value: value}}); - expect(isValid).toBe(true); - } : function () { expect(isValid).toBe(true); }; - } - - function fail(value) { - return fail.length ? function () { - TestUtils.Simulate.change(input, {target: {value: value}}); - expect(isValid).toBe(false); - } : function () { expect(isValid).toBe(false); }; - } - - beforeEach(function() { - TestInput = React.createClass({ - mixins: [Formsy.Mixin], - updateValue: function (event) { - this.setValue(event.target.value); - }, - render: function () { - isValid = this.isValid(); - return - } - }); - - form = TestUtils.renderIntoDocument( - - - - ); - - input = TestUtils.findRenderedDOMComponentWithTag(form, 'INPUT'); - - }); - - afterEach(function() { - TestInput = isValid = form = null; - }); - - it('should fail with a default value', fail()); - - it('should fail with non-empty string', fail('asd')); - - it('should pass with an empty string', pass('')); - - it('should fail with a undefined', fail(undefined)); - - it('should fail with a null', fail(null)); - - it('should fail with a number', fail(123)); - - it('should fail with a zero', fail(0)); - -}); diff --git a/specs/Rules-isExisty-spec.jsx b/specs/Rules-isExisty-spec.jsx deleted file mode 100644 index e946b81..0000000 --- a/specs/Rules-isExisty-spec.jsx +++ /dev/null @@ -1,62 +0,0 @@ -var React = require('react/addons'); -var TestUtils = React.addons.TestUtils; -var Formsy = require('./../src/main.js'); - -describe('Rules: isExisty', function() { - var TestInput, isValid, form, input; - - function pass(value) { - return pass.length ? function () { - TestUtils.Simulate.change(input, {target: {value: value}}); - expect(isValid).toBe(true); - } : function () { expect(isValid).toBe(true); }; - } - - function fail(value) { - return fail.length ? function () { - TestUtils.Simulate.change(input, {target: {value: value}}); - expect(isValid).toBe(false); - } : function () { expect(isValid).toBe(false); }; - } - - beforeEach(function() { - TestInput = React.createClass({ - mixins: [Formsy.Mixin], - updateValue: function (event) { - this.setValue(event.target.value); - }, - render: function () { - isValid = this.isValid(); - return - } - }); - - form = TestUtils.renderIntoDocument( - - - - ); - - input = TestUtils.findRenderedDOMComponentWithTag(form, 'INPUT'); - - }); - - afterEach(function() { - TestInput = isValid = form = null; - }); - - it('should fail with a default value', fail()); - - it('should pass with a string', pass('myValue')); - - it('should pass with an empty string', pass('')); - - it('should fail with an undefined', fail(undefined)); - - it('should fail with a null', fail(null)); - - it('should pass with a number', pass(42)); - - it('should pass with a zero', pass(0)); - -}); diff --git a/specs/Rules-isLength-spec.jsx b/specs/Rules-isLength-spec.jsx deleted file mode 100644 index 1e52cc3..0000000 --- a/specs/Rules-isLength-spec.jsx +++ /dev/null @@ -1,95 +0,0 @@ -var React = require('react/addons'); -var TestUtils = React.addons.TestUtils; -var Formsy = require('./../src/main.js'); - -describe('Rules: isLength', function() { - var TestInput, isValid, form, input; - - function pass(value) { - return pass.length ? function () { - TestUtils.Simulate.change(input, {target: {value: value}}); - expect(isValid).toBe(true); - } : function () { expect(isValid).toBe(true); }; - } - - function fail(value) { - return fail.length ? function () { - TestUtils.Simulate.change(input, {target: {value: value}}); - expect(isValid).toBe(false); - } : function () { expect(isValid).toBe(false); }; - } - - beforeEach(function() { - TestInput = React.createClass({ - mixins: [Formsy.Mixin], - updateValue: function (event) { - this.setValue(event.target.value); - }, - render: function () { - isValid = this.isValid(); - return - } - }); - }); - - afterEach(function() { - TestInput = isValid = form = null; - }); - - describe('isLength:3', function() { - - beforeEach(function() { - form = TestUtils.renderIntoDocument( - - - - ); - - input = TestUtils.findRenderedDOMComponentWithTag(form, 'INPUT'); - }); - - it('should pass with a default value', pass()); - - it('should fail with a string too small', fail('hi')); - - it('should fail with a string too long', fail('foo bar')); - - it('should pass with the right length', pass('sup')); - - it('should pass with an undefined', pass(undefined)); - - it('should pass with a null', pass(null)); - - it('should pass with an empty string', pass('')); - - it('should fail with a number', fail(123)); - - }); - - describe('isLength:0', function() { - - beforeEach(function() { - form = TestUtils.renderIntoDocument( - - - - ); - - input = TestUtils.findRenderedDOMComponentWithTag(form, 'INPUT'); - }); - - it('should pass with a default value', pass()); - - it('should fail with a string too long', fail('foo bar')); - - it('should pass with an undefined', pass(undefined)); - - it('should pass with a null', pass(null)); - - it('should pass with an empty string', pass('')); - - it('should fail with a number', fail(123)); - - }); - -}); diff --git a/specs/Rules-isNumeric-spec.jsx b/specs/Rules-isNumeric-spec.jsx deleted file mode 100644 index 94e21fd..0000000 --- a/specs/Rules-isNumeric-spec.jsx +++ /dev/null @@ -1,68 +0,0 @@ -var React = require('react/addons'); -var TestUtils = React.addons.TestUtils; -var Formsy = require('./../src/main.js'); - -describe('Rules: isNumeric', function() { - var TestInput, isValid, form, input; - - function pass(value) { - return pass.length ? function () { - TestUtils.Simulate.change(input, {target: {value: value}}); - expect(isValid).toBe(true); - } : function () { expect(isValid).toBe(true); }; - } - - function fail(value) { - return fail.length ? function () { - TestUtils.Simulate.change(input, {target: {value: value}}); - expect(isValid).toBe(false); - } : function () { expect(isValid).toBe(false); }; - } - - beforeEach(function() { - TestInput = React.createClass({ - mixins: [Formsy.Mixin], - updateValue: function (event) { - this.setValue(event.target.value); - }, - render: function () { - isValid = this.isValid(); - return - } - }); - - form = TestUtils.renderIntoDocument( - - - - ); - - input = TestUtils.findRenderedDOMComponentWithTag(form, 'INPUT'); - - }); - - afterEach(function() { - TestInput = isValid = form = null; - }); - - it('should pass with a default value', pass()); - - it('should pass with an empty string', pass('')); - - it('should fail with an unempty string', fail('myValue')); - - it('should pass with a number as string', pass('+42')); - - it('should fail with a number as string with not digits', fail('42 as an answer')); - - it('should pass with an int', pass(42)); - - it('should pass with a float', pass(Math.PI)); - - it('should pass with an undefined', pass(undefined)); - - it('should pass with a null', pass(null)); - - it('should pass with a zero', pass(0)); - -}); diff --git a/specs/Rules-isUrl-spec.jsx b/specs/Rules-isUrl-spec.jsx deleted file mode 100644 index cabf8ac..0000000 --- a/specs/Rules-isUrl-spec.jsx +++ /dev/null @@ -1,62 +0,0 @@ -var React = require('react/addons'); -var TestUtils = React.addons.TestUtils; -var Formsy = require('./../src/main.js'); - -describe('Rules: isUrl', function() { - var TestInput, isValid, form, input; - - function pass(value) { - return pass.length ? function () { - TestUtils.Simulate.change(input, {target: {value: value}}); - expect(isValid).toBe(true); - } : function () { expect(isValid).toBe(true); }; - } - - function fail(value) { - return fail.length ? function () { - TestUtils.Simulate.change(input, {target: {value: value}}); - expect(isValid).toBe(false); - } : function () { expect(isValid).toBe(false); }; - } - - beforeEach(function() { - TestInput = React.createClass({ - mixins: [Formsy.Mixin], - updateValue: function (event) { - this.setValue(event.target.value); - }, - render: function () { - isValid = this.isValid(); - return - } - }); - - form = TestUtils.renderIntoDocument( - - - - ); - - input = TestUtils.findRenderedDOMComponentWithTag(form, 'INPUT'); - - }); - - afterEach(function() { - TestInput = isValid = form = null; - }); - - it('should pass with a default value', pass()); - - it('should fail with "foo"', fail('foo')); - - it('should pass with "https://www.google.com/"', pass('https://www.google.com/')); - - it('should pass with an undefined', pass(undefined)); - - it('should pass with a null', pass(null)); - - it('should fail with a number', fail(42)); - - it('should pass with an empty string', pass('')); - -}); diff --git a/specs/Rules-isWords-spec.jsx b/specs/Rules-isWords-spec.jsx deleted file mode 100644 index f7752f4..0000000 --- a/specs/Rules-isWords-spec.jsx +++ /dev/null @@ -1,62 +0,0 @@ -var React = require('react/addons'); -var TestUtils = React.addons.TestUtils; -var Formsy = require('./../src/main.js'); - -describe('Rules: isWords', function() { - var TestInput, isValid, form, input; - - function pass(value) { - return pass.length ? function () { - TestUtils.Simulate.change(input, {target: {value: value}}); - expect(isValid).toBe(true); - } : function () { expect(isValid).toBe(true); }; - } - - function fail(value) { - return fail.length ? function () { - TestUtils.Simulate.change(input, {target: {value: value}}); - expect(isValid).toBe(false); - } : function () { expect(isValid).toBe(false); }; - } - - beforeEach(function() { - TestInput = React.createClass({ - mixins: [Formsy.Mixin], - updateValue: function (event) { - this.setValue(event.target.value); - }, - render: function () { - isValid = this.isValid(); - return - } - }); - - form = TestUtils.renderIntoDocument( - - - - ); - - input = TestUtils.findRenderedDOMComponentWithTag(form, 'INPUT'); - - }); - - afterEach(function() { - TestInput = isValid = form = null; - }); - - it('should pass with a default value', pass()); - - it('should pass with a 1 word', pass('sup')); - - it('should pass with 2 words', pass('sup dude')); - - it('should fail with a string with numbers', fail('myValue 42')); - - it('should pass with an undefined', pass(undefined)); - - it('should pass with a null', pass(null)); - - it('should fail with a number', fail(42)); - -}); diff --git a/specs/Rules-maxLength-spec.jsx b/specs/Rules-maxLength-spec.jsx deleted file mode 100644 index 5c00103..0000000 --- a/specs/Rules-maxLength-spec.jsx +++ /dev/null @@ -1,64 +0,0 @@ -var React = require('react/addons'); -var TestUtils = React.addons.TestUtils; -var Formsy = require('./../src/main.js'); - -describe('Rules: maxLength', function() { - var TestInput, isValid, form, input; - - function pass(value) { - return pass.length ? function () { - TestUtils.Simulate.change(input, {target: {value: value}}); - expect(isValid).toBe(true); - } : function () { expect(isValid).toBe(true); }; - } - - function fail(value) { - return fail.length ? function () { - TestUtils.Simulate.change(input, {target: {value: value}}); - expect(isValid).toBe(false); - } : function () { expect(isValid).toBe(false); }; - } - - beforeEach(function() { - TestInput = React.createClass({ - mixins: [Formsy.Mixin], - updateValue: function (event) { - this.setValue(event.target.value); - }, - render: function () { - isValid = this.isValid(); - return - } - }); - - form = TestUtils.renderIntoDocument( - - - - ); - - input = TestUtils.findRenderedDOMComponentWithTag(form, 'INPUT'); - - }); - - afterEach(function() { - TestInput = isValid = form = null; - }); - - it('should pass with a default value', pass()); - - it('should pass when a string\'s length is smaller', pass('hi')); - - it('should pass when a string\'s length is equal', pass('bar')); - - it('should fail when a string\'s length is bigger', fail('myValue')); - - it('should pass with an empty string', pass('')); - - it('should pass with an undefined', pass(undefined)); - - it('should pass with a null', pass(null)); - - it('should fail with a number', fail(123)); - -}); diff --git a/specs/Rules-minLength-spec.jsx b/specs/Rules-minLength-spec.jsx deleted file mode 100644 index 57b7712..0000000 --- a/specs/Rules-minLength-spec.jsx +++ /dev/null @@ -1,95 +0,0 @@ -var React = require('react/addons'); -var TestUtils = React.addons.TestUtils; -var Formsy = require('./../src/main.js'); - -describe('Rules: minLength', function() { - var TestInput, isValid, form, input; - - function pass(value) { - return pass.length ? function () { - TestUtils.Simulate.change(input, {target: {value: value}}); - expect(isValid).toBe(true); - } : function () { expect(isValid).toBe(true); }; - } - - function fail(value) { - return fail.length ? function () { - TestUtils.Simulate.change(input, {target: {value: value}}); - expect(isValid).toBe(false); - } : function () { expect(isValid).toBe(false); }; - } - - beforeEach(function() { - TestInput = React.createClass({ - mixins: [Formsy.Mixin], - updateValue: function (event) { - this.setValue(event.target.value); - }, - render: function () { - isValid = this.isValid(); - return - } - }); - }); - - afterEach(function() { - TestInput = isValid = form = null; - }); - - describe('minLength:3', function() { - - beforeEach(function() { - form = TestUtils.renderIntoDocument( - - - - ); - - input = TestUtils.findRenderedDOMComponentWithTag(form, 'INPUT'); - }); - - it('should pass with a default value', pass()); - - it('should fail when a string\'s length is smaller', fail('hi')); - - it('should pass when a string\'s length is equal', pass('bar')); - - it('should pass when a string\'s length is bigger', pass('myValue')); - - it('should pass with an empty string', pass('')); - - it('should pass with an undefined', pass(undefined)); - - it('should pass with a null', pass(null)); - - it('should fail with a number', fail(123)); - - }); - - describe('minLength:0', function() { - - beforeEach(function() { - form = TestUtils.renderIntoDocument( - - - - ); - - input = TestUtils.findRenderedDOMComponentWithTag(form, 'INPUT'); - }); - - it('should pass with a default value', pass()); - - it('should pass when a string\'s length is bigger', pass('myValue')); - - it('should pass with empty string', pass('')); - - it('should pass with undefined', pass(undefined)); - - it('should pass with null', pass(null)); - - it('should fail with a number', fail(123)); - - }); - -}); diff --git a/specs/Utils-spec.jsx b/specs/Utils-spec.jsx deleted file mode 100644 index c32fa8c..0000000 --- a/specs/Utils-spec.jsx +++ /dev/null @@ -1,28 +0,0 @@ -var utils = require('./../src/utils.js'); - -describe('Utils', function() { - - it('should check equality of objects and arrays', function () { - var objA = { foo: 'bar' }; - var objB = { foo: 'bar' }; - var objC = [{ foo: ['bar'] }]; - var objD = [{ foo: ['bar'] }]; - var objE, objF; - var objG = null; - var objH = null; - - expect(utils.isSame(objA, objB)).toBe(true); - expect(utils.isSame(objC, objD)).toBe(true); - expect(utils.isSame(objA, objD)).toBe(false); - - expect(utils.isSame(objE, objF)).toBe(true); - expect(utils.isSame(objA, objF)).toBe(false); - expect(utils.isSame(objE, objA)).toBe(false); - - expect(utils.isSame(objG, objH)).toBe(true); - expect(utils.isSame(objA, objH)).toBe(false); - expect(utils.isSame(objG, objA)).toBe(false); - }); - - -}); diff --git a/specs/Validation-spec.jsx b/specs/Validation-spec.jsx deleted file mode 100644 index fd6fabe..0000000 --- a/specs/Validation-spec.jsx +++ /dev/null @@ -1,281 +0,0 @@ -var React = require('react/addons'); -var TestUtils = React.addons.TestUtils; -var Formsy = require('./../src/main.js'); - -describe('Validation', function() { - - it('should reset only changed form element when external error is passed', function (done) { - - var onSubmit = function (model, reset, invalidate) { - invalidate({ - foo: 'bar', - bar: 'foo' - }); - } - var TestInput = React.createClass({ - mixins: [Formsy.Mixin], - updateValue: function (event) { - this.setValue(event.target.value); - }, - render: function () { - return - } - }); - var form = TestUtils.renderIntoDocument( - - - - - ); - - var input = TestUtils.scryRenderedDOMComponentsWithTag(form, 'INPUT')[0]; - var inputComponents = TestUtils.scryRenderedComponentsWithType(form, TestInput); - - form.submit(); - expect(inputComponents[0].isValid()).toBe(false); - expect(inputComponents[1].isValid()).toBe(false); - TestUtils.Simulate.change(input, {target: {value: 'bar'}}); - setTimeout(function () { - expect(inputComponents[0].isValid()).toBe(true); - expect(inputComponents[1].isValid()).toBe(false); - done(); - }, 0); - }); - - it('should let normal validation take over when component with external error is changed', function (done) { - - var onSubmit = function (model, reset, invalidate) { - invalidate({ - foo: 'bar' - }); - } - var TestInput = React.createClass({ - mixins: [Formsy.Mixin], - updateValue: function (event) { - this.setValue(event.target.value); - }, - render: function () { - return - } - }); - var form = TestUtils.renderIntoDocument( - - - - ); - - var input = TestUtils.findRenderedDOMComponentWithTag(form, 'INPUT'); - var inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); - - form.submit(); - expect(inputComponent.isValid()).toBe(false); - TestUtils.Simulate.change(input, {target: {value: 'bar'}}); - setTimeout(function () { - expect(inputComponent.getValue()).toBe('bar'); - expect(inputComponent.isValid()).toBe(false); - done(); - }, 0); - }); - - it('should trigger an onValid handler, if passed, when form is valid', function () { - - var onValid = jasmine.createSpy('valid'); - var TestInput = React.createClass({ - mixins: [Formsy.Mixin], - updateValue: function (event) { - this.setValue(event.target.value); - }, - render: function () { - return - } - }); - var form = TestUtils.renderIntoDocument( - - - - ); - - var input = TestUtils.findRenderedDOMComponentWithTag(form, 'INPUT'); - TestUtils.Simulate.change(input, {target: {value: 'foo'}}); - expect(onValid).toHaveBeenCalled(); - - }); - - it('should trigger an onInvalid handler, if passed, when form is invalid', function () { - - var onInvalid = jasmine.createSpy('invalid'); - var TestInput = React.createClass({ - mixins: [Formsy.Mixin], - updateValue: function (event) { - this.setValue(event.target.value); - }, - render: function () { - return - } - }); - var form = TestUtils.renderIntoDocument( - - - - ); - - var input = TestUtils.findRenderedDOMComponentWithTag(form, 'INPUT'); - TestUtils.Simulate.change(input, {target: {value: ''}}); - expect(onInvalid).toHaveBeenCalled(); - - }); - - it('should use provided validate function', function () { - - var isValid = jasmine.createSpy('valid'); - var TestInput = React.createClass({ - mixins: [Formsy.Mixin], - updateValue: function (event) { - this.setValue(event.target.value); - }, - render: function () { - if (this.isValid()) { - isValid(); - } - return - }, - validate: function () { - return this.getValue() === "checkValidity"; - } - }); - var form = TestUtils.renderIntoDocument( - - - - ); - - var input = TestUtils.findRenderedDOMComponentWithTag(form, 'INPUT'); - TestUtils.Simulate.change(input, {target: {value: 'checkValidity'}}); - expect(isValid).toHaveBeenCalled(); - - }); - - it('should provide invalidate callback on onValiSubmit', function () { - - var TestInput = React.createClass({ - mixins: [Formsy.Mixin], - render: function () { - return - } - }); - var TestForm = React.createClass({ - invalidate: function (model, reset, invalidate) { - invalidate({ - foo: 'bar' - }); - }, - render: function () { - return ( - - - - ); - } - }); - var form = TestUtils.renderIntoDocument(); - - var formEl = TestUtils.findRenderedDOMComponentWithTag(form, 'form'); - var input = TestUtils.findRenderedComponentWithType(form, TestInput); - TestUtils.Simulate.submit(formEl); - expect(input.isValid()).toBe(false); - - }); - - it('should provide invalidate callback on onInvalidSubmit', function () { - - var TestInput = React.createClass({ - mixins: [Formsy.Mixin], - render: function () { - return - } - }); - var TestForm = React.createClass({ - invalidate: function (model, reset, invalidate) { - invalidate({ - foo: 'bar' - }); - }, - render: function () { - return ( - - - - ); - } - }); - var form = TestUtils.renderIntoDocument(); - - var formEl = TestUtils.findRenderedDOMComponentWithTag(form, 'form'); - var input = TestUtils.findRenderedComponentWithType(form, TestInput); - TestUtils.Simulate.submit(formEl); - expect(input.getErrorMessage()).toBe('bar'); - - }); - - - it('should not invalidate inputs on external errors with preventExternalInvalidation prop', function () { - - var TestInput = React.createClass({ - mixins: [Formsy.Mixin], - render: function () { - return - } - }); - var TestForm = React.createClass({ - invalidate: function (model, reset, invalidate) { - invalidate({ - foo: 'bar' - }); - }, - render: function () { - return ( - - - - ); - } - }); - var form = TestUtils.renderIntoDocument(); - var formEl = TestUtils.findRenderedDOMComponentWithTag(form, 'form'); - var input = TestUtils.findRenderedComponentWithType(form, TestInput); - TestUtils.Simulate.submit(formEl); - expect(input.isValid()).toBe(true); - - }); - - it('should invalidate inputs on external errors without preventExternalInvalidation prop', function () { - - var TestInput = React.createClass({ - mixins: [Formsy.Mixin], - render: function () { - return - } - }); - var TestForm = React.createClass({ - invalidate: function (model, reset, invalidate) { - invalidate({ - foo: 'bar' - }); - }, - render: function () { - return ( - - - - ); - } - }); - var form = TestUtils.renderIntoDocument(); - var formEl = TestUtils.findRenderedDOMComponentWithTag(form, 'form'); - var input = TestUtils.findRenderedComponentWithType(form, TestInput); - TestUtils.Simulate.submit(formEl); - expect(input.isValid()).toBe(false); - - }); - -}); diff --git a/src/Decorator.js b/src/Decorator.js new file mode 100644 index 0000000..f9a4b35 --- /dev/null +++ b/src/Decorator.js @@ -0,0 +1,29 @@ +var React = global.React || require('react'); +var Mixin = require('./Mixin.js'); +module.exports = function () { + return function (Component) { + return React.createClass({ + mixins: [Mixin], + render: function () { + return React.createElement(Component, { + setValidations: this.setValidations, + setValue: this.setValue, + resetValue: this.resetValue, + getValue: this.getValue, + hasValue: this.hasValue, + getErrorMessage: this.getErrorMessage, + getErrorMessages: this.getErrorMessages, + isFormDisabled: this.isFormDisabled, + isValid: this.isValid, + isPristine: this.isPristine, + isFormSubmitted: this.isFormSubmitted, + isRequired: this.isRequired, + showRequired: this.showRequired, + showError: this.showError, + isValidValue: this.isValidValue, + ...this.props + }); + } + }); + }; +}; diff --git a/src/HOC.js b/src/HOC.js new file mode 100644 index 0000000..acbffa9 --- /dev/null +++ b/src/HOC.js @@ -0,0 +1,26 @@ +var React = global.React || require('react'); +var Mixin = require('./Mixin.js'); +module.exports = function (Component) { + return React.createClass({ + mixins: [Mixin], + render: function () { + return React.createElement(Component, { + setValidations: this.setValidations, + setValue: this.setValue, + resetValue: this.resetValue, + getValue: this.getValue, + hasValue: this.hasValue, + getErrorMessage: this.getErrorMessage, + getErrorMessages: this.getErrorMessages, + isFormDisabled: this.isFormDisabled, + isValid: this.isValid, + isPristine: this.isPristine, + isFormSubmitted: this.isFormSubmitted, + isRequired: this.isRequired, + showRequired: this.showRequired, + showError: this.showError, + isValidValue: this.isValidValue + }); + } + }); +}; diff --git a/src/Mixin.js b/src/Mixin.js index a6ae2ee..d615fc7 100644 --- a/src/Mixin.js +++ b/src/Mixin.js @@ -1,4 +1,5 @@ var utils = require('./utils.js'); +var React = global.React || require('react'); var convertValidationsToObject = function (validations) { @@ -37,11 +38,14 @@ module.exports = { _isValid: true, _isPristine: true, _pristineValue: this.props.value, - _validationError: '', + _validationError: [], _externalError: null, _formSubmitted: false }; }, + contextTypes: { + formsy: React.PropTypes.object // What about required? + }, getDefaultProps: function () { return { validationError: '', @@ -52,13 +56,17 @@ module.exports = { componentWillMount: function () { var configure = function () { this.setValidations(this.props.validations, this.props.required); - this.props._attachToForm(this); + + // Pass a function instead? + this.context.formsy.attachToForm(this); + //this.props._attachToForm(this); }.bind(this); if (!this.props.name) { throw new Error('Form Input requires a name property when used'); } + /* if (!this.props._attachToForm) { return setTimeout(function () { if (!this.isMounted()) return; @@ -68,12 +76,14 @@ module.exports = { configure(); }.bind(this), 0); } + */ configure(); }, // We have to make the validate method is kept when new props are added componentWillReceiveProps: function (nextProps) { this.setValidations(nextProps.validations, nextProps.required); + }, componentDidUpdate: function (prevProps) { @@ -83,11 +93,17 @@ module.exports = { if (!utils.isSame(this.props.value, prevProps.value)) { this.setValue(this.props.value); } + + // If validations or required is changed, run a new validation + if (!utils.isSame(this.props.validations, prevProps.validations) || !utils.isSame(this.props.required, prevProps.required)) { + this.context.formsy.validate(this); + } }, // Detach it when component unmounts componentWillUnmount: function () { - this.props._detachFromForm(this); + this.context.formsy.detachFromForm(this); + //this.props._detachFromForm(this); }, setValidations: function (validations, required) { @@ -104,7 +120,8 @@ module.exports = { _value: value, _isPristine: false }, function () { - this.props._validate(this); + this.context.formsy.validate(this); + //this.props._validate(this); }.bind(this)); }, resetValue: function () { @@ -112,7 +129,8 @@ module.exports = { _value: this.state._pristineValue, _isPristine: true }, function () { - this.props._validate(this); + this.context.formsy.validate(this); + //this.props._validate(this); }); }, getValue: function () { @@ -122,10 +140,15 @@ module.exports = { return this.state._value !== ''; }, getErrorMessage: function () { - return !this.isValid() || this.showRequired() ? (this.state._externalError || this.state._validationError) : null; + var messages = this.getErrorMessages(); + return messages.length ? messages[0] : null; + }, + getErrorMessages: function () { + return !this.isValid() || this.showRequired() ? (this.state._externalError || this.state._validationError || []) : []; }, isFormDisabled: function () { - return this.props._isFormDisabled(); + return this.context.formsy.isFormDisabled(); + //return this.props._isFormDisabled(); }, isValid: function () { return this.state._isValid; @@ -146,6 +169,7 @@ module.exports = { return !this.showRequired() && !this.isValid(); }, isValidValue: function (value) { - return this.props._isValidValue.call(null, this, value); + return this.context.formsy.isValidValue.call(null, this, value); + //return this.props._isValidValue.call(null, this, value); } }; diff --git a/src/main.js b/src/main.js index fa38284..bf4a3ee 100644 --- a/src/main.js +++ b/src/main.js @@ -1,11 +1,16 @@ var React = global.React || require('react'); var Formsy = {}; var validationRules = require('./validationRules.js'); +var formDataToObject = require('form-data-to-object'); var utils = require('./utils.js'); var Mixin = require('./Mixin.js'); +var HOC = require('./HOC.js'); +var Decorator = require('./Decorator.js'); var options = {}; Formsy.Mixin = Mixin; +Formsy.HOC = HOC; +Formsy.Decorator = Decorator; Formsy.defaults = function (passedOptions) { options = passedOptions; @@ -40,6 +45,23 @@ Formsy.Form = React.createClass({ }; }, + childContextTypes: { + formsy: React.PropTypes.object + }, + getChildContext: function () { + return { + formsy: { + attachToForm: this.attachToForm, + detachFromForm: this.detachFromForm, + validate: this.validate, + isFormDisabled: this.isFormDisabled, + isValidValue: function (component, value) { + return this.runValidation(component, value).isValid; + }.bind(this) + } + } + }, + // Add a map to store the inputs of the form, a model to store // the values of the form and register child inputs componentWillMount: function () { @@ -98,7 +120,7 @@ Formsy.Form = React.createClass({ if (this.props.mapping) { return this.props.mapping(this.model) } else { - return Object.keys(this.model).reduce(function (mappedModel, key) { + return formDataToObject(Object.keys(this.model).reduce(function (mappedModel, key) { var keyArray = key.split('.'); var base = mappedModel; @@ -109,7 +131,7 @@ Formsy.Form = React.createClass({ return mappedModel; - }.bind(this), {}); + }.bind(this), {})); } }, @@ -139,7 +161,7 @@ Formsy.Form = React.createClass({ var component = this.inputs[name]; var args = [{ _isValid: !(name in errors), - _validationError: errors[name] + _validationError: typeof errors[name] === 'string' ? [errors[name]] : errors[name] }]; component.setState.apply(component, args); }.bind(this)); @@ -171,45 +193,12 @@ Formsy.Form = React.createClass({ } var args = [{ _isValid: this.props.preventExternalInvalidation || false, - _externalError: errors[name] + _externalError: typeof errors[name] === 'string' ? [errors[name]] : errors[name] }]; component.setState.apply(component, args); }.bind(this)); }, - // Traverse the children and children of children to find - // all inputs by checking the name prop. Maybe do a better - // check here - traverseChildrenAndRegisterInputs: function (children) { - - if (typeof children !== 'object' || children === null) { - return children; - } - return React.Children.map(children, function (child) { - - if (typeof child !== 'object' || child === null) { - return child; - } - - if (child.props && child.props.name) { - - return React.cloneElement(child, { - _attachToForm: this.attachToForm, - _detachFromForm: this.detachFromForm, - _validate: this.validate, - _isFormDisabled: this.isFormDisabled, - _isValidValue: function (component, value) { - return this.runValidation(component, value).isValid; - }.bind(this) - }, child.props && child.props.children); - } else { - return React.cloneElement(child, {}, this.traverseChildrenAndRegisterInputs(child.props && child.props.children)); - } - - }, this); - - }, - isFormDisabled: function () { return this.props.disabled; }, @@ -288,23 +277,29 @@ Formsy.Form = React.createClass({ error: (function () { if (isValid && !isRequired) { - return ''; + return []; } if (validationResults.errors.length) { - return validationResults.errors[0]; + return validationResults.errors; } if (this.props.validationErrors && this.props.validationErrors[component.props.name]) { - return this.props.validationErrors[component.props.name]; + return typeof this.props.validationErrors[component.props.name] === 'string' ? [this.props.validationErrors[component.props.name]] : this.props.validationErrors[component.props.name]; } if (isRequired) { - return validationErrors[requiredResults.success[0]] || null; + var error = validationErrors[requiredResults.success[0]]; + return error ? [error] : null; } - if (!isValid) { - return validationErrors[validationResults.failed[0]] || validationError; + if (validationResults.failed.length) { + return validationResults.failed.map(function(failed) { + return validationErrors[failed] ? validationErrors[failed] : validationError; + }).filter(function(x, pos, arr) { + // Remove duplicates + return arr.indexOf(x) === pos; + }); } }.call(this)) @@ -438,7 +433,7 @@ Formsy.Form = React.createClass({ return (
- {this.traverseChildrenAndRegisterInputs(this.props.children)} + {this.props.children}
); diff --git a/src/validationRules.js b/src/validationRules.js index 9b5f527..f6c0e95 100644 --- a/src/validationRules.js +++ b/src/validationRules.js @@ -38,16 +38,25 @@ var validations = { if (typeof value === 'number') { return true; } - return validations.matchRegexp(values, value, /^[-+]?(\d*[.])?\d+$/); + return validations.matchRegexp(values, value, /^[-+]?(?:\d*[.])?\d+$/); }, isAlpha: function (values, value) { - return validations.matchRegexp(values, value, /^[a-zA-Z]+$/); + return validations.matchRegexp(values, value, /^[A-Z]+$/i); + }, + isAlphanumeric: function (values, value) { + return validations.matchRegexp(values, value, /^[0-9A-Z]+$/i); + }, + isInt: function (values, value) { + return validations.matchRegexp(values, value, /^(?:[-+]?(?:0|[1-9]\d*))$/); + }, + isFloat: function (values, value) { + return validations.matchRegexp(values, value, /^(?:[-+]?(?:\d+))?(?:\.\d*)?(?:[eE][\+\-]?(?:\d+))?$/); }, isWords: function (values, value) { - return validations.matchRegexp(values, value, /^[a-zA-Z\s]+$/); + return validations.matchRegexp(values, value, /^[A-Z\s]+$/i); }, isSpecialWords: function (values, value) { - return validations.matchRegexp(values, value, /^[a-zA-Z\s\u00C0-\u017F]+$/); + return validations.matchRegexp(values, value, /^[A-Z\s\u00C0-\u017F]+$/i); }, isLength: function (values, value, length) { return !isExisty(value) || isEmpty(value) || value.length === length; diff --git a/testrunner.js b/testrunner.js index 48f4251..6936e57 100644 --- a/testrunner.js +++ b/testrunner.js @@ -1,25 +1,22 @@ -require('babel/register'); - -var path = require('path'); -var jsdom = require('jsdom').jsdom; -var jasmine = require('jasmine-node'); +import path from 'path'; +import testrunner from 'nodeunit/lib/reporters/default.js'; +import {jsdom} from 'jsdom'; global.document = jsdom(); global.window = document.defaultView; global.navigator = global.window.navigator; -var jasmineOptions = { - specFolders: [path.resolve(__dirname, 'specs')], - //onComplete: onComplete, - isVerbose: true, - showColors: true, - teamcity: false, - useRequireJs: false, - regExpSpec: /spec\.jsx$/, - junitreport: true, - includeStackTrace: true, - //coffee: options.coffee, - //growl: options.growl -}; - -jasmine.executeSpecsInFolder(jasmineOptions); +testrunner.run(['tests'], { + "error_prefix": "\u001B[31m", + "error_suffix": "\u001B[39m", + "ok_prefix": "\u001B[32m", + "ok_suffix": "\u001B[39m", + "bold_prefix": "\u001B[1m", + "bold_suffix": "\u001B[22m", + "assertion_prefix": "\u001B[35m", + "assertion_suffix": "\u001B[39m" +}, function(err) { + if (err) { + process.exit(1); + } +}); diff --git a/tests/Element-spec.js b/tests/Element-spec.js new file mode 100644 index 0000000..90be589 --- /dev/null +++ b/tests/Element-spec.js @@ -0,0 +1,519 @@ +import React from 'react'; +import TestUtils from 'react-addons-test-utils'; + +import Formsy from './..'; +import TestInput, { InputFactory } from './utils/TestInput'; +import immediate from './utils/immediate'; + +export default { + + 'should return passed and setValue() value when using getValue()': function (test) { + + const form = TestUtils.renderIntoDocument( + + + + ); + + const input = TestUtils.findRenderedDOMComponentWithTag(form, 'INPUT'); + test.equal(input.value, 'foo'); + TestUtils.Simulate.change(input, {target: {value: 'foobar'}}); + test.equal(input.value, 'foobar'); + + test.done(); + + }, + + 'should set back to pristine value when running reset': function (test) { + + let reset = null; + const Input = InputFactory({ + componentDidMount() { + reset = this.resetValue; + } + }); + const form = TestUtils.renderIntoDocument( + + + + ); + + const input = TestUtils.findRenderedDOMComponentWithTag(form, 'INPUT'); + TestUtils.Simulate.change(input, {target: {value: 'foobar'}}); + reset(); + test.equal(input.value, 'foo'); + + test.done(); + + }, + + 'should return error message passed when calling getErrorMessage()': function (test) { + + let getErrorMessage = null; + const Input = InputFactory({ + componentDidMount() { + getErrorMessage = this.getErrorMessage; + } + }); + TestUtils.renderIntoDocument( + + + + ); + + test.equal(getErrorMessage(), 'Has to be email'); + + test.done(); + + }, + + 'should return true or false when calling isValid() depending on valid state': function (test) { + + let isValid = null; + const Input = InputFactory({ + componentDidMount() { + isValid = this.isValid; + } + }); + const form = TestUtils.renderIntoDocument( + + + + ); + + test.equal(isValid(), false); + const input = TestUtils.findRenderedDOMComponentWithTag(form, 'INPUT'); + TestUtils.Simulate.change(input, {target: {value: 'foo@foo.com'}}); + test.equal(isValid(), true); + + test.done(); + + }, + + 'should return true or false when calling isRequired() depending on passed required attribute': function (test) { + + const isRequireds = []; + const Input = InputFactory({ + componentDidMount() { + isRequireds.push(this.isRequired); + } + }); + TestUtils.renderIntoDocument( + + + + + + ); + + test.equal(isRequireds[0](), false); + test.equal(isRequireds[1](), true); + test.equal(isRequireds[2](), true); + + test.done(); + + }, + + 'should return true or false when calling showRequired() depending on input being empty and required is passed, or not': function (test) { + + const showRequireds = []; + const Input = InputFactory({ + componentDidMount() { + showRequireds.push(this.showRequired); + } + }); + TestUtils.renderIntoDocument( + + + + + + ); + + test.equal(showRequireds[0](), false); + test.equal(showRequireds[1](), true); + test.equal(showRequireds[2](), false); + + test.done(); + + }, + + 'should return true or false when calling isPristine() depending on input has been "touched" or not': function (test) { + + let isPristine = null; + const Input = InputFactory({ + componentDidMount() { + isPristine = this.isPristine; + } + }); + const form = TestUtils.renderIntoDocument( + + + + ); + + test.equal(isPristine(), true); + const input = TestUtils.findRenderedDOMComponentWithTag(form, 'INPUT'); + TestUtils.Simulate.change(input, {target: {value: 'foo'}}); + test.equal(isPristine(), false); + + test.done(); + + }, + + 'should allow an undefined value to be updated to a value': function (test) { + + const TestForm = React.createClass({ + getInitialState() { + return {value: undefined}; + }, + changeValue() { + this.setState({ + value: 'foo' + }); + }, + render() { + return ( + + + + ); + } + }); + const form = TestUtils.renderIntoDocument(); + + form.changeValue(); + const input = TestUtils.findRenderedDOMComponentWithTag(form, 'INPUT'); + immediate(() => { + test.equal(input.value, 'foo'); + test.done(); + }); + + }, + + 'should be able to test a values validity': function (test) { + + const TestForm = React.createClass({ + render() { + return ( + + + + ); + } + }); + const form = TestUtils.renderIntoDocument(); + + const input = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(input.isValidValue('foo@bar.com'), true); + test.equal(input.isValidValue('foo@bar'), false); + test.done(); + + }, + + 'should be able to use an object as validations property': function (test) { + + const TestForm = React.createClass({ + render() { + return ( + + + + ); + } + }); + const form = TestUtils.renderIntoDocument(); + + const input = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(input.isValidValue('foo@bar.com'), true); + test.equal(input.isValidValue('foo@bar'), false); + + test.done(); + + }, + + 'should be able to pass complex values to a validation rule': function (test) { + + const TestForm = React.createClass({ + render() { + return ( + + + + ); + } + }); + const form = TestUtils.renderIntoDocument(); + + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), true); + const input = TestUtils.findRenderedDOMComponentWithTag(form, 'INPUT'); + TestUtils.Simulate.change(input, {target: {value: 'bar'}}); + test.equal(inputComponent.isValid(), false); + + test.done(); + + }, + + 'should be able to run a function to validate': function (test) { + + const TestForm = React.createClass({ + customValidationA(values, value) { + return value === 'foo'; + }, + customValidationB(values, value) { + return value === 'foo' && values.A === 'foo'; + }, + render() { + return ( + + + + + ); + } + }); + const form = TestUtils.renderIntoDocument(); + + const inputComponent = TestUtils.scryRenderedComponentsWithType(form, TestInput); + test.equal(inputComponent[0].isValid(), true); + test.equal(inputComponent[1].isValid(), true); + const input = TestUtils.scryRenderedDOMComponentsWithTag(form, 'INPUT'); + TestUtils.Simulate.change(input[0], {target: {value: 'bar'}}); + test.equal(inputComponent[0].isValid(), false); + test.equal(inputComponent[1].isValid(), false); + + test.done(); + + }, + + 'should override all error messages with error messages passed by form': function (test) { + + const TestForm = React.createClass({ + render() { + return ( + + + + ); + } + }); + const form = TestUtils.renderIntoDocument(); + + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.getErrorMessage(), 'bar'); + + test.done(); + + }, + + 'should override validation rules with required rules': function (test) { + + const TestForm = React.createClass({ + render() { + return ( + + + + ); + } + }); + const form = TestUtils.renderIntoDocument(); + + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.getErrorMessage(), 'bar3'); + + test.done(); + + }, + + 'should fall back to default error message when non exist in validationErrors map': function (test) { + + const TestForm = React.createClass({ + render() { + return ( + + + + ); + } + }); + const form = TestUtils.renderIntoDocument(); + + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.getErrorMessage(), 'bar'); + + test.done(); + + }, + + 'should not be valid if it is required and required rule is true': function (test) { + + const TestForm = React.createClass({ + render() { + return ( + + + + ); + } + }); + const form = TestUtils.renderIntoDocument(); + + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), false); + + test.done(); + + }, + + 'should handle objects and arrays as values': function (test) { + + const TestForm = React.createClass({ + getInitialState() { + return { + foo: {foo: 'bar'}, + bar: ['foo'] + }; + }, + render() { + return ( + + + + + ); + } + }); + const form = TestUtils.renderIntoDocument(); + + form.setState({ + foo: {foo: 'foo'}, + bar: ['bar'] + }); + + const inputs = TestUtils.scryRenderedComponentsWithType(form, TestInput); + test.deepEqual(inputs[0].getValue(), {foo: 'foo'}); + test.deepEqual(inputs[1].getValue(), ['bar']); + + test.done(); + + }, + + 'should handle isFormDisabled with dynamic inputs': function (test) { + + const TestForm = React.createClass({ + getInitialState() { + return { + bool: true + }; + }, + flip() { + this.setState({ + bool: !this.state.bool + }); + }, + render() { + return ( + + {this.state.bool ? + : + + } + + ); + } + }); + const form = TestUtils.renderIntoDocument(); + + const input = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(input.isFormDisabled(), true); + form.flip(); + test.equal(input.isFormDisabled(), false); + + test.done(); + + }, + + 'should allow for dot notation in name which maps to a deep object': function (test) { + + const TestForm = React.createClass({ + onSubmit(model) { + test.deepEqual(model, {foo: {bar: 'foo', test: 'test'}}); + }, + render() { + return ( + + + + + ); + } + }); + const form = TestUtils.renderIntoDocument(); + + test.expect(1); + + const formEl = TestUtils.findRenderedDOMComponentWithTag(form, 'form'); + TestUtils.Simulate.submit(formEl); + + test.done(); + + }, + + 'should allow for application/x-www-form-urlencoded syntax and convert to object': function (test) { + + const TestForm = React.createClass({ + onSubmit(model) { + test.deepEqual(model, {foo: ['foo', 'bar']}); + }, + render() { + return ( + + + + + ); + } + }); + const form = TestUtils.renderIntoDocument(); + + test.expect(1); + + const formEl = TestUtils.findRenderedDOMComponentWithTag(form, 'form'); + TestUtils.Simulate.submit(formEl); + + test.done(); + + } + +}; diff --git a/tests/Formsy-spec.js b/tests/Formsy-spec.js new file mode 100755 index 0000000..1a52e5b --- /dev/null +++ b/tests/Formsy-spec.js @@ -0,0 +1,706 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; +import TestUtils from 'react-addons-test-utils'; + +import Formsy from './..'; +import TestInput from './utils/TestInput'; +import immediate from './utils/immediate'; +import sinon from 'sinon'; + +export default { + + 'Setting up a form': { + + 'should render a form into the document': function (test) { + + const form = TestUtils.renderIntoDocument(); + test.equal(ReactDOM.findDOMNode(form).tagName, 'FORM'); + + test.done(); + + }, + + 'should set a class name if passed': function (test) { + + const form = TestUtils.renderIntoDocument( ); + test.equal(ReactDOM.findDOMNode(form).className, 'foo'); + + test.done(); + + }, + + 'should allow for null/undefined children': function (test) { + + let model = null; + const TestForm = React.createClass({ + render() { + return ( + (model = formModel)}> +

Test

+ { null } + { undefined } + +
+ ); + } + }); + + const form = TestUtils.renderIntoDocument(); + immediate(() => { + TestUtils.Simulate.submit(ReactDOM.findDOMNode(form)); + test.deepEqual(model, {name: 'foo'}); + test.done(); + }); + + }, + + 'should allow for inputs being added dynamically': function (test) { + + const inputs = []; + let forceUpdate = null; + let model = null; + const TestForm = React.createClass({ + componentWillMount() { + forceUpdate = this.forceUpdate.bind(this); + }, + render() { + return ( + (model = formModel)}> + {inputs} + ); + } + }); + const form = TestUtils.renderIntoDocument(); + + // Wait before adding the input + setTimeout(() => { + inputs.push(); + + forceUpdate(() => { + // Wait for next event loop, as that does the form + immediate(() => { + TestUtils.Simulate.submit(ReactDOM.findDOMNode(form)); + test.ok('test' in model); + test.done(); + }); + + }); + + }, 10); + + }, + + 'should allow dynamically added inputs to update the form-model': function (test) { + + const inputs = []; + let forceUpdate = null; + let model = null; + const TestForm = React.createClass({ + componentWillMount() { + forceUpdate = this.forceUpdate.bind(this); + }, + render() { + return ( + (model = formModel)}> + {inputs} + ); + } + }); + const form = TestUtils.renderIntoDocument(); + + // Wait before adding the input + immediate(() => { + inputs.push(); + + forceUpdate(() => { + + // Wait for next event loop, as that does the form + immediate(() => { + TestUtils.Simulate.change(TestUtils.findRenderedDOMComponentWithTag(form, 'INPUT'), {target: {value: 'foo'}}); + TestUtils.Simulate.submit(ReactDOM.findDOMNode(form)); + test.equal(model.test, 'foo'); + test.done(); + }); + + }); + + }); + + }, + + 'should allow a dynamically updated input to update the form-model': function (test) { + + let forceUpdate = null; + let model = null; + + const TestForm = React.createClass({ + componentWillMount() { + forceUpdate = this.forceUpdate.bind(this); + }, + render() { + const input = ; + + return ( + (model = formModel)}> + {input} + ); + } + }); + let form = TestUtils.renderIntoDocument(); + + // Wait before changing the input + immediate(() => { + form = TestUtils.renderIntoDocument(); + + forceUpdate(() => { + // Wait for next event loop, as that does the form + immediate(() => { + TestUtils.Simulate.submit(ReactDOM.findDOMNode(form)); + test.equal(model.test, 'bar'); + test.done(); + }); + + }); + + }); + + } + + }, + + 'validations': { + + 'should run when the input changes': function (test) { + + const runRule = sinon.spy(); + const notRunRule = sinon.spy(); + + Formsy.addValidationRule('runRule', runRule); + Formsy.addValidationRule('notRunRule', notRunRule); + + const form = TestUtils.renderIntoDocument( + + + + ); + + const input = TestUtils.findRenderedDOMComponentWithTag(form, 'input'); + TestUtils.Simulate.change(ReactDOM.findDOMNode(input), {target: {value: 'bar'}}); + test.equal(runRule.calledWith({one: 'bar'}, 'bar', true), true); + test.equal(notRunRule.called, false); + + test.done(); + + }, + + 'should allow the validation to be changed': function (test) { + + const ruleA = sinon.spy(); + const ruleB = sinon.spy(); + Formsy.addValidationRule('ruleA', ruleA); + Formsy.addValidationRule('ruleB', ruleB); + + class TestForm extends React.Component { + constructor(props) { + super(props); + this.state = {rule: 'ruleA'}; + } + changeRule() { + this.setState({ + rule: 'ruleB' + }); + } + render() { + return ( + + + + ); + } + } + + const form = TestUtils.renderIntoDocument(); + form.changeRule(); + const input = TestUtils.findRenderedDOMComponentWithTag(form, 'input'); + TestUtils.Simulate.change(ReactDOM.findDOMNode(input), {target: {value: 'bar'}}); + test.equal(ruleB.calledWith({one: 'bar'}, 'bar', true), true); + + test.done(); + + }, + + 'should invalidate a form if dynamically inserted input is invalid': function (test) { + + const isInValidSpy = sinon.spy(); + + class TestForm extends React.Component { + constructor(props) { + super(props); + this.state = {showSecondInput: false}; + } + addInput() { + this.setState({ + showSecondInput: true + }); + } + render() { + return ( + + + { + this.state.showSecondInput ? + + : + null + } + + ); + } + } + + const form = TestUtils.renderIntoDocument(); + + test.equal(form.refs.formsy.state.isValid, true); + form.addInput(); + + immediate(() => { + test.equal(isInValidSpy.called, true); + test.done(); + }); + + }, + + 'should validate a form when removing an invalid input': function (test) { + + const isValidSpy = sinon.spy(); + + class TestForm extends React.Component { + constructor(props) { + super(props); + this.state = {showSecondInput: true}; + } + removeInput() { + this.setState({ + showSecondInput: false + }); + } + render() { + return ( + + + { + this.state.showSecondInput ? + + : + null + } + + ); + } + } + + const form = TestUtils.renderIntoDocument(); + + test.equal(form.refs.formsy.state.isValid, false); + form.removeInput(); + + immediate(() => { + test.equal(isValidSpy.called, true); + test.done(); + }); + + + }, + + 'runs multiple validations': function (test) { + + const ruleA = sinon.spy(); + const ruleB = sinon.spy(); + Formsy.addValidationRule('ruleA', ruleA); + Formsy.addValidationRule('ruleB', ruleB); + + const form = TestUtils.renderIntoDocument( + + + + ); + + const input = TestUtils.findRenderedDOMComponentWithTag(form, 'input'); + TestUtils.Simulate.change(ReactDOM.findDOMNode(input), {target: {value: 'bar'}}); + test.equal(ruleA.calledWith({one: 'bar'}, 'bar', true), true); + test.equal(ruleB.calledWith({one: 'bar'}, 'bar', true), true); + test.done(); + + } + + }, + + 'should not trigger onChange when form is mounted': function (test) { + + + const hasChanged = sinon.spy(); + const TestForm = React.createClass({ + render() { + return ; + } + }); + TestUtils.renderIntoDocument(); + test.equal(hasChanged.called, false); + test.done(); + + }, + + 'should trigger onChange when form element is changed': function (test) { + + const hasChanged = sinon.spy(); + const form = TestUtils.renderIntoDocument( + + + + ); + TestUtils.Simulate.change(TestUtils.findRenderedDOMComponentWithTag(form, 'INPUT'), {target: {value: 'bar'}}); + test.equal(hasChanged.called, true); + test.done(); + + }, + + 'should trigger onChange when new input is added to form': function (test) { + + const hasChanged = sinon.spy(); + const TestForm = React.createClass({ + getInitialState() { + return { + showInput: false + }; + }, + addInput() { + this.setState({ + showInput: true + }) + }, + render() { + return ( + + { + this.state.showInput ? + + : + null + } + ); + } + }); + + const form = TestUtils.renderIntoDocument(); + form.addInput(); + immediate(() => { + test.equal(hasChanged.called, true); + test.done(); + }); + + }, + + 'Update a form': { + + 'should allow elements to check if the form is disabled': function (test) { + + const TestForm = React.createClass({ + getInitialState() { return { disabled: true }; }, + enableForm() { this.setState({ disabled: false }); }, + render() { + return ( + + + ); + } + }); + + const form = TestUtils.renderIntoDocument(); + const input = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(input.isFormDisabled(), true); + + form.enableForm(); + immediate(() => { + test.equal(input.isFormDisabled(), false); + test.done(); + }); + + }, + + 'should be possible to pass error state of elements by changing an errors attribute': function (test) { + + const TestForm = React.createClass({ + getInitialState() { return { validationErrors: { foo: 'bar' } }; }, + onChange(values) { + this.setState(values.foo ? { validationErrors: {} } : { validationErrors: {foo: 'bar'} }); + }, + render() { + return ( + + + ); + } + }); + const form = TestUtils.renderIntoDocument(); + + // Wait for update + immediate(() => { + const input = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(input.getErrorMessage(), 'bar'); + input.setValue('gotValue'); + + // Wait for update + immediate(() => { + test.equal(input.getErrorMessage(), null); + test.done(); + }); + }); + + }, + + 'should trigger an onValidSubmit when submitting a valid form': function (test) { + + let isCalled = sinon.spy(); + const TestForm = React.createClass({ + render() { + return ( + + + ); + } + }); + const form = TestUtils.renderIntoDocument(); + const FoundForm = TestUtils.findRenderedComponentWithType(form, TestForm); + TestUtils.Simulate.submit(ReactDOM.findDOMNode(FoundForm)); + test.equal(isCalled.called,true); + test.done(); + + }, + + 'should trigger an onInvalidSubmit when submitting an invalid form': function (test) { + + let isCalled = sinon.spy(); + const TestForm = React.createClass({ + render() { + return ( + + + ); + } + }); + const form = TestUtils.renderIntoDocument(); + + const FoundForm = TestUtils.findRenderedComponentWithType(form, TestForm); + TestUtils.Simulate.submit(ReactDOM.findDOMNode(FoundForm)); + test.equal(isCalled.called, true); + + test.done(); + + } + + }, + + 'value === false': { + + 'should call onSubmit correctly': function (test) { + + const onSubmit = sinon.spy(); + const TestForm = React.createClass({ + render() { + return ( + + + + + ); + } + }); + + const form = TestUtils.renderIntoDocument(); + TestUtils.Simulate.submit(ReactDOM.findDOMNode(form)); + test.equal(onSubmit.calledWith({foo: false}), true); + test.done(); + + }, + + 'should allow dynamic changes to false': function (test) { + + const onSubmit = sinon.spy(); + const TestForm = React.createClass({ + getInitialState() { + return { + value: true + }; + }, + changeValue() { + this.setState({ + value: false + }); + }, + render() { + return ( + + + + + ); + } + }); + + const form = TestUtils.renderIntoDocument(); + form.changeValue(); + TestUtils.Simulate.submit(ReactDOM.findDOMNode(form)); + test.equal(onSubmit.calledWith({foo: false}), true); + test.done(); + + }, + + 'should say the form is submitted': function (test) { + + const TestForm = React.createClass({ + render() { + return ( + + + + + ); + } + }); + const form = TestUtils.renderIntoDocument(); + const input = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(input.isFormSubmitted(), false); + TestUtils.Simulate.submit(ReactDOM.findDOMNode(form)); + test.equal(input.isFormSubmitted(), true); + test.done(); + + }, + + 'should be able to reset the form to its pristine state': function (test) { + + const TestForm = React.createClass({ + getInitialState() { + return { + value: true + }; + }, + changeValue() { + this.setState({ + value: false + }); + }, + render() { + return ( + + + + + ); + } + }); + const form = TestUtils.renderIntoDocument(); + const input = TestUtils.findRenderedComponentWithType(form, TestInput); + const formsyForm = TestUtils.findRenderedComponentWithType(form, Formsy.Form); + test.equal(input.getValue(), true); + form.changeValue(); + test.equal(input.getValue(), false); + formsyForm.reset(); + test.equal(input.getValue(), true); + + test.done(); + + }, + + 'should be able to reset the form using custom data': function (test) { + + const TestForm = React.createClass({ + getInitialState() { + return { + value: true + }; + }, + changeValue() { + this.setState({ + value: false + }); + }, + render() { + return ( + + + + + ); + } + }); + const form = TestUtils.renderIntoDocument(); + const input = TestUtils.findRenderedComponentWithType(form, TestInput); + const formsyForm = TestUtils.findRenderedComponentWithType(form, Formsy.Form); + + test.equal(input.getValue(), true); + form.changeValue(); + test.equal(input.getValue(), false); + formsyForm.reset({ + foo: 'bar' + }); + test.equal(input.getValue(), 'bar'); + test.done(); + + } + + }, + + '.isChanged()': { + + 'initially returns false': function (test) { + + const hasOnChanged = sinon.spy(); + const form = TestUtils.renderIntoDocument( + + + + ); + test.equal(form.isChanged(), false); + test.equal(hasOnChanged.called, false); + test.done(); + + }, + + 'returns true when changed': function (test) { + + const hasOnChanged = sinon.spy(); + const form = TestUtils.renderIntoDocument( + + + + ); + const input = TestUtils.findRenderedDOMComponentWithTag(form, 'input'); + TestUtils.Simulate.change(ReactDOM.findDOMNode(input), {target: {value: 'bar'}}); + test.equal(form.isChanged(), true); + test.equal(hasOnChanged.calledWith({one: 'bar'}), true); + test.done(); + + }, + + 'returns false if changes are undone': function (test) { + + const hasOnChanged = sinon.spy(); + const form = TestUtils.renderIntoDocument( + + + + ); + const input = TestUtils.findRenderedDOMComponentWithTag(form, 'input'); + TestUtils.Simulate.change(ReactDOM.findDOMNode(input), {target: {value: 'bar'}}); + test.equal(hasOnChanged.calledWith({one: 'bar'}, true), true); + + TestUtils.Simulate.change(ReactDOM.findDOMNode(input), {target: {value: 'foo'}}); + test.equal(form.isChanged(), false); + test.equal(hasOnChanged.calledWith({one: 'foo'}, false), true); + test.done(); + + } + + } + +}; diff --git a/tests/Rules-equals-spec.js b/tests/Rules-equals-spec.js new file mode 100644 index 0000000..dc13ad0 --- /dev/null +++ b/tests/Rules-equals-spec.js @@ -0,0 +1,80 @@ +import React from 'react'; +import TestUtils from 'react-addons-test-utils'; +import immediate from './utils/immediate'; + +import Formsy from './..'; +import { InputFactory } from './utils/TestInput'; + +const TestInput = InputFactory({ + render() { + return ; + } +}); + +const TestForm = React.createClass({ + render() { + return ( + + + + ); + } +}); + +export default { + + 'should pass when the value is equal': function (test) { + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), true); + test.done(); + + }, + + 'should fail when the value is not equal': function (test) { + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), false); + test.done(); + + }, + + 'should pass with an empty string': function (test) { + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), true); + test.done(); + + }, + + 'should pass with an undefined': function (test) { + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), true); + test.done(); + + }, + + 'should pass with a null': function (test) { + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), true); + test.done(); + + }, + + 'should fail with a number': function (test) { + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), false); + test.done(); + + } + +}; diff --git a/tests/Rules-isAlpha-spec.js b/tests/Rules-isAlpha-spec.js new file mode 100644 index 0000000..cfbec5f --- /dev/null +++ b/tests/Rules-isAlpha-spec.js @@ -0,0 +1,88 @@ +import React from 'react'; +import TestUtils from 'react-addons-test-utils'; + +import Formsy from './..'; +import { InputFactory } from './utils/TestInput'; + +const TestInput = InputFactory({ + render() { + return ; + } +}); + +const TestForm = React.createClass({ + render() { + return ( + + + + ); + } +}); + +export default { + + 'should pass with a default value': function (test) { + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), true); + test.done(); + + }, + + 'should pass with a string is only latin letters': function (test) { + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), true); + test.done(); + + }, + + 'should fail with a string with numbers': function (test) { + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), false); + test.done(); + + }, + + 'should pass with an undefined': function (test) { + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), true); + test.done(); + + }, + + 'should pass with a null': function (test) { + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), true); + test.done(); + + }, + + 'should pass with an empty string': function (test) { + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), true); + test.done(); + + }, + + 'should fail with a number': function (test) { + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), false); + test.done(); + + } + +}; diff --git a/tests/Rules-isAlphanumeric-spec.js b/tests/Rules-isAlphanumeric-spec.js new file mode 100644 index 0000000..5ae5e40 --- /dev/null +++ b/tests/Rules-isAlphanumeric-spec.js @@ -0,0 +1,98 @@ +import React from 'react'; +import TestUtils from 'react-addons-test-utils'; + +import Formsy from './..'; +import { InputFactory } from './utils/TestInput'; + +const TestInput = InputFactory({ + render() { + return ; + } +}); + +const TestForm = React.createClass({ + render() { + return ( + + + + ); + } +}); + +export default { + + 'should pass with a default value': function (test) { + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), true); + test.done(); + + }, + + 'should pass with a string is only latin letters': function (test) { + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), true); + test.done(); + + }, + + 'should fail with a string with numbers': function (test) { + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), true); + test.done(); + + }, + + 'should pass with an undefined': function (test) { + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), true); + test.done(); + + }, + + 'should pass with a null': function (test) { + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), true); + test.done(); + + }, + + 'should pass with an empty string': function (test) { + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), true); + test.done(); + + }, + + 'should pass with a number': function (test) { + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), true); + test.done(); + + }, + + 'should fail with a non alpha and number symbols': function (test) { + + const value = '!@#$%^&*()'; + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), false); + test.done(); + + } + +}; diff --git a/tests/Rules-isEmail-spec.js b/tests/Rules-isEmail-spec.js new file mode 100644 index 0000000..4696e2a --- /dev/null +++ b/tests/Rules-isEmail-spec.js @@ -0,0 +1,88 @@ +import React from 'react'; +import TestUtils from 'react-addons-test-utils'; + +import Formsy from './..'; +import { InputFactory } from './utils/TestInput'; + +const TestInput = InputFactory({ + render() { + return ; + } +}); + +const TestForm = React.createClass({ + render() { + return ( + + + + ); + } +}); + +export default { + + 'should pass with a default value': function (test) { + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), true); + test.done(); + + }, + + 'should fail with "foo"': function (test) { + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), false); + test.done(); + + }, + + 'should pass with "foo@foo.com"': function (test) { + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), true); + test.done(); + + }, + + 'should pass with an undefined': function (test) { + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), true); + test.done(); + + }, + + 'should pass with a null': function (test) { + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), true); + test.done(); + + }, + + 'should pass with an empty string': function (test) { + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), true); + test.done(); + + }, + + 'should fail with a number': function (test) { + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), false); + test.done(); + + } + +}; diff --git a/tests/Rules-isEmptyString-spec.js b/tests/Rules-isEmptyString-spec.js new file mode 100644 index 0000000..a14683f --- /dev/null +++ b/tests/Rules-isEmptyString-spec.js @@ -0,0 +1,88 @@ +import React from 'react'; +import TestUtils from 'react-addons-test-utils'; + +import Formsy from './..'; +import { InputFactory } from './utils/TestInput'; + +const TestInput = InputFactory({ + render() { + return ; + } +}); + +const TestForm = React.createClass({ + render() { + return ( + + + + ); + } +}); + +export default { + + 'should pass with a default value': function (test) { + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), false); + test.done(); + + }, + + 'should fail with non-empty string': function (test) { + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), false); + test.done(); + + }, + + 'should pass with an empty string': function (test) { + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), true); + test.done(); + + }, + + 'should fail with undefined': function (test) { + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), false); + test.done(); + + }, + + 'should fail with null': function (test) { + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), false); + test.done(); + + }, + + 'should fail with a number': function (test) { + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), false); + test.done(); + + }, + + 'should fail with a zero': function (test) { + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), false); + test.done(); + + } + +}; diff --git a/tests/Rules-isExisty-spec.js b/tests/Rules-isExisty-spec.js new file mode 100644 index 0000000..76ea729 --- /dev/null +++ b/tests/Rules-isExisty-spec.js @@ -0,0 +1,88 @@ +import React from 'react'; +import TestUtils from 'react-addons-test-utils'; + +import Formsy from './..'; +import { InputFactory } from './utils/TestInput'; + +const TestInput = InputFactory({ + render() { + return ; + } +}); + +const TestForm = React.createClass({ + render() { + return ( + + + + ); + } +}); + +export default { + + 'should pass with a default value': function (test) { + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), false); + test.done(); + + }, + + 'should pass with a string': function (test) { + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), true); + test.done(); + + }, + + 'should pass with an empty string': function (test) { + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), true); + test.done(); + + }, + + 'should fail with undefined': function (test) { + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), false); + test.done(); + + }, + + 'should fail with null': function (test) { + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), false); + test.done(); + + }, + + 'should pass with a number': function (test) { + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), true); + test.done(); + + }, + + 'should pass with a zero': function (test) { + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), true); + test.done(); + + } + +}; diff --git a/tests/Rules-isFloat-spec.js b/tests/Rules-isFloat-spec.js new file mode 100644 index 0000000..847c1f7 --- /dev/null +++ b/tests/Rules-isFloat-spec.js @@ -0,0 +1,125 @@ +import React from 'react'; +import TestUtils from 'react-addons-test-utils'; + +import Formsy from './..'; +import { InputFactory } from './utils/TestInput'; + +const TestInput = InputFactory({ + render() { + return ; + } +}); + +const TestForm = React.createClass({ + render() { + return ( + + + + ); + } +}); + +export default { + + 'should pass with a default value': function (test) { + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), true); + test.done(); + + }, + + 'should pass with an empty string': function (test) { + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), true); + test.done(); + + }, + + 'should fail with a string': function (test) { + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), false); + test.done(); + + }, + + 'should pass with a number as string': function (test) { + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), true); + test.done(); + + }, + + 'should fail string with digits': function (test) { + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), false); + test.done(); + + }, + + 'should pass with an int': function (test) { + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), true); + test.done(); + + }, + + 'should pass with a float': function (test) { + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), true); + test.done(); + + }, + + 'should pass with a float in science notation': function (test) { + + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), true); + test.done(); + + }, + + 'should pass with undefined': function (test) { + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), true); + test.done(); + + }, + + 'should pass with null': function (test) { + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), true); + test.done(); + + }, + + 'should pass with a zero': function (test) { + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), true); + test.done(); + + } + +}; diff --git a/tests/Rules-isInt-spec.js b/tests/Rules-isInt-spec.js new file mode 100644 index 0000000..84cbac7 --- /dev/null +++ b/tests/Rules-isInt-spec.js @@ -0,0 +1,125 @@ +import React from 'react'; +import TestUtils from 'react-addons-test-utils'; + +import Formsy from './..'; +import { InputFactory } from './utils/TestInput'; + +const TestInput = InputFactory({ + render() { + return ; + } +}); + +const TestForm = React.createClass({ + render() { + return ( + + + + ); + } +}); + +export default { + + 'should pass with a default value': function (test) { + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), true); + test.done(); + + }, + + 'should pass with an empty string': function (test) { + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), true); + test.done(); + + }, + + 'should fail with a string': function (test) { + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), false); + test.done(); + + }, + + 'should pass with a number as string': function (test) { + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), true); + test.done(); + + }, + + 'should fail string with digits': function (test) { + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), false); + test.done(); + + }, + + 'should pass with an int': function (test) { + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), true); + test.done(); + + }, + + 'should fail with a float': function (test) { + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), false); + test.done(); + + }, + + 'should fail with a float in science notation': function (test) { + + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), false); + test.done(); + + }, + + 'should pass with undefined': function (test) { + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), true); + test.done(); + + }, + + 'should pass with null': function (test) { + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), true); + test.done(); + + }, + + 'should pass with a zero': function (test) { + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), true); + test.done(); + + } + +}; diff --git a/tests/Rules-isLength-spec.js b/tests/Rules-isLength-spec.js new file mode 100644 index 0000000..74ae2ef --- /dev/null +++ b/tests/Rules-isLength-spec.js @@ -0,0 +1,177 @@ +import React from 'react'; +import TestUtils from 'react-addons-test-utils'; + +import Formsy from './..'; +import { InputFactory } from './utils/TestInput'; + +const TestInput = InputFactory({ + render() { + return ; + } +}); + +const TestForm = React.createClass({ + render() { + return ( + + + + ); + } +}); + +export default { + + 'isLength:3': { + + 'should pass with a default value': function (test) { + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), true); + test.done(); + + }, + + 'should fail with a string too small': function (test) { + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), false); + test.done(); + + }, + + 'should fail with a string too long': function (test) { + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), false); + test.done(); + + }, + + 'should pass with matching length': function (test) { + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), true); + test.done(); + + }, + + 'should pass with undefined': function (test) { + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), true); + test.done(); + + }, + + 'should pass with null': function (test) { + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), true); + test.done(); + + }, + + 'should pass with empty string': function (test) { + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), true); + test.done(); + + }, + + 'should fail with a number': function (test) { + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), false); + test.done(); + + } + + }, + + 'isLength:0': { + + 'should pass with a default value': function (test) { + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), true); + test.done(); + + }, + + 'should fail with a string too small': function (test) { + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), false); + test.done(); + + }, + + 'should fail with a string too long': function (test) { + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), false); + test.done(); + + }, + + 'should pass with matching length': function (test) { + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), true); + test.done(); + + }, + + 'should pass with undefined': function (test) { + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), true); + test.done(); + + }, + + 'should pass with null': function (test) { + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), true); + test.done(); + + }, + + 'should pass with empty string': function (test) { + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), true); + test.done(); + + }, + + 'should fail with a number': function (test) { + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), false); + test.done(); + + } + + } + +}; diff --git a/tests/Rules-isNumeric-spec.js b/tests/Rules-isNumeric-spec.js new file mode 100644 index 0000000..87b1537 --- /dev/null +++ b/tests/Rules-isNumeric-spec.js @@ -0,0 +1,124 @@ +import React from 'react'; +import TestUtils from 'react-addons-test-utils'; + +import Formsy from './..'; +import { InputFactory } from './utils/TestInput'; + +const TestInput = InputFactory({ + render() { + return ; + } +}); + +const TestForm = React.createClass({ + render() { + return ( + + + + ); + } +}); + +export default { + + 'should pass with a default value': function (test) { + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), true); + test.done(); + + }, + + 'should pass with an empty string': function (test) { + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), true); + test.done(); + + }, + + 'should fail with an unempty string': function (test) { + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), false); + test.done(); + + }, + + 'should pass with a number as string': function (test) { + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), true); + test.done(); + + }, + + 'should fail with a number as string with not digits': function (test) { + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), false); + test.done(); + + }, + + 'should pass with an int': function (test) { + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), true); + test.done(); + + }, + + 'should pass with a float': function (test) { + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), true); + test.done(); + + }, + + 'should fail with a float in science notation': function (test) { + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), false); + test.done(); + + }, + + 'should pass with an undefined': function (test) { + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), true); + test.done(); + + }, + + 'should pass with a null': function (test) { + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), true); + test.done(); + + }, + + 'should pass with a zero': function (test) { + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), true); + test.done(); + + } + +}; diff --git a/tests/Rules-isUrl-spec.js b/tests/Rules-isUrl-spec.js new file mode 100644 index 0000000..0392fad --- /dev/null +++ b/tests/Rules-isUrl-spec.js @@ -0,0 +1,88 @@ +import React from 'react'; +import TestUtils from 'react-addons-test-utils'; + +import Formsy from './..'; +import { InputFactory } from './utils/TestInput'; + +const TestInput = InputFactory({ + render() { + return ; + } +}); + +const TestForm = React.createClass({ + render() { + return ( + + + + ); + } +}); + +export default { + + 'should pass with default value': function (test) { + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), true); + test.done(); + + }, + + 'should fail with "foo"': function (test) { + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), false); + test.done(); + + }, + + 'should pass with "https://www.google.com/"': function (test) { + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), true); + test.done(); + + }, + + 'should pass with an undefined': function (test) { + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), true); + test.done(); + + }, + + 'should pass with a null': function (test) { + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), true); + test.done(); + + }, + + 'should fail with a number': function (test) { + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), false); + test.done(); + + }, + + 'should pass with an empty string': function (test) { + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), true); + test.done(); + + } + +}; diff --git a/tests/Rules-isWords-spec.js b/tests/Rules-isWords-spec.js new file mode 100644 index 0000000..f10a80c --- /dev/null +++ b/tests/Rules-isWords-spec.js @@ -0,0 +1,88 @@ +import React from 'react'; +import TestUtils from 'react-addons-test-utils'; + +import Formsy from './..'; +import { InputFactory } from './utils/TestInput'; + +const TestInput = InputFactory({ + render() { + return ; + } +}); + +const TestForm = React.createClass({ + render() { + return ( + + + + ); + } +}); + +export default { + + 'should pass with a default value': function (test) { + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), true); + test.done(); + + }, + + 'should pass with a 1 word': function (test) { + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), true); + test.done(); + + }, + + 'should pass with 2 words': function (test) { + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), true); + test.done(); + + }, + + 'should fail with a string with numbers': function (test) { + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), false); + test.done(); + + }, + + 'should pass with an undefined': function (test) { + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), true); + test.done(); + + }, + + 'should pass with a null': function (test) { + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), true); + test.done(); + + }, + + 'should fail with a number': function (test) { + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), false); + test.done(); + + } + +}; diff --git a/tests/Rules-maxLength-spec.js b/tests/Rules-maxLength-spec.js new file mode 100644 index 0000000..61f5279 --- /dev/null +++ b/tests/Rules-maxLength-spec.js @@ -0,0 +1,97 @@ +import React from 'react'; +import TestUtils from 'react-addons-test-utils'; + +import Formsy from './..'; +import { InputFactory } from './utils/TestInput'; + +const TestInput = InputFactory({ + render() { + return ; + } +}); + +const TestForm = React.createClass({ + render() { + return ( + + + + ); + } +}); + +export default { + + 'should pass with a default value': function (test) { + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), true); + test.done(); + + }, + + 'should pass when a string\'s length is smaller': function (test) { + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), true); + test.done(); + + }, + + 'should pass when a string\'s length is equal': function (test) { + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), true); + test.done(); + + }, + + 'should fail when a string\'s length is bigger': function (test) { + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), false); + test.done(); + + }, + + 'should pass with empty string': function (test) { + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), true); + test.done(); + + }, + + 'should pass with an undefined': function (test) { + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), true); + test.done(); + + }, + + 'should pass with a null': function (test) { + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), true); + test.done(); + + }, + + 'should fail with a number': function (test) { + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), false); + test.done(); + + } + +}; diff --git a/tests/Rules-minLength-spec.js b/tests/Rules-minLength-spec.js new file mode 100644 index 0000000..6c85d27 --- /dev/null +++ b/tests/Rules-minLength-spec.js @@ -0,0 +1,141 @@ +import React from 'react'; +import TestUtils from 'react-addons-test-utils'; + +import Formsy from './..'; +import { InputFactory } from './utils/TestInput'; + +const TestInput = InputFactory({ + render() { + return ; + } +}); + +const TestForm = React.createClass({ + render() { + return ( + + + + ); + } +}); + +export default { + + 'minLength:3': { + + 'should pass with a default value': function (test) { + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), true); + test.done(); + + }, + + 'should pass when a string\'s length is bigger': function (test) { + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), true); + test.done(); + + }, + + 'should pass with empty string': function (test) { + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), true); + test.done(); + + }, + + 'should pass with an undefined': function (test) { + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), true); + test.done(); + + }, + + 'should pass with a null': function (test) { + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), true); + test.done(); + + }, + + 'should fail with a number': function (test) { + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), false); + test.done(); + + } + + }, + + 'minLength:0': { + + 'should pass with a default value': function (test) { + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), true); + test.done(); + + }, + + 'should pass when a string\'s length is bigger': function (test) { + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), true); + test.done(); + + }, + + 'should pass with empty string': function (test) { + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), true); + test.done(); + + }, + + 'should pass with an undefined': function (test) { + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), true); + test.done(); + + }, + + 'should pass with a null': function (test) { + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), true); + test.done(); + + }, + + 'should fail with a number': function (test) { + + const form = TestUtils.renderIntoDocument(); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + test.equal(inputComponent.isValid(), false); + test.done(); + + } + + } + +}; diff --git a/tests/Utils-spec.js b/tests/Utils-spec.js new file mode 100644 index 0000000..fdec451 --- /dev/null +++ b/tests/Utils-spec.js @@ -0,0 +1,32 @@ +import utils from './../src/utils.js'; + +export default { + + 'should check equality of objects and arrays': function (test) { + + const objA = { foo: 'bar' }; + const objB = { foo: 'bar' }; + const objC = [{ foo: ['bar'] }]; + const objD = [{ foo: ['bar'] }]; + const objE = undefined; + const objF = undefined; + const objG = null; + const objH = null; + + test.equal(utils.isSame(objA, objB), true); + test.equal(utils.isSame(objC, objD), true); + test.equal(utils.isSame(objA, objD), false); + + test.equal(utils.isSame(objE, objF), true); + test.equal(utils.isSame(objA, objF), false); + test.equal(utils.isSame(objE, objA), false); + + test.equal(utils.isSame(objG, objH), true); + test.equal(utils.isSame(objA, objH), false); + test.equal(utils.isSame(objG, objA), false); + + test.done(); + + } + +}; diff --git a/tests/Validation-spec.js b/tests/Validation-spec.js new file mode 100644 index 0000000..211c4b5 --- /dev/null +++ b/tests/Validation-spec.js @@ -0,0 +1,201 @@ +import React from 'react'; +import TestUtils from 'react-addons-test-utils'; + +import Formsy from './..'; +import TestInput, {InputFactory} from './utils/TestInput'; +import immediate from './utils/immediate'; +import sinon from 'sinon'; + +export default { + + 'should reset only changed form element when external error is passed': function (test) { + + const form = TestUtils.renderIntoDocument( + invalidate({ foo: 'bar', bar: 'foo' })}> + + + + ); + + const input = TestUtils.scryRenderedDOMComponentsWithTag(form, 'INPUT')[0]; + const inputComponents = TestUtils.scryRenderedComponentsWithType(form, TestInput); + + form.submit(); + test.equal(inputComponents[0].isValid(), false); + test.equal(inputComponents[1].isValid(), false); + + TestUtils.Simulate.change(input, {target: {value: 'bar'}}); + immediate(() => { + test.equal(inputComponents[0].isValid(), true); + test.equal(inputComponents[1].isValid(), false); + test.done(); + }); + + }, + + 'should let normal validation take over when component with external error is changed': function (test) { + + const form = TestUtils.renderIntoDocument( + invalidate({ foo: 'bar' })}> + + + ); + + const input = TestUtils.findRenderedDOMComponentWithTag(form, 'INPUT'); + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + + form.submit(); + test.equal(inputComponent.isValid(), false); + + TestUtils.Simulate.change(input, {target: {value: 'bar'}}); + immediate(() => { + test.equal(inputComponent.getValue(), 'bar'); + test.equal(inputComponent.isValid(), false); + test.done(); + }); + + }, + + 'should trigger an onValid handler, if passed, when form is valid': function (test) { + + const onValid = sinon.spy(); + const onInvalid = sinon.spy(); + + TestUtils.renderIntoDocument( + + + + ); + + test.equal(onValid.called, true); + test.equal(onInvalid.called, false); + test.done(); + + }, + + 'should trigger an onInvalid handler, if passed, when form is invalid': function (test) { + + const onValid = sinon.spy(); + const onInvalid = sinon.spy(); + + TestUtils.renderIntoDocument( + + + + ); + + test.equal(onValid.called, false); + test.equal(onInvalid.called, true); + test.done(); + + }, + + 'should be able to use provided validate function': function (test) { + + let isValid = false; + const CustomInput = InputFactory({ + componentDidMount() { + isValid = this.isValid(); + } + }); + const form = TestUtils.renderIntoDocument( + + + + ); + + const input = TestUtils.findRenderedDOMComponentWithTag(form, 'INPUT'); + test.equal(isValid, true); + test.done(); + + }, + + 'should provide invalidate callback on onValiSubmit': function (test) { + + const TestForm = React.createClass({ + render() { + return ( + invalidate({ foo: 'bar' })}> + + + ); + } + }); + + const form = TestUtils.renderIntoDocument(); + + const formEl = TestUtils.findRenderedDOMComponentWithTag(form, 'form'); + const input = TestUtils.findRenderedComponentWithType(form, TestInput); + TestUtils.Simulate.submit(formEl); + test.equal(input.isValid(), false); + test.done(); + + }, + + 'should provide invalidate callback on onInvalidSubmit': function (test) { + + const TestForm = React.createClass({ + render() { + return ( + invalidate({ foo: 'bar' })}> + + + ); + } + }); + + const form = TestUtils.renderIntoDocument(); + const formEl = TestUtils.findRenderedDOMComponentWithTag(form, 'form'); + const input = TestUtils.findRenderedComponentWithType(form, TestInput); + TestUtils.Simulate.submit(formEl); + test.equal(input.getErrorMessage(), 'bar'); + + test.done(); + + }, + + 'should not invalidate inputs on external errors with preventExternalInvalidation prop': function (test) { + + const TestForm = React.createClass({ + render() { + return ( + invalidate({ foo: 'bar' })}> + + + ); + } + }); + + const form = TestUtils.renderIntoDocument(); + const formEl = TestUtils.findRenderedDOMComponentWithTag(form, 'form'); + const input = TestUtils.findRenderedComponentWithType(form, TestInput); + TestUtils.Simulate.submit(formEl); + test.equal(input.isValid(), true); + test.done(); + + }, + + 'should invalidate inputs on external errors without preventExternalInvalidation prop': function (test) { + + const TestForm = React.createClass({ + render() { + return ( + invalidate({ foo: 'bar' })}> + + + ); + } + }); + + const form = TestUtils.renderIntoDocument(); + const formEl = TestUtils.findRenderedDOMComponentWithTag(form, 'form'); + const input = TestUtils.findRenderedComponentWithType(form, TestInput); + TestUtils.Simulate.submit(formEl); + test.equal(input.isValid(), false); + test.done(); + + } + +}; diff --git a/tests/utils/TestInput.js b/tests/utils/TestInput.js new file mode 100644 index 0000000..e72a1f4 --- /dev/null +++ b/tests/utils/TestInput.js @@ -0,0 +1,22 @@ +import React from 'react'; +import Formsy from './../..'; +import assign from 'react/lib/Object.assign'; + +const defaultProps = { + mixins: [Formsy.Mixin], + getDefaultProps() { + return { type: 'text' }; + }, + updateValue(event) { + this.setValue(event.target[this.props.type === 'checkbox' ? 'checked' : 'value']); + }, + render() { + return ; + } +}; + +export function InputFactory(props) { + return React.createClass(assign(defaultProps, props)); +} + +export default React.createClass(defaultProps); diff --git a/tests/utils/immediate.js b/tests/utils/immediate.js new file mode 100644 index 0000000..ed311ae --- /dev/null +++ b/tests/utils/immediate.js @@ -0,0 +1,3 @@ +export default function (fn) { + setTimeout(fn, 0); +}