diff --git a/src/Wizard.js b/src/Wizard.js index 1fc6c7d693..2c225fadef 100644 --- a/src/Wizard.js +++ b/src/Wizard.js @@ -1015,7 +1015,11 @@ export default class Wizard extends Webform { onChange(flags, changed, modified, changes) { super.onChange(flags, changed, modified, changes); // The onChange loop doesn't need all components for wizards - const errors = this.submitted ? this.validate(this.localData, { dirty: true }) : this.validateCurrentPage(); + const errors = flags?.noValidate + ? [] + : this.submitted + ? this.validate(this.localData, { dirty: true }) + : this.validateCurrentPage(); if (this.alert) { this.showErrors(errors, true, true); } diff --git a/test/forms/wizardWithBlurValidation.js b/test/forms/wizardWithBlurValidation.js new file mode 100644 index 0000000000..aac2679b24 --- /dev/null +++ b/test/forms/wizardWithBlurValidation.js @@ -0,0 +1,75 @@ +export default { + _id: '68a599505fddd6a69fb39f2d', + title: 'test blur validation', + name: 'testBlurValidation', + path: 'testblurvalidation', + type: 'form', + display: 'wizard', + components: [ + { + title: 'Page 1', + label: 'Page 1', + type: 'panel', + key: 'page1', + components: [ + { + label: 'Text Field', + applyMaskOn: 'change', + tableView: true, + validateOn: 'blur', + validate: { + minLength: 4, + }, + validateWhenHidden: false, + key: 'textField', + type: 'textfield', + input: true, + }, + { + label: 'Number', + applyMaskOn: 'change', + mask: false, + tableView: false, + delimiter: false, + requireDecimal: false, + inputFormat: 'plain', + truncateMultipleSpaces: false, + validateOn: 'blur', + validate: { + min: 1000, + }, + validateWhenHidden: false, + key: 'number', + type: 'number', + input: true, + }, + ], + input: false, + tableView: false, + }, + { + title: 'Page 2', + label: 'Page 2', + type: 'panel', + key: 'page2', + components: [ + { + label: 'Text Area', + applyMaskOn: 'change', + autoExpand: false, + tableView: true, + validateOn: 'blur', + validate: { + minLength: 55, + }, + validateWhenHidden: false, + key: 'textArea', + type: 'textarea', + input: true, + }, + ], + input: false, + tableView: false, + }, + ], +}; diff --git a/test/unit/Wizard.unit.js b/test/unit/Wizard.unit.js index cf1ddafa0f..093c83d6c2 100644 --- a/test/unit/Wizard.unit.js +++ b/test/unit/Wizard.unit.js @@ -47,6 +47,7 @@ import simpleWizardWithRequiredFields from '../forms/simpleWizardWithRequiredFie import wizardWithLazyLoadSelect from '../forms/wizardWithLazyLoadSelect'; import testRequiredFieldsInNestedWizard from '../forms/testRequiredFieldsInNestedWizard'; import testWizardWithNestedForm from '../forms/testWizardWithNestedForm'; +import wizardWithBlurValidation from '../forms/wizardWithBlurValidation'; import { wait } from '../util'; // eslint-disable-next-line max-statements @@ -199,6 +200,37 @@ describe('Wizard Form with Nested Form validation', () => { }); }); + it('Should validate components on blur', function (done) { + const formElement = document.createElement('div'); + + Formio.createForm(formElement, wizardWithBlurValidation) + .then((wizard) => { + setTimeout(() => { + const textField = wizard.getComponent('textField'); + const tfInput = textField.refs.input[0]; + const inputEvent = new Event('input'); + const blurEvent = new Event('blur'); + tfInput.value = 't'; + tfInput.dispatchEvent(inputEvent); + + setTimeout(() => { + assert.equal(wizard.errors.length, 0); + assert.equal(textField.errors.length, 0); + tfInput.dispatchEvent(blurEvent); + + setTimeout(() => { + assert.equal(wizard.errors.length, 1); + assert.equal(textField.errors.length, 1); + assert.equal(textField.element.classList.contains('has-error'), true); + assert.equal(textField.refs.messageContainer.textContent.includes('Text Field must have at least 4 characters'), true); + done(); + }, 300); + }, 300); + }, 300); + }) + .catch((err) => done(err)); + }); + it('Should show validation error for required components inside nested form on submit when the page with nested from is not visited', function (done) { const formElement = document.createElement('div');