Skip to content

Commit

Permalink
Refactor the paths to ensure that we are always referencing the corre…
Browse files Browse the repository at this point in the history
…ct path through the scope.
  • Loading branch information
travist committed Nov 8, 2024
1 parent 310d86d commit bf2959c
Show file tree
Hide file tree
Showing 33 changed files with 791 additions and 643 deletions.
15 changes: 7 additions & 8 deletions src/modules/jsonlogic/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { BaseEvaluator, EvaluatorOptions } from 'utils';
import { jsonLogic } from './jsonLogic';
import { EvaluatorContext, normalizeContext } from 'utils/Evaluator';
export class JSONLogicEvaluator extends BaseEvaluator {
public static evaluate(
func: any,

Check warning on line 6 in src/modules/jsonlogic/index.ts

View workflow job for this annotation

GitHub Actions / setup

Unexpected any. Specify a different type

Check warning on line 6 in src/modules/jsonlogic/index.ts

View workflow job for this annotation

GitHub Actions / publish

Unexpected any. Specify a different type
Expand All @@ -24,12 +25,6 @@ export class JSONLogicEvaluator extends BaseEvaluator {
}
}

export type EvaluatorContext = {
evalContext?: (context: any) => any;
instance?: any;
[key: string]: any;
};

export type EvaluatorFn = (context: EvaluatorContext) => any;

Check warning on line 28 in src/modules/jsonlogic/index.ts

View workflow job for this annotation

GitHub Actions / setup

Unexpected any. Specify a different type

Check warning on line 28 in src/modules/jsonlogic/index.ts

View workflow job for this annotation

GitHub Actions / publish

Unexpected any. Specify a different type

export function evaluate(
Expand All @@ -40,7 +35,9 @@ export function evaluate(
options: EvaluatorOptions = {},
) {
const { evalContext, instance } = context;
const evalContextValue = evalContext ? evalContext(context) : context;
const evalContextValue = evalContext
? evalContext(normalizeContext(context))
: normalizeContext(context);
if (evalContextFn) {
evalContextFn(evalContextValue);
}
Expand All @@ -63,7 +60,9 @@ export function interpolate(
evalContextFn?: EvaluatorFn,
): string {
const { evalContext, instance } = context;
const evalContextValue = evalContext ? evalContext(context) : context;
const evalContextValue = evalContext
? evalContext(normalizeContext(context))
: normalizeContext(context);
if (evalContextFn) {
evalContextFn(evalContextValue);
}
Expand Down
9 changes: 5 additions & 4 deletions src/process/__tests__/process.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {
skipValidForLogicallyHiddenComp,
skipValidWithHiddenParentComp,
} from './fixtures';
import { get } from 'lodash';
import _ from 'lodash';

/*
describe('Process Tests', () => {
Expand Down Expand Up @@ -959,6 +959,7 @@ describe('Process Tests', function () {

const errors: any = [];
const context = {
_,
form,
submission,
data: submission.data,
Expand Down Expand Up @@ -1114,8 +1115,8 @@ describe('Process Tests', function () {
submission.data = context.data;
context.processors = ProcessTargets.evaluator;
processSync(context);
assert.equal(get(context.submission.data, 'form1.data.form.data.textField'), 'one 1');
assert.equal(get(context.submission.data, 'form1.data.form.data.textField1'), 'two 2');
assert.equal(_.get(context.submission.data, 'form1.data.form.data.textField'), 'one 1');
assert.equal(_.get(context.submission.data, 'form1.data.form.data.textField1'), 'two 2');
});

it('should remove submission data not in a nested form definition', async function () {
Expand Down Expand Up @@ -3437,7 +3438,7 @@ describe('Process Tests', function () {
});
});

it('Should allow the submission to go through without errors if there is no the subform reference value', async function () {
it('Should allow the submission to go through without errors if there is no subform reference value', async function () {
const form = {
_id: '66bc5cff7ca1729623a182db',
title: 'form2',
Expand Down
5 changes: 4 additions & 1 deletion src/process/calculation/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
ProcessorInfo,
} from 'types';
import { set } from 'lodash';
import { normalizeContext } from 'utils/Evaluator';

export const shouldCalculate = (context: CalculationContext): boolean => {
const { component, config } = context;
Expand All @@ -23,7 +24,9 @@ export const calculateProcessSync: ProcessorFnSync<CalculationScope> = (
if (!shouldCalculate(context)) {
return;
}
const evalContextValue = evalContext ? evalContext(context) : context;
const evalContextValue = evalContext
? evalContext(normalizeContext(context))
: normalizeContext(context);
evalContextValue.value = value || null;
if (!scope.calculated) scope.calculated = [];
const newValue = JSONLogicEvaluator.evaluate(component.calculateValue, evalContextValue, 'value');
Expand Down
10 changes: 4 additions & 6 deletions src/process/clearHidden.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import {
ProcessorFnSync,
ConditionsScope,
} from 'types';
import { getComponentAbsolutePath } from 'utils/formUtil';

type ClearHiddenScope = ProcessorScope & {
clearHidden: {
Expand All @@ -19,7 +18,6 @@ type ClearHiddenScope = ProcessorScope & {
*/
export const clearHiddenProcess: ProcessorFnSync<ClearHiddenScope> = (context) => {
const { component, data, value, scope, path } = context;
const absolutePath = getComponentAbsolutePath(component) || path;

// No need to unset the value if it's undefined
if (value === undefined) {
Expand All @@ -32,18 +30,18 @@ export const clearHiddenProcess: ProcessorFnSync<ClearHiddenScope> = (context) =

// Check if there's a conditional set for the component and if it's marked as conditionally hidden
const isConditionallyHidden = (scope as ConditionsScope).conditionals?.find((cond) => {
return absolutePath === cond.path && cond.conditionallyHidden;
return path === cond.path && cond.conditionallyHidden;
});

const shouldClearValueWhenHidden =
!component.hasOwnProperty('clearOnHide') || component.clearOnHide;

if (
shouldClearValueWhenHidden &&
(isConditionallyHidden || component.hidden || component.ephemeralState?.conditionallyHidden)
(isConditionallyHidden || component.hidden || component.scope?.conditionallyHidden)
) {
unset(data, absolutePath);
scope.clearHidden[absolutePath] = true;
unset(data, path);
scope.clearHidden[path] = true;
}
};

Expand Down
52 changes: 52 additions & 0 deletions src/process/clearHidden/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { unset } from 'lodash';
import {
ProcessorScope,
ProcessorContext,
ProcessorInfo,
ProcessorFnSync,
ConditionsScope,
} 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<ClearHiddenScope> = (context) => {
const { component, data, value, scope, path } = context;

// No need to unset the value if it's undefined
if (value === undefined) {
return;
}

if (!scope.clearHidden) {
scope.clearHidden = {};
}

// Check if there's a conditional set for the component and if it's marked as conditionally hidden
const isConditionallyHidden = (scope as ConditionsScope).conditionals?.find((cond) => {
return path === cond.path && cond.conditionallyHidden;
});

const shouldClearValueWhenHidden =
!component.hasOwnProperty('clearOnHide') || component.clearOnHide;

if (
shouldClearValueWhenHidden &&
(isConditionallyHidden || component.scope?.conditionallyHidden)
) {
unset(data, path);
scope.clearHidden[path] = true;
}
};

export const clearHiddenProcessInfo: ProcessorInfo<ProcessorContext<ClearHiddenScope>, void> = {
name: 'clearHidden',
shouldProcess: () => true,
processSync: clearHiddenProcess,
};
10 changes: 4 additions & 6 deletions src/process/conditions/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
ProcessorInfo,
ConditionsContext,
} from 'types';
import { registerEphemeralState } from 'utils';
import { setComponentScope } from 'utils/formUtil';
import {
checkCustomConditional,
checkJsonConditional,
Expand All @@ -15,7 +15,6 @@ import {
isSimpleConditional,
isJSONConditional,
} from 'utils/conditions';
import { getComponentAbsolutePath } from 'utils/formUtil';

const hasCustomConditions = (context: ConditionsContext): boolean => {
const { component } = context;
Expand Down Expand Up @@ -85,24 +84,23 @@ export type ConditionallyHidden = (context: ConditionsContext) => boolean;

export const conditionalProcess = (context: ConditionsContext, isHidden: ConditionallyHidden) => {
const { scope, path, component } = context;
const absolutePath = getComponentAbsolutePath(component) || path;
if (!hasConditions(context)) {
return;
}

if (!scope.conditionals) {
scope.conditionals = [];
}
let conditionalComp = scope.conditionals.find((cond) => cond.path === absolutePath);
let conditionalComp = scope.conditionals.find((cond) => cond.path === path);
if (!conditionalComp) {
conditionalComp = { path: absolutePath, conditionallyHidden: false };
conditionalComp = { path, conditionallyHidden: false };
scope.conditionals.push(conditionalComp);
}

conditionalComp.conditionallyHidden =
conditionalComp.conditionallyHidden || isHidden(context) === true;
if (conditionalComp.conditionallyHidden) {
registerEphemeralState(context.component, 'conditionallyHidden', true);
setComponentScope(component, 'conditionallyHidden', true);
}
};

Expand Down
5 changes: 4 additions & 1 deletion src/process/defaultValue/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
} from 'types';
import { set, has } from 'lodash';
import { getComponentKey } from 'utils/formUtil';
import { normalizeContext } from 'utils/Evaluator';

export const hasCustomDefaultValue = (context: DefaultValueContext): boolean => {
const { component } = context;
Expand Down Expand Up @@ -48,7 +49,9 @@ export const customDefaultValueProcessSync: ProcessorFnSync<ConditionsScope> = (
}
let defaultValue = null;
if (component.customDefaultValue) {
const evalContextValue = evalContext ? evalContext(context) : context;
const evalContextValue = evalContext
? evalContext(normalizeContext(context))
: normalizeContext(context);
evalContextValue.value = null;
defaultValue = JSONLogicEvaluator.evaluate(
component.customDefaultValue,
Expand Down
5 changes: 4 additions & 1 deletion src/process/fetch/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
import { get, set } from 'lodash';
import { Evaluator } from 'utils';
import { getComponentKey } from 'utils/formUtil';
import { normalizeContext } from 'utils/Evaluator';

export const shouldFetch = (context: FetchContext): boolean => {
const { component, config } = context;
Expand Down Expand Up @@ -38,7 +39,9 @@ export const fetchProcess: ProcessorFn<FetchScope> = async (context: FetchContex
return;
}
if (!scope.fetched) scope.fetched = {};
const evalContextValue = evalContext ? evalContext(context) : context;
const evalContextValue = evalContext
? evalContext(normalizeContext(context))
: normalizeContext(context);
const url = Evaluator.interpolateString(get(component, 'fetch.url', ''), evalContextValue);
if (!url) {
return;
Expand Down
16 changes: 7 additions & 9 deletions src/process/filter/index.ts
Original file line number Diff line number Diff line change
@@ -1,46 +1,44 @@
import { FilterContext, FilterScope, ProcessorFn, ProcessorFnSync, ProcessorInfo } from 'types';
import { set } from 'lodash';
import { Utils } from 'utils';
import { get, isObject } from 'lodash';
import { getComponentAbsolutePath } from 'utils/formUtil';
import { getModelType } from 'utils/formUtil';
export const filterProcessSync: ProcessorFnSync<FilterScope> = (context: FilterContext) => {
const { scope, component, path } = context;
const { value } = context;
const absolutePath = getComponentAbsolutePath(component) || path;
if (!scope.filter) scope.filter = {};
if (value !== undefined) {
const modelType = Utils.getModelType(component);
const modelType = getModelType(component);
switch (modelType) {
case 'dataObject':
scope.filter[absolutePath] = {
scope.filter[path] = {
compModelType: modelType,
include: true,
value: { data: {} },
};
break;
case 'nestedArray':
scope.filter[absolutePath] = {
scope.filter[path] = {
compModelType: modelType,
include: true,
value: [],
};
break;
case 'nestedDataArray':
scope.filter[absolutePath] = {
scope.filter[path] = {
compModelType: modelType,
include: true,
value: Array.isArray(value) ? value.map((v) => ({ ...v, data: {} })) : [],
};
break;
case 'object':
scope.filter[absolutePath] = {
scope.filter[path] = {
compModelType: modelType,
include: true,
value: component.type === 'address' ? false : {},
};
break;
default:
scope.filter[absolutePath] = {
scope.filter[path] = {
compModelType: modelType,
include: true,
};
Expand Down
10 changes: 4 additions & 6 deletions src/process/hideChildren.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,26 +6,24 @@ import {
ConditionsScope,
ProcessorFn,
} from 'types';
import { registerEphemeralState } from 'utils';
import { getComponentAbsolutePath } from 'utils/formUtil';
import { setComponentScope } from 'utils/formUtil';

/**
* This processor function checks components for the `hidden` property and, if children are present, sets them to hidden as well.
*/
export const hideChildrenProcessor: ProcessorFnSync<ConditionsScope> = (context) => {
const { component, path, parent, scope } = context;
const absolutePath = getComponentAbsolutePath(component) || path;
// Check if there's a conditional set for the component and if it's marked as conditionally hidden
const isConditionallyHidden = scope.conditionals?.find((cond) => {
return absolutePath === cond.path && cond.conditionallyHidden;
return path === cond.path && cond.conditionallyHidden;
});

if (!scope.conditionals) {
scope.conditionals = [];
}

if (isConditionallyHidden || component.hidden || parent?.ephemeralState?.conditionallyHidden) {
registerEphemeralState(component, 'conditionallyHidden', true);
if (isConditionallyHidden || component.hidden || parent?.scope?.conditionallyHidden) {
setComponentScope(component, 'conditionallyHidden', true);
}
};

Expand Down
7 changes: 3 additions & 4 deletions src/process/populate/index.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
import { get, set } from 'lodash';
import { PopulateContext, PopulateScope, ProcessorFnSync } from 'types';
import { componentPath, getContextualRowPath, getModelType } from 'utils/formUtil';

// This processor ensures that a "linked" row context is provided to every component.
export const populateProcessSync: ProcessorFnSync<PopulateScope> = (context: PopulateContext) => {
const { component, path, scope } = context;
const { data } = scope;
const compDataPath = componentPath(component, getContextualRowPath(component, path));
const compData: any = get(data, compDataPath);
const compDataPath = component.path || '';
const compData: any = get(data, compDataPath, data);
if (!scope.populated) scope.populated = [];
switch (getModelType(component)) {
switch (component.modelType) {
case 'nestedArray':
if (!compData || !compData.length) {
set(data, compDataPath, [{}]);
Expand Down
Loading

0 comments on commit bf2959c

Please sign in to comment.