fix: Updated drop down control memo usage (#11218)
* Stopped props drilling of eval state * Connect drop down to redux state * Added extra check to formcontrol memo function * Reduced modification of section at top * Stopped mutating the initial state * Created selector to get dynamic fetched values * <refactor> Added comments and refactors - Added key to the ES fragment - Cleaned drop down component from redundant code - Added comments * <refactor> Removed test files - Removed testing JSON configs * <fix> Added null check for form eval output - Added check to prevent null evalOutput in forms * <chore> Removed console error - Removed console error which is causing the vercel builds to fail
This commit is contained in:
parent
a2240c7107
commit
fe2d625f5e
|
|
@ -1,10 +1,7 @@
|
||||||
import { Component } from "react";
|
import { Component } from "react";
|
||||||
import { ControlType } from "constants/PropertyControlConstants";
|
import { ControlType } from "constants/PropertyControlConstants";
|
||||||
import { InputType } from "components/constants";
|
import { InputType } from "components/constants";
|
||||||
import {
|
import { ConditonalObject } from "reducers/evaluationReducers/formEvaluationReducer";
|
||||||
ConditonalObject,
|
|
||||||
DynamicValues,
|
|
||||||
} from "reducers/evaluationReducers/formEvaluationReducer";
|
|
||||||
import { DropdownOption } from "components/ads/Dropdown";
|
import { DropdownOption } from "components/ads/Dropdown";
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||||
abstract class BaseControl<P extends ControlProps, S = {}> extends Component<
|
abstract class BaseControl<P extends ControlProps, S = {}> extends Component<
|
||||||
|
|
@ -77,7 +74,6 @@ export interface ControlData {
|
||||||
identifier?: string;
|
identifier?: string;
|
||||||
sectionName?: string;
|
sectionName?: string;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
dynamicFetchedValues?: DynamicValues; // Object that holds the output of the dynamic fetched values
|
|
||||||
}
|
}
|
||||||
export type FormConfig = Omit<ControlData, "configProperty"> & {
|
export type FormConfig = Omit<ControlData, "configProperty"> & {
|
||||||
configProperty?: string;
|
configProperty?: string;
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,9 @@ import {
|
||||||
WrappedFieldInputProps,
|
WrappedFieldInputProps,
|
||||||
WrappedFieldMetaProps,
|
WrappedFieldMetaProps,
|
||||||
} from "redux-form";
|
} from "redux-form";
|
||||||
import { DynamicValues } from "reducers/evaluationReducers/formEvaluationReducer";
|
import { connect } from "react-redux";
|
||||||
|
import { AppState } from "reducers";
|
||||||
|
import { getDynamicFetchedValues } from "selectors/formSelectors";
|
||||||
|
|
||||||
const DropdownSelect = styled.div`
|
const DropdownSelect = styled.div`
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
|
|
@ -27,23 +29,12 @@ class DropDownControl extends BaseControl<DropDownControlProps> {
|
||||||
width = this.props.customStyles.width;
|
width = this.props.customStyles.width;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Options will be set dynamically if the config has fetchOptionsConditionally set to true
|
|
||||||
let options = this.props.options;
|
|
||||||
let isLoading = false;
|
|
||||||
if (
|
|
||||||
this.props.fetchOptionsCondtionally &&
|
|
||||||
!!this.props.dynamicFetchedValues
|
|
||||||
) {
|
|
||||||
options = this.props.dynamicFetchedValues.data;
|
|
||||||
isLoading = this.props.dynamicFetchedValues.isLoading;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DropdownSelect data-cy={this.props.configProperty} style={{ width }}>
|
<DropdownSelect data-cy={this.props.configProperty} style={{ width }}>
|
||||||
<Field
|
<Field
|
||||||
component={renderDropdown}
|
component={renderDropdown}
|
||||||
name={this.props.configProperty}
|
name={this.props.configProperty}
|
||||||
props={{ ...this.props, width, isLoading, options }} // Passing options and isLoading in props allows the component to get the updated values
|
props={{ ...this.props, width }}
|
||||||
type={this.props?.isMultiSelect ? "select-multiple" : undefined}
|
type={this.props?.isMultiSelect ? "select-multiple" : undefined}
|
||||||
/>
|
/>
|
||||||
</DropdownSelect>
|
</DropdownSelect>
|
||||||
|
|
@ -55,39 +46,38 @@ class DropDownControl extends BaseControl<DropDownControlProps> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderDropdown(props: {
|
function renderDropdown(
|
||||||
input?: WrappedFieldInputProps;
|
props: {
|
||||||
meta?: WrappedFieldMetaProps;
|
input?: WrappedFieldInputProps;
|
||||||
props: DropDownControlProps;
|
meta?: Partial<WrappedFieldMetaProps>;
|
||||||
width: string;
|
width: string;
|
||||||
formName: string;
|
} & DropDownControlProps,
|
||||||
isLoading?: boolean;
|
): JSX.Element {
|
||||||
options: DropdownOption[];
|
|
||||||
disabled?: boolean;
|
|
||||||
}): JSX.Element {
|
|
||||||
let selectedValue = props.input?.value;
|
let selectedValue = props.input?.value;
|
||||||
if (_.isUndefined(props.input?.value)) {
|
if (_.isUndefined(props.input?.value)) {
|
||||||
selectedValue = props?.props?.initialValue;
|
selectedValue = props?.initialValue;
|
||||||
|
}
|
||||||
|
let options: DropdownOption[] = [];
|
||||||
|
let selectedOption = {};
|
||||||
|
if (typeof props.options === "object" && Array.isArray(props.options)) {
|
||||||
|
options = props.options;
|
||||||
|
selectedOption =
|
||||||
|
options.find(
|
||||||
|
(option: DropdownOption) => option.value === selectedValue,
|
||||||
|
) || {};
|
||||||
}
|
}
|
||||||
|
|
||||||
const selectedOption =
|
|
||||||
props.options.find(
|
|
||||||
(option: DropdownOption) => option.value === selectedValue,
|
|
||||||
) || {};
|
|
||||||
return (
|
return (
|
||||||
<Dropdown
|
<Dropdown
|
||||||
boundary="window"
|
boundary="window"
|
||||||
disabled={props.disabled}
|
disabled={props.disabled}
|
||||||
dontUsePortal={false}
|
dontUsePortal={false}
|
||||||
dropdownMaxHeight="250px"
|
dropdownMaxHeight="250px"
|
||||||
errorMsg={props.props?.errorText}
|
|
||||||
helperText={props.props?.info}
|
|
||||||
isLoading={props.isLoading}
|
isLoading={props.isLoading}
|
||||||
isMultiSelect={props?.props?.isMultiSelect}
|
isMultiSelect={props?.isMultiSelect}
|
||||||
onSelect={props.input?.onChange}
|
onSelect={props.input?.onChange}
|
||||||
optionWidth={props.width}
|
optionWidth={props.width}
|
||||||
options={props.options}
|
options={options}
|
||||||
placeholder={props.props?.placeholderText}
|
placeholder={props?.placeholderText}
|
||||||
selected={selectedOption}
|
selected={selectedOption}
|
||||||
showLabelOnly
|
showLabelOnly
|
||||||
width={props.width}
|
width={props.width}
|
||||||
|
|
@ -103,7 +93,36 @@ export interface DropDownControlProps extends ControlProps {
|
||||||
isMultiSelect?: boolean;
|
isMultiSelect?: boolean;
|
||||||
isSearchable?: boolean;
|
isSearchable?: boolean;
|
||||||
fetchOptionsCondtionally?: boolean;
|
fetchOptionsCondtionally?: boolean;
|
||||||
dynamicFetchedValues?: DynamicValues;
|
isLoading: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default DropDownControl;
|
const mapStateToProps = (
|
||||||
|
state: AppState,
|
||||||
|
ownProps: DropDownControlProps,
|
||||||
|
): { isLoading: boolean; options: DropdownOption[] } => {
|
||||||
|
// Added default options to prevent error when options is undefined
|
||||||
|
let isLoading = false;
|
||||||
|
let options: DropdownOption[] = ownProps.fetchOptionsCondtionally
|
||||||
|
? []
|
||||||
|
: ownProps.options;
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (ownProps.fetchOptionsCondtionally) {
|
||||||
|
const dynamicFetchedValues = getDynamicFetchedValues(
|
||||||
|
state,
|
||||||
|
ownProps.configProperty,
|
||||||
|
);
|
||||||
|
isLoading = dynamicFetchedValues.isLoading;
|
||||||
|
options = dynamicFetchedValues.data;
|
||||||
|
}
|
||||||
|
return { isLoading, options };
|
||||||
|
} catch (e) {
|
||||||
|
return {
|
||||||
|
isLoading,
|
||||||
|
options,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Connecting this componenet to the state to allow for dynamic fetching of options to be updated.
|
||||||
|
export default connect(mapStateToProps)(DropDownControl);
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,9 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import FormControl from "pages/Editor/FormControl";
|
import FormControl from "pages/Editor/FormControl";
|
||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
import FormLabel from "components/editorComponents/FormLabel";
|
|
||||||
import { ControlProps } from "./BaseControl";
|
import { ControlProps } from "./BaseControl";
|
||||||
import { Colors } from "constants/Colors";
|
import { Colors } from "constants/Colors";
|
||||||
import Icon, { IconSize } from "components/ads/Icon";
|
import Icon, { IconSize } from "components/ads/Icon";
|
||||||
import { getBindingOrConfigPathsForEntitySelectorControl } from "entities/Action/actionProperties";
|
|
||||||
import { allowedControlTypes } from "components/formControls/utils";
|
import { allowedControlTypes } from "components/formControls/utils";
|
||||||
|
|
||||||
const dropDownFieldConfig: any = {
|
const dropDownFieldConfig: any = {
|
||||||
|
|
@ -39,15 +37,6 @@ const EntitySelectorContainer = styled.div`
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const StyledBottomLabel = styled(FormLabel)`
|
|
||||||
margin-top: 5px;
|
|
||||||
margin-left: 5px;
|
|
||||||
font-weight: 400;
|
|
||||||
font-size: 12px;
|
|
||||||
color: ${Colors.GREY_7};
|
|
||||||
line-height: 16px;
|
|
||||||
`;
|
|
||||||
|
|
||||||
function EntitySelectorComponent(props: any) {
|
function EntitySelectorComponent(props: any) {
|
||||||
const { configProperty, schema } = props;
|
const { configProperty, schema } = props;
|
||||||
|
|
||||||
|
|
@ -61,25 +50,20 @@ function EntitySelectorComponent(props: any) {
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<EntitySelectorContainer>
|
<EntitySelectorContainer key={`ES_${configProperty}`}>
|
||||||
{schema &&
|
{schema &&
|
||||||
schema.length > 0 &&
|
schema.length > 0 &&
|
||||||
schema.map((singleSchema: any, index: number) => {
|
schema.map((singleSchema: any, index: number) => {
|
||||||
const columnPath = getBindingOrConfigPathsForEntitySelectorControl(
|
|
||||||
configProperty,
|
|
||||||
index,
|
|
||||||
);
|
|
||||||
return (
|
return (
|
||||||
allowedControlTypes.includes(singleSchema.controlType) && (
|
allowedControlTypes.includes(singleSchema.controlType) && (
|
||||||
<>
|
<React.Fragment key={`ES_FRAG_${singleSchema.configProperty}`}>
|
||||||
{singleSchema.controlType === "DROP_DOWN" ? (
|
{singleSchema.controlType === "DROP_DOWN" ? (
|
||||||
<FormControl
|
<FormControl
|
||||||
config={{
|
config={{
|
||||||
...dropDownFieldConfig,
|
...dropDownFieldConfig,
|
||||||
...singleSchema,
|
...singleSchema,
|
||||||
customStyles,
|
customStyles,
|
||||||
configProperty: columnPath,
|
key: `ES_${singleSchema.configProperty}`,
|
||||||
key: columnPath,
|
|
||||||
}}
|
}}
|
||||||
formName={props.formName}
|
formName={props.formName}
|
||||||
/>
|
/>
|
||||||
|
|
@ -89,19 +73,19 @@ function EntitySelectorComponent(props: any) {
|
||||||
...inputFieldConfig,
|
...inputFieldConfig,
|
||||||
...singleSchema,
|
...singleSchema,
|
||||||
customStyles,
|
customStyles,
|
||||||
configProperty: columnPath,
|
key: `ES_${singleSchema.configProperty}`,
|
||||||
key: columnPath,
|
|
||||||
}}
|
}}
|
||||||
formName={props.formName}
|
formName={props.formName}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{index < schema.length - 1 && (
|
{index < schema.length - 1 && (
|
||||||
<CenteredIcon
|
<CenteredIcon
|
||||||
|
key={`ES_ICON_${configProperty}`}
|
||||||
name="double-arrow-right"
|
name="double-arrow-right"
|
||||||
size={IconSize.SMALL}
|
size={IconSize.SMALL}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</>
|
</React.Fragment>
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
|
@ -109,6 +93,8 @@ function EntitySelectorComponent(props: any) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This is a wrapper component that just encapsulated the children dropdown and dynamic text
|
||||||
|
// components & changes their appearance
|
||||||
export default function EntitySelectorControl(
|
export default function EntitySelectorControl(
|
||||||
props: EntitySelectorControlProps,
|
props: EntitySelectorControlProps,
|
||||||
) {
|
) {
|
||||||
|
|
@ -122,7 +108,7 @@ export default function EntitySelectorControl(
|
||||||
<EntitySelectorComponent
|
<EntitySelectorComponent
|
||||||
configProperty={configProperty}
|
configProperty={configProperty}
|
||||||
formName={formName}
|
formName={formName}
|
||||||
key={configProperty}
|
key={`ES_PARENT_${configProperty}`}
|
||||||
name={configProperty}
|
name={configProperty}
|
||||||
schema={schema}
|
schema={schema}
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
|
|
@ -51,7 +51,7 @@ function FormControl(props: FormControlProps) {
|
||||||
props.formName,
|
props.formName,
|
||||||
props?.multipleConfig,
|
props?.multipleConfig,
|
||||||
),
|
),
|
||||||
[],
|
[props],
|
||||||
);
|
);
|
||||||
|
|
||||||
if (hidden) return null;
|
if (hidden) return null;
|
||||||
|
|
@ -133,7 +133,13 @@ function FormConfig(props: FormConfigProps) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default memo(FormControl);
|
// Updated the memo function to allow for disabled props to be compared
|
||||||
|
export default memo(FormControl, (prevProps, nextProps) => {
|
||||||
|
return (
|
||||||
|
prevProps === nextProps &&
|
||||||
|
prevProps.config.disabled === nextProps.config.disabled
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
function renderFormConfigTop(props: { config: ControlProps }) {
|
function renderFormConfigTop(props: { config: ControlProps }) {
|
||||||
const {
|
const {
|
||||||
|
|
|
||||||
|
|
@ -80,7 +80,6 @@ import Spinner from "components/ads/Spinner";
|
||||||
import {
|
import {
|
||||||
ConditionalOutput,
|
ConditionalOutput,
|
||||||
FormEvalOutput,
|
FormEvalOutput,
|
||||||
DynamicValues,
|
|
||||||
} from "reducers/evaluationReducers/formEvaluationReducer";
|
} from "reducers/evaluationReducers/formEvaluationReducer";
|
||||||
|
|
||||||
const QueryFormContainer = styled.form`
|
const QueryFormContainer = styled.form`
|
||||||
|
|
@ -598,6 +597,7 @@ export function EditorJSONtoForm(props: Props) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Extract the output of conditionals attached to the form from the state
|
||||||
const extractConditionalOutput = (section: any): ConditionalOutput => {
|
const extractConditionalOutput = (section: any): ConditionalOutput => {
|
||||||
let conditionalOutput: ConditionalOutput = {};
|
let conditionalOutput: ConditionalOutput = {};
|
||||||
if (
|
if (
|
||||||
|
|
@ -649,67 +649,35 @@ export function EditorJSONtoForm(props: Props) {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Function to modify the section config based on the output of evaluations
|
// Function to modify the section config based on the output of evaluations
|
||||||
const modifySectionConfig = (
|
const modifySectionConfig = (section: any, enabled: boolean): any => {
|
||||||
section: any,
|
|
||||||
enabled: boolean,
|
|
||||||
dynamicFetchedValues: DynamicValues | undefined,
|
|
||||||
): any => {
|
|
||||||
if (!enabled) {
|
if (!enabled) {
|
||||||
section.disabled = true;
|
section.disabled = true;
|
||||||
} else {
|
} else {
|
||||||
section.disabled = false;
|
section.disabled = false;
|
||||||
}
|
}
|
||||||
if (!!dynamicFetchedValues) {
|
|
||||||
section.dynamicFetchedValues = dynamicFetchedValues;
|
|
||||||
}
|
|
||||||
|
|
||||||
return section;
|
return section;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Function to extract the object for dynamicValues if it is there in the evaluation state
|
|
||||||
const extractDynamicValuesIfPresent = (
|
|
||||||
conditionalOutput: ConditionalOutput,
|
|
||||||
) => {
|
|
||||||
// By default, the section is enabled. This is to allow for the case where no conditional is provided.
|
|
||||||
// The evaluation state disables the section if the condition is not met. (Checkout formEval.ts)
|
|
||||||
let dynamicFetchedValues: DynamicValues | undefined;
|
|
||||||
if (conditionalOutput.hasOwnProperty("fetchDynamicValues")) {
|
|
||||||
dynamicFetchedValues = conditionalOutput.fetchDynamicValues;
|
|
||||||
}
|
|
||||||
return dynamicFetchedValues;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Render function to render the V2 of form editor type (UQI)
|
// Render function to render the V2 of form editor type (UQI)
|
||||||
// Section argument is a nested config object, this function recursively renders the UI based on the config
|
// Section argument is a nested config object, this function recursively renders the UI based on the config
|
||||||
const renderEachConfigV2 = (formName: string, section: any, idx: number) => {
|
const renderEachConfigV2 = (formName: string, section: any, idx: number) => {
|
||||||
let enabled = true;
|
let enabled = true;
|
||||||
let dynamicFetchedValues: DynamicValues | undefined;
|
|
||||||
if (!!section) {
|
if (!!section) {
|
||||||
|
// If the section is a nested component, recursively check for conditional statements
|
||||||
if ("schema" in section && section.schema.length > 0) {
|
if ("schema" in section && section.schema.length > 0) {
|
||||||
section.schema.forEach((subSection: any, index: number) => {
|
section.schema.forEach((subSection: any) => {
|
||||||
const configPropertyOfSubSection = `${
|
|
||||||
section.configProperty
|
|
||||||
}.column_${index + 1}`;
|
|
||||||
const conditionalOutput = extractConditionalOutput({
|
const conditionalOutput = extractConditionalOutput({
|
||||||
...subSection,
|
...subSection,
|
||||||
configProperty: configPropertyOfSubSection,
|
|
||||||
});
|
});
|
||||||
enabled = checkIfSectionIsEnabled(conditionalOutput);
|
enabled = checkIfSectionIsEnabled(conditionalOutput);
|
||||||
dynamicFetchedValues = extractDynamicValuesIfPresent(
|
subSection = modifySectionConfig(subSection, enabled);
|
||||||
conditionalOutput,
|
|
||||||
);
|
|
||||||
subSection = modifySectionConfig(
|
|
||||||
subSection,
|
|
||||||
enabled,
|
|
||||||
dynamicFetchedValues,
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
// If the component is not allowed to render, return null
|
// If the component is not allowed to render, return null
|
||||||
const conditionalOutput = extractConditionalOutput(section);
|
const conditionalOutput = extractConditionalOutput(section);
|
||||||
if (!checkIfSectionCanRender(conditionalOutput)) return null;
|
if (!checkIfSectionCanRender(conditionalOutput)) return null;
|
||||||
enabled = checkIfSectionIsEnabled(conditionalOutput);
|
enabled = checkIfSectionIsEnabled(conditionalOutput);
|
||||||
dynamicFetchedValues = extractDynamicValuesIfPresent(conditionalOutput);
|
|
||||||
}
|
}
|
||||||
if (section.hasOwnProperty("controlType")) {
|
if (section.hasOwnProperty("controlType")) {
|
||||||
// If component is type section, render it's children
|
// If component is type section, render it's children
|
||||||
|
|
@ -723,11 +691,7 @@ export function EditorJSONtoForm(props: Props) {
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
const { configProperty } = section;
|
const { configProperty } = section;
|
||||||
const modifiedSection = modifySectionConfig(
|
const modifiedSection = modifySectionConfig(section, enabled);
|
||||||
section,
|
|
||||||
enabled,
|
|
||||||
dynamicFetchedValues,
|
|
||||||
);
|
|
||||||
return (
|
return (
|
||||||
<FieldWrapper key={`${configProperty}_${idx}`}>
|
<FieldWrapper key={`${configProperty}_${idx}`}>
|
||||||
<FormControl config={modifiedSection} formName={formName} />
|
<FormControl config={modifiedSection} formName={formName} />
|
||||||
|
|
|
||||||
|
|
@ -87,16 +87,18 @@ function* setFormEvaluationSagaAsync(
|
||||||
// Once all the actions are done, extract the actions that need to be fetched dynamically
|
// Once all the actions are done, extract the actions that need to be fetched dynamically
|
||||||
const formId = action.payload.formId;
|
const formId = action.payload.formId;
|
||||||
const evalOutput = workerResponse[formId];
|
const evalOutput = workerResponse[formId];
|
||||||
const queueOfValuesToBeFetched = extractQueueOfValuesToBeFetched(
|
if (!!evalOutput && typeof evalOutput === "object") {
|
||||||
evalOutput,
|
const queueOfValuesToBeFetched = extractQueueOfValuesToBeFetched(
|
||||||
);
|
evalOutput,
|
||||||
// Pass the queue to the saga to fetch the dynamic values
|
);
|
||||||
yield call(
|
// Pass the queue to the saga to fetch the dynamic values
|
||||||
fetchDynamicValuesSaga,
|
yield call(
|
||||||
queueOfValuesToBeFetched,
|
fetchDynamicValuesSaga,
|
||||||
formId,
|
queueOfValuesToBeFetched,
|
||||||
evalOutput,
|
formId,
|
||||||
);
|
evalOutput,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
log.error(e);
|
log.error(e);
|
||||||
|
|
@ -112,11 +114,10 @@ function* fetchDynamicValuesSaga(
|
||||||
evalOutput: FormEvalOutput,
|
evalOutput: FormEvalOutput,
|
||||||
) {
|
) {
|
||||||
for (const key of Object.keys(queueOfValuesToBeFetched)) {
|
for (const key of Object.keys(queueOfValuesToBeFetched)) {
|
||||||
evalOutput = yield call(
|
evalOutput[key].fetchDynamicValues = yield call(
|
||||||
fetchDynamicValueSaga,
|
fetchDynamicValueSaga,
|
||||||
queueOfValuesToBeFetched[key],
|
queueOfValuesToBeFetched[key],
|
||||||
key,
|
Object.assign({}, evalOutput[key].fetchDynamicValues as DynamicValues),
|
||||||
evalOutput,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
// Set the values to the state once all values are fetched
|
// Set the values to the state once all values are fetched
|
||||||
|
|
@ -128,34 +129,31 @@ function* fetchDynamicValuesSaga(
|
||||||
|
|
||||||
function* fetchDynamicValueSaga(
|
function* fetchDynamicValueSaga(
|
||||||
value: ConditionalOutput,
|
value: ConditionalOutput,
|
||||||
key: string,
|
dynamicFetchedValues: DynamicValues,
|
||||||
evalOutput: FormEvalOutput,
|
|
||||||
) {
|
) {
|
||||||
try {
|
try {
|
||||||
const { config } = value.fetchDynamicValues as DynamicValues;
|
const { config } = value.fetchDynamicValues as DynamicValues;
|
||||||
const { url } = config;
|
const { url } = config;
|
||||||
|
|
||||||
(evalOutput[key].fetchDynamicValues as DynamicValues).hasStarted = true;
|
dynamicFetchedValues.hasStarted = true;
|
||||||
|
|
||||||
// Call the API to fetch the dynamic values
|
// Call the API to fetch the dynamic values
|
||||||
const response = yield call(PluginsApi.fetchDynamicFormValues, url);
|
const response = yield call(PluginsApi.fetchDynamicFormValues, url);
|
||||||
(evalOutput[key].fetchDynamicValues as DynamicValues).isLoading = false;
|
dynamicFetchedValues.isLoading = false;
|
||||||
if (!!response && response instanceof Array) {
|
if (!!response && response instanceof Array) {
|
||||||
(evalOutput[key].fetchDynamicValues as DynamicValues).data = response;
|
dynamicFetchedValues.data = response;
|
||||||
(evalOutput[key]
|
dynamicFetchedValues.hasFetchFailed = false;
|
||||||
.fetchDynamicValues as DynamicValues).hasFetchFailed = false;
|
|
||||||
} else {
|
} else {
|
||||||
(evalOutput[key]
|
dynamicFetchedValues.hasFetchFailed = true;
|
||||||
.fetchDynamicValues as DynamicValues).hasFetchFailed = true;
|
dynamicFetchedValues.data = [];
|
||||||
(evalOutput[key].fetchDynamicValues as DynamicValues).data = [];
|
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
log.error(e);
|
log.error(e);
|
||||||
(evalOutput[key].fetchDynamicValues as DynamicValues).hasFetchFailed = true;
|
dynamicFetchedValues.hasFetchFailed = true;
|
||||||
(evalOutput[key].fetchDynamicValues as DynamicValues).isLoading = false;
|
dynamicFetchedValues.isLoading = false;
|
||||||
(evalOutput[key].fetchDynamicValues as DynamicValues).data = [];
|
dynamicFetchedValues.data = [];
|
||||||
}
|
}
|
||||||
return evalOutput;
|
return dynamicFetchedValues;
|
||||||
}
|
}
|
||||||
|
|
||||||
function* formEvaluationChangeListenerSaga() {
|
function* formEvaluationChangeListenerSaga() {
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,17 @@
|
||||||
import { getFormValues, isValid, getFormInitialValues } from "redux-form";
|
import { getFormValues, isValid, getFormInitialValues } from "redux-form";
|
||||||
import { AppState } from "reducers";
|
import { AppState } from "reducers";
|
||||||
import { ActionData } from "reducers/entityReducers/actionsReducer";
|
import { ActionData } from "reducers/entityReducers/actionsReducer";
|
||||||
import { FormEvaluationState } from "reducers/evaluationReducers/formEvaluationReducer";
|
import {
|
||||||
|
DynamicValues,
|
||||||
|
FormEvaluationState,
|
||||||
|
} from "reducers/evaluationReducers/formEvaluationReducer";
|
||||||
import { createSelector } from "reselect";
|
import { createSelector } from "reselect";
|
||||||
import _ from "lodash";
|
import { replace } from "lodash";
|
||||||
import { getDataTree } from "./dataTreeSelectors";
|
import { getDataTree } from "./dataTreeSelectors";
|
||||||
import { DataTree } from "entities/DataTree/dataTreeFactory";
|
import { DataTree } from "entities/DataTree/dataTreeFactory";
|
||||||
import { Action } from "entities/Action";
|
import { Action } from "entities/Action";
|
||||||
import { EvaluationError } from "utils/DynamicBindingUtils";
|
import { EvaluationError } from "utils/DynamicBindingUtils";
|
||||||
|
import { getActionIdFromURL } from "pages/Editor/Explorer/helpers";
|
||||||
|
|
||||||
type GetFormData = (
|
type GetFormData = (
|
||||||
state: AppState,
|
state: AppState,
|
||||||
|
|
@ -30,6 +34,16 @@ export const getApiName = (state: AppState, id: string) => {
|
||||||
export const getFormEvaluationState = (state: AppState): FormEvaluationState =>
|
export const getFormEvaluationState = (state: AppState): FormEvaluationState =>
|
||||||
state.evaluations.formEvaluation;
|
state.evaluations.formEvaluation;
|
||||||
|
|
||||||
|
// Selector to return the fetched values of the form components, only called for components that
|
||||||
|
// have the fetchOptionsDynamically option set to true
|
||||||
|
export const getDynamicFetchedValues = (
|
||||||
|
state: AppState,
|
||||||
|
configProperty: string,
|
||||||
|
): DynamicValues =>
|
||||||
|
state.evaluations.formEvaluation[getActionIdFromURL() as string][
|
||||||
|
configProperty
|
||||||
|
].fetchDynamicValues as DynamicValues;
|
||||||
|
|
||||||
type ConfigErrorProps = { configProperty: string; formName: string };
|
type ConfigErrorProps = { configProperty: string; formName: string };
|
||||||
|
|
||||||
export const getConfigErrors = createSelector(
|
export const getConfigErrors = createSelector(
|
||||||
|
|
@ -53,7 +67,7 @@ export const getConfigErrors = createSelector(
|
||||||
const actionError = action && action?.__evaluation__?.errors;
|
const actionError = action && action?.__evaluation__?.errors;
|
||||||
|
|
||||||
// get the configProperty for this form control and format it to resemble the format used in the action details errors object.
|
// get the configProperty for this form control and format it to resemble the format used in the action details errors object.
|
||||||
const formattedConfig = _.replace(
|
const formattedConfig = replace(
|
||||||
configProperty,
|
configProperty,
|
||||||
"actionConfiguration",
|
"actionConfiguration",
|
||||||
"config",
|
"config",
|
||||||
|
|
|
||||||
|
|
@ -85,11 +85,8 @@ const generateInitialEvalState = (formConfig: FormConfig) => {
|
||||||
);
|
);
|
||||||
|
|
||||||
if ("schema" in formConfig && !!formConfig.schema)
|
if ("schema" in formConfig && !!formConfig.schema)
|
||||||
formConfig.schema.forEach((config: FormConfig, index: number) =>
|
formConfig.schema.forEach((config: FormConfig) =>
|
||||||
generateInitialEvalState({
|
generateInitialEvalState({ ...config }),
|
||||||
...config,
|
|
||||||
configProperty: `${formConfig.configProperty}.column_${index + 1}`,
|
|
||||||
}),
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user