diff --git a/API.md b/API.md index 3021dd36..4fb4592b 100644 --- a/API.md +++ b/API.md @@ -271,11 +271,13 @@ The message that will show when the form input component is invalid. It will be name="email" validations={{ isEmail: true, - maxLength: 50 + maxLength: 50, + someCustomValidation: [1, 2, 3] }} validationErrors={{ isEmail: 'You have to type valid email', - maxLength: 'You can not type in more than 50 characters' + maxLength: 'You can not type in more than {0} characters', + someCustomValidation: '{0} + {1} = {2}' }} /> ``` diff --git a/examples/dynamic-form-fields/app.js b/examples/dynamic-form-fields/app.js index 9549c4a0..c7b4269f 100644 --- a/examples/dynamic-form-fields/app.js +++ b/examples/dynamic-form-fields/app.js @@ -7,6 +7,15 @@ import MySelect from './../components/Select'; import MyRadioGroup from './../components/RadioGroup'; import MyMultiCheckboxSet from './../components/MultiCheckboxSet'; +const validationErrors = { + 'isEmail': 'The field must contain a valid email address', + 'isNumeric': 'The field must contain only numbers', + 'isAlphanumeric': 'The field must only contain alpha-numeric characters', + 'equals': 'The field must be equal to {0}', + 'minLength': 'The field must be at least {0} characters in length', + 'maxLength': 'The field must not exceed {0} characters in length' +}; + const Fields = props => { function onRemove(pos) { return event => { @@ -27,6 +36,7 @@ const Fields = props => { title={field.validations ? JSON.stringify(field.validations) : 'No validations'} required={field.required} validations={field.validations} + validationErrors={validationErrors} /> ) : ( @@ -35,6 +45,7 @@ const Fields = props => { title={field.validations ? JSON.stringify(field.validations) : 'No validations'} required={field.required} validations={field.validations} + validationErrors={validationErrors} options={[ {title: '123', value: '123'}, {title: 'some long text', value: 'some long text'}, @@ -86,7 +97,6 @@ const App = React.createClass({ cmp={(a, b) => JSON.stringify(a) === JSON.stringify(b)} items={[ {isEmail: true}, - {isEmptyString: true}, {isNumeric: true}, {isAlphanumeric: true}, {equals: 5}, diff --git a/src/main.js b/src/main.js index 7edb786d..e6419534 100644 --- a/src/main.js +++ b/src/main.js @@ -255,7 +255,7 @@ Formsy.Form = React.createClass({ // the component defines an explicit validate function if (typeof component.validate === "function") { - validationResults.failed = component.validate() ? [] : ['failed']; + validationResults.failed = component.validate() ? [] : [{ method: 'failed' }]; } var isRequired = Object.keys(component._requiredValidations).length ? !!requiredResults.success.length : false; @@ -285,7 +285,13 @@ Formsy.Form = React.createClass({ if (validationResults.failed.length) { return validationResults.failed.map(function(failed) { - return validationErrors[failed] ? validationErrors[failed] : validationError; + var errorMessage = validationErrors[failed.method] ? validationErrors[failed.method] : validationError; + + failed.args && [].concat(failed.args).forEach((arg, i) => { + errorMessage = errorMessage.replace(new RegExp('\\{' + i + '\\}', 'g'), arg); + }); + + return errorMessage; }).filter(function(x, pos, arr) { // Remove duplicates return arr.indexOf(x) === pos; @@ -319,9 +325,9 @@ Formsy.Form = React.createClass({ var validation = validations[validationMethod](currentValues, value); if (typeof validation === 'string') { results.errors.push(validation); - results.failed.push(validationMethod); + results.failed.push({ method: validationMethod }); } else if (!validation) { - results.failed.push(validationMethod); + results.failed.push({ method: validationMethod }); } return; @@ -329,9 +335,9 @@ Formsy.Form = React.createClass({ var validation = validationRules[validationMethod](currentValues, value, validations[validationMethod]); if (typeof validation === 'string') { results.errors.push(validation); - results.failed.push(validationMethod); + results.failed.push({ method: validationMethod, args: validations[validationMethod] }); } else if (!validation) { - results.failed.push(validationMethod); + results.failed.push({ method: validationMethod, args: validations[validationMethod] }); } else { results.success.push(validationMethod); } diff --git a/tests/Element-spec.js b/tests/Element-spec.js index 38b93acc..24813813 100644 --- a/tests/Element-spec.js +++ b/tests/Element-spec.js @@ -399,6 +399,39 @@ export default { }, + 'should validation parameters passed to validation errors messages': function (test) { + + const TestForm = React.createClass({ + render() { + return ( + + + + ); + } + }); + const form = TestUtils.renderIntoDocument(); + + const inputComponent = TestUtils.findRenderedComponentWithType(form, TestInput); + const input = TestUtils.findRenderedDOMComponentWithTag(form, 'INPUT'); + TestUtils.Simulate.change(input, {target: {value: 'xx'}}); + test.equal(inputComponent.getErrorMessage(), 'The field must be at least 3 characters in length'); + TestUtils.Simulate.change(input, {target: {value: 'xxxxxx'}}); + test.equal(inputComponent.getErrorMessage(), 'The field must not exceed 5 characters in length'); + + test.done(); + + }, + 'should not be valid if it is required and required rule is true': function (test) { const TestForm = React.createClass({