From f0a57e8aabc9ccbacd59cb27d3eccb261c245dd8 Mon Sep 17 00:00:00 2001 From: Nikhil Nandagopal Date: Mon, 11 Nov 2019 11:34:42 +0000 Subject: [PATCH 1/3] Master --- .../src/mockResponses/PropertyPaneConfigResponse.tsx | 8 +++++++- app/client/src/sagas/ActionSagas.ts | 2 +- app/client/src/widgets/RadioGroupWidget.tsx | 2 +- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/app/client/src/mockResponses/PropertyPaneConfigResponse.tsx b/app/client/src/mockResponses/PropertyPaneConfigResponse.tsx index 38aadde87c..08e4735b98 100644 --- a/app/client/src/mockResponses/PropertyPaneConfigResponse.tsx +++ b/app/client/src/mockResponses/PropertyPaneConfigResponse.tsx @@ -327,7 +327,7 @@ const PropertyPaneConfigResponse: PropertyPaneConfigState = { { id: "9.7", propertyName: "isVisible", - label: "Visibile", + label: "Visible", controlType: "SWITCH", }, { @@ -392,6 +392,12 @@ const PropertyPaneConfigResponse: PropertyPaneConfigState = { label: "Visible", controlType: "SWITCH", }, + { + id: "11.6", + propertyName: "tableData", + label: "Enter data array", + controlType: "INPUT_TEXT", + }, ], }, { diff --git a/app/client/src/sagas/ActionSagas.ts b/app/client/src/sagas/ActionSagas.ts index 20856b3ec3..3f9789be6d 100644 --- a/app/client/src/sagas/ActionSagas.ts +++ b/app/client/src/sagas/ActionSagas.ts @@ -50,7 +50,7 @@ export function* evaluateJSONPathSaga(path: string): any { return getDynamicBoundValue(dataTree, path); } -export function* executeAPIQueryActionSaga(apiAction: ActionPayload) { +export function* executeAPIQueryActionSaga(apiAction: { actionId: string }) { const api: PageAction = yield select(getAction, apiAction.actionId); const executeActionRequest: ExecuteActionRequest = { diff --git a/app/client/src/widgets/RadioGroupWidget.tsx b/app/client/src/widgets/RadioGroupWidget.tsx index 503e894223..ccf0e9de80 100644 --- a/app/client/src/widgets/RadioGroupWidget.tsx +++ b/app/client/src/widgets/RadioGroupWidget.tsx @@ -1,4 +1,4 @@ -import * as React from "react"; +import React from "react"; import BaseWidget, { WidgetProps, WidgetState } from "./BaseWidget"; import { WidgetType } from "../constants/WidgetConstants"; import RadioGroupComponent from "../components/designSystems/blueprint/RadioGroupComponent"; From 93c1b790d019afb4cca1c9ff01271da27b816d2d Mon Sep 17 00:00:00 2001 From: Satbir Singh Date: Tue, 12 Nov 2019 05:22:32 +0000 Subject: [PATCH 2/3] Added functionality to execute actions on successful call or on error of an action. --- .../propertyControls/ActionSelector.tsx | 256 ++++++++++++++++-- app/client/src/constants/ActionConstants.tsx | 12 +- app/client/src/sagas/ActionSagas.ts | 41 ++- 3 files changed, 261 insertions(+), 48 deletions(-) diff --git a/app/client/src/components/propertyControls/ActionSelector.tsx b/app/client/src/components/propertyControls/ActionSelector.tsx index 6992664b1c..17f4c121e4 100644 --- a/app/client/src/components/propertyControls/ActionSelector.tsx +++ b/app/client/src/components/propertyControls/ActionSelector.tsx @@ -14,34 +14,91 @@ import { connect } from "react-redux"; import { AppState } from "../../reducers"; import { ActionDataState } from "../../reducers/entityReducers/actionsReducer"; -const DEFAULT_ACTION_TYPE = "Select Action Type"; +const DEFAULT_ACTION_TYPE = "Select Action Type" as ActionType; +const DEFAULT_ACTION_LABEL = "Select Action"; +enum ACTION_RESOLUTION_TYPE { + SUCCESS, + ERROR, +} + +function getActions( + actionPayloads: ActionPayload[] | undefined, +): { + action: ActionPayload | undefined; + onSuccessAction: ActionPayload | undefined; + onErrorAction: ActionPayload | undefined; +} { + let action: ActionPayload | undefined = actionPayloads && actionPayloads[0]; + let onSuccessAction: ActionPayload | undefined = + action && action.onSuccess && action.onSuccess[0]; + let onErrorAction: ActionPayload | undefined = + action && action.onError && action.onError[0]; + return { + action, + onSuccessAction, + onErrorAction, + }; +} class ActionSelectorControl extends BaseControl< ControlProps & ActionDataState > { - getSelectionActionType(): string { - const selectedActionTypeValue = - this.props.propertyValue && - this.props.propertyValue[0] && - this.props.propertyValue[0].actionType; + getSelectionActionType(type: ACTION_RESOLUTION_TYPE | string): ActionType { + let selectedActionTypeValue: ActionType | undefined; + const { action, onSuccessAction, onErrorAction } = getActions( + this.props.propertyValue, + ); + + switch (type) { + case this.props.propertyName: + selectedActionTypeValue = action && action.actionType; + break; + case ACTION_RESOLUTION_TYPE.SUCCESS: + selectedActionTypeValue = onSuccessAction && onSuccessAction.actionType; + break; + case ACTION_RESOLUTION_TYPE.ERROR: + selectedActionTypeValue = onErrorAction && onErrorAction.actionType; + break; + default: + break; + } const foundActionType = PropertyPaneActionDropdownOptions.find( actionType => actionType.value === selectedActionTypeValue, ); - return foundActionType ? foundActionType.label : DEFAULT_ACTION_TYPE; + return foundActionType + ? (foundActionType.label as ActionType) + : DEFAULT_ACTION_TYPE; } - getSelectionActionLabel(allActions: DropdownOption[]): string { - const selectedActionId = - this.props.propertyValue && - this.props.propertyValue[0] && - this.props.propertyValue[0].actionId; + getSelectionActionLabel( + type: ACTION_RESOLUTION_TYPE | string, + allActions: DropdownOption[], + ): string { + let selectedActionId: string | undefined = ""; + const { action, onSuccessAction, onErrorAction } = getActions( + this.props.propertyValue, + ); + + switch (type) { + case this.props.propertyName: + selectedActionId = action && action.actionId; + break; + case ACTION_RESOLUTION_TYPE.SUCCESS: + selectedActionId = onSuccessAction && onSuccessAction.actionId; + break; + case ACTION_RESOLUTION_TYPE.ERROR: + selectedActionId = onErrorAction && onErrorAction.actionId; + break; + default: + break; + } const foundAction = allActions.find( action => action.value === selectedActionId, ); - return foundAction ? foundAction.label : "Select Action"; + return foundAction ? foundAction.label : DEFAULT_ACTION_LABEL; } render() { const actionTypeOptions: DropdownOption[] = PropertyPaneActionDropdownOptions; @@ -51,16 +108,98 @@ class ActionSelectorControl extends BaseControl< value: action.id, }; }); - const selectedActionType = this.getSelectionActionType(); - const selectedActionLabel = this.getSelectionActionLabel(allActions); + const selectedActionType = this.getSelectionActionType( + this.props.propertyName, + ); + const selectedActionLabel = this.getSelectionActionLabel( + this.props.propertyName, + allActions, + ); + + const selectedSuccessActionType = this.getSelectionActionType( + ACTION_RESOLUTION_TYPE.SUCCESS, + ); + const selectedSuccessActionLabel = this.getSelectionActionLabel( + ACTION_RESOLUTION_TYPE.SUCCESS, + allActions, + ); + + const selectedErrorActionType = this.getSelectionActionType( + ACTION_RESOLUTION_TYPE.ERROR, + ); + const selectedErrorActionLabel = this.getSelectionActionLabel( + ACTION_RESOLUTION_TYPE.ERROR, + allActions, + ); return ( - + {this.renderActionSelector( + allActions, + actionTypeOptions, + selectedActionType, + selectedActionLabel, + this.props.propertyName, + this.props.propertyName, + )} + {selectedActionLabel !== DEFAULT_ACTION_LABEL && + this.renderActionSelector( + allActions, + actionTypeOptions, + selectedSuccessActionType, + selectedSuccessActionLabel, + "On Success", + ACTION_RESOLUTION_TYPE.SUCCESS, + )} + {selectedActionLabel !== DEFAULT_ACTION_LABEL && + this.renderActionSelector( + allActions, + actionTypeOptions, + selectedErrorActionType, + selectedErrorActionLabel, + "On Error", + ACTION_RESOLUTION_TYPE.ERROR, + )} + + ); + } + + renderActionSelector( + allActions: DropdownOption[], + actionTypeOptions: DropdownOption[], + selectedActionType: ActionType, + selectedActionLabel: string, + label: string, + actionResolutionType: ACTION_RESOLUTION_TYPE | string, + ) { + let onTypeSelect = this.onActionTypeSelect; + switch (actionResolutionType) { + case ACTION_RESOLUTION_TYPE.SUCCESS: + onTypeSelect = this.onSuccessActionTypeSelect; + break; + case ACTION_RESOLUTION_TYPE.ERROR: + onTypeSelect = this.onErrorActionTypeSelect; + break; + } + + let onActionSelect = this.onActionSelect; + switch (actionResolutionType) { + case ACTION_RESOLUTION_TYPE.SUCCESS: + onTypeSelect = this.onSuccessActionSelect; + break; + case ACTION_RESOLUTION_TYPE.ERROR: + onTypeSelect = this.onErrorActionSelect; + break; + } + return ( +
+
+ +
} >
); } - - onTypeSelect = (item: DropdownOption): void => { + onActionTypeSelect = (item: DropdownOption) => { const actionPayloads: ActionPayload[] = this.props.propertyValue ? this.props.propertyValue.slice() : []; - const actionPayload = actionPayloads[0]; - if (actionPayload) { + let actionPayload = actionPayloads[0]; + + if (actionPayload && actionPayload.actionType !== item.value) { actionPayload.actionId = ""; + actionPayload.onError = undefined; + actionPayload.onSuccess = undefined; actionPayload.actionType = item.value as ActionType; } else { const actionPayload = { actionType: item.value } as ActionPayload; @@ -95,16 +236,83 @@ class ActionSelectorControl extends BaseControl< } this.updateProperty(this.props.propertyName, actionPayloads); }; + onSuccessActionTypeSelect = (item: DropdownOption) => { + const actionPayloads: ActionPayload[] = this.props.propertyValue + ? this.props.propertyValue.slice() + : []; + let actionPayload = actionPayloads[0]; + + if (actionPayload) { + const successActionPayloads: ActionPayload[] = + actionPayload.onSuccess || []; + let successActionPayload = successActionPayloads[0]; + if (successActionPayload) { + successActionPayload.actionId = ""; + successActionPayload.actionType = item.value as ActionType; + } else { + const successActionPayload = { + actionType: item.value, + } as ActionPayload; + successActionPayloads.push(successActionPayload); + } + actionPayload.onSuccess = successActionPayloads; + } + this.updateProperty(this.props.propertyName, actionPayloads); + }; + onErrorActionTypeSelect = (item: DropdownOption) => { + const actionPayloads: ActionPayload[] = this.props.propertyValue + ? this.props.propertyValue.slice() + : []; + let actionPayload = actionPayloads[0]; + + if (actionPayload) { + const errorActionPayloads: ActionPayload[] = actionPayload.onError || []; + let errorActionPayload = errorActionPayloads[0]; + if (errorActionPayload) { + errorActionPayload.actionId = ""; + errorActionPayload.actionType = item.value as ActionType; + } else { + const errorActionPayload = { + actionType: item.value, + } as ActionPayload; + errorActionPayloads.push(errorActionPayload); + } + actionPayload.onError = errorActionPayloads; + } + this.updateProperty(this.props.propertyName, actionPayloads); + }; onActionSelect = (item: DropdownOption): void => { const actionPayloads: ActionPayload[] = this.props.propertyValue ? this.props.propertyValue.slice() : []; const actionPayload = actionPayloads[0]; - actionPayload.actionId = item.value as ActionType; + actionPayload.actionId = item.value; this.updateProperty(this.props.propertyName, actionPayloads); }; + onSuccessActionSelect = (item: DropdownOption): void => { + const actionPayloads: ActionPayload[] = this.props.propertyValue + ? this.props.propertyValue.slice() + : []; + const actionPayload = actionPayloads[0]; + const successActionPayloads: ActionPayload[] = actionPayload.onSuccess as ActionPayload[]; + const successActionPayload = successActionPayloads[0]; + successActionPayload.actionId = item.value; + actionPayload.onSuccess = successActionPayloads; + this.updateProperty(this.props.propertyName, actionPayloads); + }; + onErrorActionSelect = (item: DropdownOption): void => { + const actionPayloads: ActionPayload[] = this.props.propertyValue + ? this.props.propertyValue.slice() + : []; + const actionPayload = actionPayloads[0]; + const errorActionPayloads: ActionPayload[] = actionPayload.onError as ActionPayload[]; + const errorActionPayload = errorActionPayloads[0]; + errorActionPayload.actionId = item.value; + actionPayload.onError = errorActionPayloads; + this.updateProperty(this.props.propertyName, actionPayloads); + }; renderItem = (option: DropdownOption, itemProps: IItemRendererProps) => { if (!itemProps.modifiers.matchesPredicate) { diff --git a/app/client/src/constants/ActionConstants.tsx b/app/client/src/constants/ActionConstants.tsx index e928c659db..9474567c66 100644 --- a/app/client/src/constants/ActionConstants.tsx +++ b/app/client/src/constants/ActionConstants.tsx @@ -22,16 +22,6 @@ export type ActionType = | "SET_VALUE" | "DOWNLOAD"; -export enum ActionType1 { - "API", - "QUERY", - "NAVIGATION", - "ALERT", - "JS_FUNCTION", - "SET_VALUE", - "DOWNLOAD", -} - export const PropertyPaneActionDropdownOptions: DropdownOption[] = [ { label: "Call API", value: "API" }, // { label: "Run Query", value: "QUERY" }, @@ -41,6 +31,8 @@ export interface ActionPayload { actionId: string; actionType: ActionType; contextParams: Record; + onSuccess?: ActionPayload[]; + onError?: ActionPayload[]; } export type NavigationType = "NEW_TAB" | "INLINE"; diff --git a/app/client/src/sagas/ActionSagas.ts b/app/client/src/sagas/ActionSagas.ts index 3f9789be6d..08a838e849 100644 --- a/app/client/src/sagas/ActionSagas.ts +++ b/app/client/src/sagas/ActionSagas.ts @@ -50,7 +50,7 @@ export function* evaluateJSONPathSaga(path: string): any { return getDynamicBoundValue(dataTree, path); } -export function* executeAPIQueryActionSaga(apiAction: { actionId: string }) { +export function* executeAPIQueryActionSaga(apiAction: ActionPayload) { const api: PageAction = yield select(getAction, apiAction.actionId); const executeActionRequest: ExecuteActionRequest = { @@ -80,6 +80,13 @@ export function* executeAPIQueryActionSaga(apiAction: { actionId: string }) { statusCode: response.responseMeta.error.code, ...response, }; + if (apiAction.onError) { + yield call(executeActionSaga, apiAction.onError); + } + } else { + if (apiAction.onSuccess) { + yield call(executeActionSaga, apiAction.onSuccess); + } } yield put({ type: ReduxActionTypes.EXECUTE_ACTION_SUCCESS, @@ -88,19 +95,25 @@ export function* executeAPIQueryActionSaga(apiAction: { actionId: string }) { return response; } -export function* executeActionSaga(action: ReduxAction) { +// TODO(satbir): Refact this to not make this recursive. +export function* executeActionSaga(actionPayloads: ActionPayload[]): any { + yield all( + _.map(actionPayloads, (actionPayload: ActionPayload) => { + switch (actionPayload.actionType) { + case "API": + return call(executeAPIQueryActionSaga, actionPayload); + case "QUERY": + return call(executeAPIQueryActionSaga, actionPayload); + default: + return undefined; + } + }), + ); +} + +export function* executeReduxActionSaga(action: ReduxAction) { if (!_.isNil(action.payload)) { - yield all( - _.map(action.payload, (actionPayload: ActionPayload) => { - switch (actionPayload.actionType) { - case "API": - return call(executeAPIQueryActionSaga, actionPayload); - case "QUERY": - return call(executeAPIQueryActionSaga, actionPayload); - } - return undefined; - }), - ); + yield call(executeActionSaga, action.payload); } } @@ -181,7 +194,7 @@ export function* deleteActionSaga(actionPayload: ReduxAction<{ id: string }>) { export function* watchActionSagas() { yield all([ takeEvery(ReduxActionTypes.FETCH_ACTIONS_INIT, fetchActionsSaga), - takeLatest(ReduxActionTypes.EXECUTE_ACTION, executeActionSaga), + takeLatest(ReduxActionTypes.EXECUTE_ACTION, executeReduxActionSaga), takeLatest(ReduxActionTypes.CREATE_ACTION_INIT, createActionSaga), takeLatest(ReduxActionTypes.UPDATE_ACTION_INIT, updateActionSaga), takeLatest(ReduxActionTypes.DELETE_ACTION_INIT, deleteActionSaga), From 20e829dceb29cb16139a2d9ba199140e52e8c434 Mon Sep 17 00:00:00 2001 From: Satbir Singh Date: Tue, 12 Nov 2019 06:03:46 +0000 Subject: [PATCH 3/3] Selected row data has been moved from selectedRow.data to selectedRow --- app/client/src/widgets/TableWidget.tsx | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/app/client/src/widgets/TableWidget.tsx b/app/client/src/widgets/TableWidget.tsx index e27779ec17..b6f339c737 100644 --- a/app/client/src/widgets/TableWidget.tsx +++ b/app/client/src/widgets/TableWidget.tsx @@ -57,13 +57,13 @@ class TableWidget extends BaseWidget { data={tableData} maxHeight={height} selectedRowIndex={ - this.props.selectedRow && this.props.selectedRow.index + this.props.selectedRow && this.props.selectedRow.rowIndex } onRowClick={(rowData: object, index: number) => { const { widgetId, onRowSelected } = this.props; super.updateWidgetProperty(widgetId, "selectedRow", { - data: rowData, - index: index, + ...rowData, + rowIndex: index, }); super.executeAction(onRowSelected); }} @@ -84,6 +84,10 @@ export interface TableAction extends ActionPayload { actionName: string; } +interface RowData { + rowIndex: number; +} + export interface TableWidgetProps extends WidgetProps { nextPageKey?: string; prevPageKey?: string; @@ -92,10 +96,7 @@ export interface TableWidgetProps extends WidgetProps { recordActions?: TableAction[]; onPageChange?: ActionPayload[]; onRowSelected?: ActionPayload[]; - selectedRow?: { - data: object; - index: number; - }; + selectedRow?: object & RowData; } export default TableWidget;