diff --git a/app/client/src/components/editorComponents/actioncreator/ActionCreator.tsx b/app/client/src/components/editorComponents/actioncreator/ActionCreator.tsx index 3c77c06578..6c472b782e 100644 --- a/app/client/src/components/editorComponents/actioncreator/ActionCreator.tsx +++ b/app/client/src/components/editorComponents/actioncreator/ActionCreator.tsx @@ -28,6 +28,7 @@ import { import { NavigationTargetType } from "../../../sagas/ActionExecutionSagas"; import { checkCurrentStep } from "sagas/OnboardingSagas"; import { OnboardingStep } from "constants/OnboardingConstants"; +import { getWidgets } from "sagas/selectors"; /* eslint-disable @typescript-eslint/ban-types */ /* TODO: Function and object types need to be updated to enable the lint rule */ @@ -39,6 +40,11 @@ const ALERT_STYLE_OPTIONS = [ { label: "Warning", value: "'warning'", id: "warning" }, ]; +const RESET_CHILDREN_OPTIONS = [ + { label: "Yes", value: "true", id: "true" }, + { label: "No", value: "false", id: "false" }, +]; + const FILE_TYPE_OPTIONS = [ { label: "Select file type (optional)", value: "", id: "" }, { label: "Plain text", value: "'text/plain'", id: "text/plain" }, @@ -224,6 +230,7 @@ const ActionType = { storeValue: "storeValue", download: "download", copyToClipboard: "copyToClipboard", + resetWidget: "resetWidget", }; type ActionType = typeof ActionType[keyof typeof ActionType]; @@ -231,6 +238,7 @@ const ViewTypes = { SELECTOR_VIEW: "SELECTOR_VIEW", KEY_VALUE_VIEW: "KEY_VALUE_VIEW", TEXT_VIEW: "TEXT_VIEW", + BOOL_VIEW: "BOOL_VIEW", }; type ViewTypes = typeof ViewTypes[keyof typeof ViewTypes]; @@ -336,6 +344,8 @@ const FieldType = { 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", }; type FieldType = typeof FieldType[keyof typeof FieldType]; @@ -515,6 +525,24 @@ const fieldConfigs: FieldConfigs = { }, view: ViewTypes.TEXT_VIEW, }, + [FieldType.WIDGET_NAME_FIELD]: { + getter: (value: any) => { + return enumTypeGetter(value, 0); + }, + setter: (option: any, currentValue: string) => { + return enumTypeSetter(option.value, currentValue, 0); + }, + view: ViewTypes.SELECTOR_VIEW, + }, + [FieldType.RESET_CHILDREN_FIELD]: { + getter: (value: any) => { + return enumTypeGetter(value, 1); + }, + setter: (option: any, currentValue: string) => { + return enumTypeSetter(option.value, currentValue, 1); + }, + view: ViewTypes.SELECTOR_VIEW, + }, }; const baseOptions: any = [ @@ -558,6 +586,10 @@ const baseOptions: any = [ label: "Copy to Clipboard", value: ActionType.copyToClipboard, }, + { + label: "Reset Widget", + value: ActionType.resetWidget, + }, ]; function getOptionsWithChildren( options: TreeDropdownOption[], @@ -691,6 +723,16 @@ function getFieldFromValue( }, ); } + if (value.indexOf("resetWidget") !== -1) { + fields.push( + { + field: FieldType.WIDGET_NAME_FIELD, + }, + { + field: FieldType.RESET_CHILDREN_FIELD, + }, + ); + } if (value.indexOf("download") !== -1) { fields.push( { @@ -728,6 +770,7 @@ function renderField(props: { isValid: boolean; validationMessage?: string; apiOptionTree: TreeDropdownOption[]; + widgetOptionTree: TreeDropdownOption[]; queryOptionTree: TreeDropdownOption[]; modalDropdownList: TreeDropdownOption[]; pageDropdownOptions: TreeDropdownOption[]; @@ -751,6 +794,8 @@ function renderField(props: { case FieldType.ALERT_TYPE_SELECTOR_FIELD: case FieldType.DOWNLOAD_FILE_TYPE_FIELD: case FieldType.NAVIGATION_TARGET_FIELD: + case FieldType.RESET_CHILDREN_FIELD: + case FieldType.WIDGET_NAME_FIELD: let label = ""; let defaultText = "Select Action"; let options = props.apiOptionTree; @@ -794,6 +839,16 @@ function renderField(props: { options = props.modalDropdownList; defaultText = "Select Modal"; } + if (fieldType === FieldType.RESET_CHILDREN_FIELD) { + label = "Reset Children"; + options = RESET_CHILDREN_OPTIONS; + defaultText = "false"; + } + if (fieldType === FieldType.WIDGET_NAME_FIELD) { + label = "Widget"; + options = props.widgetOptionTree; + defaultText = ""; + } if (fieldType === FieldType.PAGE_SELECTOR_FIELD) { label = "Page Name"; options = props.pageDropdownOptions; @@ -900,6 +955,7 @@ function Fields(props: { isValid: boolean; validationMessage?: string; apiOptionTree: TreeDropdownOption[]; + widgetOptionTree: TreeDropdownOption[]; queryOptionTree: TreeDropdownOption[]; modalDropdownList: TreeDropdownOption[]; pageDropdownOptions: TreeDropdownOption[]; @@ -932,6 +988,7 @@ function Fields(props: { isValid={props.isValid} validationMessage={props.validationMessage} apiOptionTree={props.apiOptionTree} + widgetOptionTree={props.widgetOptionTree} queryOptionTree={props.queryOptionTree} modalDropdownList={props.modalDropdownList} pageDropdownOptions={props.pageDropdownOptions} @@ -977,6 +1034,7 @@ function Fields(props: { isValid={props.isValid} validationMessage={props.validationMessage} apiOptionTree={props.apiOptionTree} + widgetOptionTree={props.widgetOptionTree} queryOptionTree={props.queryOptionTree} modalDropdownList={props.modalDropdownList} pageDropdownOptions={props.pageDropdownOptions} @@ -1068,6 +1126,18 @@ function useApiOptionTree() { return apiOptionTree; } +function useWidgetOptionTree() { + const widgets = useSelector(getWidgets) || {}; + return Object.values(widgets) + .filter((w) => w.type !== "CANVAS_WIDGET" && w.type !== "BUTTON_WIDGET") + .map((w) => { + return { + label: w.widgetName, + id: w.widgetName, + value: `"${w.widgetName}"`, + }; + }); +} function getQueryOptionsWithChildren( options: TreeDropdownOption[], queries: ActionDataState, @@ -1116,6 +1186,7 @@ function useQueryOptionTree() { export function ActionCreator(props: ActionCreatorProps) { const apiOptionTree = useApiOptionTree(); + const widgetOptionTree = useWidgetOptionTree(); const queryOptionTree = useQueryOptionTree(); const modalDropdownList = useModalDropdownList(); const pageDropdownOptions = useSelector(getPageDropdownOptions); @@ -1128,6 +1199,7 @@ export function ActionCreator(props: ActionCreatorProps) { isValid={props.isValid} validationMessage={props.validationMessage} apiOptionTree={apiOptionTree} + widgetOptionTree={widgetOptionTree} queryOptionTree={queryOptionTree} modalDropdownList={modalDropdownList} pageDropdownOptions={pageDropdownOptions} diff --git a/app/client/src/sagas/ActionExecutionSagas.ts b/app/client/src/sagas/ActionExecutionSagas.ts index 41cc91aa58..6d55b51ddf 100644 --- a/app/client/src/sagas/ActionExecutionSagas.ts +++ b/app/client/src/sagas/ActionExecutionSagas.ts @@ -89,6 +89,11 @@ import copy from "copy-to-clipboard"; import { EMPTY_RESPONSE } from "../components/editorComponents/ApiResponseView"; import localStorage from "utils/localStorage"; +import { getWidgetByName } from "./selectors"; +import { + resetChildrenMetaProperty, + resetWidgetMetaProperty, +} from "actions/metaActions"; export enum NavigationTargetType { SAME_WINDOW = "SAME_WINDOW", @@ -227,6 +232,30 @@ function* copySaga( } } +function* resetWidgetMetaByNameRecursiveSaga( + payload: { widgetName: string; resetChildren: boolean }, + event: ExecuteActionPayloadEvent, +) { + const fail = (msg: string) => { + console.error(msg); + if (event.callback) event.callback({ success: false }); + }; + if (typeof payload.widgetName !== "string") { + return fail("widgetName needs to be a string"); + } + + const widget = yield select(getWidgetByName, payload.widgetName); + if (!widget) { + return fail(`widget ${payload.widgetName} not found`); + } + + yield put(resetWidgetMetaProperty(widget.widgetId)); + if (payload.resetChildren) { + yield put(resetChildrenMetaProperty(widget.widgetId)); + } + if (event.callback) event.callback({ success: true }); +} + function* showAlertSaga( payload: { message: string; style?: TypeOptions }, event: ExecuteActionPayloadEvent, @@ -543,6 +572,9 @@ function* executeActionTriggers( case "COPY_TO_CLIPBOARD": yield call(copySaga, trigger.payload, event); break; + case "RESET_WIDGET_META_RECURSIVE_BY_NAME": + yield call(resetWidgetMetaByNameRecursiveSaga, trigger.payload, event); + break; default: log.error("Trigger type unknown", trigger.type); } diff --git a/app/client/src/utils/autocomplete/EntityDefinitions.ts b/app/client/src/utils/autocomplete/EntityDefinitions.ts index c03dd637b2..11384b5e31 100644 --- a/app/client/src/utils/autocomplete/EntityDefinitions.ts +++ b/app/client/src/utils/autocomplete/EntityDefinitions.ts @@ -284,4 +284,8 @@ export const GLOBAL_FUNCTIONS = { "!doc": "Copy text to clipboard", "!type": "fn(data: string, options: object) -> void", }, + resetWidget: { + "!doc": "Reset widget values", + "!type": "fn(widgetName: string, resetChildren: boolean) -> void", + }, }; diff --git a/app/client/src/workers/evaluationUtils.ts b/app/client/src/workers/evaluationUtils.ts index 367fdec08c..2d598907ed 100644 --- a/app/client/src/workers/evaluationUtils.ts +++ b/app/client/src/workers/evaluationUtils.ts @@ -462,5 +462,16 @@ export const addFunctions = (dataTree: Readonly): DataTree => { }; withFunction.actionPaths.push("copyToClipboard"); + withFunction.resetWidget = function( + widgetName: string, + resetChildren = false, + ) { + return { + type: "RESET_WIDGET_META_RECURSIVE_BY_NAME", + payload: { widgetName, resetChildren }, + }; + }; + withFunction.actionPaths.push("resetWidget"); + return withFunction; };