From 5802c02598ad1646a64f8f778753b5f09c865e4a Mon Sep 17 00:00:00 2001 From: Hanna Kurban Date: Wed, 30 Apr 2025 14:04:39 +0300 Subject: [PATCH 1/6] FIO-9974 fixed Clear on Hide for layout components --- .../_classes/component/Component.js | 2 +- .../clearOnHideInsideLayoutComponent.json | 85 +++++++++++++++++++ test/formtest/index.js | 2 + test/unit/Webform.unit.js | 36 ++++++++ 4 files changed, 124 insertions(+), 1 deletion(-) create mode 100644 test/formtest/clearOnHideInsideLayoutComponent.json diff --git a/src/components/_classes/component/Component.js b/src/components/_classes/component/Component.js index 308d2ea9b1..a19d6d2c48 100644 --- a/src/components/_classes/component/Component.js +++ b/src/components/_classes/component/Component.js @@ -823,7 +823,7 @@ export default class Component extends Element { this._conditionallyClear = true; return this._conditionallyClear; } - this._conditionallyClear = this.hasSetValue ? false : this.parentShouldConditionallyClear(); + this._conditionallyClear = this.parentShouldConditionallyClear(); return this._conditionallyClear; } diff --git a/test/formtest/clearOnHideInsideLayoutComponent.json b/test/formtest/clearOnHideInsideLayoutComponent.json new file mode 100644 index 0000000000..efe57502af --- /dev/null +++ b/test/formtest/clearOnHideInsideLayoutComponent.json @@ -0,0 +1,85 @@ +{ + "type": "form", + "components": [ + { + "label": "Checkbox", + "tableView": false, + "defaultValue": true, + "validateWhenHidden": false, + "key": "checkbox", + "type": "checkbox", + "input": true + }, + { + "label": "Panel", + "collapsible": false, + "key": "panel", + "conditional": { + "show": true, + "conjunction": "all", + "conditions": [ + { + "component": "checkbox", + "operator": "isEqual", + "value": true + } + ] + }, + "type": "panel", + "input": false, + "tableView": false, + "components": [ + { + "label": "Text Field", + "applyMaskOn": "change", + "tableView": true, + "validateWhenHidden": false, + "key": "textFieldInPanel", + "type": "textfield", + "input": true + } + ] + }, + { + "label": "", + "key": "fieldset", + "conditional": { + "show": true, + "conjunction": "all", + "conditions": [ + { + "component": "checkbox", + "operator": "isEqual", + "value": true + } + ] + }, + "type": "fieldset", + "input": false, + "tableView": false, + "components": [ + { + "label": "Text Field", + "applyMaskOn": "change", + "tableView": true, + "validateWhenHidden": false, + "key": "textFieldInFieldset", + "type": "textfield", + "input": true + } + ] + }, + { + "type": "button", + "label": "Submit", + "key": "submit", + "disableOnInvalid": true, + "input": true, + "tableView": false + } + ], + "title": "clearOnHide", + "display": "form", + "name": "clearOnHide", + "path": "clearonhide" +} diff --git a/test/formtest/index.js b/test/formtest/index.js index ac4d15f9fa..600af1eeb8 100644 --- a/test/formtest/index.js +++ b/test/formtest/index.js @@ -6,6 +6,7 @@ const layout = require('./layout.json'); const premium = require('./premium.json'); const settingErrors = require('./settingErrors.json'); const clearOnHide = require('./clearOnHide.json'); +const clearOnHideInsideLayoutComponent = require('./clearOnHideInsideLayoutComponent.json'); const manualOverride = require('./manualOverride.json'); const uniqueApiKeys = require('./uniqueApiKeys.json'); const uniqueApiKeysLayout = require('./uniqueApiKeysLayout.json'); @@ -53,6 +54,7 @@ module.exports = { premium, settingErrors, clearOnHide, + clearOnHideInsideLayoutComponent, manualOverride, uniqueApiKeys, uniqueApiKeysLayout, diff --git a/test/unit/Webform.unit.js b/test/unit/Webform.unit.js index 176a023eef..dd1063e900 100644 --- a/test/unit/Webform.unit.js +++ b/test/unit/Webform.unit.js @@ -11,6 +11,7 @@ import { Formio } from '../../src/formio.form.js'; import { settingErrors, clearOnHide, + clearOnHideInsideLayoutComponent, manualOverride, validationOnBlur, calculateValueWithManualOverride, @@ -2398,6 +2399,41 @@ describe('Webform tests', function() { }); }); + it('Should delete value of component inside parent conditionally hidden component if clearOnHide is turned on', function(done) { + const formElement = document.createElement('div'); + const form = new Webform(formElement); + form.setForm(clearOnHideInsideLayoutComponent).then(() => { + const visibleData = { + data: { + checkbox: true, + textFieldInPanel: 'some text in panel', + textFieldInFieldset: 'some text in fieldset', + submit: false + } + }; + + const hiddenData = { + data: { + checkbox: false, + submit: false + } + }; + const textFieldInPanel = form.getComponent('textFieldInPanel'); + textFieldInPanel.setValue('some text in panel'); + const textFieldInFieldset = form.getComponent('textFieldInFieldset'); + textFieldInFieldset.setValue('some text in fieldset'); + setTimeout(() => { + assert.deepEqual(form.data, visibleData.data); + const checkbox = form.getComponent('checkbox'); + checkbox.setValue(false); + setTimeout(() => { + assert.deepEqual(form.data, hiddenData.data); + done(); + }, 250); + }, 250); + }); + }); + const formElement = document.createElement('div'); const checkForErrors = function(form, flags = {}, submission, numErrors, done) { form.setSubmission(submission, flags).then(() => { From c77b1a58ca0caff908262fcc2765b43b6a63fce2 Mon Sep 17 00:00:00 2001 From: Hanna Kurban Date: Wed, 30 Apr 2025 15:23:10 +0300 Subject: [PATCH 2/6] FIO-9974 fixed tests --- .../clearOnHideInsideLayoutComponent.js} | 2 +- test/formtest/index.js | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) rename test/{formtest/clearOnHideInsideLayoutComponent.json => forms/clearOnHideInsideLayoutComponent.js} (99%) diff --git a/test/formtest/clearOnHideInsideLayoutComponent.json b/test/forms/clearOnHideInsideLayoutComponent.js similarity index 99% rename from test/formtest/clearOnHideInsideLayoutComponent.json rename to test/forms/clearOnHideInsideLayoutComponent.js index efe57502af..472d479d62 100644 --- a/test/formtest/clearOnHideInsideLayoutComponent.json +++ b/test/forms/clearOnHideInsideLayoutComponent.js @@ -1,4 +1,4 @@ -{ +export default { "type": "form", "components": [ { diff --git a/test/formtest/index.js b/test/formtest/index.js index 600af1eeb8..ac4d15f9fa 100644 --- a/test/formtest/index.js +++ b/test/formtest/index.js @@ -6,7 +6,6 @@ const layout = require('./layout.json'); const premium = require('./premium.json'); const settingErrors = require('./settingErrors.json'); const clearOnHide = require('./clearOnHide.json'); -const clearOnHideInsideLayoutComponent = require('./clearOnHideInsideLayoutComponent.json'); const manualOverride = require('./manualOverride.json'); const uniqueApiKeys = require('./uniqueApiKeys.json'); const uniqueApiKeysLayout = require('./uniqueApiKeysLayout.json'); @@ -54,7 +53,6 @@ module.exports = { premium, settingErrors, clearOnHide, - clearOnHideInsideLayoutComponent, manualOverride, uniqueApiKeys, uniqueApiKeysLayout, From eca58457ee38fdaa68100f1218994cbedef05b58 Mon Sep 17 00:00:00 2001 From: Hanna Kurban Date: Wed, 30 Apr 2025 15:40:20 +0300 Subject: [PATCH 3/6] FIO-9974 fixed form import for tests --- test/unit/Webform.unit.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/unit/Webform.unit.js b/test/unit/Webform.unit.js index dd1063e900..134e5c5af1 100644 --- a/test/unit/Webform.unit.js +++ b/test/unit/Webform.unit.js @@ -11,7 +11,6 @@ import { Formio } from '../../src/formio.form.js'; import { settingErrors, clearOnHide, - clearOnHideInsideLayoutComponent, manualOverride, validationOnBlur, calculateValueWithManualOverride, @@ -93,6 +92,7 @@ import simpleController from '../forms/formWithSimpleController.js'; import formWithHiddenComponents from '../forms/formWithHiddenComponents.js'; import formWithShowAsString from '../forms/formWithShowAsString.js'; import formWithMergeComponentSchemaAndCustomLogic from '../forms/formWithMergeComponentSchemaAndCustomLogic.js'; +import clearOnHideInsideLayoutComponent from '../forms/clearOnHideInsideLayoutComponent.js'; const SpySanitize = sinon.spy(FormioUtils, 'sanitize'); From 06b0f264efaa34c32d73711027475c18597c5b1f Mon Sep 17 00:00:00 2001 From: Hanna Kurban Date: Mon, 16 Jun 2025 13:16:16 +0300 Subject: [PATCH 4/6] FIO-9974 fixed clearOnHide for layout components --- .../_classes/component/Component.js | 13 ++++++- src/components/fieldset/Fieldset.js | 1 + test/unit/Webform.unit.js | 36 ++++++++++++++++++- 3 files changed, 48 insertions(+), 2 deletions(-) diff --git a/src/components/_classes/component/Component.js b/src/components/_classes/component/Component.js index a19d6d2c48..21fedc1fa2 100644 --- a/src/components/_classes/component/Component.js +++ b/src/components/_classes/component/Component.js @@ -823,7 +823,7 @@ export default class Component extends Element { this._conditionallyClear = true; return this._conditionallyClear; } - this._conditionallyClear = this.parentShouldConditionallyClear(); + this._conditionallyClear = this.hasSetValue ? false : this.parentShouldConditionallyClear(); return this._conditionallyClear; } @@ -2982,6 +2982,17 @@ export default class Component extends Element { noUpdateEvent: true, noDefault: true }); + + if (FormioUtils.isLayoutComponent(this.component) && this.component.clearOnHide === true && !this.hasValue()) { + FormioUtils.eachComponent(this.components, (component) => { + component.setValue(null, { + noUpdateEvent: true, + noDefault: true + }); + component.unset(); + }); + } + this.unset(); } diff --git a/src/components/fieldset/Fieldset.js b/src/components/fieldset/Fieldset.js index 9b71fe87e0..131dfecb2d 100644 --- a/src/components/fieldset/Fieldset.js +++ b/src/components/fieldset/Fieldset.js @@ -8,6 +8,7 @@ export default class FieldsetComponent extends NestedComponent { type: 'fieldset', legend: '', components: [], + clearOnHide: false, input: false, persistent: false }, ...extend); diff --git a/test/unit/Webform.unit.js b/test/unit/Webform.unit.js index 134e5c5af1..64ca6240c3 100644 --- a/test/unit/Webform.unit.js +++ b/test/unit/Webform.unit.js @@ -2399,7 +2399,8 @@ describe('Webform tests', function() { }); }); - it('Should delete value of component inside parent conditionally hidden component if clearOnHide is turned on', function(done) { + + it('Should not delete value of component inside parent conditionally hidden layout component by default', function(done) { const formElement = document.createElement('div'); const form = new Webform(formElement); form.setForm(clearOnHideInsideLayoutComponent).then(() => { @@ -2412,6 +2413,39 @@ describe('Webform tests', function() { } }; + const textFieldInPanel = form.getComponent('textFieldInPanel'); + textFieldInPanel.setValue('some text in panel'); + const textFieldInFieldset = form.getComponent('textFieldInFieldset'); + textFieldInFieldset.setValue('some text in fieldset'); + setTimeout(() => { + assert.deepEqual(form.data, visibleData.data); + const checkbox = form.getComponent('checkbox'); + checkbox.setValue(false); + setTimeout(() => { + assert.equal(form.data.textFieldInPanel, 'some text in panel'); + assert.equal(form.data.textFieldInFieldset, 'some text in fieldset'); + done(); + }, 250); + }, 250); + }); + }); + + it('Should delete value of component inside parent conditionally hidden layout component if clearOnHide is set to true', function(done) { + const formElement = document.createElement('div'); + const form = new Webform(formElement); + const testForm = fastCloneDeep(clearOnHideInsideLayoutComponent); + _.set(testForm, 'components[1].clearOnHide', true); + _.set(testForm, 'components[2].clearOnHide', true) + form.setForm(testForm).then(() => { + const visibleData = { + data: { + checkbox: true, + textFieldInPanel: 'some text in panel', + textFieldInFieldset: 'some text in fieldset', + submit: false + } + }; + const hiddenData = { data: { checkbox: false, From 86987a5686af5282a5444c3c11ad724cff5ae6fc Mon Sep 17 00:00:00 2001 From: Hanna Kurban Date: Mon, 16 Jun 2025 15:03:48 +0300 Subject: [PATCH 5/6] FIO-9974 code improvements --- src/components/_classes/component/Component.js | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/components/_classes/component/Component.js b/src/components/_classes/component/Component.js index 21fedc1fa2..345ac23e54 100644 --- a/src/components/_classes/component/Component.js +++ b/src/components/_classes/component/Component.js @@ -2985,11 +2985,13 @@ export default class Component extends Element { if (FormioUtils.isLayoutComponent(this.component) && this.component.clearOnHide === true && !this.hasValue()) { FormioUtils.eachComponent(this.components, (component) => { - component.setValue(null, { - noUpdateEvent: true, - noDefault: true - }); - component.unset(); + if (component.component.clearOnHide !== false) { + component.setValue(null, { + noUpdateEvent: true, + noDefault: true + }); + component.unset(); + } }); } From b627acb466a4163b20212655c430cb72ed0283de Mon Sep 17 00:00:00 2001 From: Hanna Kurban Date: Fri, 29 Aug 2025 16:12:35 +0300 Subject: [PATCH 6/6] FIO-9974 replace eachComponent method --- .../_classes/component/Component.js | 27 +++++++++---------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/src/components/_classes/component/Component.js b/src/components/_classes/component/Component.js index 6026e1b40c..6e9d8a55af 100644 --- a/src/components/_classes/component/Component.js +++ b/src/components/_classes/component/Component.js @@ -511,6 +511,18 @@ export default class Component extends Element { return false; } + + hasCondionallyHiddenLayoutParent() { + let currentParent = this.parent; + while (currentParent) { + if (currentParent._conditionallyHidden && FormioUtils.isLayoutComponent(currentParent) && currentParent.component.clearOnHide === true) { + return true; + } + currentParent = currentParent.parent; + } + return false + } + parentConditionallyHidden() { let currentParent = this.parent; while (currentParent) { @@ -822,7 +834,7 @@ export default class Component extends Element { this._conditionallyClear = true; return this._conditionallyClear; } - this._conditionallyClear = this.hasSetValue ? false : this.parentShouldConditionallyClear(); + this._conditionallyClear = this.hasSetValue ? this.hasCondionallyHiddenLayoutParent() : this.parentShouldConditionallyClear(); return this._conditionallyClear; } @@ -2994,19 +3006,6 @@ export default class Component extends Element { noUpdateEvent: true, noDefault: true }); - - if (FormioUtils.isLayoutComponent(this.component) && this.component.clearOnHide === true && !this.hasValue()) { - FormioUtils.eachComponent(this.components, (component) => { - if (component.component.clearOnHide !== false) { - component.setValue(null, { - noUpdateEvent: true, - noDefault: true - }); - component.unset(); - } - }); - } - this.unset(); }