From 4e9068ddba46765ee43a6e2d42515bc53a074a2c Mon Sep 17 00:00:00 2001 From: Rimil Dey Date: Mon, 26 Sep 2022 10:05:04 +0530 Subject: [PATCH] chore: Move constants, types and utils to their own files in Action Creator (#16947) * Move constants and regex to their own files from index and fields file * Move types to to its own file * Move utils to its file * Add proper types for functions in fields file * Rename Switch -> Switchtype * Fix imports * Fix NAVIGATION_TARGET_FIELD_OPTIONS constant so that the build works * Jest tests to cover the utils * Add jest test cases * Update cases * Code review changes --- app/client/src/ce/constants/messages.ts | 10 +- .../ActionCreator/Fields.test.ts | 80 ----- .../editorComponents/ActionCreator/Fields.tsx | 337 +++--------------- .../ActionCreator/constants.ts | 82 +++++ .../editorComponents/ActionCreator/index.tsx | 80 ++--- .../editorComponents/ActionCreator/regex.ts | 8 + .../editorComponents/ActionCreator/types.ts | 58 +++ .../ActionCreator/utils.test.ts | 334 +++++++++++++++++ .../editorComponents/ActionCreator/utils.ts | 145 ++++++++ .../ComputeTablePropertyControl.tsx | 2 +- .../propertyControls/TableComputeValue.tsx | 2 +- .../TableInlineEditValidation.tsx | 2 +- 12 files changed, 713 insertions(+), 427 deletions(-) delete mode 100644 app/client/src/components/editorComponents/ActionCreator/Fields.test.ts create mode 100644 app/client/src/components/editorComponents/ActionCreator/regex.ts create mode 100644 app/client/src/components/editorComponents/ActionCreator/types.ts create mode 100644 app/client/src/components/editorComponents/ActionCreator/utils.test.ts create mode 100644 app/client/src/components/editorComponents/ActionCreator/utils.ts diff --git a/app/client/src/ce/constants/messages.ts b/app/client/src/ce/constants/messages.ts index 732923c9cc..c0038dc6cb 100644 --- a/app/client/src/ce/constants/messages.ts +++ b/app/client/src/ce/constants/messages.ts @@ -7,7 +7,7 @@ export function createMessage( /* For self hosted, it displays the string "Appsmith Community v1.10.0" or "Appsmith Business v1.10.0". - For cloud hosting, it displays "Appsmith v1.10.0". + For cloud hosting, it displays "Appsmith v1.10.0". This is because Appsmith Cloud doesn't support business features yet. */ export const APPSMITH_DISPLAY_VERSION = ( @@ -1244,3 +1244,11 @@ export const GENERATE_PAGE = () => "Generate page from data table"; export const GENERATE_PAGE_DESCRIPTION = () => "Start app with a simple CRUD UI and customize it"; export const ADD_PAGE_FROM_TEMPLATE = () => "Add Page From Template"; + +// Alert options and labels for showMessage types +export const ALERT_STYLE_OPTIONS = [ + { label: "Info", value: "'info'", id: "info" }, + { label: "Success", value: "'success'", id: "success" }, + { label: "Error", value: "'error'", id: "error" }, + { label: "Warning", value: "'warning'", id: "warning" }, +]; diff --git a/app/client/src/components/editorComponents/ActionCreator/Fields.test.ts b/app/client/src/components/editorComponents/ActionCreator/Fields.test.ts deleted file mode 100644 index c1cf70c25a..0000000000 --- a/app/client/src/components/editorComponents/ActionCreator/Fields.test.ts +++ /dev/null @@ -1,80 +0,0 @@ -jest.mock("sagas/ActionExecution/NavigateActionSaga", () => ({ - __esModule: true, - default: "", - NavigationTargetType: { SAME_WINDOW: "" }, -})); - -import { argsStringToArray } from "./Fields"; - -describe("Test argStringToArray", () => { - const cases = [ - { index: 0, input: "", expected: [""] }, - { index: 1, input: "'a'", expected: ["'a'"] }, - { index: 2, input: "a", expected: ["a"] }, - { index: 3, input: "'a,b,c'", expected: ["'a,b,c'"] }, - { index: 4, input: "a,b,c", expected: ["a", "b", "c"] }, - { index: 5, input: "a, b, c", expected: ["a", " b", " c"] }, - { index: 6, input: "a , b , c", expected: ["a ", " b ", " c"] }, - { index: 7, input: "a\n,\nb,\nc", expected: ["a\n", "\nb", "\nc"] }, - { index: 8, input: "[a,b,c]", expected: ["[a,b,c]"] }, - { index: 9, input: "[a, b, c]", expected: ["[a, b, c]"] }, - { - index: 10, - input: "[\n\ta,\n\tb,\n\tc\n]", - expected: ["[\n\ta,\n\tb,\n\tc\n]"], - }, - { index: 11, input: "{a:1,b:2,c:3}", expected: ["{a:1,b:2,c:3}"] }, - { - index: 12, - input: '{"a":1,"b":2,"c":3}', - expected: ['{"a":1,"b":2,"c":3}'], - }, - { - index: 13, - input: "{\n\ta:1,\n\tb:2,\n\tc:3}", - expected: ["{\n\ta:1,\n\tb:2,\n\tc:3}"], - }, - { - index: 14, - input: "()=>{}", - expected: ["()=>{}"], - }, - { - index: 15, - input: "(a, b)=>{return a+b}", - expected: ["(a, b)=>{return a+b}"], - }, - { - index: 16, - input: "(a, b)=>{\n\treturn a+b;\n\t}", - expected: ["(a, b)=>{\n\treturn a+b;\n\t}"], - }, - { - index: 17, - input: "(\n\ta,\n\tb\n)=>{\n\treturn a+b;\n\t}", - expected: ["(\n\ta,\n\tb\n)=>{\n\treturn a+b;\n\t}"], - }, - { - index: 18, - input: `() => {return 5}`, - expected: ["() => {return 5}"], - }, - { - index: 19, - input: `(a) => {return a + 1}`, - expected: ["(a) => {return a + 1}"], - }, - { - index: 20, - input: `(a, b) => {return a + b}`, - expected: ["(a, b) => {return a + b}"], - }, - ]; - test.each(cases.map((x) => [x.index, x.input, x.expected]))( - "test case %d", - (_, input, expected) => { - const result = argsStringToArray(input as string); - expect(result).toStrictEqual(expected); - }, - ); -}); diff --git a/app/client/src/components/editorComponents/ActionCreator/Fields.tsx b/app/client/src/components/editorComponents/ActionCreator/Fields.tsx index 92487c27ac..e6594010e7 100644 --- a/app/client/src/components/editorComponents/ActionCreator/Fields.tsx +++ b/app/client/src/components/editorComponents/ActionCreator/Fields.tsx @@ -1,11 +1,9 @@ import React from "react"; - import { TreeDropdown, Setter, TreeDropdownOption, Switcher, - SwitcherProps, } from "design-system"; import { ControlWrapper, @@ -16,7 +14,6 @@ import { } from "components/propertyControls/StyledControls"; import { KeyValueComponent } from "components/propertyControls/KeyValueComponent"; import { InputText } from "components/propertyControls/InputTextControl"; -import { getDynamicBindings, isDynamicValue } from "utils/DynamicBindingUtils"; import HightlightedCode from "components/editorComponents/HighlightedCode"; import { Skin } from "constants/DefaultTheme"; import { DropdownOption } from "components/constants"; @@ -26,13 +23,33 @@ import DividerComponent from "widgets/DividerWidget/component"; import store from "store"; import { getPageList } from "selectors/entitiesSelector"; import { - APPSMITH_GLOBAL_FUNCTIONS, - APPSMITH_NAMESPACED_FUNCTIONS, + RESET_CHILDREN_OPTIONS, + FILE_TYPE_OPTIONS, + NAVIGATION_TARGET_FIELD_OPTIONS, + ViewTypes, + AppsmithFunction, + FieldType, } from "./constants"; import { PopoverPosition } from "@blueprintjs/core"; - -/* eslint-disable @typescript-eslint/ban-types */ -/* TODO: Function and object types need to be updated to enable the lint rule */ +import { ACTION_TRIGGER_REGEX } from "./regex"; +import { + SwitchType, + ActionType, + SelectorViewProps, + KeyValueViewProps, + TextViewProps, + TabViewProps, + FieldConfigs, +} from "./types"; +import { + modalSetter, + modalGetter, + textSetter, + textGetter, + enumTypeSetter, + enumTypeGetter, +} from "./utils"; +import { ALERT_STYLE_OPTIONS } from "../../../ce/constants/messages"; /** ******** Steps to add a new function ******* @@ -54,234 +71,6 @@ import { PopoverPosition } from "@blueprintjs/core"; * 2. Attach fields to the new action in the getFieldFromValue function **/ -type Switch = { - id: string; - text: string; - action: () => void; -}; - -const ALERT_STYLE_OPTIONS = [ - { label: "Info", value: "'info'", id: "info" }, - { label: "Success", value: "'success'", id: "success" }, - { label: "Error", value: "'error'", id: "error" }, - { label: "Warning", value: "'warning'", id: "warning" }, -]; - -const RESET_CHILDREN_OPTIONS = [ - { label: "true", value: "true", id: "true" }, - { label: "false", value: "false", id: "false" }, -]; - -const FILE_TYPE_OPTIONS = [ - { label: "Select file type (optional)", value: "", id: "" }, - { label: "Plain text", value: "'text/plain'", id: "text/plain" }, - { label: "HTML", value: "'text/html'", id: "text/html" }, - { label: "CSV", value: "'text/csv'", id: "text/csv" }, - { label: "JSON", value: "'application/json'", id: "application/json" }, - { label: "JPEG", value: "'image/jpeg'", id: "image/jpeg" }, - { label: "PNG", value: "'image/png'", id: "image/png" }, - { label: "SVG", value: "'image/svg+xml'", id: "image/svg+xml" }, -]; - -const NAVIGATION_TARGET_FIELD_OPTIONS = [ - { - label: "Same window", - value: `'${NavigationTargetType.SAME_WINDOW}'`, - id: NavigationTargetType.SAME_WINDOW, - }, - { - label: "New window", - value: `'${NavigationTargetType.NEW_WINDOW}'`, - id: NavigationTargetType.NEW_WINDOW, - }, -]; - -export const FUNC_ARGS_REGEX = /((["][^"]*["])|([\[][\s\S]*[\]])|([\{][\s\S]*[\}])|(['][^']*['])|([\(][\s\S]*[\)][ ]*=>[ ]*[{][\s\S]*[}])|([^'",][^,"+]*[^'",]*))*/gi; -export const ACTION_TRIGGER_REGEX = /^{{([\s\S]*?)\(([\s\S]*?)\)}}$/g; -//Old Regex:: /\(\) => ([\s\S]*?)(\([\s\S]*?\))/g; -export const ACTION_ANONYMOUS_FUNC_REGEX = /\(\) => (({[\s\S]*?})|([\s\S]*?)(\([\s\S]*?\)))/g; -export const IS_URL_OR_MODAL = /^'.*'$/; -const modalSetter = (changeValue: any, currentValue: string) => { - const matches = [...currentValue.matchAll(ACTION_TRIGGER_REGEX)]; - let args: string[] = []; - if (matches.length) { - args = matches[0][2].split(","); - if (isDynamicValue(changeValue)) { - args[0] = `${changeValue.substring(2, changeValue.length - 2)}`; - } else { - args[0] = `'${changeValue}'`; - } - } - return currentValue.replace( - ACTION_TRIGGER_REGEX, - `{{$1(${args.join(",")})}}`, - ); -}; - -export const modalGetter = (value: string) => { - const matches = [...value.matchAll(ACTION_TRIGGER_REGEX)]; - let name = "none"; - if (matches.length) { - const modalName = matches[0][2].split(",")[0]; - if (IS_URL_OR_MODAL.test(modalName) || modalName === "") { - name = modalName.substring(1, modalName.length - 1); - } else { - name = `{{${modalName}}}`; - } - } - return name; -}; - -export const stringToJS = (string: string): string => { - const { jsSnippets, stringSegments } = getDynamicBindings(string); - const js = stringSegments - .map((segment, index) => { - if (jsSnippets[index] && jsSnippets[index].length > 0) { - return jsSnippets[index]; - } else { - return `'${segment}'`; - } - }) - .join(" + "); - return js; -}; - -export const JSToString = (js: string): string => { - const segments = js.split(" + "); - return segments - .map((segment) => { - if (segment.charAt(0) === "'") { - return segment.substring(1, segment.length - 1); - } else return "{{" + segment + "}}"; - }) - .join(""); -}; - -export const argsStringToArray = (funcArgs: string): string[] => { - const argsplitMatches = [...funcArgs.matchAll(FUNC_ARGS_REGEX)]; - const arr: string[] = []; - let isPrevUndefined = true; - argsplitMatches.forEach((match) => { - const matchVal = match[0]; - if (!matchVal || matchVal === "") { - if (isPrevUndefined) { - arr.push(matchVal); - } - isPrevUndefined = true; - } else { - isPrevUndefined = false; - arr.push(matchVal); - } - }); - return arr; -}; - -const textSetter = ( - changeValue: any, - currentValue: string, - argNum: number, -): string => { - const matches = [...currentValue.matchAll(ACTION_TRIGGER_REGEX)]; - let args: string[] = []; - if (matches.length) { - args = argsStringToArray(matches[0][2]); - const jsVal = stringToJS(changeValue); - args[argNum] = jsVal; - } - const result = currentValue.replace( - ACTION_TRIGGER_REGEX, - `{{$1(${args.join(",")})}}`, - ); - return result; -}; - -const textGetter = (value: string, argNum: number) => { - const matches = [...value.matchAll(ACTION_TRIGGER_REGEX)]; - if (matches.length) { - const args = argsStringToArray(matches[0][2]); - const arg = args[argNum]; - const stringFromJS = arg ? JSToString(arg.trim()) : arg; - return stringFromJS; - } - return ""; -}; - -const enumTypeSetter = ( - changeValue: any, - currentValue: string, - argNum: number, -): string => { - const matches = [...currentValue.matchAll(ACTION_TRIGGER_REGEX)]; - let args: string[] = []; - if (matches.length) { - args = argsStringToArray(matches[0][2]); - args[argNum] = changeValue as string; - } - const result = currentValue.replace( - ACTION_TRIGGER_REGEX, - `{{$1(${args.join(",")})}}`, - ); - return result; -}; - -const enumTypeGetter = ( - value: string, - argNum: number, - defaultValue = "", -): string => { - const matches = [...value.matchAll(ACTION_TRIGGER_REGEX)]; - if (matches.length) { - const args = argsStringToArray(matches[0][2]); - const arg = args[argNum]; - return arg ? arg.trim() : defaultValue; - } - return defaultValue; -}; - -export const ActionType = { - none: "none", - integration: "integration", - jsFunction: "jsFunction", - ...APPSMITH_GLOBAL_FUNCTIONS, - ...APPSMITH_NAMESPACED_FUNCTIONS, -}; - -type ActionType = typeof ActionType[keyof typeof ActionType]; - -const ViewTypes = { - SELECTOR_VIEW: "SELECTOR_VIEW", - KEY_VALUE_VIEW: "KEY_VALUE_VIEW", - TEXT_VIEW: "TEXT_VIEW", - BOOL_VIEW: "BOOL_VIEW", - TAB_VIEW: "TAB_VIEW", -}; -type ViewTypes = typeof ViewTypes[keyof typeof ViewTypes]; - -type ViewProps = { - label: string; - get: Function; - set: Function; - value: string; -}; -type SelectorViewProps = ViewProps & { - options: TreeDropdownOption[]; - defaultText: string; - getDefaults?: (value?: any) => any; - displayValue?: string; - selectedLabelModifier?: ( - option: TreeDropdownOption, - displayValue?: string, - ) => React.ReactNode; - index?: number; -}; - -type KeyValueViewProps = ViewProps; -type TextViewProps = ViewProps & { - index?: number; - additionalAutoComplete?: Record>; -}; -type TabViewProps = Omit & SwitcherProps; - const views = { [ViewTypes.SELECTOR_VIEW]: function SelectorView(props: SelectorViewProps) { return ( @@ -357,46 +146,6 @@ const views = { }, }; -export enum FieldType { - ACTION_SELECTOR_FIELD = "ACTION_SELECTOR_FIELD", - JS_ACTION_SELECTOR_FIELD = "JS_ACTION_SELECTOR_FIELD", - ON_SUCCESS_FIELD = "ON_SUCCESS_FIELD", - ON_ERROR_FIELD = "ON_ERROR_FIELD", - SHOW_MODAL_FIELD = "SHOW_MODAL_FIELD", - CLOSE_MODAL_FIELD = "CLOSE_MODAL_FIELD", - PAGE_SELECTOR_FIELD = "PAGE_SELECTOR_FIELD", - KEY_VALUE_FIELD = "KEY_VALUE_FIELD", - URL_FIELD = "URL_FIELD", - ALERT_TEXT_FIELD = "ALERT_TEXT_FIELD", - ALERT_TYPE_SELECTOR_FIELD = "ALERT_TYPE_SELECTOR_FIELD", - KEY_TEXT_FIELD = "KEY_TEXT_FIELD", - VALUE_TEXT_FIELD = "VALUE_TEXT_FIELD", - QUERY_PARAMS_FIELD = "QUERY_PARAMS_FIELD", - DOWNLOAD_DATA_FIELD = "DOWNLOAD_DATA_FIELD", - DOWNLOAD_FILE_NAME_FIELD = "DOWNLOAD_FILE_NAME_FIELD", - DOWNLOAD_FILE_TYPE_FIELD = "DOWNLOAD_FILE_TYPE_FIELD", - COPY_TEXT_FIELD = "COPY_TEXT_FIELD", - NAVIGATION_TARGET_FIELD = "NAVIGATION_TARGET_FIELD", - WIDGET_NAME_FIELD = "WIDGET_NAME_FIELD", - RESET_CHILDREN_FIELD = "RESET_CHILDREN_FIELD", - ARGUMENT_KEY_VALUE_FIELD = "ARGUMENT_KEY_VALUE_FIELD", - CALLBACK_FUNCTION_FIELD = "CALLBACK_FUNCTION_FIELD", - DELAY_FIELD = "DELAY_FIELD", - ID_FIELD = "ID_FIELD", - CLEAR_INTERVAL_ID_FIELD = "CLEAR_INTERVAL_ID_FIELD", - MESSAGE_FIELD = "MESSAGE_FIELD", - TARGET_ORIGIN_FIELD = "TARGET_ORIGIN_FIELD", - PAGE_NAME_AND_URL_TAB_SELECTOR_FIELD = "PAGE_NAME_AND_URL_TAB_SELECTOR_FIELD", -} - -type FieldConfig = { - getter: Function; - setter: Function; - view: ViewTypes; -}; - -type FieldConfigs = Partial>; - const fieldConfigs: FieldConfigs = { [FieldType.ACTION_SELECTOR_FIELD]: { getter: (storedValue: string) => { @@ -406,9 +155,9 @@ const fieldConfigs: FieldConfigs = { ? [...storedValue.matchAll(ACTION_TRIGGER_REGEX)] : []; } - let mainFuncSelectedValue = ActionType.none; + let mainFuncSelectedValue = AppsmithFunction.none; if (matches.length) { - mainFuncSelectedValue = matches[0][1] || ActionType.none; + mainFuncSelectedValue = matches[0][1] || AppsmithFunction.none; } const mainFuncSelectedValueSplit = mainFuncSelectedValue.split("."); if (mainFuncSelectedValueSplit[1] === "run") { @@ -422,22 +171,22 @@ const fieldConfigs: FieldConfigs = { let defaultParams = ""; let defaultArgs: Array = []; switch (type) { - case ActionType.integration: + case AppsmithFunction.integration: value = `${value}.run`; break; - case ActionType.navigateTo: + case AppsmithFunction.navigateTo: defaultParams = `'', {}, 'SAME_WINDOW'`; break; - case ActionType.jsFunction: + case AppsmithFunction.jsFunction: defaultArgs = option.args ? option.args : []; break; - case ActionType.setInterval: + case AppsmithFunction.setInterval: defaultParams = "() => { \n\t // add code here \n}, 5000"; break; - case ActionType.getGeolocation: + case AppsmithFunction.getGeolocation: defaultParams = "(location) => { \n\t // add code here \n }"; break; - case ActionType.resetWidget: + case AppsmithFunction.resetWidget: defaultParams = `"",true`; break; default: @@ -687,7 +436,7 @@ const fieldConfigs: FieldConfigs = { }; function renderField(props: { - onValueChange: Function; + onValueChange: (newValue: string, isUpdatedViaKeyboard: boolean) => void; value: string; field: { field: FieldType; value: string; label: string; index: number }; label?: string; @@ -698,8 +447,8 @@ function renderField(props: { depth: number; maxDepth: number; additionalAutoComplete?: Record>; - activeNavigateToTab: Switch; - navigateToSwitches: Array; + activeNavigateToTab: SwitchType; + navigateToSwitches: Array; }) { const { field } = props; const fieldType = field.field; @@ -739,7 +488,7 @@ function renderField(props: { option: TreeDropdownOption, displayValue?: string, ) { - if (option.type === ActionType.integration) { + if (option.type === AppsmithFunction.integration) { return ( { return { - [ActionType.navigateTo]: `'${props.pageDropdownOptions[0].label}'`, + [AppsmithFunction.navigateTo]: `'${props.pageDropdownOptions[0].label}'`, }[value]; }; } @@ -838,7 +587,7 @@ function renderField(props: { props.value, props.field.index, ); - props.onValueChange(finalValueToSet); + props.onValueChange(finalValueToSet, false); }, index: props.field.index, value: props.value || "", @@ -851,7 +600,7 @@ function renderField(props: { get: fieldConfig.getter, set: (value: string | DropdownOption) => { const finalValueToSet = fieldConfig.setter(value, props.value); - props.onValueChange(finalValueToSet); + props.onValueChange(finalValueToSet, false); }, value: props.value, defaultText: "Select Action", @@ -918,7 +667,7 @@ function renderField(props: { } function Fields(props: { - onValueChange: Function; + onValueChange: (newValue: string, isUpdatedViaKeyboard: boolean) => void; value: string; fields: any; label?: string; @@ -929,8 +678,8 @@ function Fields(props: { depth: number; maxDepth: number; additionalAutoComplete?: Record>; - navigateToSwitches: Array; - activeNavigateToTab: Switch; + navigateToSwitches: Array; + activeNavigateToTab: SwitchType; }) { const { fields, ...otherProps } = props; diff --git a/app/client/src/components/editorComponents/ActionCreator/constants.ts b/app/client/src/components/editorComponents/ActionCreator/constants.ts index 33673fa7bf..531161a5a1 100644 --- a/app/client/src/components/editorComponents/ActionCreator/constants.ts +++ b/app/client/src/components/editorComponents/ActionCreator/constants.ts @@ -16,3 +16,85 @@ export const APPSMITH_NAMESPACED_FUNCTIONS = { watchGeolocation: "appsmith.geolocation.watchPosition", stopWatchGeolocation: "appsmith.geolocation.clearWatch", }; + +export const AppsmithFunction = { + none: "none", + integration: "integration", + jsFunction: "jsFunction", + ...APPSMITH_GLOBAL_FUNCTIONS, + ...APPSMITH_NAMESPACED_FUNCTIONS, +}; + +export const RESET_CHILDREN_OPTIONS = [ + { label: "true", value: "true", id: "true" }, + { label: "false", value: "false", id: "false" }, +]; + +export const FILE_TYPE_OPTIONS = [ + { label: "Select file type (optional)", value: "", id: "" }, + { label: "Plain text", value: "'text/plain'", id: "text/plain" }, + { label: "HTML", value: "'text/html'", id: "text/html" }, + { label: "CSV", value: "'text/csv'", id: "text/csv" }, + { label: "JSON", value: "'application/json'", id: "application/json" }, + { label: "JPEG", value: "'image/jpeg'", id: "image/jpeg" }, + { label: "PNG", value: "'image/png'", id: "image/png" }, + { label: "SVG", value: "'image/svg+xml'", id: "image/svg+xml" }, +]; + +export const NAVIGATION_TARGET_FIELD_OPTIONS = [ + { + label: "Same window", + value: "SAME_WINDOW", + id: "SAME_WINDOW", + }, + { + label: "New window", + value: "NEW_WINDOW", + id: "NEW_WINDOW", + }, +]; + +export const ViewTypes = { + SELECTOR_VIEW: "SELECTOR_VIEW", + KEY_VALUE_VIEW: "KEY_VALUE_VIEW", + TEXT_VIEW: "TEXT_VIEW", + BOOL_VIEW: "BOOL_VIEW", + TAB_VIEW: "TAB_VIEW", +}; + +export const NAVIGATE_TO_TAB_OPTIONS = { + PAGE_NAME: "page-name", + URL: "url", +}; + +export enum FieldType { + ACTION_SELECTOR_FIELD = "ACTION_SELECTOR_FIELD", + JS_ACTION_SELECTOR_FIELD = "JS_ACTION_SELECTOR_FIELD", + ON_SUCCESS_FIELD = "ON_SUCCESS_FIELD", + ON_ERROR_FIELD = "ON_ERROR_FIELD", + SHOW_MODAL_FIELD = "SHOW_MODAL_FIELD", + CLOSE_MODAL_FIELD = "CLOSE_MODAL_FIELD", + PAGE_SELECTOR_FIELD = "PAGE_SELECTOR_FIELD", + KEY_VALUE_FIELD = "KEY_VALUE_FIELD", + URL_FIELD = "URL_FIELD", + ALERT_TEXT_FIELD = "ALERT_TEXT_FIELD", + ALERT_TYPE_SELECTOR_FIELD = "ALERT_TYPE_SELECTOR_FIELD", + KEY_TEXT_FIELD = "KEY_TEXT_FIELD", + VALUE_TEXT_FIELD = "VALUE_TEXT_FIELD", + QUERY_PARAMS_FIELD = "QUERY_PARAMS_FIELD", + DOWNLOAD_DATA_FIELD = "DOWNLOAD_DATA_FIELD", + DOWNLOAD_FILE_NAME_FIELD = "DOWNLOAD_FILE_NAME_FIELD", + DOWNLOAD_FILE_TYPE_FIELD = "DOWNLOAD_FILE_TYPE_FIELD", + COPY_TEXT_FIELD = "COPY_TEXT_FIELD", + NAVIGATION_TARGET_FIELD = "NAVIGATION_TARGET_FIELD", + WIDGET_NAME_FIELD = "WIDGET_NAME_FIELD", + RESET_CHILDREN_FIELD = "RESET_CHILDREN_FIELD", + ARGUMENT_KEY_VALUE_FIELD = "ARGUMENT_KEY_VALUE_FIELD", + CALLBACK_FUNCTION_FIELD = "CALLBACK_FUNCTION_FIELD", + DELAY_FIELD = "DELAY_FIELD", + ID_FIELD = "ID_FIELD", + CLEAR_INTERVAL_ID_FIELD = "CLEAR_INTERVAL_ID_FIELD", + MESSAGE_FIELD = "MESSAGE_FIELD", + TARGET_ORIGIN_FIELD = "TARGET_ORIGIN_FIELD", + PAGE_NAME_AND_URL_TAB_SELECTOR_FIELD = "PAGE_NAME_AND_URL_TAB_SELECTOR_FIELD", +} diff --git a/app/client/src/components/editorComponents/ActionCreator/index.tsx b/app/client/src/components/editorComponents/ActionCreator/index.tsx index 31c3667a6b..2e6b68606b 100644 --- a/app/client/src/components/editorComponents/ActionCreator/index.tsx +++ b/app/client/src/components/editorComponents/ActionCreator/index.tsx @@ -24,12 +24,7 @@ import { getModalDropdownList, getNextModalName, } from "selectors/widgetSelectors"; -import Fields, { - ACTION_ANONYMOUS_FUNC_REGEX, - ACTION_TRIGGER_REGEX, - ActionType, - FieldType, -} from "./Fields"; +import Fields from "./Fields"; import { getDataTree } from "selectors/dataTreeSelectors"; import { DataTree, ENTITY_TYPE } from "entities/DataTree/dataTreeFactory"; import { getEntityNameAndPropertyPath } from "workers/evaluationUtils"; @@ -62,69 +57,74 @@ import { selectFeatureFlags } from "selectors/usersSelectors"; import FeatureFlags from "entities/FeatureFlags"; import { connect } from "react-redux"; import { isValidURL } from "utils/URLUtils"; +import { ACTION_ANONYMOUS_FUNC_REGEX, ACTION_TRIGGER_REGEX } from "./regex"; +import { + NAVIGATE_TO_TAB_OPTIONS, + AppsmithFunction, + FieldType, +} from "./constants"; +import { SwitchType, ActionCreatorProps, GenericFunction } from "./types"; -/* eslint-disable @typescript-eslint/ban-types */ -/* TODO: Function and object types need to be updated to enable the lint rule */ const baseOptions: { label: string; value: string }[] = [ { label: createMessage(NO_ACTION), - value: ActionType.none, + value: AppsmithFunction.none, }, { label: createMessage(EXECUTE_A_QUERY), - value: ActionType.integration, + value: AppsmithFunction.integration, }, { label: createMessage(NAVIGATE_TO), - value: ActionType.navigateTo, + value: AppsmithFunction.navigateTo, }, { label: createMessage(SHOW_MESSAGE), - value: ActionType.showAlert, + value: AppsmithFunction.showAlert, }, { label: createMessage(OPEN_MODAL), - value: ActionType.showModal, + value: AppsmithFunction.showModal, }, { label: createMessage(CLOSE_MODAL), - value: ActionType.closeModal, + value: AppsmithFunction.closeModal, }, { label: createMessage(STORE_VALUE), - value: ActionType.storeValue, + value: AppsmithFunction.storeValue, }, { label: createMessage(DOWNLOAD), - value: ActionType.download, + value: AppsmithFunction.download, }, { label: createMessage(COPY_TO_CLIPBOARD), - value: ActionType.copyToClipboard, + value: AppsmithFunction.copyToClipboard, }, { label: createMessage(RESET_WIDGET), - value: ActionType.resetWidget, + value: AppsmithFunction.resetWidget, }, { label: createMessage(SET_INTERVAL), - value: ActionType.setInterval, + value: AppsmithFunction.setInterval, }, { label: createMessage(CLEAR_INTERVAL), - value: ActionType.clearInterval, + value: AppsmithFunction.clearInterval, }, { label: createMessage(GET_GEO_LOCATION), - value: ActionType.getGeolocation, + value: AppsmithFunction.getGeolocation, }, { label: createMessage(WATCH_GEO_LOCATION), - value: ActionType.watchGeolocation, + value: AppsmithFunction.watchGeolocation, }, { label: createMessage(STOP_WATCH_GEO_LOCATION), - value: ActionType.stopWatchGeolocation, + value: AppsmithFunction.stopWatchGeolocation, }, ]; @@ -132,28 +132,22 @@ const getBaseOptions = (featureFlags: FeatureFlags) => { const { JS_EDITOR: isJSEditorEnabled } = featureFlags; if (isJSEditorEnabled) { const jsOption = baseOptions.find( - (option: any) => option.value === ActionType.jsFunction, + (option: any) => option.value === AppsmithFunction.jsFunction, ); if (!jsOption) { baseOptions.splice(2, 0, { label: createMessage(EXECUTE_JS_FUNCTION), - value: ActionType.jsFunction, + value: AppsmithFunction.jsFunction, }); } } return baseOptions; }; -type Switch = { - id: string; - text: string; - action: () => void; -}; - function getFieldFromValue( value: string | undefined, - activeTabNavigateTo: Switch, - getParentValue?: Function, + activeTabNavigateTo: SwitchType, + getParentValue?: (changeValue: string) => string, dataTree?: DataTree, ): any[] { const fields: any[] = []; @@ -408,7 +402,7 @@ function useModalDropdownList() { id: "create", icon: "plus", className: "t--create-modal-btn", - onSelect: (option: TreeDropdownOption, setter?: Function) => { + onSelect: (option: TreeDropdownOption, setter?: GenericFunction) => { const modalName = nextModalName; if (setter) { setter({ @@ -459,11 +453,11 @@ function getIntegrationOptionsWithChildren( action.config.pluginType === PluginType.REMOTE, ); const option = options.find( - (option) => option.value === ActionType.integration, + (option) => option.value === AppsmithFunction.integration, ); const jsOption = options.find( - (option) => option.value === ActionType.jsFunction, + (option) => option.value === AppsmithFunction.jsFunction, ); if (option) { @@ -586,18 +580,6 @@ function useIntegrationsOptionTree() { ); } -type ActionCreatorProps = { - value: string; - onValueChange: (newValue: string, isUpdatedViaKeyboard: boolean) => void; - additionalAutoComplete?: Record>; - pageDropdownOptions: TreeDropdownOption[]; -}; - -const NAVIGATE_TO_TAB_OPTIONS = { - PAGE_NAME: "page-name", - URL: "url", -}; - const isValueValidURL = (value: string) => { if (value) { const indices = []; @@ -613,7 +595,7 @@ const isValueValidURL = (value: string) => { const ActionCreator = React.forwardRef( (props: ActionCreatorProps, ref: any) => { - const NAVIGATE_TO_TAB_SWITCHER: Array = [ + const NAVIGATE_TO_TAB_SWITCHER: Array = [ { id: "page-name", text: "Page Name", diff --git a/app/client/src/components/editorComponents/ActionCreator/regex.ts b/app/client/src/components/editorComponents/ActionCreator/regex.ts new file mode 100644 index 0000000000..67bee9435c --- /dev/null +++ b/app/client/src/components/editorComponents/ActionCreator/regex.ts @@ -0,0 +1,8 @@ +export const FUNC_ARGS_REGEX = /((["][^"]*["])|([\[][\s\S]*[\]])|([\{][\s\S]*[\}])|(['][^']*['])|([\(][\s\S]*[\)][ ]*=>[ ]*[{][\s\S]*[}])|([^'",][^,"+]*[^'",]*))*/gi; + +//Old Regex:: /\(\) => ([\s\S]*?)(\([\s\S]*?\))/g; +export const ACTION_TRIGGER_REGEX = /^{{([\s\S]*?)\(([\s\S]*?)\)}}$/g; + +export const ACTION_ANONYMOUS_FUNC_REGEX = /\(\) => (({[\s\S]*?})|([\s\S]*?)(\([\s\S]*?\)))/g; + +export const IS_URL_OR_MODAL = /^'.*'$/; diff --git a/app/client/src/components/editorComponents/ActionCreator/types.ts b/app/client/src/components/editorComponents/ActionCreator/types.ts new file mode 100644 index 0000000000..95de401260 --- /dev/null +++ b/app/client/src/components/editorComponents/ActionCreator/types.ts @@ -0,0 +1,58 @@ +import { SwitcherProps, TreeDropdownOption } from "design-system"; +import React from "react"; +import { FieldType, ViewTypes, AppsmithFunction } from "./constants"; + +export type GenericFunction = (...args: any[]) => any; + +export type SwitchType = { + id: string; + text: string; + action: () => void; +}; + +export type ActionType = typeof AppsmithFunction[keyof typeof AppsmithFunction]; + +export type ViewType = typeof ViewTypes[keyof typeof ViewTypes]; + +export type ViewProps = { + label: string; + get: GenericFunction; + set: GenericFunction; + value: string; +}; + +export type SelectorViewProps = ViewProps & { + options: TreeDropdownOption[]; + defaultText: string; + getDefaults?: (value?: any) => any; + displayValue?: string; + selectedLabelModifier?: ( + option: TreeDropdownOption, + displayValue?: string, + ) => React.ReactNode; + index?: number; +}; + +export type KeyValueViewProps = ViewProps; + +export type TextViewProps = ViewProps & { + index?: number; + additionalAutoComplete?: Record>; +}; + +export type TabViewProps = Omit & SwitcherProps; + +export type FieldConfig = { + getter: GenericFunction; + setter: GenericFunction; + view: ViewType; +}; + +export type FieldConfigs = Partial>; + +export type ActionCreatorProps = { + value: string; + onValueChange: (newValue: string, isUpdatedViaKeyboard: boolean) => void; + additionalAutoComplete?: Record>; + pageDropdownOptions: TreeDropdownOption[]; +}; diff --git a/app/client/src/components/editorComponents/ActionCreator/utils.test.ts b/app/client/src/components/editorComponents/ActionCreator/utils.test.ts new file mode 100644 index 0000000000..e44ba60b4c --- /dev/null +++ b/app/client/src/components/editorComponents/ActionCreator/utils.test.ts @@ -0,0 +1,334 @@ +jest.mock("sagas/ActionExecution/NavigateActionSaga", () => ({ + __esModule: true, + default: "", + NavigationTargetType: { SAME_WINDOW: "" }, +})); + +import { + argsStringToArray, + enumTypeSetter, + enumTypeGetter, + JSToString, + modalGetter, + modalSetter, + stringToJS, + textGetter, + textSetter, +} from "./utils"; + +describe("Test argStringToArray", () => { + const cases = [ + { index: 0, input: "", expected: [""] }, + { index: 1, input: "'a'", expected: ["'a'"] }, + { index: 2, input: "a", expected: ["a"] }, + { index: 3, input: "'a,b,c'", expected: ["'a,b,c'"] }, + { index: 4, input: "a,b,c", expected: ["a", "b", "c"] }, + { index: 5, input: "a, b, c", expected: ["a", " b", " c"] }, + { index: 6, input: "a , b , c", expected: ["a ", " b ", " c"] }, + { index: 7, input: "[a,b,c]", expected: ["[a,b,c]"] }, + { index: 8, input: "[a, b, c]", expected: ["[a, b, c]"] }, + { + index: 9, + input: "[\n\ta,\n\tb,\n\tc\n]", + expected: ["[\n\ta,\n\tb,\n\tc\n]"], + }, + { index: 10, input: "{a:1,b:2,c:3}", expected: ["{a:1,b:2,c:3}"] }, + { + index: 11, + input: '{"a":1,"b":2,"c":3}', + expected: ['{"a":1,"b":2,"c":3}'], + }, + { + index: 12, + input: "{\n\ta:1,\n\tb:2,\n\tc:3}", + expected: ["{\n\ta:1,\n\tb:2,\n\tc:3}"], + }, + { + index: 13, + input: "()=>{}", + expected: ["()=>{}"], + }, + { + index: 14, + input: "(a, b)=>{return a+b}", + expected: ["(a, b)=>{return a+b}"], + }, + { + index: 15, + input: "(a, b)=>{\n\treturn a+b;\n\t}", + expected: ["(a, b)=>{\n\treturn a+b;\n\t}"], + }, + { + index: 16, + input: "(\n\ta,\n\tb\n)=>{\n\treturn a+b;\n\t}", + expected: ["(\n\ta,\n\tb\n)=>{\n\treturn a+b;\n\t}"], + }, + { + index: 17, + input: `() => {return 5}`, + expected: ["() => {return 5}"], + }, + { + index: 19, + input: `(a) => {return a + 1}`, + expected: ["(a) => {return a + 1}"], + }, + { + index: 19, + input: `(a, b) => {return a + b}`, + expected: ["(a, b) => {return a + b}"], + }, + ]; + test.each(cases.map((x) => [x.index, x.input, x.expected]))( + "test case %d", + (_, input, expected) => { + const result = argsStringToArray(input as string); + expect(result).toStrictEqual(expected); + }, + ); +}); + +describe("Test stringToJS", () => { + const cases = [ + { index: 1, input: "{{'a'}}", expected: "'a'" }, + { index: 2, input: "{{a}}", expected: "a" }, + { index: 3, input: "{{'a,b,c'}}", expected: "'a,b,c'" }, + { index: 4, input: "{{a,b,c}}", expected: "a,b,c" }, + { index: 5, input: "{{a, b, c}}", expected: "a, b, c" }, + { index: 6, input: "{{a , b , c}}", expected: "a , b , c" }, + { index: 7, input: "{{[a,b,c]}}", expected: "[a,b,c]" }, + { index: 8, input: "{{[a, b, c]}}", expected: "[a, b, c]" }, + { + index: 9, + input: "{{[\n\ta,\n\tb,\n\tc\n]}}", + expected: "[\n\ta,\n\tb,\n\tc\n]", + }, + { index: 10, input: "{{{a:1,b:2,c:3}}}", expected: "{a:1,b:2,c:3}" }, + { + index: 11, + input: '{{{"a":1,"b":2,"c":3}}}', + expected: '{"a":1,"b":2,"c":3}', + }, + { + index: 12, + input: "{{{\n\ta:1,\n\tb:2,\n\tc:3}}}", + expected: "{\n\ta:1,\n\tb:2,\n\tc:3}", + }, + { + index: 13, + input: "{{()=>{}}}", + expected: "()=>{}", + }, + { + index: 14, + input: "{{(a, b)=>{return a+b}}}", + expected: "(a, b)=>{return a+b}", + }, + { + index: 15, + input: "{{(a, b)=>{\n\treturn a+b;\n\t}}}", + expected: "(a, b)=>{\n\treturn a+b;\n\t}", + }, + { + index: 16, + input: "{{(\n\ta,\n\tb\n)=>{\n\treturn a+b;\n\t}}}", + expected: "(\n\ta,\n\tb\n)=>{\n\treturn a+b;\n\t}", + }, + { + index: 17, + input: "{{() => {return 5}}}", + expected: "() => {return 5}", + }, + { + index: 18, + input: "{{(a) => {return a + 1}}}", + expected: "(a) => {return a + 1}", + }, + { + index: 19, + input: "{{(a, b) => {return a + b}}}", + expected: "(a, b) => {return a + b}", + }, + ]; + test.each(cases.map((x) => [x.index, x.input, x.expected]))( + "test case %d", + (_, input, expected) => { + const result = stringToJS(input as string); + expect(result).toStrictEqual(expected); + }, + ); +}); + +describe("Test JSToString", () => { + const cases = [ + { index: 1, input: "'a'", expected: "a" }, + { index: 2, input: "a", expected: "{{a}}" }, + { index: 3, input: "'a,b,c'", expected: "a,b,c" }, + { index: 4, input: "a,b,c", expected: "{{a,b,c}}" }, + { index: 5, input: "a, b, c", expected: "{{a, b, c}}" }, + { index: 6, input: "a , b , c", expected: "{{a , b , c}}" }, + { index: 7, input: "[a,b,c]", expected: "{{[a,b,c]}}" }, + { index: 8, input: "[a, b, c]", expected: "{{[a, b, c]}}" }, + { + index: 9, + input: "[\n\ta,\n\tb,\n\tc\n]", + expected: "{{[\n\ta,\n\tb,\n\tc\n]}}", + }, + { index: 10, input: "{a:1,b:2,c:3}", expected: "{{{a:1,b:2,c:3}}}" }, + { + index: 11, + input: '{"a":1,"b":2,"c":3}', + expected: '{{{"a":1,"b":2,"c":3}}}', + }, + { + index: 12, + input: "{\n\ta:1,\n\tb:2,\n\tc:3}", + expected: "{{{\n\ta:1,\n\tb:2,\n\tc:3}}}", + }, + { + index: 13, + input: "()=>{}", + expected: "{{()=>{}}}", + }, + { + index: 14, + input: "(a, b)=>{return a+b}", + expected: "{{(a, b)=>{return a+b}}}", + }, + { + index: 15, + input: "(a, b)=>{\n\treturn a+b;\n\t}", + expected: "{{(a, b)=>{\n\treturn a+b;\n\t}}}", + }, + { + index: 16, + input: "(\n\ta,\n\tb\n)=>{\n\treturn a+b;\n\t}", + expected: "{{(\n\ta,\n\tb\n)=>{\n\treturn a+b;\n\t}}}", + }, + { + index: 17, + input: "() => {return 5}", + expected: "{{() => {return 5}}}", + }, + ]; + test.each(cases.map((x) => [x.index, x.input, x.expected]))( + "test case %d", + (_, input, expected) => { + const result = JSToString(input as string); + expect(result).toStrictEqual(expected); + }, + ); +}); + +describe("Test modalSetter", () => { + const result = modalSetter("Modal1", "{{closeModal()}}"); + expect(result).toStrictEqual("{{closeModal('Modal1')}}"); +}); + +describe("Test modalGetter", () => { + const result = modalGetter("{{showModal('Modal1')}}"); + expect(result).toStrictEqual("Modal1"); +}); + +describe("Test textSetter", () => { + const result = textSetter( + "google.com", + "{{navigateTo('', {},NEW_WINDOW)}}", + 0, + ); + expect(result).toStrictEqual("{{navigateTo('google.com', {},NEW_WINDOW)}}"); +}); + +describe("Test textGetter", () => { + const cases = [ + { + index: 0, + input: "{{navigateTo('google.com', {}, NEW_WINDOW)}}", + expected: "google.com", + }, + { + index: 1, + input: "{{navigateTo('google.com', {}, NEW_WINDOW)}}", + expected: "{{{}}}", + }, + ]; + test.each(cases.map((x) => [x.index, x.input, x.expected]))( + "test case %d", + (index, input, expected) => { + const result = textGetter(input as string, index as number); + expect(result).toStrictEqual(expected); + }, + ); +}); + +describe("Test enumTypeSetter", () => { + const cases = [ + { + index: 0, + value: "info", + input: "{{showAlert('hi')}}", + expected: "{{showAlert('hi',info)}}", + argNum: 1, + }, + { + index: 1, + value: "info", + input: "{{showAlert('hi','error')}}", + expected: "{{showAlert('hi',info)}}", + argNum: 1, + }, + { + index: 2, + value: "info", + input: "{{showAlert(,'')}}", + expected: "{{showAlert(,info)}}", + argNum: 1, + }, + ]; + test.each( + cases.map((x) => [x.index, x.input, x.expected, x.value, x.argNum]), + )("test case %d", (index, input, expected, value, argNum) => { + const result = enumTypeSetter( + value as string, + input as string, + argNum as number, + ); + expect(result).toStrictEqual(expected); + }); +}); + +describe("Test enumTypeGetter", () => { + const cases = [ + { + index: 0, + value: "success", + input: "{{showAlert('hi','info')}}", + expected: "{{showAlert('hi','info')}}", + argNum: 1, + }, + { + index: 1, + value: "info", + input: "{{showAlert(,'error')}}", + expected: "{{showAlert(,'error')}}", + argNum: 1, + }, + { + index: 2, + value: "info", + input: "{{showAlert()}}", + expected: "{{showAlert()}}", + argNum: 1, + }, + ]; + test.each( + cases.map((x) => [x.index, x.input, x.expected, x.value, x.argNum]), + )("test case %d", (index, input, expected, value, argNum) => { + const result = enumTypeGetter( + value as string, + argNum as number, + input as string, + ); + expect(result).toStrictEqual(expected); + }); +}); diff --git a/app/client/src/components/editorComponents/ActionCreator/utils.ts b/app/client/src/components/editorComponents/ActionCreator/utils.ts new file mode 100644 index 0000000000..fe6071b066 --- /dev/null +++ b/app/client/src/components/editorComponents/ActionCreator/utils.ts @@ -0,0 +1,145 @@ +import { + ACTION_TRIGGER_REGEX, + FUNC_ARGS_REGEX, + IS_URL_OR_MODAL, +} from "./regex"; +import { + getDynamicBindings, + isDynamicValue, +} from "../../../utils/DynamicBindingUtils"; + +export const stringToJS = (string: string): string => { + const { jsSnippets, stringSegments } = getDynamicBindings(string); + return stringSegments + .map((segment, index) => { + if (jsSnippets[index] && jsSnippets[index].length > 0) { + return jsSnippets[index]; + } else { + return `'${segment}'`; + } + }) + .join(" + "); +}; + +export const JSToString = (js: string): string => { + const segments = js.split(" + "); + return segments + .map((segment) => { + if (segment.charAt(0) === "'") { + return segment.substring(1, segment.length - 1); + } else return "{{" + segment + "}}"; + }) + .join(""); +}; + +export const argsStringToArray = (funcArgs: string): string[] => { + const argsplitMatches = [...funcArgs.matchAll(FUNC_ARGS_REGEX)]; + const arr: string[] = []; + let isPrevUndefined = true; + for (const match of argsplitMatches) { + const matchVal = match[0]; + if (!matchVal || matchVal === "") { + if (isPrevUndefined) { + arr.push(matchVal); + } + isPrevUndefined = true; + } else { + isPrevUndefined = false; + arr.push(matchVal); + } + } + return arr; +}; + +export const modalSetter = (changeValue: any, currentValue: string) => { + const matches = [...currentValue.matchAll(ACTION_TRIGGER_REGEX)]; + let args: string[] = []; + if (matches.length) { + args = matches[0][2].split(","); + if (isDynamicValue(changeValue)) { + args[0] = `${changeValue.substring(2, changeValue.length - 2)}`; + } else { + args[0] = `'${changeValue}'`; + } + } + return currentValue.replace( + ACTION_TRIGGER_REGEX, + `{{$1(${args.join(",")})}}`, + ); +}; + +export const modalGetter = (value: string) => { + const matches = [...value.matchAll(ACTION_TRIGGER_REGEX)]; + let name = "none"; + if (!matches.length) { + return name; + } else { + const modalName = matches[0][2].split(",")[0]; + if (IS_URL_OR_MODAL.test(modalName) || modalName === "") { + name = modalName.substring(1, modalName.length - 1); + } else { + name = `{{${modalName}}}`; + } + return name; + } +}; + +export const textSetter = ( + changeValue: any, + currentValue: string, + argNum: number, +): string => { + const matches = [...currentValue.matchAll(ACTION_TRIGGER_REGEX)]; + let args: string[] = []; + if (matches.length) { + args = argsStringToArray(matches[0][2]); + args[argNum] = stringToJS(changeValue); + } + return currentValue.replace( + ACTION_TRIGGER_REGEX, + `{{$1(${args.join(",")})}}`, + ); +}; + +export const textGetter = (value: string, argNum: number) => { + const matches = [...value.matchAll(ACTION_TRIGGER_REGEX)]; + if (!matches.length) { + return ""; + } else { + const args = argsStringToArray(matches[0][2]); + const arg = args[argNum]; + return arg ? JSToString(arg.trim()) : arg; + } +}; + +export const enumTypeSetter = ( + changeValue: any, + currentValue: string, + argNum: number, +): string => { + const matches = [...currentValue.matchAll(ACTION_TRIGGER_REGEX)]; + let args: string[] = []; + if (matches.length) { + args = argsStringToArray(matches[0][2]); + args[argNum] = changeValue as string; + } + return currentValue.replace( + ACTION_TRIGGER_REGEX, + `{{$1(${args.join(",")})}}`, + ); +}; + +export const enumTypeGetter = ( + value: string, + argNum: number, + defaultValue = "", +): string => { + const matches = [...value.matchAll(ACTION_TRIGGER_REGEX)]; + if (!matches.length) { + return defaultValue; + } else { + const args = argsStringToArray(matches[0][2]); + const arg = args[argNum]; + return arg ? arg.trim() : defaultValue; + } +}; diff --git a/app/client/src/components/propertyControls/ComputeTablePropertyControl.tsx b/app/client/src/components/propertyControls/ComputeTablePropertyControl.tsx index c7d72bc557..48446b5183 100644 --- a/app/client/src/components/propertyControls/ComputeTablePropertyControl.tsx +++ b/app/client/src/components/propertyControls/ComputeTablePropertyControl.tsx @@ -15,7 +15,7 @@ import { isString } from "utils/helpers"; import { JSToString, stringToJS, -} from "components/editorComponents/ActionCreator/Fields"; +} from "components/editorComponents/ActionCreator/utils"; import CodeEditor from "components/editorComponents/LazyCodeEditorWrapper"; const PromptMessage = styled.span` diff --git a/app/client/src/components/propertyControls/TableComputeValue.tsx b/app/client/src/components/propertyControls/TableComputeValue.tsx index 928fc4c317..f55c5d9a74 100644 --- a/app/client/src/components/propertyControls/TableComputeValue.tsx +++ b/app/client/src/components/propertyControls/TableComputeValue.tsx @@ -17,7 +17,7 @@ import { isString } from "utils/helpers"; import { JSToString, stringToJS, -} from "components/editorComponents/ActionCreator/Fields"; +} from "components/editorComponents/ActionCreator/utils"; import { AdditionalDynamicDataTree } from "utils/autocomplete/customTreeTypeDefCreator"; const PromptMessage = styled.span` diff --git a/app/client/src/components/propertyControls/TableInlineEditValidation.tsx b/app/client/src/components/propertyControls/TableInlineEditValidation.tsx index fd57380661..008b4bce88 100644 --- a/app/client/src/components/propertyControls/TableInlineEditValidation.tsx +++ b/app/client/src/components/propertyControls/TableInlineEditValidation.tsx @@ -17,7 +17,7 @@ import { isString } from "utils/helpers"; import { JSToString, stringToJS, -} from "components/editorComponents/ActionCreator/Fields"; +} from "components/editorComponents/ActionCreator/utils"; import { AdditionalDynamicDataTree } from "utils/autocomplete/customTreeTypeDefCreator"; import { ORIGINAL_INDEX_KEY,