From 28be07ac343a2bc2fdbe1a9d7526f6a94244a832 Mon Sep 17 00:00:00 2001 From: Brendan Bond Date: Mon, 16 Dec 2024 10:01:03 -0600 Subject: [PATCH] FIO-9159: change clearOnHide behavior to track conditionally hidden state rather than mere visibility (#5902) * This commit changes the behavior of the clearOnHide property. Previously, when this flag was present in a component, the renderer would unset the component's value from submission data if it was not visible (so, conditionally hidden, logically hidden, or intentionally hidden via the hidden property). Now, clearOnHide will only unset the component's data if it or its parent is conditionally or logically hidden. The copy of the parameter in the builder has also been changed. * Update Form.js --- .../_classes/component/Component.js | 70 +- .../component/editForm/Component.edit.data.js | 4 +- .../_classes/nested/NestedComponent.js | 24 +- src/components/datamap/DataMap.js | 2 +- src/components/editgrid/EditGrid.js | 7 +- src/components/form/Form.js | 9 +- src/components/html/HTML.js | 19 +- test/unit/EditGrid.unit.js | 103 -- test/unit/NestedComponent.unit.js | 6 +- test/unit/clearOnHide.js | 1214 +++++++++++++++++ test/util.js | 4 +- 11 files changed, 1316 insertions(+), 146 deletions(-) create mode 100644 test/unit/clearOnHide.js diff --git a/src/components/_classes/component/Component.js b/src/components/_classes/component/Component.js index 69cbeef94c..e7f36bd4df 100644 --- a/src/components/_classes/component/Component.js +++ b/src/components/_classes/component/Component.js @@ -374,11 +374,19 @@ export default class Component extends Element { // Needs for Nextgen Rules Engine this.resetCaches(); + /** + * Determines if this component is conditionally hidden. Should generally not be set outside of conditional logic pipeline. + * This is necessary because of clearOnHide behavior that only clears when conditionally hidden - we need to track + * conditionallyHidden separately from "regular" visibility. + */ + this._parentConditionallyHidden = this.options.hasOwnProperty('parentConditionallyHidden') ? this.options.parentConditionallyHidden : false; + this._conditionallyHidden = this.checkConditionallyHidden(null, data) || this._parentConditionallyHidden; + /** * Determines if this component is visible, or not. */ this._parentVisible = this.options.hasOwnProperty('parentVisible') ? this.options.parentVisible : true; - this._visible = this._parentVisible && this.conditionallyVisible(null, data); + this._visible = this._parentVisible && (this.hasCondition() ? !this._conditionallyHidden : !this.component.hidden); this._parentDisabled = false; /** @@ -454,7 +462,7 @@ export default class Component extends Element { if (this.allowData && this.key) { this.options.name += `[${this.key}]`; // If component is visible or not set to clear on hide, set the default value. - if (this.visible || !this.component.clearOnHide) { + if (!(this.conditionallyHidden && this.component.clearOnHide)) { if (!this.hasValue()) { if (this.shouldAddDefaultValue) { this.dataValue = this.defaultValue; @@ -537,7 +545,8 @@ export default class Component extends Element { init() { this.disabled = this.shouldDisabled; - this._visible = this.conditionallyVisible(null, null); + this._conditionallyHidden = this.checkConditionallyHidden(); + this._visible = (this.hasCondition() ? !this.conditionallyHidden : !this.component.hidden); if (this.component.addons?.length) { this.component.addons.forEach((addon) => this.createAddon(addon)); } @@ -706,7 +715,6 @@ export default class Component extends Element { return; } this._visible = value; - this.clearOnHide(); this.redraw(); } } @@ -729,6 +737,23 @@ export default class Component extends Element { return this._visible && this._parentVisible; } + get conditionallyHidden() { + return this._conditionallyHidden || this._parentConditionallyHidden; + } + + /** + * Evaluates whether the component is conditionally hidden (as opposed to intentionally hidden, e.g. via the `hidden` component schema property). + * @param {object} data - The data object to evaluate the condition against. + * @param {object} row - The row object to evaluate the condition against. + * @returns {boolean} - Whether the component is conditionally hidden. + */ + checkConditionallyHidden(data = null, row = null) { + if (!this.hasCondition()) { + return false; + } + return !this.conditionallyVisible(data, row); + } + get currentForm() { return this._currentForm; } @@ -2059,7 +2084,7 @@ export default class Component extends Element { rebuild() { this.destroy(); this.init(); - this.visible = this.conditionallyVisible(null, null); + this.visible = this.hasCondition() ? !this.conditionallyHidden : !this.component.hidden; return this.redraw(); } @@ -2136,8 +2161,8 @@ export default class Component extends Element { conditionallyVisible(data, row) { data = data || this.rootValue; row = row || this.data; - if (this.builderMode || this.previewMode || !this.hasCondition()) { - return !this.component.hidden; + if (this.builderMode || this.previewMode) { + return true; } data = data || (this.root ? this.root.data : {}); return this.checkCondition(row, data); @@ -2177,8 +2202,15 @@ export default class Component extends Element { this.redraw(); } - // Check advanced conditions - const visible = this.conditionallyVisible(data, row); + // Check advanced conditions (and cache the result) + const isConditionallyHidden = this.checkConditionallyHidden(data, row) || this._parentConditionallyHidden; + if (isConditionallyHidden !== this._conditionallyHidden) { + this._conditionallyHidden = isConditionallyHidden; + this.clearOnHide(); + } + + // Check visibility + const visible = (this.hasCondition() ? !this.conditionallyHidden : !this.component.hidden); if (this.visible !== visible) { this.visible = visible; @@ -2320,6 +2352,12 @@ export default class Component extends Element { const property = action.property.value; if (!_.isEqual(_.get(this.component, property), _.get(newComponent, property))) { + // Advanced Logic can modify the component's hidden property; because we track conditionally hidden state + // separately from the component's hidden property, and technically this Advanced Logic conditionally hides + // a component, we need to set _conditionallyHidden to the new value + if (property === 'hidden') { + this._conditionallyHidden = newComponent.hidden; + } changed = true; } @@ -2338,7 +2376,7 @@ export default class Component extends Element { } ); - if (!_.isEqual(oldValue, newValue) && !(this.component.clearOnHide && !this.visible)) { + if (!_.isEqual(oldValue, newValue) && !(this.component.clearOnHide && this.conditionallyHidden)) { this.setValue(newValue); if (this.viewOnly) { @@ -2383,7 +2421,7 @@ export default class Component extends Element { }, 'value'); - if (!_.isEqual(oldValue, newValue) && !(this.component.clearOnHide && !this.visible)) { + if (!_.isEqual(oldValue, newValue) && !(this.component.clearOnHide && this.conditionallyHidden)) { this.setValue(newValue); if (this.viewOnly) { @@ -2512,7 +2550,7 @@ export default class Component extends Element { !this.options.readOnly && !this.options.showHiddenFields ) { - if (!this.visible) { + if (this.conditionallyHidden) { this.deleteValue(); } else if (!this.hasValue() && this.shouldAddDefaultValue) { @@ -2807,7 +2845,7 @@ export default class Component extends Element { get dataValue() { if ( !this.key || - (!this.visible && this.component.clearOnHide && !this.rootPristine) + (this.conditionallyHidden && this.component.clearOnHide && !this.rootPristine) ) { return this.emptyValue; } @@ -2829,7 +2867,7 @@ export default class Component extends Element { if ( !this.allowData || !this.key || - (!this.visible && this.component.clearOnHide && !this.rootPristine) + (this.conditionallyHidden && this.component.clearOnHide && !this.rootPristine) ) { return; } @@ -3193,7 +3231,7 @@ export default class Component extends Element { // If no calculated value or // hidden and set to clearOnHide (Don't calculate a value for a hidden field set to clear when hidden) const { clearOnHide } = this.component; - const shouldBeCleared = !this.visible && clearOnHide; + const shouldBeCleared = this.conditionallyHidden && clearOnHide; const allowOverride = _.get(this.component, 'allowCalculateOverride', false); if (shouldBeCleared) { @@ -3917,7 +3955,7 @@ export default class Component extends Element { // If component definition changed, replace it. if (!_.isEqual(this.component, newComponent)) { this.component = newComponent; - const visible = this.conditionallyVisible(null, null); + const visible = this.hasCondition() ? !this.conditionallyHidden : !this.component.hidden; const disabled = this.shouldDisabled; // Change states which won't be recalculated during redrawing diff --git a/src/components/_classes/component/editForm/Component.edit.data.js b/src/components/_classes/component/editForm/Component.edit.data.js index ed2790d01a..de4bf0c477 100644 --- a/src/components/_classes/component/editForm/Component.edit.data.js +++ b/src/components/_classes/component/editForm/Component.edit.data.js @@ -128,10 +128,10 @@ export default [ { weight: 700, type: 'checkbox', - label: 'Clear Value When Hidden', + label: 'Omit Value From Submission Data When Conditionally Hidden', key: 'clearOnHide', defaultValue: true, - tooltip: 'When a field is hidden, clear the value.', + tooltip: 'When a field is conditionally hidden, omit the value from the submission data.', input: true }, EditFormUtils.javaScriptValue('Custom Default Value', 'customDefaultValue', 'customDefaultValue', 1000, diff --git a/src/components/_classes/nested/NestedComponent.js b/src/components/_classes/nested/NestedComponent.js index b05c4f680f..6c2bab0442 100644 --- a/src/components/_classes/nested/NestedComponent.js +++ b/src/components/_classes/nested/NestedComponent.js @@ -86,18 +86,27 @@ export default class NestedComponent extends Field { const visibilityChanged = this._visible !== value; this._visible = value; const isVisible = this.visible; + const isConditionallyHidden = this.checkConditionallyHidden(); const forceShow = this.shouldForceShow(); const forceHide = this.shouldForceHide(); - this.components.forEach(component => { + this.components.forEach((component) => { // Set the parent visibility first since we may have nested components within nested components // and they need to be able to determine their visibility based on the parent visibility. component.parentVisible = isVisible; + component._parentConditionallyHidden = isConditionallyHidden; + let visible; + if (component.hasCondition()) { + component._conditionallyHidden = component.checkConditionallyHidden() || component._parentConditionallyHidden; + visible = !component.conditionallyHidden; + } + else { + visible = !component.component.hidden; + } - const conditionallyVisible = component.conditionallyVisible(); - if (forceShow || conditionallyVisible) { + if (forceShow || visible) { component.visible = true; } - else if (forceHide || !isVisible || !conditionallyVisible) { + else if (forceHide || !isVisible || !visible ) { component.visible = false; } // If hiding a nested component, clear all errors below. @@ -105,8 +114,8 @@ export default class NestedComponent extends Field { component.error = ''; } }); + if (visibilityChanged) { - this.clearOnHide(); this.redraw(); } } @@ -399,6 +408,7 @@ export default class NestedComponent extends Field { data = data || this.data; options.parent = this; options.parentVisible = this.visible; + options.parentConditionallyHidden = this.conditionallyHidden; options.root = options?.root || this.root || this; options.localRoot = this.localRoot; options.skipInit = true; @@ -688,7 +698,7 @@ export default class NestedComponent extends Field { clearOnHide(show) { super.clearOnHide(show); if (this.component.clearOnHide) { - if (this.allowData && !this.hasValue() && !(this.options.server && !this.visible)) { + if (this.allowData && !this.hasValue() && !this.conditionallyHidden) { this.dataValue = this.defaultValue; } if (this.hasValue()) { @@ -721,7 +731,7 @@ export default class NestedComponent extends Field { calculateValue(data, flags, row) { // Do not iterate into children and calculateValues if this nested component is conditionally hidden. - if (!this.conditionallyVisible()) { + if (this.conditionallyHidden) { return false; } return this.getComponents().reduce( diff --git a/src/components/datamap/DataMap.js b/src/components/datamap/DataMap.js index 68526eab38..85eb47913d 100644 --- a/src/components/datamap/DataMap.js +++ b/src/components/datamap/DataMap.js @@ -79,7 +79,7 @@ export default class DataMapComponent extends DataGridComponent { get dataValue() { if ( !this.key || - (!this.visible && this.component.clearOnHide) + (this.conditionallyHidden && this.component.clearOnHide) ) { return this.emptyValue; } diff --git a/src/components/editgrid/EditGrid.js b/src/components/editgrid/EditGrid.js index 0424b0cefe..8151e5b5d0 100644 --- a/src/components/editgrid/EditGrid.js +++ b/src/components/editgrid/EditGrid.js @@ -1359,7 +1359,7 @@ export default class EditGridComponent extends NestedArrayComponent { } const changed = this.hasChanged(value, this.dataValue); - if (this.parent && !this.options.server) { + if (this.parent) { this.parent.checkComponentConditions(); } this.dataValue = value; @@ -1394,10 +1394,7 @@ export default class EditGridComponent extends NestedArrayComponent { this.openWhenEmpty(); this.updateOnChange(flags, changed); - // do not call checkData with server option, it is called when change is triggered in updateOnChange - if (!this.options.server) { - this.checkData(); - } + this.checkData(); this.changeState(changed, flags); diff --git a/src/components/form/Form.js b/src/components/form/Form.js index 31490bcf9d..6bd19a666a 100644 --- a/src/components/form/Form.js +++ b/src/components/form/Form.js @@ -473,11 +473,12 @@ export default class FormComponent extends Component { } hideSubmitButton(component) { - const isSubmitButton = (component.type === 'button') && - ((component.action === 'submit') || !component.action); + const isSubmitButton = component.type === 'button' && (component.action === 'submit' || !component.action); if (isSubmitButton) { component.hidden = true; + // clearOnHide no longer clears from the JSON `hidden` flag, so we make the button conditionally hidden to clear its data + component.customConditional = 'show = false'; } } @@ -487,7 +488,7 @@ export default class FormComponent extends Component { * @returns {Promise} - The promise that resolves when the subform is loaded. */ loadSubForm(fromAttach) { - if (this.builderMode || this.isHidden() || (this.isSubFormLazyLoad() && !fromAttach)) { + if (this.builderMode || this.conditionallyHidden || (this.isSubFormLazyLoad() && !fromAttach)) { return Promise.resolve(); } @@ -569,7 +570,7 @@ export default class FormComponent extends Component { * @returns {*|boolean} - TRUE if the subform should be submitted, FALSE if it should not. */ get shouldSubmit() { - return this.subFormReady && (!this.component.hasOwnProperty('reference') || this.component.reference) && !this.isHidden(); + return this.subFormReady && (!this.component.hasOwnProperty('reference') || this.component.reference) && !this.conditionallyHidden; } /** diff --git a/src/components/html/HTML.js b/src/components/html/HTML.js index c2f7a60a51..06a6de76a8 100644 --- a/src/components/html/HTML.js +++ b/src/components/html/HTML.js @@ -62,9 +62,22 @@ export default class HTMLComponent extends Component { checkRefreshOn(changed) { super.checkRefreshOn(changed); - if (!this.builderMode && this.component.refreshOnChange && this.element && - !_.isUndefined(changed) && ((_.isBoolean(changed) && changed) || !_.isEmpty(changed)) && - this.conditionallyVisible(this.data, this.row)) { + let visible; + if (this.hasCondition()) { + this._conditionallyHidden = this.checkConditionallyHidden(); + visible = !this.conditionallyHidden; + } + else { + visible = !this.component.hidden; + } + const shouldSetContent = !this.builderMode + && this.component.refreshOnChange + && this.element + && !_.isUndefined(changed) + && ((_.isBoolean(changed) && changed) || !_.isEmpty(changed)) + && visible; + + if (shouldSetContent) { this.setContent(this.element, this.renderContent()); } } diff --git a/test/unit/EditGrid.unit.js b/test/unit/EditGrid.unit.js index 47773d913c..1e8e574bbf 100644 --- a/test/unit/EditGrid.unit.js +++ b/test/unit/EditGrid.unit.js @@ -1273,30 +1273,6 @@ describe('EditGrid Component', () => { }).catch(done); }); - it('Should keep value for conditional editGrid on setValue when server option is provided', (done) => { - const element = document.createElement('div'); - - Formio.createForm(element, formsWithEditGridAndConditions.form1, { server: true }).then(form => { - const formData = { - checkbox: true, - radio: 'yes', - editGrid: [ - { textField: 'test', number: 4 }, - { textField: 'test1', number: 5 }, - ], - }; - - form.setValue({ data: _.cloneDeep(formData) }); - - setTimeout(() => { - const editGrid = form.getComponent('editGrid'); - assert.deepEqual(editGrid.dataValue, formData.editGrid); - - done(); - }, 500); - }).catch(done); - }); - it('Should set value for conditional editGrid inside editGrid on event when form is not pristine ', (done) => { const element = document.createElement('div'); @@ -1319,85 +1295,6 @@ describe('EditGrid Component', () => { }).catch(done); }); - it('Should keep value for conditional editGrid in tabs on setValue when server option is provided', (done) => { - const element = document.createElement('div'); - - Formio.createForm(element, formsWithEditGridAndConditions.form3, { server: true }).then(form => { - const formData = { - affectedRiskTypes: { - creditRisk: false, - marketRisk: true, - operationalRisk: false, - counterpartyCreditRisk: false, - creditValuationRiskAdjustment: false, - }, - rwaImpact: 'yes', - submit: true, - mr: { - quantitativeInformation: { - cva: 'yes', - sameRiskCategories: false, - impactsPerEntity: [{ number: 123 }], - sameImpactAcrossEntities: false, - }, - }, - euParentInstitution: 'EUParent', - }; - - form.setValue({ data: _.cloneDeep(formData) }); - - setTimeout(() => { - const editGrid = form.getComponent('impactsPerEntity'); - assert.deepEqual(editGrid.dataValue, formData.mr.quantitativeInformation.impactsPerEntity); - assert.deepEqual(editGrid.editRows.length, 1); - - done(); - }, 500); - }).catch(done); - }); - - it('Should calculate editGrid value when calculateOnServer is enabled and server option is passed', (done) => { - const element = document.createElement('div'); - - Formio.createForm(element, formsWithEditGridAndConditions.form4, { server: true }).then(form => { - const editGrid = form.getComponent('editGrid'); - assert.deepEqual(editGrid.dataValue, [{ textArea: 'test' }]); - assert.deepEqual(editGrid.editRows.length, 1); - done(); - }).catch(done); - }); - - it('Should keep value for conditional editGrid deeply nested in panels and containers on setValue when server option is provided', (done) => { - const element = document.createElement('div'); - - Formio.createForm(element, formsWithEditGridAndConditions.form5, { server: true }).then(form => { - const formData = { - generalInformation: { - listSupervisedEntitiesCovered: [ - { id: 6256, longName: 'Bank_DE', leiCode: 'LEI6256', countryCode: 'DE' }, - ], - deSpecific: { - criticalPartsToBeOutsourcedSuboutsourcer: 'yes', - suboutsourcers: [ - { nameSuboutsourcer: 'test' }, - { nameSuboutsourcer: 'test 1' }, - ], - }, - }, - }; - - form.setValue({ data: _.cloneDeep(formData) }); - - setTimeout(() => { - const editGrid = form.getComponent('suboutsourcers'); - assert.deepEqual(editGrid.dataValue, formData.generalInformation.deSpecific.suboutsourcers); - assert.deepEqual(editGrid.editRows.length, 2); - - done(); - }, 500); - }).catch(done); - }); - it('Should calculate editGrid value when condition is met in advanced logic', (done) => { const element = document.createElement('div'); diff --git a/test/unit/NestedComponent.unit.js b/test/unit/NestedComponent.unit.js index 6b994d093c..15f54dd3a4 100644 --- a/test/unit/NestedComponent.unit.js +++ b/test/unit/NestedComponent.unit.js @@ -123,7 +123,7 @@ describe('NestedComponent class', () => { comp.setValue(data); comp.checkConditions(data); assert.equal(comp.components[1]._visible, false); - assert.equal(comp.components[1].components[0]._visible, true); + assert.equal(comp.components[1].components[0]._visible, false); assert.equal(comp.components[1].components[1]._visible, false); // overrideParent is depricated. @@ -131,8 +131,8 @@ describe('NestedComponent class', () => { comp.setValue(data); comp.checkConditions(data); assert.equal(comp.components[1]._visible, false); - assert.equal(comp.components[1].components[0]._visible, true); - assert.equal(comp.components[1].components[1]._visible, true); + assert.equal(comp.components[1].components[0]._visible, false); + assert.equal(comp.components[1].components[1]._visible, false); }); }); diff --git a/test/unit/clearOnHide.js b/test/unit/clearOnHide.js new file mode 100644 index 0000000000..dafeea1c6e --- /dev/null +++ b/test/unit/clearOnHide.js @@ -0,0 +1,1214 @@ +import Harness from '../harness'; +import assert from 'power-assert'; +import { Formio } from '../../src/Formio'; +import { wait } from '../util'; + +describe('Clear on Hide (Omit When Conditionally Hidden) Behavior', function () { + describe('Layout components', function () { + it('Should conditionally hide children of conditionally hidden layout parents', async function () { + const formWithConditionallyHiddenPanel = { + components: [ + { + type: 'checkbox', + key: 'checkbox', + label: 'Checkbox', + input: true, + }, + { + type: 'textfield', + key: 'textField', + label: 'Text Field', + input: true, + }, + { + type: 'panel', + key: 'panel', + components: [ + { + type: 'textfield', + key: 'childTextField', + label: 'Text Field', + input: true, + }, + ], + conditional: { + json: { '!': { var: 'data.checkbox' } }, + }, + }, + ], + }; + const element = document.createElement('div'); + const form = await Formio.createForm( + element, + formWithConditionallyHiddenPanel + ); + const checkbox = form.getComponent('checkbox'); + const textField = form.getComponent('textField'); + const panel = form.getComponent('panel'); + const childTextField = form.getComponent('childTextField'); + assert(checkbox, 'Checkbox component not found'); + assert(textField, 'Text Field component not found'); + assert(panel, 'Panel component not found'); + assert(childTextField, 'Child Text Field component not found'); + + // Initially, all components should be visible. + assert.equal(checkbox.visible, true); + assert.equal(textField.visible, true); + assert.equal(panel.visible, true); + assert.equal(childTextField.visible, true); + + // Initially, all components should not be conditionally hidden + assert.equal(checkbox.conditionallyHidden, false); + assert.equal(textField.conditionallyHidden, false); + assert.equal(panel.conditionallyHidden, false); + assert.equal(childTextField.conditionallyHidden, false); + + // Set the checkbox to true, which should hide the panel and its children + await form.setSubmission({ data: { checkbox: true } }); + await wait(250); + assert.equal(checkbox.visible, true); + assert.equal(textField.visible, true); + assert.equal(panel.visible, false); + assert.equal(childTextField.visible, false); + + // They should also be conditionally hidden + assert.equal(checkbox.conditionallyHidden, false); + assert.equal(textField.conditionallyHidden, false); + assert.equal(panel.conditionallyHidden, true); + assert.equal(childTextField.conditionallyHidden, true); + }); + + it('Should not conditionally hide children of layout components that are hidden using the "hidden" property', async function () { + const formWithHiddenPanel = { + components: [ + { + type: 'panel', + key: 'panel', + components: [ + { + type: 'textfield', + key: 'childTextField', + label: 'Text Field', + input: true, + }, + ], + hidden: true, + }, + ], + }; + const element = document.createElement('div'); + const form = await Formio.createForm(element, formWithHiddenPanel); + const panel = form.getComponent('panel'); + const childTextField = form.getComponent('childTextField'); + assert(panel, 'Panel component not found'); + assert(childTextField, 'Child Text Field component not found'); + + // All components should not be visible + assert.equal(panel.visible, false); + assert.equal(childTextField.visible, false); + + // All components should NOT be conditionally hidden + assert.equal( + panel.conditionallyHidden, + false, + 'Panel should not be conditionally hidden' + ); + assert.equal(childTextField.conditionallyHidden, false); + }); + + it('Should conditionally hide children of a manually hidden layout component if they have a conditional', async function () { + const formWithHiddenPanelAndConditionalChild = { + components: [ + { + type: 'checkbox', + input: true, + label: 'Checkbox', + key: 'checkbox', + }, + { + type: 'panel', + key: 'panel', + components: [ + { + type: 'textfield', + key: 'childTextField', + label: 'Text Field', + input: true, + conditional: { + json: { '!': { var: 'data.checkbox' } }, + }, + }, + ], + hidden: true, + }, + ], + }; + const element = document.createElement('div'); + const form = await Formio.createForm( + element, + formWithHiddenPanelAndConditionalChild + ); + const checkbox = form.getComponent('checkbox'); + const panel = form.getComponent('panel'); + const childTextField = form.getComponent('childTextField'); + assert(checkbox, 'Checkbox component not found'); + assert(panel, 'Panel component not found'); + assert(childTextField, 'Child Text Field component not found'); + + // The panel and its child should not be visible + assert.equal(checkbox.visible, true); + assert.equal(panel.visible, false); + assert.equal(childTextField.visible, false); + + // Initially, the panel and its child should NOT be conditionally hidden + assert.equal(checkbox.conditionallyHidden, false); + assert.equal(panel.conditionallyHidden, false); + assert.equal(childTextField.conditionallyHidden, false); + + // Set the checkbox to true, which should conditionally hide the child + await form.setSubmission({ data: { checkbox: true } }); + await wait(250); + assert.equal(checkbox.visible, true); + assert.equal(panel.visible, false); + assert.equal(childTextField.visible, false); + assert.equal(panel.conditionallyHidden, false); + assert.equal(childTextField.conditionallyHidden, true); + }); + + it('Should not clear the value of a conditionally hidden child component of a hidden layout component when hiding if the form is pristine', async function () { + const formWithHiddenPanelAndConditionalChild = { + components: [ + { + type: 'checkbox', + input: true, + label: 'Checkbox', + key: 'checkbox', + }, + { + type: 'panel', + key: 'panel', + components: [ + { + type: 'textfield', + key: 'childTextField', + label: 'Text Field', + input: true, + conditional: { + json: { '!': { var: 'data.checkbox' } }, + }, + }, + ], + hidden: true, + }, + ], + }; + const element = document.createElement('div'); + const form = await Formio.createForm( + element, + formWithHiddenPanelAndConditionalChild + ); + const checkbox = form.getComponent('checkbox'); + const panel = form.getComponent('panel'); + const childTextField = form.getComponent('childTextField'); + assert(checkbox, 'Checkbox component not found'); + assert(panel, 'Panel component not found'); + assert(childTextField, 'Child Text Field component not found'); + + assert.equal(form.pristine, true, 'Form should be pristine'); + + // Initially, all components should not be conditionally hidden + assert.equal( + checkbox.conditionallyHidden, + false, + 'Checkbox should not be conditionally hidden' + ); + assert.equal( + panel.conditionallyHidden, + false, + 'Panel should not be conditionally hidden' + ); + assert.equal( + childTextField.conditionallyHidden, + false, + 'Child Text Field should not be conditionally hidden' + ); + + assert.deepEqual( + form.data, + { checkbox: false, childTextField: '' }, + 'Initial form data is incorrect' + ); + + // Hide the panel, which should clear the value of the child text field + await form.setSubmission({ + data: { checkbox: true, childTextField: 'test' }, + }); + await wait(250); + assert.equal( + childTextField.conditionallyHidden, + true, + 'Child Text Field should be conditionally hidden' + ); + assert.deepEqual( + form.data, + { checkbox: true, childTextField: 'test' }, + 'Form data is incorrect' + ); + }); + + it('Should clear the value of a conditionally hidden child component of a hidden layout component when hiding if the form is not pristine', async function () { + const formWithHiddenPanelAndConditionalChild = { + components: [ + { + type: 'checkbox', + input: true, + label: 'Checkbox', + key: 'checkbox', + }, + { + type: 'panel', + key: 'panel', + components: [ + { + type: 'textfield', + key: 'childTextField', + label: 'Text Field', + input: true, + conditional: { + json: { '!': { var: 'data.checkbox' } }, + }, + }, + ], + hidden: true, + }, + ], + }; + const element = document.createElement('div'); + const form = await Formio.createForm( + element, + formWithHiddenPanelAndConditionalChild + ); + const checkbox = form.getComponent('checkbox'); + const panel = form.getComponent('panel'); + const childTextField = form.getComponent('childTextField'); + assert(checkbox, 'Checkbox component not found'); + assert(panel, 'Panel component not found'); + assert(childTextField, 'Child Text Field component not found'); + + assert.equal(form.pristine, true, 'Form should be pristine'); + + // Initially, all components should not be conditionally hidden + assert.equal( + checkbox.conditionallyHidden, + false, + 'Checkbox should not be conditionally hidden' + ); + assert.equal( + panel.conditionallyHidden, + false, + 'Panel should not be conditionally hidden' + ); + assert.equal( + childTextField.conditionallyHidden, + false, + 'Child Text Field should not be conditionally hidden' + ); + + assert.deepEqual( + form.data, + { checkbox: false, childTextField: '' }, + 'Initial form data is incorrect' + ); + + // Hide the panel, which should clear the value of the child text field + form.pristine = false; + await form.setSubmission({ data: { checkbox: true } }); + await wait(250); + assert.equal( + childTextField.conditionallyHidden, + true, + 'Child Text Field should be conditionally hidden' + ); + assert.deepEqual(form.data, { checkbox: true }, 'Form data is incorrect'); + }); + }); + + describe('Container components', function () { + it('Should conditionally hide children of conditionally hidden container parents', async function () { + const formWithConditionallyHiddenContainer = { + components: [ + { + type: 'checkbox', + key: 'checkbox', + label: 'Checkbox', + input: true, + }, + { + type: 'textfield', + key: 'textField', + label: 'Text Field', + input: true, + }, + { + type: 'container', + key: 'container', + components: [ + { + type: 'textfield', + key: 'childTextField', + label: 'Text Field', + input: true, + }, + ], + conditional: { + json: { '!': { var: 'data.checkbox' } }, + }, + }, + ], + }; + + const element = document.createElement('div'); + const form = await Formio.createForm( + element, + formWithConditionallyHiddenContainer + ); + const checkbox = form.getComponent('checkbox'); + const textField = form.getComponent('textField'); + const container = form.getComponent('container'); + const childTextField = form.getComponent('childTextField'); + assert(checkbox, 'Checkbox component not found'); + assert(textField, 'Text Field component not found'); + assert(container, 'Container component not found'); + assert(childTextField, 'Child Text Field component not found'); + + // Initially, all components should be visible. + assert.equal(checkbox.visible, true); + assert.equal(textField.visible, true); + assert.equal(container.visible, true); + assert.equal(childTextField.visible, true); + + // Initially, all components should not be conditionally hidden + assert.equal(checkbox.conditionallyHidden, false); + assert.equal(textField.conditionallyHidden, false); + assert.equal(container.conditionallyHidden, false); + assert.equal(childTextField.conditionallyHidden, false); + + // Set the checkbox to true, which should hide the container and its children + await form.setSubmission({ data: { checkbox: true } }); + await wait(250); + assert.equal(checkbox.visible, true, 'Checkbox should be visible'); + assert.equal(textField.visible, true, 'Text Field should be visible'); + assert.equal(container.visible, false, 'Container should be hidden'); + assert.equal( + childTextField.visible, + false, + 'Child Text Field should be hidden' + ); + + // They should also be conditionally hidden + assert.equal( + checkbox.conditionallyHidden, + false, + 'Checkbox should not be conditionally hidden' + ); + assert.equal( + textField.conditionallyHidden, + false, + 'Text Field should not be conditionally hidden' + ); + assert.equal( + container.conditionallyHidden, + true, + 'Container should be conditionally hidden' + ); + assert.equal( + childTextField.conditionallyHidden, + true, + 'Child Text Field should be conditionally hidden' + ); + }); + + it('Should not conditionally hide children of container components that are hidden using the "hidden" property', async function () { + const formWithHiddenContainer = { + components: [ + { + type: 'container', + key: 'container', + components: [ + { + type: 'textfield', + key: 'childTextField', + label: 'Text Field', + input: true, + }, + ], + hidden: true, + }, + ], + }; + const element = document.createElement('div'); + const form = await Formio.createForm(element, formWithHiddenContainer); + const container = form.getComponent('container'); + const childTextField = form.getComponent('childTextField'); + assert(container, 'Container component not found'); + assert(childTextField, 'Child Text Field component not found'); + + // All components should not be visible + assert.equal(container.visible, false, 'Container should not be visible'); + assert.equal(childTextField.visible, false), + 'Child Text Field should not be visible'; + + // All components should NOT be conditionally hidden + assert.equal( + container.conditionallyHidden, + false, + 'Container should not be conditionally hidden' + ); + assert.equal(childTextField.conditionallyHidden, false); + }); + + it('Should conditionally hide children of a manually hidden container component if they have a conditional', async function () { + const formWithHiddenContainerAndConditionalChild = { + components: [ + { + type: 'checkbox', + input: true, + label: 'Checkbox', + key: 'checkbox', + }, + { + type: 'container', + key: 'container', + components: [ + { + type: 'textfield', + key: 'childTextField', + label: 'Text Field', + input: true, + conditional: { + json: { '!': { var: 'data.checkbox' } }, + }, + }, + ], + hidden: true, + }, + ], + }; + const element = document.createElement('div'); + const form = await Formio.createForm( + element, + formWithHiddenContainerAndConditionalChild + ); + const checkbox = form.getComponent('checkbox'); + const container = form.getComponent('container'); + const childTextField = form.getComponent('childTextField'); + assert(checkbox, 'Checkbox component not found'); + assert(container, 'Container component not found'); + assert(childTextField, 'Child Text Field component not found'); + + // The panel and its child should not be visible + assert.equal(checkbox.visible, true); + assert.equal(container.visible, false); + assert.equal(childTextField.visible, false); + + // Initially, the panel and its child should NOT be conditionally hidden + assert.equal(checkbox.conditionallyHidden, false); + assert.equal(container.conditionallyHidden, false); + assert.equal(childTextField.conditionallyHidden, false); + + // Set the checkbox to true, which should conditionally hide the child + await form.setSubmission({ data: { checkbox: true } }); + await wait(250); + assert.equal(checkbox.visible, true); + assert.equal(container.visible, false); + assert.equal(childTextField.visible, false); + assert.equal(container.conditionallyHidden, false); + assert.equal(childTextField.conditionallyHidden, true); + }); + + it('Should not clear the value of a conditionally hidden child component of a hidden container component when hiding if the form is pristine', async function () { + const formWithHiddenContainerAndConditionalChild = { + components: [ + { + type: 'checkbox', + input: true, + label: 'Checkbox', + key: 'checkbox', + }, + { + type: 'container', + key: 'container', + components: [ + { + type: 'textfield', + key: 'childTextField', + label: 'Text Field', + input: true, + conditional: { + json: { '!': { var: 'data.checkbox' } }, + }, + }, + ], + hidden: true, + }, + ], + }; + const element = document.createElement('div'); + const form = await Formio.createForm( + element, + formWithHiddenContainerAndConditionalChild + ); + const checkbox = form.getComponent('checkbox'); + const container = form.getComponent('container'); + const childTextField = form.getComponent('childTextField'); + assert(checkbox, 'Checkbox component not found'); + assert(container, 'Container component not found'); + assert(childTextField, 'Child Text Field component not found'); + + assert.equal(form.pristine, true, 'Form should be pristine'); + + // Initially, all components should not be conditionally hidden + assert.equal( + checkbox.conditionallyHidden, + false, + 'Checkbox should not be conditionally hidden' + ); + assert.equal( + container.conditionallyHidden, + false, + 'Container should not be conditionally hidden' + ); + assert.equal( + childTextField.conditionallyHidden, + false, + 'Child Text Field should not be conditionally hidden' + ); + + assert.deepEqual( + form.data, + { checkbox: false, container: { childTextField: '' } }, + 'Initial form data is incorrect' + ); + + // Hide the panel, which should NOT clear the value of the child text field because the form is pristine + await form.setSubmission({ + data: { checkbox: true, container: { childTextField: 'test' } }, + }); + await wait(250); + assert.equal( + childTextField.conditionallyHidden, + true, + 'Child Text Field should be conditionally hidden' + ); + assert.deepEqual( + form.data, + { checkbox: true, container: { childTextField: 'test' } }, + 'Form data is incorrect' + ); + }); + + it('Should clear the value of a conditionally hidden child component of a hidden container component when hiding if the form is not pristine', async function () { + const formWithHiddenContainerAndConditionalChild = { + components: [ + { + type: 'checkbox', + input: true, + label: 'Checkbox', + key: 'checkbox', + }, + { + type: 'container', + key: 'container', + components: [ + { + type: 'textfield', + key: 'childTextField', + label: 'Text Field', + input: true, + conditional: { + json: { '!': { var: 'data.checkbox' } }, + }, + }, + ], + hidden: true, + }, + ], + }; + const element = document.createElement('div'); + const form = await Formio.createForm( + element, + formWithHiddenContainerAndConditionalChild + ); + const checkbox = form.getComponent('checkbox'); + const container = form.getComponent('container'); + const childTextField = form.getComponent('childTextField'); + assert(checkbox, 'Checkbox component not found'); + assert(container, 'Container component not found'); + assert(childTextField, 'Child Text Field component not found'); + + assert.equal(form.pristine, true, 'Form should be pristine'); + + // Initially, all components should not be conditionally hidden + assert.equal( + checkbox.conditionallyHidden, + false, + 'Checkbox should not be conditionally hidden' + ); + assert.equal( + container.conditionallyHidden, + false, + 'Container should not be conditionally hidden' + ); + assert.equal( + childTextField.conditionallyHidden, + false, + 'Child Text Field should not be conditionally hidden' + ); + + assert.deepEqual( + form.data, + { checkbox: false, container: { childTextField: '' } }, + 'Initial form data is incorrect' + ); + + // Hide the panel, which should clear the value of the child container + form.pristine = false; + await form.setSubmission({ data: { checkbox: true } }); + await wait(250); + assert.equal( + childTextField.conditionallyHidden, + true, + 'Child Text Field should be conditionally hidden' + ); + assert.deepEqual( + form.data, + { checkbox: true, container: {} }, + 'Form data is incorrect' + ); + }); + }); + + describe('Nested form components', function () { + let oldMakeRequest; + before(function () { + oldMakeRequest = Formio.makeRequest; + Formio.makeRequest = (formio, type, url, method, data) => { + if (type === 'form' && method === 'get') { + return Promise.resolve({ + type: 'form', + components: [ + { + label: 'Nested First Name', + tableView: true, + key: 'nestedFirstName', + type: 'textfield', + input: true, + }, + { + label: 'Nested Last Name', + tableView: true, + key: 'nestedLastName', + type: 'textfield', + input: true, + }, + { + type: 'container', + key: 'nestedContainer', + components: [ + { + label: 'Nested Container Field', + tableView: true, + key: 'nestedContainerField', + type: 'textfield', + input: true, + }, + ], + }, + ], + }); + } + if ( + type === 'submission' && + method === 'get' && + url.includes('nestedFormSubmissionId') + ) { + return Promise.resolve({ + _id: 'nestedFormSubmissionId', + form: 'nestedFormId', + owner: 'nestedFormOwnerId', + data: { + nestedFirstName: 'Nested First Name', + nestedLastName: 'Nested Last Name', + nestedContainer: { + nestedContainerField: 'Nested Container Field', + }, + }, + project: 'nestedFormProjectId', + }); + } + throw new Error('Invalid request'); + }; + }); + + it('Should not conditionally hide intentionally hidden Nested Form components', async function () { + const parentFormWithIntentionallyHiddenChild = { + components: [ + { + label: 'Parent Form Checkbox', + tableView: true, + key: 'checkbox', + type: 'checkbox', + input: true, + }, + { + label: 'Form', + tableView: true, + // Should resolve to the nested form in the before block + src: 'http://localhost:3000/myproject/child', + key: 'form', + type: 'form', + input: true, + hidden: true, + }, + ], + }; + const form = await Formio.createForm( + document.createElement('div'), + parentFormWithIntentionallyHiddenChild + ); + const checkbox = form.getComponent('checkbox'); + const nestedForm = form.getComponent('form'); + assert(checkbox, 'Parent component not found'); + assert(nestedForm, 'Nested Form component not found'); + + assert.equal(checkbox.visible, true, 'Checkbox should be visible'); + assert.equal(nestedForm.visible, false, 'Nested Form should be hidden'); + assert.equal( + checkbox.conditionallyHidden, + false, + 'Checkbox should not be conditionally hidden' + ); + assert.equal( + nestedForm.conditionallyHidden, + false, + 'Nested Form should not be conditionally hidden' + ); + }); + + it('Should conditionally hide conditionally hidden Nested Form components', async function () { + const parentFormWithConditionallyHiddenChild = { + components: [ + { + label: 'Parent Form Checkbox', + tableView: true, + key: 'checkbox', + type: 'checkbox', + input: true, + }, + { + label: 'Form', + tableView: true, + // Should resolve to the nested form in the before block + src: 'http://localhost:3000/myproject/child', + key: 'form', + type: 'form', + input: true, + conditional: { + json: { var: 'data.checkbox' }, + }, + }, + ], + }; + const form = await Formio.createForm( + document.createElement('div'), + parentFormWithConditionallyHiddenChild + ); + const checkbox = form.getComponent('checkbox'); + const nestedForm = form.getComponent('form'); + assert(checkbox, 'Parent component not found'); + assert(nestedForm, 'Nested Form component not found'); + + assert.equal(checkbox.visible, true, 'Checkbox should be visible'); + assert.equal(nestedForm.visible, false, 'Nested Form should be hidden'); + assert.equal( + checkbox.conditionallyHidden, + false, + 'Checkbox should not be conditionally hidden' + ); + assert.equal( + nestedForm.conditionallyHidden, + true, + 'Nested Form should be conditionally hidden' + ); + }); + + it('Should not clear the data of an intentionally hidden Nested Form component', async function () { + const parentFormWithIntentionallyHiddenChild = { + components: [ + { + label: 'Parent Form Checkbox', + tableView: true, + key: 'checkbox', + type: 'checkbox', + input: true, + }, + { + label: 'Form', + tableView: true, + // Should resolve to the nested form in the before block + src: 'http://localhost:3000/myproject/child', + key: 'form', + type: 'form', + input: true, + hidden: true, + }, + ], + }; + const form = await Formio.createForm( + document.createElement('div'), + parentFormWithIntentionallyHiddenChild + ); + const checkbox = form.getComponent('checkbox'); + const nestedForm = form.getComponent('form'); + assert(checkbox, 'Parent component not found'); + assert(nestedForm, 'Nested Form component not found'); + + assert.deepEqual( + form.data, + { + checkbox: false, + form: { + data: { + nestedFirstName: '', + nestedLastName: '', + nestedContainer: { nestedContainerField: '' }, + }, + metadata: {} + }, + }, + 'Initial form data is incorrect' + ); + + await form.setSubmission({ + data: { + checkbox: true, + form: { _id: 'nestedFormSubmissionId' }, + }, + }); + await wait(250); + assert.deepEqual( + form.data, + { + checkbox: true, + form: { + _id: 'nestedFormSubmissionId', + data: { + nestedFirstName: 'Nested First Name', + nestedLastName: 'Nested Last Name', + nestedContainer: { nestedContainerField: 'Nested Container Field' }, + }, + form: 'nestedFormId', + owner: 'nestedFormOwnerId', + project: 'nestedFormProjectId', + }, + }, + 'Form data is incorrect' + ); + }); + + it('Should populate the data of a conditionally shown Nested Form component', async function () { + const parentFormWithConditionallyHiddenChild = { + components: [ + { + label: 'Parent Form Checkbox', + tableView: true, + key: 'checkbox', + type: 'checkbox', + input: true, + }, + { + label: 'Form', + tableView: true, + // Should resolve to the nested form in the before block + src: 'http://localhost:3000/myproject/child', + key: 'form', + type: 'form', + input: true, + conditional: { + json: { var: 'data.checkbox' }, + } + }, + ], + }; + const form = await Formio.createForm( + document.createElement('div'), + parentFormWithConditionallyHiddenChild + ); + const checkbox = form.getComponent('checkbox'); + const nestedForm = form.getComponent('form'); + assert(checkbox, 'Parent component not found'); + assert(nestedForm, 'Nested Form component not found'); + + assert.deepEqual( + form.data, + { + checkbox: false, + }, + 'Initial form data is incorrect' + ); + + await form.setSubmission({ + data: { + checkbox: true, + form: { _id: 'nestedFormSubmissionId' }, + }, + }); + await wait(400); + assert.deepEqual( + form.data, + { + checkbox: true, + form: { + _id: 'nestedFormSubmissionId', + data: { + nestedFirstName: 'Nested First Name', + nestedLastName: 'Nested Last Name', + nestedContainer: { nestedContainerField: 'Nested Container Field' }, + }, + form: 'nestedFormId', + owner: 'nestedFormOwnerId', + project: 'nestedFormProjectId', + metadata: {} + }, + }, + 'Form data is incorrect' + ); + }); + + it('Should not clear the data of a conditionally hidden Nested Form component if the form is pristine', async function () { + const parentFormWithConditionallyHiddenChild = { + components: [ + { + label: 'Parent Form Checkbox', + tableView: true, + key: 'checkbox', + type: 'checkbox', + input: true, + }, + { + label: 'Form', + tableView: true, + // Should resolve to the nested form in the before block + src: 'http://localhost:3000/myproject/child', + key: 'form', + type: 'form', + input: true, + conditional: { + json: { '!': { var: 'data.checkbox' } }, + } + }, + ], + }; + const form = await Formio.createForm( + document.createElement('div'), + parentFormWithConditionallyHiddenChild + ); + await wait(200); + const checkbox = form.getComponent('checkbox'); + const nestedForm = form.getComponent('form'); + assert(checkbox, 'Parent component not found'); + assert(nestedForm, 'Nested Form component not found'); + + assert.equal(checkbox.visible, true, 'Checkbox should be visible'); + assert.equal(nestedForm.visible, true, 'Nested Form should be visible'); + assert.equal(checkbox.conditionallyHidden, false, 'Checkbox should not be conditionally hidden'); + assert.equal(nestedForm.conditionallyHidden, false, 'Nested Form should not be conditionally hidden'); + + assert.deepEqual( + form.data, + { + checkbox: false, + form: { + data: { + nestedFirstName: '', + nestedLastName: '', + nestedContainer: { nestedContainerField: '' }, + }, + metadata: {} + }, + }, + 'Initial form data is incorrect' + ); + + await form.setSubmission({ + data: { + checkbox: true, + }, + }); + await wait(400); + assert.deepEqual( + form.data, + { + checkbox: true, + form: { + data: { + nestedFirstName: '', + nestedLastName: '', + nestedContainer: { nestedContainerField: '' }, + }, + metadata: {} + }, + }, + 'Form data is incorrect' + ); + }); + + it('Should clear the data of a conditionally hidden Nested Form component if the form is not pristine', async function () { + const parentFormWithConditionallyHiddenChild = { + components: [ + { + label: 'Parent Form Checkbox', + tableView: true, + key: 'checkbox', + type: 'checkbox', + input: true, + }, + { + label: 'Form', + tableView: true, + // Should resolve to the nested form in the before block + src: 'http://localhost:3000/myproject/child', + key: 'form', + type: 'form', + input: true, + conditional: { + json: { '!': { var: 'data.checkbox' } }, + } + }, + ], + }; + const form = await Formio.createForm( + document.createElement('div'), + parentFormWithConditionallyHiddenChild + ); + await wait(200); + const checkbox = form.getComponent('checkbox'); + const nestedForm = form.getComponent('form'); + assert(checkbox, 'Parent component not found'); + assert(nestedForm, 'Nested Form component not found'); + + assert.equal(checkbox.visible, true, 'Checkbox should be visible'); + assert.equal(nestedForm.visible, true, 'Nested Form should be visible'); + assert.equal(checkbox.conditionallyHidden, false, 'Checkbox should not be conditionally hidden'); + assert.equal(nestedForm.conditionallyHidden, false, 'Nested Form should not be conditionally hidden'); + + assert.deepEqual( + form.data, + { + checkbox: false, + form: { + data: { + nestedFirstName: '', + nestedLastName: '', + nestedContainer: { nestedContainerField: '' }, + }, + metadata: {} + }, + }, + 'Initial form data is incorrect' + ); + + form.pristine = false; + await form.setSubmission({ + data: { + checkbox: true, + }, + }); + await wait(400); + assert.deepEqual( + form.data, + { + checkbox: true, + }, + 'Form data is incorrect' + ); + }); + + it('Should clear the submission data of a conditionally hidden Nested Form component when hiding and the form is not pristine', async function () { + const parentFormWithConditionallyHiddenChild = { + components: [ + { + label: 'Parent Form Checkbox', + tableView: true, + key: 'checkbox', + type: 'checkbox', + input: true, + }, + { + label: 'Form', + tableView: true, + // Should resolve to the nested form in the before block + src: 'http://localhost:3000/myproject/child', + key: 'form', + type: 'form', + input: true, + conditional: { + json: { '!': { var: 'data.checkbox' } }, + } + }, + ], + }; + const form = await Formio.createForm( + document.createElement('div'), + parentFormWithConditionallyHiddenChild + ); + await wait(200); + const checkbox = form.getComponent('checkbox'); + const nestedForm = form.getComponent('form'); + assert(checkbox, 'Parent component not found'); + assert(nestedForm, 'Nested Form component not found'); + + assert.equal(checkbox.visible, true, 'Checkbox should be visible'); + assert.equal(nestedForm.visible, true, 'Nested Form should be visible'); + assert.equal(checkbox.conditionallyHidden, false, 'Checkbox should not be conditionally hidden'); + assert.equal(nestedForm.conditionallyHidden, false, 'Nested Form should not be conditionally hidden'); + + assert.deepEqual( + form.data, + { + checkbox: false, + form: { + data: { + nestedFirstName: '', + nestedLastName: '', + nestedContainer: { nestedContainerField: '' }, + }, + metadata: {} + } + }, + 'Initial form data is incorrect' + ); + + form.pristine = false; + // Hide the nested form AND set its submission id + await form.setSubmission({ + data: { + checkbox: true, + form: { _id: 'nestedFormSubmissionId' }, + }, + }); + await wait(300); + assert.deepEqual( + form.data, + { + checkbox: true, + }, + 'Form data is incorrect' + ); + }); + + after(function () { + Formio.makeRequest = oldMakeRequest; + }); + }); +}); diff --git a/test/util.js b/test/util.js index e10b7c5305..8aeba7ae38 100644 --- a/test/util.js +++ b/test/util.js @@ -1,3 +1,3 @@ export function wait(ms) { - return new Promise(resolve => setTimeout(resolve, ms)) -} \ No newline at end of file + return new Promise((resolve) => setTimeout(resolve, ms)); +}