diff --git a/src/process/__tests__/process.test.ts b/src/process/__tests__/process.test.ts index d69c7e7f..2bd382e4 100644 --- a/src/process/__tests__/process.test.ts +++ b/src/process/__tests__/process.test.ts @@ -2663,6 +2663,110 @@ describe('Process Tests', () => { processSync(context); expect(context.data).to.deep.equal({ textField: '' }); }); + + it('Should allow conditionally hidden text fields within DataGrid and EditGrids', async () => { + const form = { + display: 'form', + components: [ + { + "label": "Edit Grid", + "tableView": false, + "rowDrafts": false, + "key": "editGrid", + "type": "editgrid", + "displayAsTable": false, + "input": true, + "components": [ + { + "label": "Select", + "widget": "choicesjs", + "tableView": true, + "data": { + "values": [ + { + "label": "Action1", + "value": "action1" + }, + { + "label": "Custom", + "value": "custom" + } + ] + }, + "key": "select", + "type": "select", + "input": true + }, + { + "label": "Text Field", + "applyMaskOn": "change", + "tableView": true, + "key": "textField", + "conditional": { + "show": true, + "conjunction": "all", + "conditions": [ + { + "component": "editGrid.select", + "operator": "isEqual", + "value": "custom" + } + ] + }, + "type": "textfield", + "input": true + } + ] + }, + { + "type": "button", + "label": "Submit", + "key": "submit", + "disableOnInvalid": true, + "input": true, + "tableView": false + } + ] + }; + + const submission = { + data: { + editGrid: [ + { + "select": "action1" + }, + { + "select": "custom", + "textField": "TEST" + } + ] + } + }; + + 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({ + "editGrid": [ + { + "select": "action1" + }, + { + "select": "custom", + "textField": "TEST" + } + ] + }); + }); /* it('Should not clearOnHide when set to false', async () => { var components = [ diff --git a/src/process/clearHidden.ts b/src/process/clearHidden.ts index 33a3e268..f0c17bab 100644 --- a/src/process/clearHidden.ts +++ b/src/process/clearHidden.ts @@ -3,7 +3,8 @@ import { ProcessorScope, ProcessorContext, ProcessorInfo, - ProcessorFnSync + ProcessorFnSync, + ConditionsScope } from "types"; type ClearHiddenScope = ProcessorScope & { @@ -20,7 +21,14 @@ export const clearHiddenProcess: ProcessorFnSync = (context) = if (!scope.clearHidden) { scope.clearHidden = {}; } - if (component.hidden && value !== undefined && (!component.hasOwnProperty('clearOnHide') || component.clearOnHide)) { + const conditionallyHidden = (scope as ConditionsScope).conditionals?.find((cond) => { + return cond.path === path; + }); + if ( + conditionallyHidden?.conditionallyHidden && + (value !== undefined) && + (!component.hasOwnProperty('clearOnHide') || component.clearOnHide) + ) { unset(data, path); scope.clearHidden[path] = true; } diff --git a/src/process/conditions/index.ts b/src/process/conditions/index.ts index dd279368..673477cb 100644 --- a/src/process/conditions/index.ts +++ b/src/process/conditions/index.ts @@ -96,16 +96,18 @@ export const conditionalProcess = (context: ConditionsContext, isHidden: Conditi if (!scope.conditionals) { scope.conditionals = []; } - scope.conditionals.push({ path, conditionallyHidden: true }); - const conditionalComp = scope.conditionals[scope.conditionals.length - 1]; + let conditionalComp = scope.conditionals.find((cond) => (cond.path === path)); + if (!conditionalComp) { + conditionalComp = {path, conditionallyHidden: false}; + scope.conditionals.push(conditionalComp); + } if (skipOnServer(context)) { return false; } - const conditionallyHidden = isHidden(context); - if (conditionallyHidden) { - conditionalComp.conditionallyHidden = conditionallyHidden; + conditionalComp.conditionallyHidden = conditionalComp.conditionallyHidden || isHidden(context); + if (conditionalComp.conditionallyHidden) { const info = componentInfo(component); if (info.hasColumns || info.hasComps || info.hasRows) { // If this is a container component, we need to add all the child components as conditionally hidden as well. @@ -119,8 +121,6 @@ export const conditionalProcess = (context: ConditionsContext, isHidden: Conditi else { set(component, 'hidden', true); } - } else { - conditionalComp.conditionallyHidden = false; } }; diff --git a/src/utils/conditions.ts b/src/utils/conditions.ts index 85d96dbd..010767a5 100644 --- a/src/utils/conditions.ts +++ b/src/utils/conditions.ts @@ -58,11 +58,11 @@ export function checkCustomConditional(condition: string, context: ConditionsCon * @returns */ export function checkLegacyConditional(conditional: LegacyConditional, context: ConditionsContext): boolean | null { - const { row, data } = context; + const { row, data, component } = context; if (!conditional || !isLegacyConditional(conditional) || !conditional.when) { return null; } - const value: any = getComponentActualValue(conditional.when, data, row); + const value: any = getComponentActualValue(component, conditional.when, data, row); const eq = String(conditional.eq); const show = String(conditional.show); if (isObject(value) && has(value, eq)) { @@ -111,7 +111,7 @@ export function checkSimpleConditional(conditional: SimpleConditional, context: // Ignore conditions if there is no component path. return null; } - const value = getComponentActualValue(conditionComponentPath, data, row); + const value = getComponentActualValue(component, conditionComponentPath, data, row); const ConditionOperator = ConditionOperators[operator]; return ConditionOperator ? new ConditionOperator().getResult({ value, comparedValue, instance, component, conditionComponentPath }) diff --git a/src/utils/formUtil.ts b/src/utils/formUtil.ts index 8f5f565d..04e8357c 100644 --- a/src/utils/formUtil.ts +++ b/src/utils/formUtil.ts @@ -13,7 +13,8 @@ import { pad, isPlainObject, isArray, - isEqual + isEqual, + trim } from "lodash"; import { compare, applyPatch } from 'fast-json-patch'; import { @@ -557,7 +558,22 @@ export function getComponentData(components: Component[], data: DataObject, path return compData; } -export function getComponentActualValue(compPath: string, data: any, row: any) { +export function getComponentActualValue(component: Component, compPath: string, data: any, row: any) { + // The compPath here will NOT contain the indexes for DataGrids and EditGrids. + // + // a[0].b[2].c[3].d + // + // Because of this, we will need to determine our parent component path (not data path), + // and find the "row" based comp path. + // + // a[0].b[2].c[3].d => a.b.c.d + // + if (component.parent?.path) { + const parentCompPath = component.parent?.path.replace(/\[[0-9]+\]/g, ''); + compPath = compPath.replace(parentCompPath, ''); + compPath = trim(compPath, '. '); + } + let value = null; if (row) { value = get(row, compPath); diff --git a/src/utils/logic.ts b/src/utils/logic.ts index be1b1329..5c6c7b72 100644 --- a/src/utils/logic.ts +++ b/src/utils/logic.ts @@ -42,12 +42,36 @@ export const checkTrigger = (context: LogicContext, trigger: any): boolean => { }; export function setActionBooleanProperty(context: LogicContext, action: LogicActionPropertyBoolean): boolean { - const { component } = context; + const { component, scope, path } = context; const property = action.property.value; const currentValue = get(component, property, false).toString(); const newValue = action.state.toString(); if (currentValue !== newValue) { set(component, property, newValue === 'true'); + + // If this is "logic" forcing a component to be hidden, then we will set the "conditionallyHidden" + // flag which will trigger the clearOnHide functionality. + if ( + property === 'hidden' && + component.hidden && + path + ) { + if (!(scope as any).conditionals) { + (scope as any).conditionals = []; + } + const conditionalyHidden = (scope as any).conditionals.find((cond: any) => { + return cond.path === path + }); + if (conditionalyHidden) { + conditionalyHidden.conditionallyHidden = true; + } + else { + (scope as any).conditionals.push({ + path, + conditionallyHidden: true + }); + } + } return true; } return false;