From 69528000d9751c3fd4e16665b4c8aa0e394152e6 Mon Sep 17 00:00:00 2001 From: "ICX\\Tatsiana.Hashtold" Date: Thu, 7 Dec 2023 12:52:56 +0300 Subject: [PATCH] FIO-7514: fixed an isse where new simple conditionals donot work whan condition is based os the value of resource select with object value --- src/Webform.unit.js | 89 +++++++++++ src/components/_classes/list/ListComponent.js | 17 +- src/components/select/Select.js | 18 ++- src/utils/conditionOperators/IsEqualTo.js | 26 +++- src/utils/conditionOperators/IsNotEqualTo.js | 9 +- src/utils/utils.js | 24 +++ test/formtest/formWithObjectValueSelect.json | 146 ++++++++++++++++++ test/formtest/index.js | 2 + ...m-bootstrap-formWithObjectValueSelect.html | 48 ++++++ ...ap-readOnly-formWithObjectValueSelect.html | 48 ++++++ 10 files changed, 407 insertions(+), 20 deletions(-) create mode 100644 test/formtest/formWithObjectValueSelect.json create mode 100644 test/renders/form-bootstrap-formWithObjectValueSelect.html create mode 100644 test/renders/form-bootstrap-readOnly-formWithObjectValueSelect.html diff --git a/src/Webform.unit.js b/src/Webform.unit.js index 68fbdb2110..324d81d207 100644 --- a/src/Webform.unit.js +++ b/src/Webform.unit.js @@ -35,6 +35,7 @@ import { formWithCollapsedPanel, formWithCustomFormatDate, tooltipActivateCheckbox, + formWithObjectValueSelect } from '../test/formtest'; import UpdateErrorClassesWidgets from '../test/forms/updateErrorClasses-widgets'; import nestedModalWizard from '../test/forms/nestedModalWizard'; @@ -2645,6 +2646,94 @@ describe('Webform tests', function() { }, 300); }).catch((err) => done(err)); }); + + it('Should show field when condition is based on the values of select resource with object value', (done) => { + const element = document.createElement('div'); + const values = [ + { + _id: '656daabeebc67ecca1141569', + form: '656daab0ebc67ecca1141226', + data: { + number: 4, + notes: 'notes 4', + }, + project: '656daa20ebc67ecca1140e8d', + state: 'submitted', + created: '2023-12-04T10:32:30.821Z', + modified: '2023-12-06T14:25:00.886Z', + }, + { + _id: '656daabbebc67ecca11414a7', + form: '656daab0ebc67ecca1141226', + data: { + number: 3, + notes: 'notes 3', + }, + project: '656daa20ebc67ecca1140e8d', + state: 'submitted', + created: '2023-12-04T10:32:27.322Z', + modified: '2023-12-06T14:25:07.494Z', + }, + { + _id: '656daab8ebc67ecca11413e5', + form: '656daab0ebc67ecca1141226', + data: { + number: 2, + notes: 'notes 2', + }, + project: '656daa20ebc67ecca1140e8d', + state: 'submitted', + created: '2023-12-04T10:32:24.514Z', + modified: '2023-12-06T14:25:13.610Z', + }, + { + _id: '656daab5ebc67ecca1141322', + form: '656daab0ebc67ecca1141226', + data: { + number: 1, + notes: 'notes 1', + }, + project: '656daa20ebc67ecca1140e8d', + state: 'submitted', + created: '2023-12-04T10:32:21.205Z', + modified: '2023-12-06T14:25:20.930Z', + }, + ]; + const originalMakeRequest = Formio.makeRequest; + Formio.makeRequest = function() { + return new Promise(resolve => { + setTimeout(() => { + resolve(values); + }, 50); + }); + }; + + Formio.createForm(element, formWithObjectValueSelect) + .then(form => { + const numberComp = form.getComponent('number'); + assert.equal(numberComp.visible, false); + + const selectRef = form.getComponent('selectRef'); + selectRef.setValue(fastCloneDeep(values[3])); + const selectNoValuePropertyMult = form.getComponent('selectNoValueProperty'); + selectNoValuePropertyMult.setValue([fastCloneDeep(values[2])]); + const selectEntireObject = form.getComponent('selectEntireObject'); + selectEntireObject.setValue(fastCloneDeep(values[1].data)); + const selectEntireObjectMult = form.getComponent('selectEntireObjectMult'); + selectEntireObjectMult.setValue([fastCloneDeep(values[0].data)]); + + setTimeout(() => { + assert.equal(numberComp.visible, true); + selectRef.setValue(fastCloneDeep(values[2])); + setTimeout(() => { + assert.equal(numberComp.visible, false); + Formio.makeRequest = originalMakeRequest; + done(); + }, 400); + }, 400); + }) + .catch(done); + }); }); describe('Calculate Value with allowed manual override', () => { diff --git a/src/components/_classes/list/ListComponent.js b/src/components/_classes/list/ListComponent.js index 42f3a53e8d..ead6035c11 100644 --- a/src/components/_classes/list/ListComponent.js +++ b/src/components/_classes/list/ListComponent.js @@ -1,6 +1,7 @@ import Field from '../field/Field'; import { Formio } from '../../../Formio'; import _ from 'lodash'; +import { getItemTemplateKeys } from '../../../utils/utils'; export default class ListComponent extends Field { static schema(...extend) { @@ -50,18 +51,10 @@ export default class ListComponent extends Field { } getTemplateKeys() { - this.templateKeys = []; - if (this.options.readOnly && this.component.template) { - const keys = this.component.template.match(/({{\s*(.*?)\s*}})/g); - if (keys) { - keys.forEach((key) => { - const propKey = key.match(/{{\s*item\.(.*?)\s*}}/); - if (propKey && propKey.length > 1) { - this.templateKeys.push(propKey[1]); - } - }); - } - } + const template = this.component.template; + this.templateKeys = this.options.readOnly && template + ? getItemTemplateKeys(template) + : []; } get requestHeaders() { diff --git a/src/components/select/Select.js b/src/components/select/Select.js index d41a252c6d..b9b5b684f5 100644 --- a/src/components/select/Select.js +++ b/src/components/select/Select.js @@ -3,7 +3,7 @@ import { Formio } from '../../Formio'; import ListComponent from '../_classes/list/ListComponent'; import Input from '../_classes/input/Input'; import Form from '../../Form'; -import { getRandomComponentId, boolValue, isPromise, componentValueTypes, getComponentSavedTypes } from '../../utils/utils'; +import { getRandomComponentId, boolValue, isPromise, componentValueTypes, getComponentSavedTypes, isSelectResourceWithObjectValue } from '../../utils/utils'; import Choices from '../../utils/ChoicesWrapper'; export default class SelectComponent extends ListComponent { @@ -65,7 +65,21 @@ export default class SelectComponent extends ListComponent { return { ...super.conditionOperatorsSettings, valueComponent(classComp) { - return { ... classComp, type: 'select' }; + const valueComp = { ... classComp, type: 'select' }; + + if (isSelectResourceWithObjectValue(classComp)) { + valueComp.reference = false; + valueComp.onSetItems = ` + var templateKeys = utils.getItemTemplateKeys(component.template) || []; + items = _.map(items || [], i => { + var item = {}; + _.each(templateKeys, k => _.set(item, k, _.get(i, k))); + return item; + }) + `; + } + + return valueComp; } }; } diff --git a/src/utils/conditionOperators/IsEqualTo.js b/src/utils/conditionOperators/IsEqualTo.js index 4a844fbfc3..7823c559c8 100644 --- a/src/utils/conditionOperators/IsEqualTo.js +++ b/src/utils/conditionOperators/IsEqualTo.js @@ -1,5 +1,6 @@ import ConditionOperator from './ConditionOperator'; import _ from 'lodash'; +import { getItemTemplateKeys, isSelectResourceWithObjectValue } from '../utils'; export default class IsEqualTo extends ConditionOperator { static get operatorKey() { @@ -10,7 +11,7 @@ export default class IsEqualTo extends ConditionOperator { return 'Is Equal To'; } - execute({ value, comparedValue }) { + execute({ value, comparedValue, instance, conditionComponentPath }) { if (value && comparedValue && typeof value !== typeof comparedValue && _.isString(comparedValue)) { try { comparedValue = JSON.parse(comparedValue); @@ -19,6 +20,29 @@ export default class IsEqualTo extends ConditionOperator { catch (e) {} } + if (instance && instance.root) { + const conditionTriggerComponent = instance.root.getComponent(conditionComponentPath); + + if ( + conditionTriggerComponent + && isSelectResourceWithObjectValue(conditionTriggerComponent.component) + && conditionTriggerComponent.component?.template + ) { + if (!value || !_.isPlainObject(value)) { + return false; + } + + const { template, valueProperty } = conditionTriggerComponent.component; + + if (valueProperty === 'data') { + value = { data: value }; + comparedValue = { data: comparedValue }; + } + + return _.every(getItemTemplateKeys(template) || [], k => _.isEqual(_.get(value, k), _.get(comparedValue, k))); + } + } + //special check for select boxes if (_.isObject(value) && comparedValue && _.isString(comparedValue)) { return value[comparedValue]; diff --git a/src/utils/conditionOperators/IsNotEqualTo.js b/src/utils/conditionOperators/IsNotEqualTo.js index 96f58b8044..2daf0d6ee2 100644 --- a/src/utils/conditionOperators/IsNotEqualTo.js +++ b/src/utils/conditionOperators/IsNotEqualTo.js @@ -1,7 +1,6 @@ -import ConditionOperator from './ConditionOperator'; -import _ 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); } } diff --git a/src/utils/utils.js b/src/utils/utils.js index d308e8d313..3ae7c973a4 100644 --- a/src/utils/utils.js +++ b/src/utils/utils.js @@ -1581,3 +1581,27 @@ export function getComponentSavedTypes(fullSchema) { return null; } + +export function getItemTemplateKeys(template) { + const templateKeys = []; + if (!template) { + return templateKeys; + } + const keys = template.match(/({{\s*(.*?)\s*}})/g); + + if (keys) { + keys.forEach((key) => { + const propKey = key.match(/{{\s*item\.(.*?)\s*}}/); + if (propKey && propKey.length > 1) { + templateKeys.push(propKey[1]); + } + }); + } + + return templateKeys; +} + +export function isSelectResourceWithObjectValue(comp = {}) { + const { reference, dataSrc, valueProperty } = comp; + return reference || (dataSrc === 'resource' && (!valueProperty || valueProperty === 'data')); +} diff --git a/test/formtest/formWithObjectValueSelect.json b/test/formtest/formWithObjectValueSelect.json new file mode 100644 index 0000000000..b129d6f87e --- /dev/null +++ b/test/formtest/formWithObjectValueSelect.json @@ -0,0 +1,146 @@ +{ + "_id": "656dabe1ebc67ecca11415f7", + "title": "test conditions select", + "name": "testConditionsSelect", + "path": "testconditionsselect", + "type": "form", + "display": "form", + "components": [{ + "label": "Select ref", + "widget": "choicesjs", + "tableView": true, + "dataSrc": "resource", + "data": { + "resource": "656daab0ebc67ecca1141226" + }, + "template": "Number {{ item.data.number }}({{item.data.notes}})", + "key": "selectRef", + "type": "select", + "noRefreshOnScroll": false, + "addResource": false, + "reference": true, + "input": true + }, + { + "label": "Select no value property", + "widget": "choicesjs", + "tableView": true, + "multiple": true, + "dataSrc": "resource", + "data": { + "resource": "656daab0ebc67ecca1141226" + }, + "template": "{{ item.data.number }}", + "key": "selectNoValueProperty", + "type": "select", + "noRefreshOnScroll": false, + "addResource": false, + "reference": false, + "input": true + }, + { + "label": "Select entire object", + "widget": "choicesjs", + "tableView": true, + "dataSrc": "resource", + "data": { + "resource": "656daab0ebc67ecca1141226" + }, + "valueProperty": "data", + "template": "{{ item.data.number }}", + "validate": { + "select": false + }, + "key": "selectEntireObject", + "type": "select", + "searchField": "data__regex", + "noRefreshOnScroll": false, + "addResource": false, + "reference": false, + "input": true + }, + { + "label": "Select entire object mult", + "widget": "choicesjs", + "tableView": true, + "multiple": true, + "dataSrc": "resource", + "data": { + "resource": "656daab0ebc67ecca1141226" + }, + "valueProperty": "data", + "template": "{{ item.data.number }}", + "validate": { + "select": false + }, + "key": "selectEntireObjectMult", + "type": "select", + "searchField": "data__regex", + "noRefreshOnScroll": false, + "addResource": false, + "reference": false, + "input": true + }, + { + "label": "Number", + "applyMaskOn": "change", + "mask": false, + "tableView": false, + "delimiter": false, + "requireDecimal": false, + "inputFormat": "plain", + "truncateMultipleSpaces": false, + "key": "number", + "conditional": { + "show": true, + "conjunction": "all", + "conditions": [{ + "component": "selectRef", + "operator": "isEqual", + "value": { + "data": { + "number": 1, + "notes": "notes 1" + } + } + }, + { + "component": "selectNoValueProperty", + "operator": "isEqual", + "value": { + "data": { + "number": 2 + } + } + }, + { + "component": "selectEntireObject", + "operator": "isEqual", + "value": { + "number": 3 + } + }, + { + "component": "selectEntireObjectMult", + "operator": "isEqual", + "value": { + "number": 4 + } + } + ] + }, + "type": "number", + "input": true + }, + { + "type": "button", + "label": "Submit", + "key": "submit", + "disableOnInvalid": true, + "input": true, + "tableView": false + } + ], + "project": "656daa20ebc67ecca1140e8d", + "machineName": "yyyy-acvcpvwwqbabawl:testConditionsSelect" +} diff --git a/test/formtest/index.js b/test/formtest/index.js index 9b7467452d..4b21cb8e25 100644 --- a/test/formtest/index.js +++ b/test/formtest/index.js @@ -41,6 +41,7 @@ const wizardWithSimpleConditionalPage = require('./wizardWithSimpleConditionalPa const wizardWithTooltip = require('./wizardWithTooltip.json'); const resourceKeyCamelCase = require('./resourceKeyCamelCase.json'); const tooltipActivateCheckbox = require('./tooltipActivateCheckbox.json'); +const formWithObjectValueSelect = require('./formWithObjectValueSelect.json'); module.exports = { advanced, @@ -86,4 +87,5 @@ module.exports = { wizardWithTooltip, resourceKeyCamelCase, tooltipActivateCheckbox, + formWithObjectValueSelect }; diff --git a/test/renders/form-bootstrap-formWithObjectValueSelect.html b/test/renders/form-bootstrap-formWithObjectValueSelect.html new file mode 100644 index 0000000000..98bfc6aa82 --- /dev/null +++ b/test/renders/form-bootstrap-formWithObjectValueSelect.html @@ -0,0 +1,48 @@ +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+
+
+
+ +
+ +
+
+
+
+
+
\ No newline at end of file diff --git a/test/renders/form-bootstrap-readOnly-formWithObjectValueSelect.html b/test/renders/form-bootstrap-readOnly-formWithObjectValueSelect.html new file mode 100644 index 0000000000..01806c2525 --- /dev/null +++ b/test/renders/form-bootstrap-readOnly-formWithObjectValueSelect.html @@ -0,0 +1,48 @@ +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+
+
+
+ +
+ +
+
+
+
+
+
\ No newline at end of file