From 9a3c6ecf76926d7eeb5a3c4cb5a89cc946f23015 Mon Sep 17 00:00:00 2001 From: "ICX\\Tatsiana.Hashtold" Date: Thu, 17 Oct 2024 13:44:50 +0300 Subject: [PATCH] FIO-9189: fixed an issue where data is lost after submittion for the conditionally visible field when the condition is based on relect resource --- src/process/__tests__/process.test.ts | 130 ++++++++++++++++++++++++++ src/utils/formUtil/index.ts | 64 +++++++++++++ src/utils/operators/IsEqualTo.js | 12 +++ src/utils/operators/IsNotEqualTo.js | 9 +- 4 files changed, 210 insertions(+), 5 deletions(-) diff --git a/src/process/__tests__/process.test.ts b/src/process/__tests__/process.test.ts index 21540ea1..29a80fa5 100644 --- a/src/process/__tests__/process.test.ts +++ b/src/process/__tests__/process.test.ts @@ -3910,6 +3910,136 @@ describe('Process Tests', function () { }); }); + it('Should not unset value of the conditionally visible component when condtiion is based on select resource with save as ref', function () { + const form = { + _id: '670f638c362eca5264b5dc94', + title: 'test fire', + name: 'testFire', + path: 'testfire', + type: 'form', + display: 'form', + owner: '637b2e6b48c1227e60b1f910', + components: [ + { + label: 'Select', + widget: 'choicesjs', + tableView: true, + dataSrc: 'resource', + data: { + resource: '670f62df362eca5264b5d812', + }, + template: '{{ item.data.textField }}', + validateWhenHidden: false, + key: 'select', + type: 'select', + input: true, + noRefreshOnScroll: false, + addResource: false, + reference: true, + }, + { + label: 'Text Field Show on test1', + applyMaskOn: 'change', + tableView: true, + validateWhenHidden: false, + key: 'textFieldShowOnTest1', + conditional: { + show: true, + conjunction: 'all', + conditions: [ + { + component: 'select', + operator: 'isEqual', + value: { + data: { + textField: 'test1', + }, + }, + }, + ], + }, + type: 'textfield', + input: true, + }, + { + label: 'Text Area Not Show on test1', + applyMaskOn: 'change', + autoExpand: false, + tableView: true, + validateWhenHidden: false, + key: 'textAreaNotShowOnTest1', + conditional: { + show: true, + conjunction: 'all', + conditions: [ + { + component: 'select', + operator: 'isNotEqual', + value: { + data: { + textField: 'test1', + }, + }, + }, + ], + }, + type: 'textarea', + input: true, + }, + { + type: 'button', + label: 'Submit', + key: 'submit', + disableOnInvalid: true, + input: true, + tableView: false, + }, + ], + project: '66f66c655879bf08113cf465', + }; + + const submission = { + data: { + select: { + _id: '670f62e5362eca5264b5daf9', + form: '670f62df362eca5264b5d812', + owner: '637b2e6b48c1227e60b1f910', + data: { textField: 'test1', submit: true }, + project: '66f66c655879bf08113cf465', + state: 'submitted', + created: '2024-10-16T06:53:25.603Z', + modified: '2024-10-16T06:53:25.604Z', + }, + submit: true, + textFieldShowOnTest1: 'test', + }, + }; + + const errors: any = []; + const conditionals: any = []; + const context = { + form, + submission, + data: submission.data, + components: form.components, + processors: ProcessTargets.submission, + scope: { errors, conditionals }, + config: { + server: true, + }, + }; + + processSync(context); + submission.data = context.data; + context.processors = ProcessTargets.evaluator; + processSync(context); + console.log(111, context.data, context.scope.conditionals); + assert.deepEqual(context.data.textFieldShowOnTest1, 'test'); + context.scope.conditionals.forEach((cond: any) => { + assert.equal(cond.conditionallyHidden, cond.path !== 'textFieldShowOnTest1'); + }); + }); + describe('Required component validation in nested form in DataGrid/EditGrid', function () { const nestedForm = { key: 'form', diff --git a/src/utils/formUtil/index.ts b/src/utils/formUtil/index.ts index 7eae2d63..a15ae3bc 100644 --- a/src/utils/formUtil/index.ts +++ b/src/utils/formUtil/index.ts @@ -17,6 +17,7 @@ import { trim, isBoolean, omit, + every, } from 'lodash'; import { compare, applyPatch } from 'fast-json-patch'; @@ -38,6 +39,7 @@ import { JSONConditional, SimpleConditional, AddressComponent, + SelectComponent, } from 'types'; import { Evaluator } from '../Evaluator'; import { eachComponent } from './eachComponent'; @@ -1010,4 +1012,66 @@ export function isComponentDataEmpty( return isValueEmpty(component, value); } +/** + * Returns the template keys inside the template code. + * @param {string} template - The template to get the keys from. + * @returns {Array} - The keys inside the template. + */ +export function getItemTemplateKeys(template: any) { + const templateKeys: Array = []; + if (!template) { + return templateKeys; + } + const keys = template.match(/({{\s*(.*?)\s*}})/g); + + if (keys) { + keys.forEach((key: string) => { + const propKey = key.match(/{{\s*item\.(.*?)\s*}}/); + if (propKey && propKey.length > 1) { + templateKeys.push(propKey[1]); + } + }); + } + + return templateKeys; +} + +/** + * Returns if the component is a select resource with an object for its value. + * @param {Component} comp - The component to check. + * @returns {boolean} - TRUE if the component is a select resource with an object for its value; FALSE otherwise. + */ +export function isSelectResourceWithObjectValue(comp: any = {}) { + const { reference, dataSrc, valueProperty } = comp; + return reference || (dataSrc === 'resource' && (!valueProperty || valueProperty === 'data')); +} + +/** + * Compares real select resource value with expected value in condition. + * @param {any} value - current value of selectcomponent. + * @param {any} comparedValue - expocted value of select component. + * @param {SelectComponent} conditionComponent - select component on which the condtion is based. + * @returns {boolean} - TRUE if the select component current value is equal to the expected value; FALSE otherwise. + */ +export function compareSelectResourceWithObjectTypeValues( + value: any, + comparedValue: any, + conditionComponent: SelectComponent, +) { + if (!value || !isPlainObject(value)) { + return false; + } + + const { template, valueProperty } = conditionComponent; + + if (valueProperty === 'data') { + value = { data: value }; + comparedValue = { data: comparedValue }; + } + + return every(getItemTemplateKeys(template) || [], (k) => + isEqual(get(value, k), get(comparedValue, k)), + ); +} + export { eachComponent, eachComponentData, eachComponentAsync, eachComponentDataAsync }; diff --git a/src/utils/operators/IsEqualTo.js b/src/utils/operators/IsEqualTo.js index 7ee1c61f..c7ad34e9 100644 --- a/src/utils/operators/IsEqualTo.js +++ b/src/utils/operators/IsEqualTo.js @@ -1,3 +1,7 @@ +import { + compareSelectResourceWithObjectTypeValues, + isSelectResourceWithObjectValue, +} from 'utils/formUtil'; import ConditionOperator from './ConditionOperator'; import { isString, isEqual, get } from 'lodash'; @@ -29,6 +33,14 @@ export default class IsEqualTo extends ConditionOperator { } } + if ( + conditionComponent && + isSelectResourceWithObjectValue(conditionComponent) && + conditionComponent.template + ) { + return compareSelectResourceWithObjectTypeValues(value, comparedValue, conditionComponent); + } + return isEqual(value, comparedValue); } } diff --git a/src/utils/operators/IsNotEqualTo.js b/src/utils/operators/IsNotEqualTo.js index 00b229f5..689093e8 100644 --- a/src/utils/operators/IsNotEqualTo.js +++ b/src/utils/operators/IsNotEqualTo.js @@ -1,7 +1,6 @@ -import ConditionOperator from './ConditionOperator'; -import { isEqual } from 'lodash'; +import IsEqualTo from './IsEqualTo'; -export default class IsNotEqualTo extends ConditionOperator { +export default class IsNotEqualTo extends IsEqualTo { static get operatorKey() { return 'isNotEqual'; } @@ -10,7 +9,7 @@ export default class IsNotEqualTo extends ConditionOperator { return 'Is Not Equal To'; } - execute({ value, comparedValue }) { - return !isEqual(value, comparedValue); + execute(options) { + return !super.execute(options); } }