From 0adf189b9b3cd383e903ba42bf0426c6884c305c Mon Sep 17 00:00:00 2001 From: Blake Krammes <49688912+blakekrammes@users.noreply.github.com> Date: Tue, 17 Dec 2024 14:09:59 -0600 Subject: [PATCH] FIO-9385 Preserve non-default widget settings after evaluating field logic - originalComponent in Component.js did not capture non-default widget settings since they were added after component initialization - During field logic evaluation, the component was falling back to originalComponent and not preserving the widget settings - Update originalComponent after applying widget settings in TextField and DateTime to maintain them after field logic eval --- src/components/datetime/DateTime.js | 6 +- src/components/textfield/TextField.js | 3 + test/unit/DateTime.unit.js | 11 +++- test/unit/TextField.unit.js | 9 +++ test/unit/fixtures/datetime/index.js | 3 +- .../datetime/requiredFieldLogicComp.js | 62 +++++++++++++++++++ test/unit/fixtures/textfield/index.js | 3 +- .../textfield/requiredFieldLogicComp.js | 53 ++++++++++++++++ 8 files changed, 146 insertions(+), 4 deletions(-) create mode 100644 test/unit/fixtures/datetime/requiredFieldLogicComp.js create mode 100644 test/unit/fixtures/textfield/requiredFieldLogicComp.js diff --git a/src/components/datetime/DateTime.js b/src/components/datetime/DateTime.js index 05d222bfc9..444e3e8ffe 100644 --- a/src/components/datetime/DateTime.js +++ b/src/components/datetime/DateTime.js @@ -1,7 +1,7 @@ import _ from 'lodash'; import moment from 'moment'; import FormioUtils from '../../utils'; -import { componentValueTypes, getComponentSavedTypes } from '../../utils/utils'; +import { componentValueTypes, fastCloneDeep, getComponentSavedTypes } from '../../utils/utils'; import Input from '../_classes/input/Input'; export default class DateTimeComponent extends Input { @@ -139,6 +139,10 @@ export default class DateTimeComponent extends Input { maxDate: _.get(this.component, 'datePicker.maxDate'), ...customOptions, }; + // update originalComponent to include widget and other updated settings + // it is done here since these settings depend on properties present after the component is initialized + // originalComponent is used to restore the component (and widget) after evaluating field logic + this.originalComponent = fastCloneDeep(this.component); /* eslint-enable camelcase */ } diff --git a/src/components/textfield/TextField.js b/src/components/textfield/TextField.js index 69ffbf0cb9..c3afc31ff6 100644 --- a/src/components/textfield/TextField.js +++ b/src/components/textfield/TextField.js @@ -99,6 +99,9 @@ export default class TextFieldComponent extends Input { locale: this.component.widget.locale || this.options.language, saveAs: 'text' }; + // update originalComponent to include widget settings after component initialization + // originalComponent is used to restore the component (and widget) after evaluating field logic + this.originalComponent = FormioUtils.fastCloneDeep(this.component); } } diff --git a/test/unit/DateTime.unit.js b/test/unit/DateTime.unit.js index 3f9738b67f..0fc62b2719 100644 --- a/test/unit/DateTime.unit.js +++ b/test/unit/DateTime.unit.js @@ -18,7 +18,8 @@ import { comp11, comp12, comp13, - comp14 + comp14, + requiredFieldLogicComp, } from './fixtures/datetime'; describe('DateTime Component', () => { @@ -771,6 +772,14 @@ describe('DateTime Component', () => { }) }); + it('Should preserve the calendar widget settings after field logic is evaluated', async () => { + // see https://formio.atlassian.net/browse/FIO-9385 + // emulate viewing a submission in the portal with { readOnly: true } + const form = await Formio.createForm(document.createElement('div'), requiredFieldLogicComp, { readOnly: true }); + const dateTimeComponent = form.getComponent('dateTime'); + assert.equal(dateTimeComponent.widget.settings.readOnly, true); + }); + // it('Should provide correct date in selected timezone after submission', (done) => { // const form = _.cloneDeep(comp9); // const element = document.createElement('div'); diff --git a/test/unit/TextField.unit.js b/test/unit/TextField.unit.js index 93ebe28411..0a7a7c64a0 100644 --- a/test/unit/TextField.unit.js +++ b/test/unit/TextField.unit.js @@ -13,6 +13,7 @@ import { comp6, withDisplayAndInputMasks, comp7, + requiredFieldLogicComp, } from './fixtures/textfield'; import { comp10 as formWithCalendarTextField } from './fixtures/datetime'; @@ -1379,6 +1380,14 @@ describe('TextField Component', () => { }).catch(done); }); + it('Should preserve the calendar widget settings after field logic is evaluated', async () => { + // see https://formio.atlassian.net/browse/FIO-9385 + // emulate viewing a submission in the portal with { readOnly: true } + const form = await Formio.createForm(document.createElement('div'), requiredFieldLogicComp, { readOnly: true }); + const textFieldComponent = form.getComponent('textField'); + assert.equal(textFieldComponent.widget.settings.readOnly, true); + }); + it('Test Display mask', (done) => { const element = document.createElement('div'); diff --git a/test/unit/fixtures/datetime/index.js b/test/unit/fixtures/datetime/index.js index e1fbf329e1..336fab41d8 100644 --- a/test/unit/fixtures/datetime/index.js +++ b/test/unit/fixtures/datetime/index.js @@ -11,4 +11,5 @@ import comp11 from './comp11'; import comp12 from './comp12'; import comp13 from './comp13'; import comp14 from './comp14'; -export { comp1, comp2, comp3, comp5, comp6, comp7, comp8, comp9, comp10, comp11, comp12, comp13, comp14 }; +import requiredFieldLogicComp from './requiredFieldLogicComp'; +export { comp1, comp2, comp3, comp5, comp6, comp7, comp8, comp9, comp10, comp11, comp12, comp13, comp14, requiredFieldLogicComp }; diff --git a/test/unit/fixtures/datetime/requiredFieldLogicComp.js b/test/unit/fixtures/datetime/requiredFieldLogicComp.js new file mode 100644 index 0000000000..f25f19bf41 --- /dev/null +++ b/test/unit/fixtures/datetime/requiredFieldLogicComp.js @@ -0,0 +1,62 @@ +export default { + components: [ + { + "label": "dateTime", + "displayInTimezone": "submission", + "format": "MM/dd/yyyy:HH:mm:ss", + "tableView": false, + "datePicker": { + "disableWeekends": false, + "disableWeekdays": false + }, + "timePicker": { + "showMeridian": false + }, + "enableMinDateInput": false, + "enableMaxDateInput": false, + "validateWhenHidden": false, + "key": "dateTime", + "logic": [ + { + "name": "requiredLogic", + "trigger": { + "type": "javascript", + "javascript": "result = true;" + }, + "actions": [ + { + "name": "setRequired", + "type": "property", + "property": { + "label": "Required", + "value": "validate.required", + "type": "boolean" + }, + "state": true + } + ] + } + ], + "type": "datetime", + "input": true, + "widget": { + "type": "calendar", + "displayInTimezone": "submission", + "locale": "en", + "useLocaleSettings": false, + "allowInput": true, + "mode": "single", + "enableTime": true, + "noCalendar": false, + "format": "MM/dd/yyyy:HH:mm:ss", + "hourIncrement": 1, + "minuteIncrement": 1, + "time_24hr": true, + "minDate": null, + "disableWeekends": false, + "disableWeekdays": false, + "maxDate": null + } + } + ] +} diff --git a/test/unit/fixtures/textfield/index.js b/test/unit/fixtures/textfield/index.js index b25ddfa82e..8bd7d43fc9 100644 --- a/test/unit/fixtures/textfield/index.js +++ b/test/unit/fixtures/textfield/index.js @@ -6,4 +6,5 @@ import comp5 from './comp5'; import comp6 from './comp6'; import withDisplayAndInputMasks from './comp-with-display-and-value-masks'; import comp7 from './comp7'; -export { comp1, comp2, comp3, comp4, comp5, comp6, comp7, withDisplayAndInputMasks }; +import requiredFieldLogicComp from './requiredFieldLogicComp'; +export { comp1, comp2, comp3, comp4, comp5, comp6, comp7, withDisplayAndInputMasks, requiredFieldLogicComp }; diff --git a/test/unit/fixtures/textfield/requiredFieldLogicComp.js b/test/unit/fixtures/textfield/requiredFieldLogicComp.js new file mode 100644 index 0000000000..da7b873788 --- /dev/null +++ b/test/unit/fixtures/textfield/requiredFieldLogicComp.js @@ -0,0 +1,53 @@ +export default { + components: [ + { + "label": "Text Field", + "widget": { + "type": "calendar", + "altInput": true, + "allowInput": true, + "clickOpens": true, + "enableDate": true, + "enableTime": true, + "mode": "single", + "noCalendar": false, + "format": "MM/dd/yyyy:HH:mm:ss", + "dateFormat": "MM/dd/yyyy:HH:mm:ss", + "useLocaleSettings": false, + "hourIncrement": 1, + "minuteIncrement": 5, + "time_24hr": false, + "saveAs": "text", + "displayInTimezone": "viewer", + "locale": "en" + }, + "applyMaskOn": "change", + "tableView": true, + "validateWhenHidden": false, + "key": "textField", + "logic": [ + { + "name": "requiredLogic", + "trigger": { + "type": "javascript", + "javascript": "result = true;" + }, + "actions": [ + { + "name": "setRequired", + "type": "property", + "property": { + "label": "Required", + "value": "validate.required", + "type": "boolean" + }, + "state": true + } + ] + } + ], + "type": "textfield", + "input": true + } + ] +}