From a1cf2236dfd26dd579f095a11886b8c8fec3805d Mon Sep 17 00:00:00 2001 From: christianalfoni Date: Fri, 22 May 2015 14:46:45 +0200 Subject: [PATCH] Prepared new release --- API.md | 31 ++++++++++++++++---- build/test.js | 29 ++++++++++--------- specs/Element-spec.jsx | 29 +++++++++++++++++++ specs/Formsy-spec.jsx | 14 +++++++++ specs/Validation-spec.jsx | 61 +++++++++++++++++++++++++++++++++++++++ src/main.js | 26 +++++++++++++---- src/utils.js | 10 +++++++ 7 files changed, 175 insertions(+), 25 deletions(-) diff --git a/API.md b/API.md index 8fa34bc..7d97a92 100644 --- a/API.md +++ b/API.md @@ -12,6 +12,7 @@ - [onInvalidSubmit()](#oninvalidsubmit) - [onChange()](#onchange) - [reset()](#resetform) + - [preventExternalInvalidation](#preventexternalinvalidation) - [Formsy.Mixin](#formsymixin) - [name](#name) - [value](#value) @@ -136,13 +137,13 @@ Triggers when form is submitted with a valid state. The arguments are the same a ``` Triggers when form is submitted with an invalid state. The arguments are the same as on `onSubmit`. -#### onChange(currentValues) +#### onChange(currentValues, isChanged) ```html ``` -"onChange" triggers when setValue is called on your form elements. It is also triggered when dynamic form elements have been added to the form. The "currentValues" is an object where the key is the name of the input and the value is the current value. +"onChange" triggers when setValue is called on your form elements. It is also triggered when dynamic form elements have been added to the form. The "currentValues" is an object where the key is the name of the input and the value is the current value. The second argument states if the forms initial values actually has changed. -#### reset() +#### reset(values) ```html var MyForm = React.createClass({ resetForm: function () { @@ -157,15 +158,35 @@ var MyForm = React.createClass({ } }); ``` -Manually reset the form to its pristine state. +Manually reset the form to its pristine state. You can also pass an object that inserts new values into the inputs. Keys are name of input and value is of course the value. + +#### preventExternalInvalidation +```html +var MyForm = React.createClass({ + onSubmit: function (model, reset, invalidate) { + invalidate({ + foo: 'Got some error' + }); + }, + render: function () { + return ( + + ... + + ); + } +}); +``` +With the `preventExternalInvalidation` the input will not be invalidated though it has an error. ### Formsy.Mixin #### name ```html + ``` -The name is required to register the form input component in the form. +The name is required to register the form input component in the form. You can also use dot notation. This will result in the "form model" being a nested object. `{email: 'value', address: {street: 'value'}}`. #### value ```html diff --git a/build/test.js b/build/test.js index d38c77d..e8908b0 100644 --- a/build/test.js +++ b/build/test.js @@ -6,28 +6,29 @@ var Input = React.createClass({ mixins: [Formsy.Mixin], render: function () { - return + return ( +
+ {this.showError()} + {this.getErrorMessage()} + +
+ ); } }); var FormApp = React.createClass({ - getInitialState: function () { - return { - bool: true - }; - }, - flip: function () { - this.setState({ - bool: !this.state.bool + componentDidMount: function () { + this.refs.form.updateInputsWithError({ + 'foo.bar': 'hmmm' }); }, + onSubmit: function (model) { + console.log('model', model); + }, render: function () { return ( - - {this.state.bool ? - : - - } + + ); } diff --git a/specs/Element-spec.jsx b/specs/Element-spec.jsx index cbce2aa..68d7262 100644 --- a/specs/Element-spec.jsx +++ b/specs/Element-spec.jsx @@ -558,4 +558,33 @@ it('should allow an undefined value to be updated to a value', function (done) { }); + 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 index 5fae13c..5f74041 100755 --- a/specs/Formsy-spec.jsx +++ b/specs/Formsy-spec.jsx @@ -612,6 +612,20 @@ describe('Formsy', function () { 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() { diff --git a/specs/Validation-spec.jsx b/specs/Validation-spec.jsx index 6d31bd5..cf92542 100644 --- a/specs/Validation-spec.jsx +++ b/specs/Validation-spec.jsx @@ -217,4 +217,65 @@ describe('Validation', function() { }); + + 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/main.js b/src/main.js index c0f9b63..3ffe97f 100644 --- a/src/main.js +++ b/src/main.js @@ -34,7 +34,8 @@ Formsy.Form = React.createClass({ onValid: function () {}, onInvalid: function () {}, onChange: function () {}, - validationErrors: null + validationErrors: null, + preventExternalInvalidation: false }; }, @@ -93,7 +94,21 @@ Formsy.Form = React.createClass({ }, mapModel: function () { - return this.props.mapping ? this.props.mapping(this.model) : this.model; + if (this.props.mapping) { + return this.props.mapping(this.model) + } else { + return Object.keys(this.model).reduce(function (mappedModel, key) { + + var keyArray = key.split('.'); + while (keyArray.length) { + var currentKey = keyArray.shift(); + mappedModel[currentKey] = keyArray.length ? mappedModel[currentKey] || {} : this.model[key]; + } + + return mappedModel; + + }.bind(this), {}); + } }, // Goes through all registered components and @@ -152,9 +167,8 @@ Formsy.Form = React.createClass({ if (!component) { throw new Error('You are trying to update an input that does not exist. Verify errors object with input names. ' + JSON.stringify(errors)); } - var args = [{ - _isValid: false, + _isValid: this.props.preventExternalInvalidation || false, _externalError: errors[name] }]; component.setState.apply(component, args); @@ -421,10 +435,10 @@ Formsy.Form = React.createClass({ }, render: function () { - return React.DOM.form({ + return React.DOM.form(utils.extend({}, this.props, { onSubmit: this.submit, className: this.props.className - }, + }), this.traverseChildrenAndRegisterInputs(this.props.children) ); diff --git a/src/utils.js b/src/utils.js index 074885d..704baa9 100644 --- a/src/utils.js +++ b/src/utils.js @@ -37,5 +37,15 @@ module.exports = { } return a === b; + }, + extend: function () { + var objects = [].slice.call(arguments); + var initialObject = objects.shift(); + return objects.reduce(function (returnedObject, object) { + return Object.keys(object).reduce(function (returnedObject, key) { + returnedObject[key] = object[key]; + return returnedObject; + }, returnedObject); + }, initialObject); } };