Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions src/Webform.js
Original file line number Diff line number Diff line change
Expand Up @@ -358,6 +358,13 @@ export default class Webform extends NestedDataComponent {
return this._data;
}

get validateWhenHidden() {
if (this.parent?.component?.type === 'form' && this.parent.component.validateWhenHidden) {
return this.parent.validateWhenHidden;
}
return false;
}

/**
* Sets the language for this form.
* @param {string} lang - The language to use (e.g. 'en', 'sp', etc.)
Expand Down
14 changes: 13 additions & 1 deletion src/components/_classes/component/Component.js
Original file line number Diff line number Diff line change
Expand Up @@ -2536,6 +2536,18 @@
element.setAttribute('aria-invalid', invalid ? 'true' : 'false');
}

get validateWhenHidden() {
if (this.component.validateWhenHidden || !this.component.input) {
if (this.parent && (this.parent !== this.parent.root)) {
return this.parent.validateWhenHidden;
} else {
// Skip layout components since they don't have validateWhenHidden
return this.component.input ? this.component.validateWhenHidden : true;
}
}
return false;
}

/**
* Clears the components data if it is conditionally hidden AND clearOnHide is set to true for this component.
*/
Expand Down Expand Up @@ -3132,7 +3144,7 @@
content
});
}

Check warning on line 3147 in src/components/_classes/component/Component.js

View workflow job for this annotation

GitHub Actions / setup

Missing JSDoc @returns description
/**
* Resets the value of this component.
*/
Expand Down Expand Up @@ -3778,7 +3790,7 @@
() => this.isValueHidden(),
// Force valid if component is hidden.
() => {
if (!this.component.validateWhenHidden && (!this.visible || !this.checkCondition(row, data))) {
if (!this.validateWhenHidden && (!this.visible || !this.checkCondition(row, data))) {
// If this component is forced valid when it is hidden, then we also need to reset the errors for this component.
this._errors = [];
return true;
Expand Down
4 changes: 4 additions & 0 deletions src/components/form/Form.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export default class FormComponent extends Component {
form: '',
path: '',
tableView: true,
validateWhenHidden: false,
}, ...extend);
}

Expand Down Expand Up @@ -524,6 +525,9 @@ export default class FormComponent extends Component {
if (this.options.pdf && this.component.useOriginalRevision) {
this.formObj.display = 'form';
}
if (this.component.validateWhenHidden) {
this.formObj.validateWhenHidden = true;
}
this.subFormLoading = false;
return formObj;
})
Expand Down
8 changes: 8 additions & 0 deletions src/components/form/editForm/Form.edit.data.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,13 @@ export default [
tooltip: 'When a field is hidden, clear the value.',
input: true
},
{
weight: 100,
type: 'checkbox',
label: 'Validate When Hidden',
tooltip: 'Validates the component when it is hidden/conditionally hidden. Vaildation errors are displayed in the error alert on the form submission. Use caution when enabling this setting, as it can cause a hidden component to be invalid with no way for the form user to correct it.',
key: 'validateWhenHidden',
input: true
},
];
/* eslint-enable max-len */
81 changes: 79 additions & 2 deletions test/unit/validateWhenHidden.unit.js
Original file line number Diff line number Diff line change
Expand Up @@ -424,7 +424,8 @@ describe("Validate When Hidden behavior", function () {
assert.equal(errors.length, 0);
});

it('Should validate components that are children of an intentionally hidden container component if those components have the `validateWhenHidden` property', async function () {
it('Should not validate components that are children of an intentionally hidden container component if those' +
' components have the `validateWhenHidden` property, but the container does not', async function () {
const formWithIntentionallyHiddenContainer = {
components: [
{
Expand Down Expand Up @@ -452,6 +453,39 @@ describe("Validate When Hidden behavior", function () {
);
assert.equal(form.getComponent('foo').visible, false, 'The textfield should be hidden');
const errors = form.validate();
assert.equal(errors.length, 0);
});

it('Should validate components that are children of an intentionally hidden container component if those' +
' components and the container have the `validateWhenHidden` property', async function () {
const formWithIntentionallyHiddenContainer = {
components: [
{
type: 'container',
key: 'container',
hidden: true,
clearOnHide: false,
validateWhenHidden: true,
components: [
{
type: 'textfield',
key: 'foo',
label: 'Foo',
validateWhenHidden: true,
validate: {
required: true
}
}
]
}
]
};
const form = await Formio.createForm(
document.createElement('div'),
formWithIntentionallyHiddenContainer
);
assert.equal(form.getComponent('foo').visible, false, 'The textfield should be hidden');
const errors = form.validate();
assert.equal(errors.length, 1);
});

Expand Down Expand Up @@ -491,7 +525,9 @@ describe("Validate When Hidden behavior", function () {
assert.equal(errors.length, 0);
});

it('Should validate components that are children of a conditionally hidden container component if those components include the `validateWhenHidden` parameter (NOTE THAT CLEAR ON HIDE MUST BE FALSE)', async function () {
it('Should not validate components that are children of a conditionally hidden container component if those' +
' components include the `validateWhenHidden` parameter, but the container does not (NOTE THAT CLEAR ON HIDE' +
' MUST BE FALSE)', async function () {
const formWithConditionallyHiddenContainer = {
components: [
{
Expand Down Expand Up @@ -526,6 +562,47 @@ describe("Validate When Hidden behavior", function () {
const form = await Formio.createForm(document.createElement('div'), formWithConditionallyHiddenContainer);
assert.equal(form.getComponent('foo').visible, false, 'The textfield should be hidden');
const errors = form.validate();
assert.equal(errors.length, 0);
});

it('Should validate components that are children of a conditionally hidden container component if those' +
' components and the container include the `validateWhenHidden` parameter (NOTE THAT CLEAR ON HIDE' +
' MUST BE FALSE)', async function () {
const formWithConditionallyHiddenContainer = {
components: [
{
type: 'checkbox',
key: 'checkbox',
label: 'Checkbox',
input: true
},
{
type: 'container',
key: 'container',
clearOnHide: false,
validateWhenHidden: true,
conditional: {
json: {
var: 'data.checkbox'
}
},
components: [
{
type: 'textfield',
key: 'foo',
label: 'Foo',
validateWhenHidden: true,
validate: {
required: true
}
}
]
}
]
};
const form = await Formio.createForm(document.createElement('div'), formWithConditionallyHiddenContainer);
assert.equal(form.getComponent('foo').visible, false, 'The textfield should be hidden');
const errors = form.validate();
assert.equal(errors.length, 1);
});
})
Expand Down
Loading