* Client changes 1 * add DSL functionality * Temp commit for refactoring changes * Do I even know what I'm doing here? * chore: Second GS layout * Update: Visibility conditional outputs for schemas - Added the output from conditional outputs for schema children too * Update: Entity selector visibility control - Added logic for controlling visibility of sub components via the JS expressions system * Update: Passing disabled prop to toggle button * Update: Passing disabled prop to toggle btn * Update: Styled component for toggle button - Added disabled styles based on the disabled prop sent to the toggle form view JSON button * Update: configProperty role in Entity Selector - Removed dependance of the configProperty of the entity selector children to it's parent component * Update: type of placeholder key - Made placeholder key from form config JSON to accept either string or an object - Earlier only string was accepted - This is for pagination component * Update: Added placeholder control for pagination * Client changes 1 * add DSL functionality * Do I even know what I'm doing here? * fix: updated uqi forms ui, clubbed JS switch button to ads, updated tooltip design * fix: updated tooltip component for wrong ui on entity explore * temp triggers * fix: updated uqi forms ui, clubbed JS switch button to ads, updated tooltip design (#12395) * fix: updated uqi forms ui, clubbed JS switch button to ads, updated tooltip design * fix: updated tooltip component for wrong ui on entity explore * fix: updated tooltip ui, where condition placement, sort by ui * temp form data access logic * fix: updated sorting type width ui * fix: updated ui for spacing, width and text issues * Update: Type for tooltip of UQI forms - Added option to send an object to the tooltipText object. - This allows for composite components like pagination to have tooltips for each sub component * Update: tooltip for pagination component - Added handling to parse the tooltip for multiple components. - This allows for composite components like pagination to have tooltips for each sub component * Update: Type cast for tooltip component - Made the content passed to tooltip component as a string only * Update: Fixed tooltip component CSS * Update: Dropdown option component - Added a tooltip wrapper to each option - This is to show on hover text like disabled state * fix: updated ẇhere clause broken ui for condition * Add: functions to check and extract expressions - Loop through the formConfig and find any keys that have a value that is bindable - Used pre defined regex to check if value is a moustache binding * Add: Types for evaluated form configs - Added types for the form configs to be evaluated and their output post eval * Add: Flow to run the form config - Run the form config and update the result to the redux state * Update: Name of the type for formconfigs - Updated since it was clashing with a component of the same name * Add: Function to enforce config type checks - This is done so that the improper configs can be weeded out and the rest of the form can be shown * Add: Function to update evaluated config - Added option to update the config if it's values needed evaluation * Add: Type check for schema sections * Update: Error handling for invalid control type - We were throwing an exception till now, changed it to a warning text * Add: Exposed tooltip for dropdown option disabled state * Update: switch to json mode functionality - Added logic to convert data to a string rather than an object when the first switch to JSON mode happens * Update: Added key to tooltip for dropdown options * Trigger API modification * Add: function to fetch default trigger URL * Update: Made URL optional in dynamic trigger config * Update: Dynamic trigger API call - Made the API call for dynamic triggers have URL as optional field - Added type check to the response of the API call * Update: resp type for trigger APIs * Update: Moved code to utils folder - Moved functions for UQI form eval processing to utils file * Update: passing original controltype to JS switch * Update: config for JSON editor mode - Updated the config to have different options for JSON mode depending on the original control type * Update: Connected line numbers flag to config * Revert: CSS changes for tooltip * Refactor: Removed consle * Add: type for the config of dynamic values * Add: Feature to evaluate config for triggers * Refactor: fix type check errors * fix: dropdown ui width with text alignment * Update: fixed selector for dynamic values * Update: selector call for fetchDynamicValues * Add table header index prop for columns selector * migration partial commit * migration partial commit * Refactor: removed unused import * Update: reused function for checking dynamic value * Update: removed unused import * Fix format JSON issues * Retrieve binding paths from entity selector components * Fixes 6 remaining issues with UQI implementation * Fix dropdown issues * Fix dropdown height issues and fixes triggering of APIs when option is deselected * Migration changes * Fix QA generated UQI issues * Fix projection component height and route change logic * Fix multi select dropdown placeholder text issue and json stringify issue with switching view types * Reset entity type value when command value changes * Test changes * Review comments * Moved migrations around * Corrected import statement * Added JSON schema migration * Updated schema version * perf improvements and filter dropdown options feature * Fix Code mirror component config for toggleComponentToJson input fields. * Fix prettier issues * fix prettier issues * Fix style issues as a result of the merged conflicts * Fix failing test case * Fixed a few other flows (#14225) * Fixed a few other flows * Review comments * Fix generate CRUD, fix evaluation of dynamic bindings and fix various styling issues. * More fixes (#14367) * Factor in the root formconfig parent key. * Fix flickering issues, and evaluatedFormConfig issues * fix: Teeny bugs (#14455) * Teeny bugs * Added previous functionality as is * Improvements in the way we fetch dynamic values * Fix stringiification issue and cyclic dependency issues * Resolve projection component values deletion * Resolve merge conflicts and fix prettier issues * fix: Tsc issues * Fix property pane connection navigation * updating ee locator * updating inputfield locator * dropdown locator update * Merge conflict not properly resolved. * Fix s3 spec * Fix Mongo Spec * Fix some more tests * fix: prevent cyclic dependency when switching to js mode (#14668) * add delete events for change from array to string in diff * add test to assert absence of cyclic dependency error when switching to js in switchgroup widget * Assert that evaluation is not disabled when no cyclic dependency happens * Cypress test preparations for google sheets and form controls * Fixed a few test errors (#14874) * Add: unit tests for uqi UI updates - view type tests - conditional output extraction - processing conditional output to handle view/enabled state of the component * Add: completed isValidFormConfig test * Update: improved tests for update config - These tests cover the functionality to update a section config after it's components are done evaluating * Fix failing cypress tests and cyclic dependency issue * Fixes some more tests * Fixed migration of row objects (#14896) * Bumped the version of design system package * Update: reverted change to EE selector * Fix deletion pointer * Update: selector for js on load spec - Synced with changes related to ADS dropdown * Fix mongoDBShoppingCart spec * Remove comments * Fix: mongo shopping cart test failures * fix: mongo shopping cart spec * Dummy push to retrigger vercel * fix: mongo shopping cart spec * Update MongoDBShoppingCart_spec.js * fix: removed unused click away * dummy commit * Update: moved helper functions to separate file * Add: added tests for saga functions - Worked on testing for - extractFetchDynamicValueFormConfigs - extractQueueOfValuesToBeFetched * Add if check for queueOfValuesToBeFetched * Resolve review comments * Empty-Commit Co-authored-by: Irongade <adeoluayangade@yahoo.com> Co-authored-by: Ayush Pahwa <ayush@appsmith.com> Co-authored-by: Aman Agarwal <aman@appsmith.com> Co-authored-by: Ayangade Adeoluwa <37867493+Irongade@users.noreply.github.com> Co-authored-by: Aishwarya UR <aishwarya@appsmith.com> Co-authored-by: Favour Ohanekwu <fohanekwu@gmail.com> Co-authored-by: Albin <albin@appsmith.com>
547 lines
20 KiB
TypeScript
547 lines
20 KiB
TypeScript
import {
|
|
DynamicValues,
|
|
EvaluatedFormConfig,
|
|
FormEvalOutput,
|
|
FormEvaluationState,
|
|
FormConfigEvalObject,
|
|
DynamicValuesConfig,
|
|
} from "reducers/evaluationReducers/formEvaluationReducer";
|
|
import { ReduxActionTypes } from "ce/constants/ReduxActionConstants";
|
|
import { ActionConfig } from "entities/Action";
|
|
import { FormEvalActionPayload } from "sagas/FormEvaluationSaga";
|
|
import { FormConfigType } from "components/formControls/BaseControl";
|
|
import { isArray, isEmpty, isString, merge, uniq } from "lodash";
|
|
import { extractEvalConfigFromFormConfig } from "components/formControls/utils";
|
|
import { isDynamicValue } from "utils/DynamicBindingUtils";
|
|
import { isTrueObject } from "./evaluationUtils";
|
|
|
|
export enum ConditionType {
|
|
HIDE = "hide", // When set, the component will be shown until condition is true
|
|
SHOW = "show", // When set, the component will be hidden until condition is true
|
|
ENABLE = "enable", // When set, the component will be enabled until condition is true
|
|
DISABLE = "disable", // When set, the component will be disabled until condition is true
|
|
FETCH_DYNAMIC_VALUES = "fetchDynamicValues", // When set, the component will fetch the values dynamically
|
|
EVALUATE_FORM_CONFIG = "evaluateFormConfig", // When set, the component will evaluate the form config settings
|
|
}
|
|
|
|
export enum FormDataPaths {
|
|
COMMAND = "actionConfiguration.formData.command.data",
|
|
ENTITY_TYPE = "actionConfiguration.formData.entityType.data",
|
|
}
|
|
|
|
// Object to hold the initial eval object
|
|
let finalEvalObj: FormEvalOutput;
|
|
|
|
// This variable, holds an array of strings that represent the path for the evalConfigs.
|
|
// This path os used to configure the evalFormConfig objects for various form configs
|
|
let evalConfigPaths: string[] = [];
|
|
|
|
// This regex matches the config property string up to countless places.
|
|
export const MATCH_ACTION_CONFIG_PROPERTY = /\b(actionConfiguration\.\w+.(?:(\w+.)){1,})\b/g;
|
|
export function matchExact(r: RegExp, str: string) {
|
|
const match = str.match(r);
|
|
return match || [];
|
|
}
|
|
|
|
// Recursive function to generate the evaluation state for form config
|
|
const generateInitialEvalState = (formConfig: FormConfigType) => {
|
|
const conditionals: Record<string, any> = {};
|
|
const conditionTypes: Record<string, any> = {};
|
|
let dependencyPaths: string[] = [];
|
|
|
|
// // Any element is only added to the eval state if they have a conditional statement present, if not they are allowed to be rendered
|
|
// if ("conditionals" in formConfig && !!formConfig.conditionals) {
|
|
let key = "unknowns";
|
|
|
|
// A unique key is used to refer the object in the eval state, can be propertyName, configProperty or identifier
|
|
if ("propertyName" in formConfig && !!formConfig.propertyName) {
|
|
key = formConfig.propertyName;
|
|
} else if ("configProperty" in formConfig && !!formConfig.configProperty) {
|
|
key = formConfig.configProperty;
|
|
} else if ("identifier" in formConfig && !!formConfig.identifier) {
|
|
key = formConfig.identifier;
|
|
}
|
|
|
|
// Any element is only added to the eval state if they have a conditional statement present, if not they are allowed to be rendered
|
|
if ("conditionals" in formConfig && !!formConfig.conditionals) {
|
|
const allConditionTypes = Object.keys(formConfig.conditionals);
|
|
if (
|
|
allConditionTypes.includes(ConditionType.HIDE) ||
|
|
allConditionTypes.includes(ConditionType.SHOW)
|
|
) {
|
|
conditionTypes.visible = false;
|
|
merge(conditionals, formConfig.conditionals);
|
|
|
|
const showOrHideDependencies = matchExact(
|
|
MATCH_ACTION_CONFIG_PROPERTY,
|
|
formConfig.conditionals?.show || formConfig.conditionals?.hide || "",
|
|
);
|
|
|
|
dependencyPaths = [...dependencyPaths, ...showOrHideDependencies];
|
|
}
|
|
|
|
if (
|
|
allConditionTypes.includes(ConditionType.ENABLE) ||
|
|
allConditionTypes.includes(ConditionType.DISABLE)
|
|
) {
|
|
conditionTypes.enabled = true;
|
|
merge(conditionals, formConfig.conditionals);
|
|
|
|
const enableOrDisableDependencies = matchExact(
|
|
MATCH_ACTION_CONFIG_PROPERTY,
|
|
formConfig.conditionals?.enable ||
|
|
formConfig.conditionals?.disable ||
|
|
"",
|
|
);
|
|
|
|
dependencyPaths = [...dependencyPaths, ...enableOrDisableDependencies];
|
|
}
|
|
|
|
// if (allConditionTypes.includes(ConditionType.EVALUATE_FORM_CONFIG)) {
|
|
// // Setting the component as invisible since it has elements that will be evaluated later
|
|
// conditionTypes.visible = false;
|
|
// const evaluateFormConfig: EvaluatedFormConfig = {
|
|
// updateEvaluatedConfig: false,
|
|
// paths: formConfig.conditionals.evaluateFormConfig.paths,
|
|
// evaluateFormConfigObject: extractEvalConfigFromFormConfig(
|
|
// formConfig,
|
|
// formConfig.conditionals.evaluateFormConfig.paths,
|
|
// ),
|
|
// };
|
|
// conditionTypes.evaluateFormConfig = evaluateFormConfig;
|
|
// conditionals.evaluateFormConfig =
|
|
// formConfig.conditionals.evaluateFormConfig.condition;
|
|
// }
|
|
|
|
if (allConditionTypes.includes(ConditionType.FETCH_DYNAMIC_VALUES)) {
|
|
const fetchDynamicValuesDependencies = matchExact(
|
|
MATCH_ACTION_CONFIG_PROPERTY,
|
|
formConfig.conditionals?.fetchDynamicValues?.condition || "",
|
|
);
|
|
let dynamicDependencyPathList: Set<string> | undefined;
|
|
|
|
if (fetchDynamicValuesDependencies.length > 0) {
|
|
dynamicDependencyPathList = new Set(fetchDynamicValuesDependencies);
|
|
} else {
|
|
dynamicDependencyPathList = undefined;
|
|
}
|
|
|
|
const dynamicValues: DynamicValues = {
|
|
allowedToFetch: false,
|
|
isLoading: false,
|
|
hasStarted: false,
|
|
hasFetchFailed: false,
|
|
data: [],
|
|
config: formConfig.conditionals.fetchDynamicValues.config,
|
|
dynamicDependencyPathList,
|
|
evaluatedConfig: { params: {} },
|
|
};
|
|
conditionTypes.fetchDynamicValues = dynamicValues;
|
|
conditionals.fetchDynamicValues =
|
|
formConfig.conditionals.fetchDynamicValues.condition;
|
|
}
|
|
|
|
// make the evalConfigPaths empty before calling the generateFormEvalFormConfigPaths
|
|
// this is helpful since we are iterating through the form configs and we do not want to store the value of a
|
|
// prev form config into another one.
|
|
evalConfigPaths = [];
|
|
|
|
// recursively generate the paths for form cofigs that need evalFormConfig.
|
|
// and we store them in the evalFormFonfig
|
|
generateEvalFormConfigPaths(formConfig);
|
|
|
|
// we generate a unique array of paths, if the paths are greater than 0,
|
|
// we generate and add the evaluateFormConfig object to the current formConfig.
|
|
if (uniq(evalConfigPaths).length > 0) {
|
|
conditionTypes.visible = false;
|
|
const evaluateFormConfig: EvaluatedFormConfig = {
|
|
updateEvaluatedConfig: false,
|
|
paths: uniq(evalConfigPaths),
|
|
evaluateFormConfigObject: extractEvalConfigFromFormConfig(
|
|
formConfig,
|
|
uniq(evalConfigPaths),
|
|
),
|
|
};
|
|
conditionTypes.evaluateFormConfig = evaluateFormConfig;
|
|
conditionals.evaluateFormConfig = "{{true}}";
|
|
}
|
|
}
|
|
|
|
// keep the configProperty in the formConfig values.
|
|
let configPropertyPath;
|
|
if (!!formConfig.configProperty) {
|
|
configPropertyPath = formConfig.configProperty;
|
|
}
|
|
|
|
let staticDependencyPathList: Set<string> | undefined;
|
|
|
|
if (dependencyPaths.length > 0) {
|
|
staticDependencyPathList = new Set(dependencyPaths);
|
|
} else {
|
|
staticDependencyPathList = undefined;
|
|
}
|
|
|
|
// Conditionals are stored in the eval state itself for quick access
|
|
finalEvalObj[key] = {
|
|
...conditionTypes,
|
|
conditionals,
|
|
configPropertyPath,
|
|
staticDependencyPathList,
|
|
};
|
|
|
|
if ("children" in formConfig && !!formConfig.children)
|
|
formConfig.children.forEach((config: FormConfigType) =>
|
|
generateInitialEvalState(config),
|
|
);
|
|
|
|
if ("schema" in formConfig && !!formConfig.schema)
|
|
formConfig.schema.forEach((config: FormConfigType) =>
|
|
generateInitialEvalState({ ...config }),
|
|
);
|
|
};
|
|
|
|
// The idea here is to recursively go through each of the key value pairs of the current form config
|
|
// then we check if the form config or its children/options/schemas have dynamic values
|
|
// if the children/options/schemas have dynamic values within them, we add the key name of the parent to the evalFormConfigPaths
|
|
// this might sound strange but we add the evaluateFormConfig property to the parent.
|
|
// this is why we pass the parent key into the function and use it to update the evalFormConfig.
|
|
function generateEvalFormConfigPaths(
|
|
formConfig: FormConfigType,
|
|
parentKey = "",
|
|
) {
|
|
// this stores all the paths for the current form config,
|
|
// we then use this path to update the evalFormConfig array with the parent
|
|
const paths: string[] = [];
|
|
// we never check the conditionals object, or the placeholderText.
|
|
// we also never check children and schema cause the recursive function that this function is called in already checks the children and schemas (to prevent double recursive checks).
|
|
// the second placeHolderText is due to a rogue value in the formConfig of one of S3 datasource form config.
|
|
const configToBeChecked = {
|
|
...formConfig,
|
|
conditionals: undefined,
|
|
children: undefined,
|
|
schema: undefined,
|
|
placeholderText: undefined,
|
|
placeHolderText: undefined,
|
|
};
|
|
|
|
Object.entries(configToBeChecked).forEach(([key, value]) => {
|
|
// we check if the current value for the key is a dynamic value, if yes, we push the current key into our paths array.
|
|
if (!!value) {
|
|
if (isString(value)) {
|
|
if (isDynamicValue(value)) {
|
|
paths.push(key);
|
|
// if parent key is empty, then there is a very good chance it's coming from the root form config.
|
|
// and in that case we can just set it to it.
|
|
if (!parentKey) parentKey = key;
|
|
}
|
|
}
|
|
|
|
// if it's an array, we run it recursively on the array values.
|
|
if (isArray(value)) {
|
|
value.forEach((val) => {
|
|
generateEvalFormConfigPaths(val, key);
|
|
});
|
|
}
|
|
|
|
// if it is an object, we do the same.
|
|
if (isTrueObject(value as FormConfigType)) {
|
|
generateEvalFormConfigPaths(value, key);
|
|
}
|
|
}
|
|
});
|
|
|
|
// if the path array is greater than one, we update the evalConfigPaths with parent key.
|
|
if (paths.length > 0) {
|
|
evalConfigPaths.push(parentKey);
|
|
}
|
|
}
|
|
|
|
function evaluateDynamicValuesConfig(
|
|
actionConfiguration: ActionConfig,
|
|
config: Record<string, any>,
|
|
) {
|
|
const evaluatedConfig: Record<string, any> = { ...config };
|
|
const configArray = Object.entries(config);
|
|
if (configArray.length > 0) {
|
|
configArray.forEach(([key, value]) => {
|
|
if (typeof value === "object") {
|
|
evaluatedConfig[key] = evaluateDynamicValuesConfig(
|
|
actionConfiguration,
|
|
value,
|
|
);
|
|
} else if (typeof value === "string" && value.length > 0) {
|
|
if (isDynamicValue(value)) {
|
|
let evaluatedValue = "";
|
|
try {
|
|
evaluatedValue = eval(value);
|
|
} catch (e) {
|
|
evaluatedValue = "error";
|
|
} finally {
|
|
evaluatedConfig[key] = evaluatedValue;
|
|
}
|
|
}
|
|
}
|
|
});
|
|
}
|
|
return evaluatedConfig;
|
|
}
|
|
|
|
function evaluateFormConfigElements(
|
|
actionConfiguration: ActionConfig,
|
|
config: FormConfigEvalObject,
|
|
) {
|
|
const paths = Object.keys(config);
|
|
if (paths.length > 0) {
|
|
paths.forEach((path) => {
|
|
const { expression } = config[path];
|
|
try {
|
|
const evaluatedVal = eval(expression);
|
|
config[path].output = evaluatedVal;
|
|
} catch (e) {}
|
|
});
|
|
}
|
|
return config;
|
|
}
|
|
|
|
// Function to run the eval for the whole form when data changes
|
|
function evaluate(
|
|
actionConfiguration: ActionConfig,
|
|
currentEvalState: FormEvalOutput,
|
|
actionDiffPath?: string,
|
|
hasRouteChanged?: boolean,
|
|
) {
|
|
Object.keys(currentEvalState).forEach((key: string) => {
|
|
try {
|
|
if (currentEvalState[key].hasOwnProperty("conditionals")) {
|
|
const conditionBlock = currentEvalState[key].conditionals;
|
|
if (!!conditionBlock) {
|
|
Object.keys(conditionBlock).forEach((conditionType: string) => {
|
|
const output = eval(conditionBlock[conditionType]);
|
|
if (conditionType === ConditionType.HIDE) {
|
|
currentEvalState[key].visible = !output;
|
|
} else if (conditionType === ConditionType.SHOW) {
|
|
currentEvalState[key].visible = output;
|
|
} else if (conditionType === ConditionType.DISABLE) {
|
|
currentEvalState[key].enabled = !output;
|
|
} else if (conditionType === ConditionType.ENABLE) {
|
|
currentEvalState[key].enabled = output;
|
|
} else if (
|
|
conditionType === ConditionType.FETCH_DYNAMIC_VALUES &&
|
|
currentEvalState[key].hasOwnProperty("fetchDynamicValues") &&
|
|
!!currentEvalState[key].fetchDynamicValues
|
|
) {
|
|
// this boolean value represents if the current action diff path is a dependency to the form config.
|
|
let isActionDiffADependency = false;
|
|
|
|
// If the key in the currentEval state has dynamicDependencyPathList, we check to see if the path of the changed value
|
|
// exists in the path list, if it does, we evaluate the dynamicValues and fetch the data via API call,
|
|
// but if the value does not exist in the path list, we prevent the dynamic value from being refetched via API call.
|
|
// in other words, if the current actionDiffPath is a dependency, then isActionDiffADependency becomes true.
|
|
if (
|
|
currentEvalState[key] &&
|
|
!!currentEvalState[key]?.fetchDynamicValues
|
|
?.dynamicDependencyPathList &&
|
|
!isEmpty(
|
|
currentEvalState[key]?.fetchDynamicValues
|
|
?.dynamicDependencyPathList,
|
|
) &&
|
|
!!actionDiffPath &&
|
|
currentEvalState[
|
|
key
|
|
]?.fetchDynamicValues?.dynamicDependencyPathList?.has(
|
|
actionDiffPath,
|
|
)
|
|
) {
|
|
isActionDiffADependency = true;
|
|
}
|
|
|
|
// if the actionDiffPath is a dependency or if the route has changed (navigated to another action/page) of if there's no actionDiffPath at all (when the page is refreshed)
|
|
// we want to trigger an API call for the dynamic values.
|
|
if (
|
|
isActionDiffADependency ||
|
|
!actionDiffPath ||
|
|
hasRouteChanged
|
|
) {
|
|
(currentEvalState[key]
|
|
.fetchDynamicValues as DynamicValues).allowedToFetch = output;
|
|
(currentEvalState[key]
|
|
.fetchDynamicValues as DynamicValues).isLoading = output;
|
|
(currentEvalState[key]
|
|
.fetchDynamicValues as DynamicValues).evaluatedConfig = evaluateDynamicValuesConfig(
|
|
actionConfiguration,
|
|
(currentEvalState[key].fetchDynamicValues as DynamicValues)
|
|
.config,
|
|
) as DynamicValuesConfig;
|
|
} else {
|
|
(currentEvalState[key]
|
|
.fetchDynamicValues as DynamicValues).allowedToFetch = false;
|
|
(currentEvalState[key]
|
|
.fetchDynamicValues as DynamicValues).isLoading = false;
|
|
}
|
|
} else if (
|
|
conditionType === ConditionType.EVALUATE_FORM_CONFIG &&
|
|
currentEvalState[key].hasOwnProperty("evaluateFormConfig") &&
|
|
!!currentEvalState[key].evaluateFormConfig
|
|
) {
|
|
(currentEvalState[key]
|
|
.evaluateFormConfig as EvaluatedFormConfig).updateEvaluatedConfig = output;
|
|
currentEvalState[key].visible = output;
|
|
if (output && !!currentEvalState[key].evaluateFormConfig)
|
|
(currentEvalState[key]
|
|
.evaluateFormConfig as EvaluatedFormConfig).evaluateFormConfigObject = evaluateFormConfigElements(
|
|
actionConfiguration,
|
|
(currentEvalState[key]
|
|
.evaluateFormConfig as EvaluatedFormConfig)
|
|
.evaluateFormConfigObject,
|
|
);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
} catch (e) {}
|
|
});
|
|
return currentEvalState;
|
|
}
|
|
|
|
// Fetches current evaluation and runs a new one based on the new data
|
|
function getFormEvaluation(
|
|
formId: string,
|
|
actionConfiguration: ActionConfig,
|
|
currentEvalState: FormEvaluationState,
|
|
actionDiffPath?: string,
|
|
hasRouteChanged?: boolean,
|
|
): FormEvaluationState {
|
|
// Only change the form evaluation state if the form ID is same or the evaluation state is present
|
|
if (!!currentEvalState && currentEvalState.hasOwnProperty(formId)) {
|
|
const currentFormIdEvalState = currentEvalState[formId];
|
|
// specific conditions to be evaluated
|
|
let conditionToBeEvaluated = {};
|
|
// dynamic conditions always need evaluations
|
|
let dynamicConditionsToBeFetched = {};
|
|
for (const [key, value] of Object.entries(currentFormIdEvalState)) {
|
|
if (
|
|
value &&
|
|
!!value.configPropertyPath &&
|
|
!!actionDiffPath &&
|
|
actionDiffPath?.includes(value.configPropertyPath)
|
|
) {
|
|
conditionToBeEvaluated = { ...conditionToBeEvaluated, [key]: value };
|
|
}
|
|
|
|
// static dependency pathlist should be a key of identifiers that point to formControls that are dependent on the result of the current form config value.
|
|
// it is important to note the difference between staticDependencyPathList and dynamicDependencyPathList is that the former is for formConfigs that don't require API calls.
|
|
// they are mostly layout based i.e. show/hide, enable/disable
|
|
if (!!value.staticDependencyPathList && !!actionDiffPath) {
|
|
value.staticDependencyPathList.forEach(() => {
|
|
if (value.staticDependencyPathList?.has(actionDiffPath)) {
|
|
conditionToBeEvaluated = {
|
|
...conditionToBeEvaluated,
|
|
[key]: value,
|
|
};
|
|
}
|
|
});
|
|
}
|
|
|
|
// if there are dynamic values present, add them to the condition to be evaluated.
|
|
if (value && (!!value.fetchDynamicValues || !!value.evaluateFormConfig)) {
|
|
dynamicConditionsToBeFetched = {
|
|
...dynamicConditionsToBeFetched,
|
|
[key]: value,
|
|
};
|
|
}
|
|
}
|
|
|
|
// if no condition is to be evaluated or if the currently changing action diff path is the command path
|
|
// then we run evaluations on the whole form.
|
|
if (
|
|
isEmpty(conditionToBeEvaluated) ||
|
|
actionDiffPath === FormDataPaths.COMMAND
|
|
) {
|
|
conditionToBeEvaluated = evaluate(
|
|
actionConfiguration,
|
|
currentEvalState[formId],
|
|
actionDiffPath,
|
|
hasRouteChanged,
|
|
);
|
|
} else {
|
|
conditionToBeEvaluated = {
|
|
...conditionToBeEvaluated,
|
|
...dynamicConditionsToBeFetched,
|
|
};
|
|
conditionToBeEvaluated = evaluate(
|
|
actionConfiguration,
|
|
conditionToBeEvaluated,
|
|
actionDiffPath,
|
|
hasRouteChanged,
|
|
);
|
|
}
|
|
|
|
currentEvalState[formId] = {
|
|
...currentEvalState[formId],
|
|
...conditionToBeEvaluated,
|
|
};
|
|
}
|
|
|
|
return currentEvalState;
|
|
}
|
|
|
|
// Filter function to assign a function to the Action dispatched
|
|
export function setFormEvaluationSaga(
|
|
type: string,
|
|
payload: FormEvalActionPayload,
|
|
currentEvalState: FormEvaluationState,
|
|
) {
|
|
if (type === ReduxActionTypes.INIT_FORM_EVALUATION) {
|
|
finalEvalObj = {};
|
|
|
|
// Config is extracted from the editor json first
|
|
if (
|
|
"editorConfig" in payload &&
|
|
!!payload.editorConfig &&
|
|
payload.editorConfig.length > 0
|
|
) {
|
|
payload.editorConfig.forEach((config: FormConfigType) => {
|
|
generateInitialEvalState(config);
|
|
});
|
|
}
|
|
|
|
// Then the form config is extracted from the settings json
|
|
if (
|
|
"settingConfig" in payload &&
|
|
!!payload.settingConfig &&
|
|
payload.settingConfig.length > 0
|
|
) {
|
|
payload.settingConfig.forEach((config: FormConfigType) => {
|
|
generateInitialEvalState(config);
|
|
});
|
|
}
|
|
|
|
// if the evaluations are empty, then the form is not valid, don't mutate the state
|
|
if (isEmpty(finalEvalObj)) {
|
|
return currentEvalState;
|
|
}
|
|
|
|
// This is the initial evaluation state, evaluations can now be run on top of this
|
|
return { [payload.formId]: finalEvalObj };
|
|
} else {
|
|
const {
|
|
actionConfiguration,
|
|
actionDiffPath,
|
|
formId,
|
|
hasRouteChanged,
|
|
} = payload;
|
|
// In case the formData is not ready or the form is not of type UQI, return empty state
|
|
if (!actionConfiguration || !actionConfiguration.formData) {
|
|
return currentEvalState;
|
|
} else {
|
|
return getFormEvaluation(
|
|
formId,
|
|
actionConfiguration,
|
|
currentEvalState,
|
|
actionDiffPath,
|
|
hasRouteChanged,
|
|
);
|
|
}
|
|
}
|
|
}
|