feat: Add enable/disable functionality to form components (#10215)

* Added new condition type

* Added new variables to cater to enable/disable conditionals

* Adding functionality to disable the drop down control

* Added ability to enable/disable to dynamic input text fields

* Updated input text control to have enabled/disabled feature

* Added enable/disable functionality to FixedKeyInputControl

* Added enable/disable func to switch control and stanrdasied var name

* Added disable functionality for file picker

* Added enable/disable functionality to QUER_DYNAMIC_TEXT
This commit is contained in:
Ayush Pahwa 2022-01-06 20:09:21 +05:30 committed by GitHub
parent 92a5cbf7f2
commit 3ab11fa942
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 132 additions and 25 deletions

View File

@ -22,6 +22,7 @@ class DynamicTextField extends React.Component<
border?: CodeEditorBorder;
showLightningMenu?: boolean;
height?: string;
disabled?: boolean;
}
> {
render() {

View File

@ -72,6 +72,7 @@ export interface ControlData {
propertyName?: string;
identifier?: string;
sectionName?: string;
disabled?: boolean;
}
export type FormConfig = Omit<ControlData, "configProperty"> & {
configProperty?: string;

View File

@ -6,11 +6,14 @@ import { ControlType } from "constants/PropertyControlConstants";
import _ from "lodash";
import {
Field,
getFormValues,
WrappedFieldInputProps,
WrappedFieldMetaProps,
} from "redux-form";
import { connect } from "react-redux";
import { AppState } from "reducers";
import { QUERY_EDITOR_FORM_NAME } from "constants/forms";
import { QueryAction } from "entities/Action";
const DropdownSelect = styled.div`
font-size: 14px;
@ -49,6 +52,7 @@ function renderDropdown(props: {
fetchOptionsCondtionally: boolean;
formName: string;
dropDownOptions: DropdownOption[];
disabled?: boolean;
}): JSX.Element {
let selectedValue = props.input?.value;
if (_.isUndefined(props.input?.value)) {
@ -61,6 +65,7 @@ function renderDropdown(props: {
return (
<Dropdown
boundary="window"
disabled={props.disabled}
dontUsePortal={false}
dropdownMaxHeight="250px"
errorMsg={props.props?.errorText}
@ -83,7 +88,6 @@ export interface DropDownControlProps extends ControlProps {
propertyValue: string;
subtitle?: string;
isMultiSelect?: boolean;
isDisabled?: boolean;
isSearchable?: boolean;
fetchOptionsCondtionally?: boolean;
}
@ -97,6 +101,19 @@ const mapStateToProps = (state: AppState, ownProps: DropDownControlProps) => {
{ label: "Test1", value: "SINGLE" },
{ label: "Test2", value: "ALL" },
];
const dynamicFormDataString = _.get(
getFormValues(QUERY_EDITOR_FORM_NAME)(state) as QueryAction,
"actionConfiguration.formData.updateMany.query",
);
// ownProps.configProperty will be used to filter from the array of data
// const dynamicFormDataString = getFormEvaluationState(state);
try {
dropDownOptions = JSON.parse(dynamicFormDataString);
} catch (e) {
dropDownOptions = [];
}
} else {
dropDownOptions = ownProps.options;
}

View File

@ -43,6 +43,7 @@ export function InputText(props: {
actionName: string;
inputType?: INPUT_TEXT_INPUT_TYPES;
customStyles?: any;
disabled?: boolean;
}) {
const { actionName, inputType, name, placeholder } = props;
const dataTreePath = actionPathFromName(actionName, name);
@ -70,6 +71,7 @@ export function InputText(props: {
<div style={customStyle}>
<StyledDynamicTextField
dataTreePath={dataTreePath}
disabled={props.disabled}
name={name}
placeholder={placeholder}
showLightningMenu={false}
@ -86,6 +88,7 @@ class DynamicInputTextControl extends BaseControl<DynamicInputControlProps> {
actionName,
configProperty,
customStyles,
disabled,
inputType,
label,
placeholderText,
@ -100,6 +103,7 @@ class DynamicInputTextControl extends BaseControl<DynamicInputControlProps> {
<InputText
actionName={actionName}
customStyles={customStyles}
disabled={disabled}
inputType={inputTypeProp}
label={label}
name={configProperty}

View File

@ -99,6 +99,7 @@ class DynamicTextControl extends BaseControl<
<DynamicTextField
className="dynamic-text-field"
dataTreePath={dataTreePath}
disabled={this.props.disabled}
evaluationSubstitutionType={evaluationSubstitutionType}
mode={mode}
name={this.props.configProperty}

View File

@ -60,6 +60,7 @@ const FilePickerWrapper = styled.div`
type RenderFilePickerProps = FilePickerControlProps & {
input?: WrappedFieldInputProps;
meta?: WrappedFieldMetaProps;
disabled?: boolean;
onChange: (event: any) => void;
};
@ -110,6 +111,7 @@ function RenderFilePicker(props: RenderFilePickerProps) {
<SelectButton
buttonStyle="PRIMARY"
buttonVariant={ButtonVariantTypes.SECONDARY}
disabled={props.disabled}
onClick={() => {
setIsOpen(true);
}}
@ -145,8 +147,14 @@ class FilePickerControl extends BaseControl<FilePickerControlProps> {
}
render() {
const { configProperty } = this.props;
return <Field component={RenderFilePicker} name={configProperty} />;
const { configProperty, disabled } = this.props;
return (
<Field
component={RenderFilePicker}
disabled={disabled}
name={configProperty}
/>
);
}
getControlType(): ControlType {

View File

@ -11,11 +11,18 @@ const Wrapper = styled.div`
class FixKeyInputControl extends BaseControl<FixedKeyInputControlProps> {
render() {
const { configProperty, dataType, fixedKey, placeholderText } = this.props;
const {
configProperty,
dataType,
disabled,
fixedKey,
placeholderText,
} = this.props;
return (
<Wrapper>
<TextField
disabled={disabled}
format={(value) => {
// Get the value property
if (value) {

View File

@ -9,6 +9,7 @@ type SwitchFieldProps = WrappedFieldProps & {
label: string;
isRequired: boolean;
info: string;
disabled: boolean;
};
const StyledToggle = styled(Toggle)`
@ -52,6 +53,7 @@ export class SwitchField extends React.Component<SwitchFieldProps, any> {
<SwitchWrapped data-cy={this.props.input.name}>
<StyledToggle
className="switch-control"
disabled={this.props.disabled}
name={this.props.input.name}
onToggle={(value: any) => {
this.props.input.onChange(value);
@ -66,11 +68,12 @@ export class SwitchField extends React.Component<SwitchFieldProps, any> {
class SwitchControl extends BaseControl<SwitchControlProps> {
render() {
const { configProperty, info, isRequired, label } = this.props;
const { configProperty, disabled, info, isRequired, label } = this.props;
return (
<Field
component={SwitchField}
disabled={disabled}
info={info}
isRequired={isRequired}
label={label}

View File

@ -76,6 +76,10 @@ import { DEBUGGER_TAB_KEYS } from "components/editorComponents/Debugger/helpers"
import { getErrorAsString } from "sagas/ActionExecution/errorUtils";
import { EDITOR_TABS } from "constants/QueryEditorConstants";
import Spinner from "components/ads/Spinner";
import {
ConditionalOutput,
FormEvalOutput,
} from "reducers/evaluationReducers/formEvaluationReducer";
const QueryFormContainer = styled.form`
flex: 1;
@ -417,7 +421,7 @@ type ReduxProps = {
plugin?: Plugin;
pluginId: string;
documentationLink: string | undefined;
formEvaluationState: Record<string, any>;
formEvaluationState: FormEvalOutput;
};
export type EditorJSONtoFormProps = QueryFormProps & ReduxProps;
@ -591,38 +595,65 @@ export function EditorJSONtoForm(props: Props) {
}
};
// Function to check if the section config is allowed to render (Only for UQI forms)
const checkIfSectionCanRender = (section: any) => {
// By default, allow the section to render. This is to allow for the case where no conditional is provided.
// The evaluation state disallows the section to render if the condition is not met. (Checkout formEval.ts)
let allowToRender = true;
const extractConditionalOutput = (section: any): ConditionalOutput => {
let conditionalOutput: ConditionalOutput = {};
if (
section.hasOwnProperty("propertyName") &&
props.formEvaluationState.hasOwnProperty(section.propertyName)
) {
allowToRender = props?.formEvaluationState[section.propertyName].visible;
conditionalOutput = props?.formEvaluationState[section.propertyName];
} else if (
section.hasOwnProperty("configProperty") &&
props.formEvaluationState.hasOwnProperty(section.configProperty)
) {
allowToRender =
props?.formEvaluationState[section.configProperty].visible;
conditionalOutput = props?.formEvaluationState[section.configProperty];
} else if (
section.hasOwnProperty("identifier") &&
!!section.identifier &&
props.formEvaluationState.hasOwnProperty(section.identifier)
) {
allowToRender = props?.formEvaluationState[section.identifier].visible;
conditionalOutput = props?.formEvaluationState[section.identifier];
}
return conditionalOutput;
};
// Function to check if the section config is allowed to render (Only for UQI forms)
const checkIfSectionCanRender = (conditionalOutput: ConditionalOutput) => {
// By default, allow the section to render. This is to allow for the case where no conditional is provided.
// The evaluation state disallows the section to render if the condition is not met. (Checkout formEval.ts)
let allowToRender = true;
if (
conditionalOutput.hasOwnProperty("visible") &&
typeof conditionalOutput.visible === "boolean"
) {
allowToRender = conditionalOutput.visible;
}
return allowToRender;
};
// Function to check if the section config is enabled (Only for UQI forms)
const checkIfSectionIsEnabled = (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 enabled = true;
if (
conditionalOutput.hasOwnProperty("enabled") &&
typeof conditionalOutput.enabled === "boolean"
) {
enabled = conditionalOutput.enabled;
}
return enabled;
};
// 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
const renderEachConfigV2 = (formName: string, section: any, idx: number) => {
let enabled = true;
if (!!section) {
// If the component is not allowed to render, return null
if (!checkIfSectionCanRender(section)) return null;
const conditionalOutput = extractConditionalOutput(section);
if (!checkIfSectionCanRender(conditionalOutput)) return null;
enabled = checkIfSectionIsEnabled(conditionalOutput);
}
if (section.hasOwnProperty("controlType")) {
// If component is type section, render it's children
@ -636,9 +667,15 @@ export function EditorJSONtoForm(props: Props) {
}
try {
const { configProperty } = section;
let modifiedSection;
if (!enabled) {
modifiedSection = { ...section, disabled: true };
} else {
modifiedSection = { ...section };
}
return (
<FieldWrapper key={`${configProperty}_${idx}`}>
<FormControl config={section} formName={formName} />
<FormControl config={modifiedSection} formName={formName} />
</FieldWrapper>
);
} catch (e) {

View File

@ -4,13 +4,13 @@ import { FetchPageRequest } from "api/PageApi";
export type ConditonalObject = Record<string, string>;
export type FormEvalOutput = Record<
string,
{
visible?: boolean;
conditionals?: ConditonalObject;
}
>;
export type ConditionalOutput = {
visible?: boolean;
enabled?: boolean;
conditionals?: ConditonalObject;
};
export type FormEvalOutput = Record<string, ConditionalOutput>;
export type FormEvaluationState = Record<string, FormEvalOutput>;

View File

@ -10,6 +10,8 @@ import { FormConfig } from "components/formControls/BaseControl";
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
}
// Object to hold the initial eval object
@ -18,6 +20,8 @@ let finalEvalObj: FormEvalOutput;
// Recursive function to generate the evaluation state for form config
const generateInitialEvalState = (formConfig: FormConfig) => {
const visible = false;
const enabled = true;
let conditionTypes = {};
// 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) {
@ -32,9 +36,29 @@ const generateInitialEvalState = (formConfig: FormConfig) => {
key = formConfig.identifier;
}
const allConditionTypes = Object.keys(formConfig.conditionals);
if (
allConditionTypes.includes(ConditionType.HIDE) ||
allConditionTypes.includes(ConditionType.SHOW)
) {
conditionTypes = {
...conditionTypes,
visible,
};
}
if (
allConditionTypes.includes(ConditionType.ENABLE) ||
allConditionTypes.includes(ConditionType.DISABLE)
) {
conditionTypes = {
...conditionTypes,
enabled,
};
}
// Conditionals are stored in the eval state itself for quick access
finalEvalObj[key] = {
visible,
...conditionTypes,
conditionals: formConfig.conditionals,
};
}
@ -61,6 +85,10 @@ function evaluate(
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;
}
});
}