diff --git a/src/process/__tests__/process.test.ts b/src/process/__tests__/process.test.ts index bb17faf9..d69c7e7f 100644 --- a/src/process/__tests__/process.test.ts +++ b/src/process/__tests__/process.test.ts @@ -2541,6 +2541,128 @@ describe('Process Tests', () => { assert.deepEqual(context.data, { selector: 'one' }); assert.equal(context.scope.errors.length, 0); }); + + it('Should not include submission data for conditionally hidden fields', async () => { + const form = { + display: 'form', + components: [ + { + type: 'textfield', + key: 'textField', + label: 'Text Field', + input: true, + }, + { + type: 'textarea', + key: 'textArea', + label: 'Text Area', + input: true, + conditional: { + show: false, + conjunction: 'all', + conditions: [ + { + component: 'textField', + operator: 'isEmpty' + } + ] + }, + } + ] + }; + + const submission = { + data: { + textField: '', + textArea: 'should not be in submission' + } + }; + + const context = { + form, + submission, + data: submission.data, + components: form.components, + processors: ProcessTargets.evaluator, + scope: {}, + config: { + server: true + } + }; + processSync(context); + expect(context.data).to.deep.equal({ textField: '' }); + }); + + it('Should not include submission data for logically hidden fields', async () => { + const form = { + display: 'form', + components: [ + { + type: 'textfield', + key: 'textField', + label: 'Text Field', + input: true, + }, + { + type: 'textarea', + key: 'textArea', + label: 'Text Area', + input: true, + logic: [ + { + name: 'Hide When Empty', + trigger: { + type: 'simple' as const, + simple: { + show: true, + conjunction: 'all', + conditions: [ + { + component: 'textField', + operator: 'isEmpty', + }, + ], + }, + }, + actions: [ + { + name: 'Hide', + type: 'property' as const, + property: { + label: 'Hidden', + value: 'hidden', + type: 'boolean' as const, + }, + state: true, + }, + ], + }, + ] + } + ] + }; + + const submission = { + data: { + textField: '', + textArea: 'should not be in submission' + } + }; + + const context = { + form, + submission, + data: submission.data, + components: form.components, + processors: ProcessTargets.evaluator, + scope: {}, + config: { + server: true + } + }; + processSync(context); + expect(context.data).to.deep.equal({ textField: '' }); + }); /* it('Should not clearOnHide when set to false', async () => { var components = [ @@ -2621,7 +2743,7 @@ describe('Process Tests', () => { } } ]; - + helper .form('test', components) .submission({ @@ -2632,13 +2754,13 @@ describe('Process Tests', () => { if (err) { return done(err); } - + var submission = helper.getLastSubmission(); assert.deepEqual({selector: 'one', noClear: 'testing'}, submission.data); done(); }); }); - + it('Should clearOnHide when set to true', async () => { var components = [ { @@ -2718,7 +2840,7 @@ describe('Process Tests', () => { } } ]; - + helper .form('test', components) .submission({ @@ -2729,11 +2851,11 @@ describe('Process Tests', () => { if (err) { return done(err); } - + var submission = helper.getLastSubmission(); assert.deepEqual({selector: 'one'}, submission.data); done(); }); }); */ -}); \ No newline at end of file +}); diff --git a/src/process/clearHidden.ts b/src/process/clearHidden.ts new file mode 100644 index 00000000..33a3e268 --- /dev/null +++ b/src/process/clearHidden.ts @@ -0,0 +1,33 @@ +import unset from 'lodash/unset'; +import { + ProcessorScope, + ProcessorContext, + ProcessorInfo, + ProcessorFnSync +} from "types"; + +type ClearHiddenScope = ProcessorScope & { + clearHidden: { + [path: string]: boolean; + } +} + +/** + * This processor function checks components for the `hidden` property and unsets corresponding data + */ +export const clearHiddenProcess: ProcessorFnSync = (context) => { + const { component, data, path, value, scope } = context; + if (!scope.clearHidden) { + scope.clearHidden = {}; + } + if (component.hidden && value !== undefined && (!component.hasOwnProperty('clearOnHide') || component.clearOnHide)) { + unset(data, path); + scope.clearHidden[path] = true; + } +} + +export const clearHiddenProcessInfo: ProcessorInfo, void> = { + name: 'clearHidden', + shouldProcess: () => true, + processSync: clearHiddenProcess, +} diff --git a/src/process/conditions/__tests__/conditions.test.ts b/src/process/conditions/__tests__/conditions.test.ts index 5e5aa966..6e418b9d 100644 --- a/src/process/conditions/__tests__/conditions.test.ts +++ b/src/process/conditions/__tests__/conditions.test.ts @@ -1,44 +1,54 @@ import { expect } from 'chai'; -import { process } from '../../process' +import { processSync } from '../../process' import { conditionProcessInfo } from '../index'; import { ConditionsScope, ProcessContext } from 'types'; -const processForm = async (form: any, submission: any) => { +const processForm = (form: any, submission: any) => { const context: ProcessContext = { processors: [conditionProcessInfo], components: form.components, data: submission.data, scope: {} }; - await process(context); + processSync(context); return context; }; describe('Condition processor', () => { - it('Perform conditional data with "clearOnHide" enabled.', async () => { + it('Should modify component\'s "hidden" property when conditionally visible is false', async () => { const form = { components: [ { - type: 'number', + type: 'textfield', key: 'a', input: true }, { - type: 'number', + type: 'textfield', key: 'b', - input: true + input: true, + conditional: { + show: false, + conjunction: 'all', + conditions: [ + { + component: 'a', + operator: 'isEmpty' + } + ] + }, } ] }; - + const submission = { data: { - a: 1, - b: 2 + a: '', } }; - - const context: ProcessContext = await processForm(form, submission); - console.log(context); + + const context: ProcessContext = processForm(form, submission); + expect(context.components[1]).to.haveOwnProperty('hidden'); + expect(context.components[1].hidden).to.be.true; }); -}); \ No newline at end of file +}); diff --git a/src/process/conditions/index.ts b/src/process/conditions/index.ts index b117f0bc..dd279368 100644 --- a/src/process/conditions/index.ts +++ b/src/process/conditions/index.ts @@ -1,6 +1,6 @@ import { ProcessorFn, ProcessorFnSync, ConditionsScope, ProcessorInfo, ConditionsContext, SimpleConditional, JSONConditional, LegacyConditional, SimpleConditionalConditions, Component, NestedComponent, FilterScope } from 'types'; import { Utils } from 'utils'; -import unset from 'lodash/unset'; +import set from 'lodash/set'; import { componentInfo, getComponentKey, getComponentPath } from 'utils/formUtil'; import { checkCustomConditional, @@ -112,16 +112,12 @@ export const conditionalProcess = (context: ConditionsContext, isHidden: Conditi Utils.eachComponentData([component], row, (comp: Component, data: any, compRow: any, compPath: string) => { if (comp !== component) { scope.conditionals?.push({ path: getComponentPath(comp, compPath), conditionallyHidden: true }); - } - if (!comp.hasOwnProperty('clearOnHide') || comp.clearOnHide) { - unset(compRow, getComponentKey(comp)); } + set(comp, 'hidden', true); }); } else { - if (!component.hasOwnProperty('clearOnHide') || component.clearOnHide) { - unset(data, path); - } + set(component, 'hidden', true); } } else { conditionalComp.conditionallyHidden = false; diff --git a/src/process/process.ts b/src/process/process.ts index af662cc8..54280d0c 100644 --- a/src/process/process.ts +++ b/src/process/process.ts @@ -12,6 +12,7 @@ import { validateCustomProcessInfo, validateProcessInfo, validateServerProcessIn import { filterProcessInfo } from "./filter"; import { normalizeProcessInfo } from "./normalize"; import { dereferenceProcessInfo } from "./dereference"; +import { clearHiddenProcessInfo } from "./clearHidden"; export async function process(context: ProcessContext): Promise { const { instances, components, data, scope, flat, processors } = context; @@ -91,6 +92,7 @@ export const ProcessorMap: Record> = { simpleConditions: simpleConditionProcessInfo, normalize: normalizeProcessInfo, dereference: dereferenceProcessInfo, + clearHidden: clearHiddenProcessInfo, fetch: fetchProcessInfo, logic: logicProcessInfo, validate: validateProcessInfo, @@ -113,6 +115,7 @@ export const ProcessTargets: ProcessTarget = { calculateProcessInfo, logicProcessInfo, conditionProcessInfo, + clearHiddenProcessInfo, validateProcessInfo ] }; diff --git a/src/types/BaseComponent.ts b/src/types/BaseComponent.ts index 6af9101e..a5e5924d 100644 --- a/src/types/BaseComponent.ts +++ b/src/types/BaseComponent.ts @@ -3,7 +3,7 @@ import { AdvancedLogic } from "./AdvancedLogic"; export type JSONConditional = { json: RulesLogic; }; export type LegacyConditional = { show: boolean | string | null; when: string | null; eq: boolean | string }; -export type SimpleConditionalConditions = { component: string; operator: string; value: any}[]; +export type SimpleConditionalConditions = { component: string; operator: string; value?: any}[]; export type SimpleConditional = { show: boolean | null; conjunction: string; conditions: SimpleConditionalConditions}; export type BaseComponent = {