From 1bd5a8c5e703e5429215eed905a08bc8ba15fbde Mon Sep 17 00:00:00 2001 From: Hetu Nandu Date: Fri, 13 Mar 2020 01:57:39 +0530 Subject: [PATCH 1/8] fix page load actions issues --- app/client/src/api/ActionAPI.tsx | 8 +- .../src/pages/Editor/APIEditor/index.tsx | 4 +- app/client/src/sagas/ActionSagas.ts | 82 ++++++++++++------- 3 files changed, 59 insertions(+), 35 deletions(-) diff --git a/app/client/src/api/ActionAPI.tsx b/app/client/src/api/ActionAPI.tsx index 3e22563a25..bbbba58866 100644 --- a/app/client/src/api/ActionAPI.tsx +++ b/app/client/src/api/ActionAPI.tsx @@ -62,12 +62,12 @@ export interface RestAction { cacheResponse?: string; } -export type PaginationField = "PREV" | "NEXT" | undefined; +export type PaginationField = "PREV" | "NEXT"; export interface ExecuteActionRequest extends APIRequest { action: Pick | Omit; params?: Property[]; - paginationField: PaginationField; + paginationField?: PaginationField; } export interface ExecuteActionResponse extends ApiResponse { @@ -80,7 +80,7 @@ export interface ActionApiResponse { data: { body: object; headers: Record; - statusCode: string | number; + statusCode: string; }; clientMeta: { duration: string; @@ -91,7 +91,7 @@ export interface ActionApiResponse { export interface ActionResponse { body: object; headers: Record; - statusCode: string | number; + statusCode: string; duration: string; size: string; } diff --git a/app/client/src/pages/Editor/APIEditor/index.tsx b/app/client/src/pages/Editor/APIEditor/index.tsx index a4e4a9a0ef..7a92c19b79 100644 --- a/app/client/src/pages/Editor/APIEditor/index.tsx +++ b/app/client/src/pages/Editor/APIEditor/index.tsx @@ -35,7 +35,7 @@ interface ReduxStateProps { interface ReduxActionProps { submitForm: (name: string) => void; createAction: (values: RestAction) => void; - runAction: (id: string, paginationField: PaginationField) => void; + runAction: (id: string, paginationField?: PaginationField) => void; deleteAction: (id: string, name: string) => void; updateAction: (data: RestAction) => void; } @@ -158,7 +158,7 @@ const mapStateToProps = (state: AppState): ReduxStateProps => ({ const mapDispatchToProps = (dispatch: any): ReduxActionProps => ({ submitForm: (name: string) => dispatch(submit(name)), createAction: (action: RestAction) => dispatch(createActionRequest(action)), - runAction: (id: string, paginationField: PaginationField) => + runAction: (id: string, paginationField?: PaginationField) => dispatch(runApiAction(id, paginationField)), deleteAction: (id: string, name: string) => dispatch(deleteAction({ id, name })), diff --git a/app/client/src/sagas/ActionSagas.ts b/app/client/src/sagas/ActionSagas.ts index 84d9e3dc8a..054dab3e0b 100644 --- a/app/client/src/sagas/ActionSagas.ts +++ b/app/client/src/sagas/ActionSagas.ts @@ -38,12 +38,12 @@ import { deleteActionSuccess, executeApiActionRequest, executeApiActionSuccess, + fetchActionsForPageSuccess, FetchActionsPayload, moveActionError, moveActionSuccess, runApiAction, updateActionSuccess, - fetchActionsForPageSuccess, } from "actions/actionActions"; import { getDynamicBindings, @@ -82,11 +82,20 @@ export const getAction = ( return action ? action.config : undefined; }; -const createActionResponse = (response: ActionApiResponse): ActionResponse => ({ +const createActionSuccessResponse = ( + response: ActionApiResponse, +): ActionResponse => ({ ...response.data, ...response.clientMeta, }); +const isErrorResponse = (response: ActionApiResponse) => { + return ( + (response.responseMeta && response.responseMeta.error) || + !/2\d\d/.test(response.data.statusCode) + ); +}; + function getCurrentPageNameByActionId( state: AppState, actionId: string, @@ -102,8 +111,7 @@ function getPageNameByPageId(state: AppState, pageId: string): string { const page = state.entities.pageList.pages.find( page => page.pageId === pageId, ); - const pageName = page ? page.pageName : ""; - return pageName; + return page ? page.pageName : ""; } const createActionErrorResponse = ( @@ -111,7 +119,7 @@ const createActionErrorResponse = ( ): ActionResponse => ({ body: response.responseMeta.error || { error: "Error" }, statusCode: response.responseMeta.error - ? response.responseMeta.error.code + ? response.responseMeta.error.code.toString() : "Error", headers: {}, duration: "0", @@ -157,7 +165,7 @@ export function* getActionParams(jsonPathKeys: string[] | undefined) { // }); // } -export function* executeAPIQueryActionSaga( +export function* executeActionSaga( apiAction: RunActionPayload, event: ExecuteActionPayloadEvent, ) { @@ -180,9 +188,8 @@ export function* executeAPIQueryActionSaga( const response: ActionApiResponse = yield ActionAPI.executeAction( executeActionRequest, ); - let payload = createActionResponse(response); - if (response.responseMeta && response.responseMeta.error) { - payload = createActionErrorResponse(response); + if (isErrorResponse(response)) { + const payload = createActionErrorResponse(response); if (onError) { yield put( executeAction({ @@ -206,6 +213,7 @@ export function* executeAPIQueryActionSaga( }), ); } else { + const payload = createActionSuccessResponse(response); yield put( executeApiActionSuccess({ id: apiAction.actionId, @@ -281,7 +289,7 @@ export function* executeActionTriggers( ) { switch (trigger.type) { case "RUN_ACTION": - yield call(executeAPIQueryActionSaga, trigger.payload, event); + yield call(executeActionSaga, trigger.payload, event); break; case "NAVIGATE_TO": AnalyticsUtil.logEvent("NAVIGATE", { @@ -513,7 +521,7 @@ export function* runApiActionSaga( params, paginationField, }); - let payload = createActionResponse(response); + let payload = createActionSuccessResponse(response); if (response.responseMeta && response.responseMeta.error) { payload = createActionErrorResponse(response); } @@ -541,27 +549,43 @@ export function* runApiActionSaga( } } +function* executePageLoadAction(pageAction: PageAction) { + const params: Property[] = yield call( + getActionParams, + pageAction.jsonPathKeys, + ); + const executeActionRequest: ExecuteActionRequest = { + action: { id: pageAction.id }, + params, + }; + const response: ActionApiResponse = yield ActionAPI.executeAction( + executeActionRequest, + ); + + if (isErrorResponse(response)) { + yield put( + executeActionError({ + actionId: pageAction.id, + error: response.responseMeta.error, + }), + ); + } else { + const payload = createActionSuccessResponse(response); + yield put( + executeApiActionSuccess({ + id: pageAction.id, + response: payload, + }), + ); + } +} + function* executePageLoadActionsSaga(action: ReduxAction) { const pageActions = action.payload; - const actionPayloads: RunActionPayload[][] = pageActions.map(actionSet => - actionSet.map(action => ({ - actionId: action.id, - onSuccess: "", - onError: "", - })), - ); - for (const actionSet of actionPayloads) { + for (const actionSet of pageActions) { const apiResponses = yield select(getActionResponses); - const filteredSet = actionSet.filter( - action => !apiResponses[action.actionId], - ); - yield* yield all( - filteredSet.map(a => - call(executeAPIQueryActionSaga, a, { - type: EventType.ON_PAGE_LOAD, - }), - ), - ); + const filteredSet = actionSet.filter(action => !apiResponses[action.id]); + yield* yield all(filteredSet.map(a => call(executePageLoadAction, a))); } } From 3faf95b882822f6c25a5d8cb29e49fbf9d48fc17 Mon Sep 17 00:00:00 2001 From: Hetu Nandu Date: Fri, 13 Mar 2020 07:24:03 +0000 Subject: [PATCH 2/8] Fix issues with Form Widget --- .../appsmith/FilePickerComponent.tsx | 2 +- .../blueprint/CheckboxComponent.tsx | 8 +- .../blueprint/DatePickerComponent.tsx | 28 ++++- .../blueprint/DropdownComponent.tsx | 114 +++++++++--------- .../blueprint/InputComponent.tsx | 18 ++- .../blueprint/RadioGroupComponent.tsx | 15 ++- .../editorComponents/ErrorTooltip.tsx | 45 ++++--- app/client/src/jsExecution/RealmExecutor.ts | 2 +- .../mockResponses/WidgetConfigResponse.tsx | 2 - app/client/src/utils/Validators.ts | 8 +- app/client/src/widgets/CheckboxWidget.tsx | 30 ++++- app/client/src/widgets/ContainerWidget.tsx | 9 +- app/client/src/widgets/DropdownWidget.tsx | 36 ++++-- app/client/src/widgets/FilepickerWidget.tsx | 33 +++-- app/client/src/widgets/FormButtonWidget.tsx | 5 +- app/client/src/widgets/InputWidget.tsx | 13 +- app/client/src/widgets/RadioGroupWidget.tsx | 27 ++++- 17 files changed, 256 insertions(+), 139 deletions(-) diff --git a/app/client/src/components/designSystems/appsmith/FilePickerComponent.tsx b/app/client/src/components/designSystems/appsmith/FilePickerComponent.tsx index 0665f0d672..f46d59f5c2 100644 --- a/app/client/src/components/designSystems/appsmith/FilePickerComponent.tsx +++ b/app/client/src/components/designSystems/appsmith/FilePickerComponent.tsx @@ -32,7 +32,7 @@ class FilePickerComponent extends React.Component< diff --git a/app/client/src/components/designSystems/blueprint/CheckboxComponent.tsx b/app/client/src/components/designSystems/blueprint/CheckboxComponent.tsx index 44c7e7613c..d6238dc53a 100644 --- a/app/client/src/components/designSystems/blueprint/CheckboxComponent.tsx +++ b/app/client/src/components/designSystems/blueprint/CheckboxComponent.tsx @@ -26,22 +26,22 @@ class CheckboxComponent extends React.Component { className={ this.props.isLoading ? "bp3-skeleton" : Classes.RUNNING_TEXT } - defaultChecked={this.props.defaultCheckedState} onChange={this.onCheckChange} disabled={this.props.isDisabled} + checked={this.props.isChecked} /> ); } - onCheckChange = (event: React.ChangeEvent) => { - this.props.onCheckChange(event.target.value === "true"); + onCheckChange = () => { + this.props.onCheckChange(!this.props.isChecked); }; } export interface CheckboxComponentProps extends ComponentProps { label: string; - defaultCheckedState: boolean; + isChecked: boolean; onCheckChange: (isChecked: boolean) => void; isLoading: boolean; } diff --git a/app/client/src/components/designSystems/blueprint/DatePickerComponent.tsx b/app/client/src/components/designSystems/blueprint/DatePickerComponent.tsx index 1b983e774f..36b6e49b5b 100644 --- a/app/client/src/components/designSystems/blueprint/DatePickerComponent.tsx +++ b/app/client/src/components/designSystems/blueprint/DatePickerComponent.tsx @@ -8,15 +8,39 @@ import moment from "moment-timezone"; import "../../../../node_modules/@blueprintjs/datetime/lib/css/blueprint-datetime.css"; import { DatePickerType } from "widgets/DatePickerWidget"; import { WIDGET_PADDING } from "constants/WidgetConstants"; +import { Colors } from "constants/Colors"; const StyledControlGroup = styled(ControlGroup)` &&& { + .${Classes.INPUT} { + box-shadow: none; + border: 1px solid; + border-color: ${Colors.GEYSER_LIGHT}; + border-radius: ${props => props.theme.radii[1]}px; + width: 100%; + height: inherit; + align-items: center; + &:active { + border-color: ${Colors.HIT_GRAY}; + } + &:focus { + border-color: ${Colors.MYSTIC}; + } + } + .${Classes.INPUT_GROUP} { + display: block; + margin: 0; + } + .${Classes.CONTROL_GROUP} { + justify-content: flex-start; + } label { ${labelStyle} flex: 0 1 30%; + margin: 7px ${WIDGET_PADDING * 2}px 0 0; text-align: right; - margin: 0 ${WIDGET_PADDING * 2}px 0 0; - align-self: center; + align-self: flex-start; + max-width: calc(30% - ${WIDGET_PADDING}px); } } `; diff --git a/app/client/src/components/designSystems/blueprint/DropdownComponent.tsx b/app/client/src/components/designSystems/blueprint/DropdownComponent.tsx index 6a18effe4f..70644d9ae1 100644 --- a/app/client/src/components/designSystems/blueprint/DropdownComponent.tsx +++ b/app/client/src/components/designSystems/blueprint/DropdownComponent.tsx @@ -21,6 +21,7 @@ import _ from "lodash"; import { WIDGET_PADDING } from "constants/WidgetConstants"; import "../../../../node_modules/@blueprintjs/select/lib/css/blueprint-select.css"; import styled, { + createGlobalStyle, labelStyle, BlueprintCSSTransform, BlueprintInputTransform, @@ -79,53 +80,61 @@ const StyledControlGroup = styled(ControlGroup)<{ haslabel: string }>` } `; -const DropdownContainer = styled.div` - ${BlueprintCSSTransform} - &&&& .${Classes.MENU_ITEM} { - border-radius: ${props => props.theme.radii[1]}px; - &:hover{ - background: ${Colors.POLAR}; - } - &.${Classes.ACTIVE} { - background: ${Colors.POLAR}; - color: ${props => props.theme.colors.textDefault}; - position:relative; - &.single-select{ - &:before{ - left: 0; - top: -2px; - position: absolute; - content: ""; - background: ${props => props.theme.colors.primary}; - border-radius: 4px 0 0 4px; - width: 4px; - height:100%; - } - } - } -} - && .${Classes.POPOVER} { +const DropdownStyles = createGlobalStyle` + .select-popover-wrapper { width: 100%; border-radius: ${props => props.theme.radii[1]}px; box-shadow: 0px 2px 4px rgba(67, 70, 74, 0.14); padding: ${props => props.theme.spaces[3]}px; background: white; - } - - && .${Classes.POPOVER_WRAPPER} { - position:relative; - .${Classes.OVERLAY} { - position: absolute; - .${Classes.TRANSITION_CONTAINER} { - width: 100%; + && .${Classes.MENU} { + max-width: 100%; + max-height: auto; + } + &&&& .${Classes.MENU_ITEM} { + border-radius: ${props => props.theme.radii[1]}px; + &:hover{ + background: ${Colors.POLAR}; + } + &.${Classes.ACTIVE} { + background: ${Colors.POLAR}; + color: ${props => props.theme.colors.textDefault}; + position:relative; + &.single-select{ + &:before{ + left: 0; + top: -2px; + position: absolute; + content: ""; + background: ${props => props.theme.colors.primary}; + border-radius: 4px 0 0 4px; + width: 4px; + height:100%; + } + } + } + .${Classes.CONTROL} .${Classes.CONTROL_INDICATOR} { + background: white; + box-shadow: none; + border-width: 2px; + border-style: solid; + border-color: ${Colors.GEYSER}; + &::before { + width: auto; + height: 1em; + }& + } + .${Classes.CONTROL} input:checked ~ .${Classes.CONTROL_INDICATOR} { + background: ${props => props.theme.colors.primary}; + color: ${props => props.theme.colors.textOnDarkBG}; + border-color: ${props => props.theme.colors.primary}; } } } - && .${Classes.MENU} { - max-width: 100%; - max-height: auto; - } - width: 100%; +`; + +const DropdownContainer = styled.div` + ${BlueprintCSSTransform} `; const StyledMultiDropDown = styled(MultiDropDown)` @@ -169,24 +178,6 @@ const StyledMultiDropDown = styled(MultiDropDown)` } } } - &&&& { - .${Classes.CONTROL} .${Classes.CONTROL_INDICATOR} { - background: white; - box-shadow: none; - border-width: 2px; - border-style: solid; - border-color: ${Colors.GEYSER}; - &::before { - width: auto; - height: 1em; - } - } - .${Classes.CONTROL} input:checked ~ .${Classes.CONTROL_INDICATOR} { - background: ${props => props.theme.colors.primary}; - color: ${props => props.theme.colors.textOnDarkBG}; - border-color: ${props => props.theme.colors.primary}; - } - } `; const StyledCheckbox = styled(Checkbox)` @@ -204,6 +195,7 @@ class DropDownComponent extends React.Component { : []; return ( + { onItemSelect={this.onItemSelect} popoverProps={{ minimal: true, - usePortal: false, + usePortal: true, + popoverClassName: "select-popover-wrapper", }} >