From e1b399447cf6dc23de3c39c3f54e87e47240a71b Mon Sep 17 00:00:00 2001 From: Aliaksandra Ramanenka Date: Fri, 8 Nov 2024 16:21:54 +0200 Subject: [PATCH 1/2] FIO-9267,FIO9268: Fixes an issue where nested forms will be not validated if there are no data provided --- src/utils/formUtil/eachComponentData.ts | 18 ++++++++++-------- src/utils/formUtil/eachComponentDataAsync.ts | 20 +++++++++++--------- 2 files changed, 21 insertions(+), 17 deletions(-) diff --git a/src/utils/formUtil/eachComponentData.ts b/src/utils/formUtil/eachComponentData.ts index 10e4a673..c7309161 100644 --- a/src/utils/formUtil/eachComponentData.ts +++ b/src/utils/formUtil/eachComponentData.ts @@ -27,7 +27,7 @@ export const eachComponentData = ( parent?: Component, includeAll: boolean = false, ) => { - if (!components || !data) { + if (!components) { return; } return eachComponent( @@ -61,14 +61,16 @@ export const eachComponentData = ( return true; } if (getModelType(component) === 'dataObject') { - // No need to bother processing all the children data if there is no data for this form or the reference value has not been loaded. const nestedFormValue: any = get(data, component.path); - const noReferenceAttached = - nestedFormValue?._id && isEmpty(nestedFormValue.data) && !has(nestedFormValue, 'form'); - const shouldProcessNestedFormData = nestedFormValue?._id - ? !noReferenceAttached - : has(data, component.path); - if (shouldProcessNestedFormData) { + const noReferenceAttached = nestedFormValue?._id + ? isEmpty(nestedFormValue.data) && !has(nestedFormValue, 'form') + : false; + const shouldBeCleared = + (!component.hasOwnProperty('clearOnHide') || component.clearOnHide) && + (component.hidden || component.ephemeralState?.conditionallyHidden); + // Skip all the nested components processing if nested form is hidden and should be cleared on hide or if submission is saved as reference and not loaded + const shouldSkipProcessingNestedFormData = noReferenceAttached || shouldBeCleared; + if (!shouldSkipProcessingNestedFormData) { // For nested forms, we need to reset the "data" and "path" objects for all of the children components, and then re-establish the data when it is done. const childPath: string = componentDataPath(component, path, compPath); const childData: any = get(data, childPath, {}); diff --git a/src/utils/formUtil/eachComponentDataAsync.ts b/src/utils/formUtil/eachComponentDataAsync.ts index 3889be0e..314d7190 100644 --- a/src/utils/formUtil/eachComponentDataAsync.ts +++ b/src/utils/formUtil/eachComponentDataAsync.ts @@ -1,4 +1,4 @@ -import { get, set, has, isEmpty } from 'lodash'; +import { get, set, isEmpty, has } from 'lodash'; import { Component, @@ -28,7 +28,7 @@ export const eachComponentDataAsync = async ( parent?: Component, includeAll: boolean = false, ) => { - if (!components || !data) { + if (!components) { return; } return await eachComponentAsync( @@ -64,14 +64,16 @@ export const eachComponentDataAsync = async ( return true; } if (getModelType(component) === 'dataObject') { - // No need to bother processing all the children data if there is no data for this form or the reference value has not been loaded. const nestedFormValue: any = get(data, component.path); - const noReferenceAttached = - nestedFormValue?._id && isEmpty(nestedFormValue.data) && !has(nestedFormValue, 'form'); - const shouldProcessNestedFormData = nestedFormValue?._id - ? !noReferenceAttached - : has(data, component.path); - if (shouldProcessNestedFormData) { + const noReferenceAttached = nestedFormValue?._id + ? isEmpty(nestedFormValue.data) && !has(nestedFormValue, 'form') + : false; + const shouldBeCleared = + (!component.hasOwnProperty('clearOnHide') || component.clearOnHide) && + (component.hidden || component.ephemeralState?.conditionallyHidden); + // Skip all the nested components processing if nested form is hidden and should be cleared on hide or if submission is saved as reference and not loaded + const shouldSkipProcessingNestedFormData = noReferenceAttached || shouldBeCleared; + if (!shouldSkipProcessingNestedFormData) { // For nested forms, we need to reset the "data" and "path" objects for all of the children components, and then re-establish the data when it is done. const childPath: string = componentDataPath(component, path, compPath); const childData: any = get(data, childPath, null); From a34e0e0034902f6abc1f1466a5f5eb637360130e Mon Sep 17 00:00:00 2001 From: Aliaksandra Ramanenka Date: Thu, 14 Nov 2024 16:43:21 +0200 Subject: [PATCH 2/2] Added tests --- src/process/__tests__/process.test.ts | 129 ++++++++++++++++++++++++++ 1 file changed, 129 insertions(+) diff --git a/src/process/__tests__/process.test.ts b/src/process/__tests__/process.test.ts index 77d02182..55d04237 100644 --- a/src/process/__tests__/process.test.ts +++ b/src/process/__tests__/process.test.ts @@ -4548,6 +4548,135 @@ describe('Process Tests', function () { }); }); + it("Should validate Nested Form's components if it should not be cleared and no data provided", async function () { + const nestedForm = { + key: 'form', + type: 'form', + display: 'form', + input: true, + components: [ + { + key: 'textField', + type: 'textfield', + validate: { + required: true, + }, + input: true, + }, + ], + }; + const submission = { + data: { + submit: true, + }, + state: 'submitted', + }; + const form = { + title: 'Parent Form', + name: 'parentForm', + path: 'parentform', + type: 'form', + display: 'form', + components: [ + nestedForm, + { + ...nestedForm, + key: 'form1', + }, + { + type: 'button', + label: 'Submit', + key: 'submit', + input: true, + tableView: false, + }, + ], + }; + const context = { + form, + submission, + data: submission.data, + components: form.components, + processors: ProcessTargets.submission, + scope: {}, + config: { + server: true, + }, + }; + processSync(context); + context.processors = ProcessTargets.evaluator; + const scope = processSync(context); + expect((scope as ValidationScope).errors).to.have.length(2); + }); + + it("Should validate Nested Form's components if it should not be cleared and no data provided and the parent form has errors itself", async function () { + const nestedForm = { + key: 'form', + type: 'form', + input: true, + components: [ + { + key: 'textField', + type: 'textfield', + validate: { + required: true, + }, + input: true, + }, + ], + }; + const submission = { + data: { + submit: true, + }, + state: 'submitted', + }; + const form = { + title: 'Parent Form', + name: 'parentForm', + path: 'parentform', + type: 'form', + display: 'form', + components: [ + { + key: 'textField', + type: 'textfield', + validate: { + required: true, + }, + input: true, + }, + nestedForm, + { + ...nestedForm, + key: 'form1', + }, + { + type: 'button', + label: 'Submit', + key: 'submit', + input: true, + tableView: false, + }, + ], + }; + const context = { + form, + submission, + data: submission.data, + components: form.components, + processors: ProcessTargets.submission, + scope: {}, + config: { + server: true, + }, + }; + processSync(context); + context.processors = ProcessTargets.evaluator; + const scope = processSync(context); + expect((scope as ValidationScope).errors).to.have.length(3); + }); + it('Should not return errors for empty multiple values for url and dateTime', function () { const form = { _id: '671f7fbeaf87b0e2a26e4212',