PromucFlow_constructor/app/client/src/components/propertyControls/FieldConfigurationControl.tsx

280 lines
7.6 KiB
TypeScript
Raw Normal View History

feat: JSON Form widget (#8472) * initial layout * updated parser to support nested array * array field rendering * changes * ts fix * minor revert FormWidget * modified schema structure * select and switch fields * added checkbox field * added RadioGroupField * partial DateField and defaults, typing refactoring * added label and field type change * minor ts changes * changes * modified widget/utils for nested panelConfig, modified schema to object approach * array/object label support * hide field configuration when children not present * added tooltip * field visibility option * disabled state * upgraded tslib, form initial values * custom field configuration - add/hide/edit * field configuration - label change * return input when field configuration reaches max depth * minor changes * form - scroll, fixedfooter, enitity defn and other minior changes * form title * unregister on unmount * fixes * zero state * fix field padding * patched updating form values, removed linting warnings * configured action buttons * minor fix * minor change * property pane - sort fields in field configuration * refactor include all properties * checkbox properties * date properties * refactor typings and radio group properties * switch, multselect, select, array, object properties * minor changes * default value * ts fixes * checkbox field properties implementation * date field prop implementation * switch field * select field and fix deep nested meta properties * multiselect implementation * minor change * input field implementation * fix position jump on field type change * initial accordian * field state property and auto-complete of JSONFormComputeControl * merge fixes * renamed FormBuilder to JSONForm * source data validation minor change * custom field default value fix * Editable keys for custom field * minor fixes * replaced useFieldArray with custom logic, added widget icon * array and object accordian with border/background styling * minor change * disabled states for array and objects * default value minor fix * form level styles * modified logic for isDisabled for array and object, added disabledWhenInvalid, exposed isValid to fieldState for text input, removed useDisableChildren * added isValid for all field types * fixed reset to default values * debounce form values update * minor change * minor change * fix crash - source data change multi-select to array, fix crash - change of options * fix positioning * detect date type in source data * fix crash - when object is passed to regex input field * fixed default sourceData path for fields * accodion keep children mounted on collapse * jest test for schemaParser * widget/helper and useRegisterFieldInvalid test * tests for property config helper and generatePanelPropertyConfig * fix input field validation not appearing * fix date field type detection * rename data -> formData * handle null/undefined field value change in sourceData * added null/undefined as valid values for defaultValue text field * auto detect email field * set formData default value on initial load * switch field inline positioning * field margin fix for row direction * select full width * fiex date field default value - out of range * fix any field type to array * array default value logic change * base cypress test changes * initial json form render cy test * key sanitization * fix fieldState update logic * required design, object/array background color, accordion changes, fix - add new custom field * minor change * cypress tests * fix date formatted value, field state cypress test * cypress - field properties test and fixes * rename test file * fix accessort change to blank value, cypress tests * fix array field default value for modified accessor * minor fix * added animate loading * fix empty state, add new custom field * test data fix * fix warnings * fix timePrecision visibility * button styling * ported input v2 * fix jest tests * fix cypress tests * perf changes * perf improvement * added comments * multiselect changes * input field perf refactor * array field, object field refactor performance * checkbox field refactor * refectored date, radio, select and switch * fixes * test fixes * fixes * minor fix * rename field renderer * remove tracked fieldRenderer field * cypress test fixes * cypress changes * array default value fixes * arrayfield passedDefaultValue * auto enabled JS mode for few properties, reverted swith and date property controls * cypress changes * added widget sniping mode and fixed object passedDefaultValue * multiselect v2 * select v2 * fix jest tests * test fixes * field limit * rename field type dropdown texts * field type changes fixes * jest fixes * loading state submit button * default source data for new widget * modify limit message * multiseelct default value changes and cypress fix * select default value * keep default value intact on field type change * TextTable cypress text fix * review changes * fixed footer changes * collapse styles section by default * fixed footer changes * form modes * custom field key rentention * fixed footer fix in view mode * non ascii characters * fix meta merge in dataTreeWidget * minor fixes * rename useRegisterFieldInvalid.ts -> useRegisterFieldValidity.ts * modified dependency injection into evaluated values * refactored fixedfooter logic * minor change * accessor update * minor change * fixes * QA fixes date field, scroll content * fix phone number field, removed visiblity option from array item * fix sourceData autocomplete * reset logic * fix multiselect reset * form values hydration on widget drag * code review changes * reverted order of merge dataTreeWidget * fixes * added button titles, fixed hydration issue * default value fixes * upgraded react hook form, modified array-level/field-level default value logic * fixed select validation * added icon entity explorer, modified icon align control * modify accessor validation for mongo db _id * update email field regex * review changes * explicitly handle empty source data validation
2022-03-24 07:13:25 +00:00
import React from "react";
import { isEmpty, isString, maxBy, set, sortBy } from "lodash";
import log from "loglevel";
import BaseControl, { ControlProps } from "./BaseControl";
import EmptyDataState from "components/utils/EmptyDataState";
import SchemaParser, {
getKeysFromSchema,
} from "widgets/JSONFormWidget/schemaParser";
import styled from "constants/DefaultTheme";
import { ARRAY_ITEM_KEY, Schema } from "widgets/JSONFormWidget/constants";
import { Category, Size } from "components/ads/Button";
import {
BaseItemProps,
DroppableComponent,
} from "components/ads/DraggableListComponent";
import { DraggableListCard } from "components/ads/DraggableListCard";
import { StyledPropertyPaneButton } from "./StyledControls";
import { getNextEntityName } from "utils/AppsmithUtils";
import { InputText } from "./InputTextControl";
import { klona } from "klona/full";
feat: JSON Form widget (#8472) * initial layout * updated parser to support nested array * array field rendering * changes * ts fix * minor revert FormWidget * modified schema structure * select and switch fields * added checkbox field * added RadioGroupField * partial DateField and defaults, typing refactoring * added label and field type change * minor ts changes * changes * modified widget/utils for nested panelConfig, modified schema to object approach * array/object label support * hide field configuration when children not present * added tooltip * field visibility option * disabled state * upgraded tslib, form initial values * custom field configuration - add/hide/edit * field configuration - label change * return input when field configuration reaches max depth * minor changes * form - scroll, fixedfooter, enitity defn and other minior changes * form title * unregister on unmount * fixes * zero state * fix field padding * patched updating form values, removed linting warnings * configured action buttons * minor fix * minor change * property pane - sort fields in field configuration * refactor include all properties * checkbox properties * date properties * refactor typings and radio group properties * switch, multselect, select, array, object properties * minor changes * default value * ts fixes * checkbox field properties implementation * date field prop implementation * switch field * select field and fix deep nested meta properties * multiselect implementation * minor change * input field implementation * fix position jump on field type change * initial accordian * field state property and auto-complete of JSONFormComputeControl * merge fixes * renamed FormBuilder to JSONForm * source data validation minor change * custom field default value fix * Editable keys for custom field * minor fixes * replaced useFieldArray with custom logic, added widget icon * array and object accordian with border/background styling * minor change * disabled states for array and objects * default value minor fix * form level styles * modified logic for isDisabled for array and object, added disabledWhenInvalid, exposed isValid to fieldState for text input, removed useDisableChildren * added isValid for all field types * fixed reset to default values * debounce form values update * minor change * minor change * fix crash - source data change multi-select to array, fix crash - change of options * fix positioning * detect date type in source data * fix crash - when object is passed to regex input field * fixed default sourceData path for fields * accodion keep children mounted on collapse * jest test for schemaParser * widget/helper and useRegisterFieldInvalid test * tests for property config helper and generatePanelPropertyConfig * fix input field validation not appearing * fix date field type detection * rename data -> formData * handle null/undefined field value change in sourceData * added null/undefined as valid values for defaultValue text field * auto detect email field * set formData default value on initial load * switch field inline positioning * field margin fix for row direction * select full width * fiex date field default value - out of range * fix any field type to array * array default value logic change * base cypress test changes * initial json form render cy test * key sanitization * fix fieldState update logic * required design, object/array background color, accordion changes, fix - add new custom field * minor change * cypress tests * fix date formatted value, field state cypress test * cypress - field properties test and fixes * rename test file * fix accessort change to blank value, cypress tests * fix array field default value for modified accessor * minor fix * added animate loading * fix empty state, add new custom field * test data fix * fix warnings * fix timePrecision visibility * button styling * ported input v2 * fix jest tests * fix cypress tests * perf changes * perf improvement * added comments * multiselect changes * input field perf refactor * array field, object field refactor performance * checkbox field refactor * refectored date, radio, select and switch * fixes * test fixes * fixes * minor fix * rename field renderer * remove tracked fieldRenderer field * cypress test fixes * cypress changes * array default value fixes * arrayfield passedDefaultValue * auto enabled JS mode for few properties, reverted swith and date property controls * cypress changes * added widget sniping mode and fixed object passedDefaultValue * multiselect v2 * select v2 * fix jest tests * test fixes * field limit * rename field type dropdown texts * field type changes fixes * jest fixes * loading state submit button * default source data for new widget * modify limit message * multiseelct default value changes and cypress fix * select default value * keep default value intact on field type change * TextTable cypress text fix * review changes * fixed footer changes * collapse styles section by default * fixed footer changes * form modes * custom field key rentention * fixed footer fix in view mode * non ascii characters * fix meta merge in dataTreeWidget * minor fixes * rename useRegisterFieldInvalid.ts -> useRegisterFieldValidity.ts * modified dependency injection into evaluated values * refactored fixedfooter logic * minor change * accessor update * minor change * fixes * QA fixes date field, scroll content * fix phone number field, removed visiblity option from array item * fix sourceData autocomplete * reset logic * fix multiselect reset * form values hydration on widget drag * code review changes * reverted order of merge dataTreeWidget * fixes * added button titles, fixed hydration issue * default value fixes * upgraded react hook form, modified array-level/field-level default value logic * fixed select validation * added icon entity explorer, modified icon align control * modify accessor validation for mongo db _id * update email field regex * review changes * explicitly handle empty source data validation
2022-03-24 07:13:25 +00:00
type DroppableItem = BaseItemProps & {
index: number;
isCustomField: boolean;
};
type State = {
focusedIndex: number | null;
};
const TabsWrapper = styled.div`
width: 100%;
display: flex;
flex-direction: column;
`;
const AddFieldButton = styled(StyledPropertyPaneButton)`
width: 100%;
display: flex;
justify-content: center;
&&&& {
margin-top: 12px;
margin-bottom: 8px;
}
`;
const DEFAULT_FIELD_NAME = "customField";
class FieldConfigurationControl extends BaseControl<ControlProps, State> {
constructor(props: ControlProps) {
super(props);
this.state = {
focusedIndex: null,
};
}
isArrayItem = () => {
const schema: Schema = this.props.propertyValue;
return Boolean(schema?.[ARRAY_ITEM_KEY]);
};
findSchemaItem = (index: number) => {
const schema: Schema = this.props.propertyValue;
const schemaItems = Object.values(schema);
const sortedSchemaItems = sortBy(schemaItems, ({ position }) => position);
return sortedSchemaItems[index];
};
onEdit = (index: number) => {
const schemaItem = this.findSchemaItem(index);
if (schemaItem) {
this.props.openNextPanel({
...schemaItem,
propPaneId: this.props.widgetProperties.widgetId,
});
}
};
onDeleteOption = (index: number) => {
const { propertyName } = this.props;
const schemaItem = this.findSchemaItem(index);
if (schemaItem) {
const itemToDeletePath = `${propertyName}.${schemaItem.identifier}`;
this.deleteProperties([itemToDeletePath]);
}
};
updateOption = (index: number, updatedLabel: string) => {
const { propertyName } = this.props;
const schemaItem = this.findSchemaItem(index);
if (schemaItem) {
const { identifier } = schemaItem;
this.updateProperty(`${propertyName}.${identifier}.label`, updatedLabel);
}
};
toggleVisibility = (index: number) => {
const { propertyName } = this.props;
const schemaItem = this.findSchemaItem(index);
if (schemaItem) {
const { identifier, isVisible } = schemaItem;
this.updateProperty(
`${propertyName}.${identifier}.isVisible`,
!isVisible,
);
}
};
addNewField = () => {
if (this.isArrayItem()) return;
const { propertyValue = {}, propertyName, widgetProperties } = this.props;
const { widgetName } = widgetProperties;
const schema: Schema = propertyValue;
const existingKeys = getKeysFromSchema(schema, ["identifier", "accessor"]);
const schemaItems = Object.values(schema);
const lastSchemaItem = maxBy(schemaItems, ({ position }) => position);
const lastSchemaItemPosition = lastSchemaItem?.position || -1;
const nextFieldKey = getNextEntityName(DEFAULT_FIELD_NAME, existingKeys);
const schemaItem = SchemaParser.getSchemaItemFor(nextFieldKey, {
currSourceData: "",
widgetName,
isCustomField: true,
skipDefaultValueProcessing: true,
identifier: nextFieldKey,
});
schemaItem.position = lastSchemaItemPosition + 1;
if (isEmpty(widgetProperties.schema)) {
const newSchema = {
schema: SchemaParser.parse(widgetProperties.widgetName, {}),
};
set(newSchema, `${propertyName}.${nextFieldKey}`, schemaItem);
this.updateProperty("schema", newSchema.schema);
} else {
this.updateProperty(`${propertyName}.${nextFieldKey}`, schemaItem);
}
};
onInputChange = (event: React.ChangeEvent<HTMLTextAreaElement> | string) => {
const value = isString(event) ? event : event.target.value;
try {
const parsedValue = JSON.parse(value);
this.updateProperty(this.props.propertyName, parsedValue);
} catch (e) {
log.error(e);
}
};
updateItems = (items: DroppableItem[]) => {
const { propertyName, propertyValue } = this.props;
const clonedSchema: Schema = klona(propertyValue);
feat: JSON Form widget (#8472) * initial layout * updated parser to support nested array * array field rendering * changes * ts fix * minor revert FormWidget * modified schema structure * select and switch fields * added checkbox field * added RadioGroupField * partial DateField and defaults, typing refactoring * added label and field type change * minor ts changes * changes * modified widget/utils for nested panelConfig, modified schema to object approach * array/object label support * hide field configuration when children not present * added tooltip * field visibility option * disabled state * upgraded tslib, form initial values * custom field configuration - add/hide/edit * field configuration - label change * return input when field configuration reaches max depth * minor changes * form - scroll, fixedfooter, enitity defn and other minior changes * form title * unregister on unmount * fixes * zero state * fix field padding * patched updating form values, removed linting warnings * configured action buttons * minor fix * minor change * property pane - sort fields in field configuration * refactor include all properties * checkbox properties * date properties * refactor typings and radio group properties * switch, multselect, select, array, object properties * minor changes * default value * ts fixes * checkbox field properties implementation * date field prop implementation * switch field * select field and fix deep nested meta properties * multiselect implementation * minor change * input field implementation * fix position jump on field type change * initial accordian * field state property and auto-complete of JSONFormComputeControl * merge fixes * renamed FormBuilder to JSONForm * source data validation minor change * custom field default value fix * Editable keys for custom field * minor fixes * replaced useFieldArray with custom logic, added widget icon * array and object accordian with border/background styling * minor change * disabled states for array and objects * default value minor fix * form level styles * modified logic for isDisabled for array and object, added disabledWhenInvalid, exposed isValid to fieldState for text input, removed useDisableChildren * added isValid for all field types * fixed reset to default values * debounce form values update * minor change * minor change * fix crash - source data change multi-select to array, fix crash - change of options * fix positioning * detect date type in source data * fix crash - when object is passed to regex input field * fixed default sourceData path for fields * accodion keep children mounted on collapse * jest test for schemaParser * widget/helper and useRegisterFieldInvalid test * tests for property config helper and generatePanelPropertyConfig * fix input field validation not appearing * fix date field type detection * rename data -> formData * handle null/undefined field value change in sourceData * added null/undefined as valid values for defaultValue text field * auto detect email field * set formData default value on initial load * switch field inline positioning * field margin fix for row direction * select full width * fiex date field default value - out of range * fix any field type to array * array default value logic change * base cypress test changes * initial json form render cy test * key sanitization * fix fieldState update logic * required design, object/array background color, accordion changes, fix - add new custom field * minor change * cypress tests * fix date formatted value, field state cypress test * cypress - field properties test and fixes * rename test file * fix accessort change to blank value, cypress tests * fix array field default value for modified accessor * minor fix * added animate loading * fix empty state, add new custom field * test data fix * fix warnings * fix timePrecision visibility * button styling * ported input v2 * fix jest tests * fix cypress tests * perf changes * perf improvement * added comments * multiselect changes * input field perf refactor * array field, object field refactor performance * checkbox field refactor * refectored date, radio, select and switch * fixes * test fixes * fixes * minor fix * rename field renderer * remove tracked fieldRenderer field * cypress test fixes * cypress changes * array default value fixes * arrayfield passedDefaultValue * auto enabled JS mode for few properties, reverted swith and date property controls * cypress changes * added widget sniping mode and fixed object passedDefaultValue * multiselect v2 * select v2 * fix jest tests * test fixes * field limit * rename field type dropdown texts * field type changes fixes * jest fixes * loading state submit button * default source data for new widget * modify limit message * multiseelct default value changes and cypress fix * select default value * keep default value intact on field type change * TextTable cypress text fix * review changes * fixed footer changes * collapse styles section by default * fixed footer changes * form modes * custom field key rentention * fixed footer fix in view mode * non ascii characters * fix meta merge in dataTreeWidget * minor fixes * rename useRegisterFieldInvalid.ts -> useRegisterFieldValidity.ts * modified dependency injection into evaluated values * refactored fixedfooter logic * minor change * accessor update * minor change * fixes * QA fixes date field, scroll content * fix phone number field, removed visiblity option from array item * fix sourceData autocomplete * reset logic * fix multiselect reset * form values hydration on widget drag * code review changes * reverted order of merge dataTreeWidget * fixes * added button titles, fixed hydration issue * default value fixes * upgraded react hook form, modified array-level/field-level default value logic * fixed select validation * added icon entity explorer, modified icon align control * modify accessor validation for mongo db _id * update email field regex * review changes * explicitly handle empty source data validation
2022-03-24 07:13:25 +00:00
items.forEach((item, index) => {
clonedSchema[item.id].position = index;
});
this.updateProperty(propertyName, clonedSchema);
};
updateFocus = (index: number, isFocused: boolean) => {
this.setState({ focusedIndex: isFocused ? index : null });
};
render() {
const { propertyValue = {}, panelConfig } = this.props;
const schema: Schema = propertyValue;
const schemaItems = Object.values(schema);
const addNewFieldButton = (
<AddFieldButton
category={Category.tertiary}
className="t--add-column-btn"
icon="plus"
onClick={this.addNewField}
size={Size.medium}
tag="button"
text="Add a new field"
type="button"
/>
);
if (isEmpty(schema)) {
return (
<>
<EmptyDataState />
{addNewFieldButton}
</>
);
}
const sortedSchemaItems = sortBy(schemaItems, ({ position }) => position);
const isMaxLevelReached = !panelConfig;
const draggableComponentColumns: DroppableItem[] = sortedSchemaItems.map(
({ identifier, isCustomField, isVisible, label }, index) => ({
id: identifier,
index,
isCustomField,
isVisible,
label,
}),
);
if (isMaxLevelReached) {
const {
additionalAutoComplete,
dataTreePath,
expected,
hideEvaluatedValue,
label,
theme,
} = this.props;
const value = JSON.stringify(schema, null, 2);
return (
<InputText
additionalAutocomplete={additionalAutoComplete}
dataTreePath={dataTreePath}
expected={expected}
hideEvaluatedValue={hideEvaluatedValue}
label={label}
onChange={this.onInputChange}
placeholder="{ name: 'John', dataType: 'string' }"
theme={theme}
value={value || "{}"}
/>
);
}
return (
<TabsWrapper>
<DroppableComponent
deleteOption={this.onDeleteOption}
focusedIndex={this.state.focusedIndex}
itemHeight={45}
items={draggableComponentColumns}
onEdit={this.onEdit}
renderComponent={(props) => {
const { id, isCustomField } = props.item;
return DraggableListCard({
...props,
toggleVisibility:
id !== ARRAY_ITEM_KEY ? props.toggleVisibility : undefined,
isDelete: isCustomField && id !== ARRAY_ITEM_KEY,
placeholder: "Field label",
});
}}
toggleVisibility={this.toggleVisibility}
updateFocus={this.updateFocus}
updateItems={this.updateItems}
updateOption={this.updateOption}
/>
{!this.isArrayItem() && addNewFieldButton}
</TabsWrapper>
);
}
static getControlType() {
return "FIELD_CONFIGURATION";
}
}
export default FieldConfigurationControl;