fix: API Body format focus retention (#37150)
## Description - Use Focus retention to store user context of the POST body format of APIs - Update component to use hooks instead of redux connect hoc - remove need of passing Action ID in form data as it is redundant for focus retention states Fixes #36984 ## Automation /ok-to-test tags="@tag.Datasource, @tag.IDE" ### 🔍 Cypress test results <!-- This is an auto-generated comment: Cypress test results --> > [!TIP] > 🟢 🟢 🟢 All cypress tests have passed! 🎉 🎉 🎉 > Workflow run: <https://github.com/appsmithorg/appsmith/actions/runs/11609483418> > Commit: a3a1a59101773372a65ded41ea1cce3cd83ffd5f > <a href="https://internal.appsmith.com/app/cypress-dashboard/rundetails-65890b3c81d7400d08fa9ee5?branch=master&workflowId=11609483418&attempt=1" target="_blank">Cypress dashboard</a>. > Tags: `@tag.Datasource, @tag.IDE` > Spec: > <hr>Thu, 31 Oct 2024 10:31:09 UTC <!-- end of auto-generated comment: Cypress test results --> ## Communication Should the DevRel and Marketing teams inform users about this change? - [ ] Yes - [ ] No <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit ## Release Notes - **New Features** - Introduced a streamlined method for managing API body content types and form data. - Added new action creator `setExtraFormData` for enhanced form data handling. - **Improvements** - Simplified the `PostBodyData` component by utilizing React hooks for state management. - Updated selectors and reducers to support a flatter structure for form data, improving clarity and maintainability. - **Updates** - Enhanced focus elements configuration to better manage plugin action form data within the application. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
parent
951be4a34e
commit
9a9a7c4b17
|
|
@ -1,15 +1,12 @@
|
|||
import React from "react";
|
||||
import { connect } from "react-redux";
|
||||
import React, { useCallback } from "react";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import styled from "styled-components";
|
||||
import { formValueSelector } from "redux-form";
|
||||
import {
|
||||
POST_BODY_FORMAT_OPTIONS,
|
||||
POST_BODY_FORMAT_TITLES,
|
||||
} from "../../../../constants/CommonApiConstants";
|
||||
import { API_EDITOR_FORM_NAME } from "ee/constants/forms";
|
||||
import KeyValueFieldArray from "components/editorComponents/form/fields/KeyValueFieldArray";
|
||||
import DynamicTextField from "components/editorComponents/form/fields/DynamicTextField";
|
||||
import type { AppState } from "ee/reducers";
|
||||
import FIELD_VALUES from "constants/FieldExpectedValue";
|
||||
import type { EditorTheme } from "components/editorComponents/CodeEditor/EditorConfig";
|
||||
import {
|
||||
|
|
@ -61,11 +58,8 @@ const NoBodyMessage = styled.div`
|
|||
`;
|
||||
|
||||
interface PostDataProps {
|
||||
displayFormat: { label: string; value: string };
|
||||
dataTreePath: string;
|
||||
theme?: EditorTheme;
|
||||
apiId: string;
|
||||
updateBodyContentType: (contentType: string, apiId: string) => void;
|
||||
}
|
||||
|
||||
type Props = PostDataProps;
|
||||
|
|
@ -77,9 +71,13 @@ const expectedPostBody: CodeEditorExpected = {
|
|||
};
|
||||
|
||||
function PostBodyData(props: Props) {
|
||||
const [selectedTab, setSelectedTab] = React.useState(
|
||||
props.displayFormat?.value,
|
||||
);
|
||||
const postBodyFormat = useSelector(getPostBodyFormat);
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const updateBodyContentType = useCallback((tab: string) => {
|
||||
dispatch(updatePostBodyContentType(tab));
|
||||
}, []);
|
||||
|
||||
const { dataTreePath, theme } = props;
|
||||
|
||||
const tabComponentsMap = (key: string) => {
|
||||
|
|
@ -172,18 +170,13 @@ function PostBodyData(props: Props) {
|
|||
value: el.key,
|
||||
}));
|
||||
|
||||
const postBodyDataOnChangeFn = (key: string) => {
|
||||
setSelectedTab(key);
|
||||
props?.updateBodyContentType(key, props.apiId);
|
||||
};
|
||||
|
||||
return (
|
||||
<PostBodyContainer>
|
||||
<Select
|
||||
data-testid="t--api-body-tab-switch"
|
||||
defaultValue={selectedTab}
|
||||
onSelect={(value) => postBodyDataOnChangeFn(value)}
|
||||
value={selectedTab}
|
||||
defaultValue={postBodyFormat.value}
|
||||
onSelect={updateBodyContentType}
|
||||
value={postBodyFormat.value}
|
||||
>
|
||||
{options.map((option) => (
|
||||
<Option key={option.value} value={option.value}>
|
||||
|
|
@ -191,31 +184,9 @@ function PostBodyData(props: Props) {
|
|||
</Option>
|
||||
))}
|
||||
</Select>
|
||||
{tabComponentsMap(selectedTab)}
|
||||
{tabComponentsMap(postBodyFormat.value)}
|
||||
</PostBodyContainer>
|
||||
);
|
||||
}
|
||||
|
||||
const selector = formValueSelector(API_EDITOR_FORM_NAME);
|
||||
|
||||
// TODO: Fix this the next time the file is edited
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const mapDispatchToProps = (dispatch: any) => ({
|
||||
updateBodyContentType: (contentType: string, apiId: string) =>
|
||||
dispatch(updatePostBodyContentType(contentType, apiId)),
|
||||
});
|
||||
|
||||
export default connect((state: AppState) => {
|
||||
const apiId = selector(state, "id");
|
||||
const postBodyFormat = getPostBodyFormat(state, apiId);
|
||||
// Defaults to NONE when format is not set
|
||||
const displayFormat = postBodyFormat || {
|
||||
label: POST_BODY_FORMAT_OPTIONS.NONE,
|
||||
value: POST_BODY_FORMAT_OPTIONS.NONE,
|
||||
};
|
||||
|
||||
return {
|
||||
displayFormat,
|
||||
apiId,
|
||||
};
|
||||
}, mapDispatchToProps)(PostBodyData);
|
||||
export default PostBodyData;
|
||||
|
|
|
|||
|
|
@ -28,10 +28,16 @@ export const openPluginActionSettings = (payload: boolean) => ({
|
|||
|
||||
export const updatePostBodyContentType = (
|
||||
title: string,
|
||||
apiId: string,
|
||||
): ReduxAction<{ title: string; apiId: string }> => ({
|
||||
): ReduxAction<{ title: string }> => ({
|
||||
type: ReduxActionTypes.UPDATE_API_ACTION_BODY_CONTENT_TYPE,
|
||||
payload: { title, apiId },
|
||||
payload: { title },
|
||||
});
|
||||
|
||||
export const setExtraFormData = (
|
||||
values: Record<string, { label: string; value: string }>,
|
||||
) => ({
|
||||
type: ReduxActionTypes.SET_EXTRA_FORMDATA,
|
||||
payload: { values },
|
||||
});
|
||||
|
||||
export const changeApi = (
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import type { AppState } from "ee/reducers";
|
|||
import { createSelector } from "reselect";
|
||||
|
||||
import { POST_BODY_FORM_DATA_KEY } from "./constants";
|
||||
import { POST_BODY_FORMAT_OPTIONS } from "../constants/CommonApiConstants";
|
||||
|
||||
export const getActionEditorSavingMap = (state: AppState) =>
|
||||
state.ui.pluginActionEditor.isSaving;
|
||||
|
|
@ -37,19 +38,25 @@ export const isActionDeleting = (id: string) =>
|
|||
(deletingMap) => id in deletingMap && deletingMap[id],
|
||||
);
|
||||
|
||||
type GetFormData = (
|
||||
state: AppState,
|
||||
id: string,
|
||||
) => { label: string; value: string } | undefined;
|
||||
export const getFormData = (state: AppState) =>
|
||||
state.ui.pluginActionEditor.formData;
|
||||
|
||||
export const getPostBodyFormat: GetFormData = (state, id) => {
|
||||
const formData = state.ui.pluginActionEditor.formData;
|
||||
type GetFormPostBodyFormat = (state: AppState) => {
|
||||
label: string;
|
||||
value: string;
|
||||
};
|
||||
|
||||
if (id in formData) {
|
||||
return formData[id][POST_BODY_FORM_DATA_KEY];
|
||||
export const getPostBodyFormat: GetFormPostBodyFormat = (state) => {
|
||||
const formData = getFormData(state);
|
||||
|
||||
if (POST_BODY_FORM_DATA_KEY in formData) {
|
||||
return formData[POST_BODY_FORM_DATA_KEY];
|
||||
}
|
||||
|
||||
return undefined;
|
||||
return {
|
||||
label: POST_BODY_FORMAT_OPTIONS.NONE,
|
||||
value: POST_BODY_FORMAT_OPTIONS.NONE,
|
||||
};
|
||||
};
|
||||
export const getPluginActionConfigSelectedTab = (state: AppState) =>
|
||||
state.ui.pluginActionEditor.selectedConfigTab;
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ export interface PluginActionEditorState {
|
|||
isDirty: Record<string, boolean>;
|
||||
runErrorMessage: Record<string, string>;
|
||||
selectedConfigTab?: string;
|
||||
formData: Record<string, Record<string, { label: string; value: string }>>;
|
||||
formData: Record<string, { label: string; value: string }>;
|
||||
debugger: PluginEditorDebuggerState;
|
||||
settingsOpen?: boolean;
|
||||
}
|
||||
|
|
@ -144,13 +144,12 @@ export const handlers = {
|
|||
[ReduxActionTypes.SET_EXTRA_FORMDATA]: (
|
||||
state: PluginActionEditorState,
|
||||
action: ReduxAction<{
|
||||
id: string;
|
||||
values: Record<string, { label: string; value: string }>;
|
||||
}>,
|
||||
) => {
|
||||
const { id, values } = action.payload;
|
||||
const { values } = action.payload;
|
||||
|
||||
set(state, ["formData", id], values);
|
||||
set(state, ["formData"], values);
|
||||
},
|
||||
[ReduxActionTypes.SET_PLUGIN_ACTION_EDITOR_FORM_SELECTED_TAB]: (
|
||||
state: PluginActionEditorState,
|
||||
|
|
|
|||
|
|
@ -77,11 +77,16 @@ import { ActionExecutionResizerHeight } from "PluginActionEditor/components/Plug
|
|||
import {
|
||||
getPluginActionConfigSelectedTab,
|
||||
getPluginActionDebuggerState,
|
||||
getFormData,
|
||||
setExtraFormData,
|
||||
setPluginActionEditorDebuggerState,
|
||||
setPluginActionEditorSelectedTab,
|
||||
} from "PluginActionEditor/store";
|
||||
import { EDITOR_TABS } from "constants/QueryEditorConstants";
|
||||
import { API_EDITOR_TABS } from "PluginActionEditor/constants/CommonApiConstants";
|
||||
import {
|
||||
API_EDITOR_TABS,
|
||||
POST_BODY_FORMAT_OPTIONS,
|
||||
} from "PluginActionEditor/constants/CommonApiConstants";
|
||||
|
||||
export const AppIDEFocusElements: FocusElementsConfigList = {
|
||||
[FocusEntity.DATASOURCE_LIST]: [
|
||||
|
|
@ -152,9 +157,13 @@ export const AppIDEFocusElements: FocusElementsConfigList = {
|
|||
},
|
||||
{
|
||||
type: FocusElementConfigType.Redux,
|
||||
name: FocusElement.InputField,
|
||||
selector: getFocusableInputField,
|
||||
setter: setFocusableInputField,
|
||||
name: FocusElement.PluginActionFormData,
|
||||
selector: getFormData,
|
||||
setter: setExtraFormData,
|
||||
defaultValue: {
|
||||
label: POST_BODY_FORMAT_OPTIONS.NONE,
|
||||
value: POST_BODY_FORMAT_OPTIONS.NONE,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: FocusElementConfigType.Redux,
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import type { AppState } from "ee/reducers";
|
|||
|
||||
export enum FocusElement {
|
||||
PluginActionConfigTabs = "PluginActionConfigTabs",
|
||||
PluginActionFormData = "PluginActionFormData",
|
||||
CodeEditorHistory = "CodeEditorHistory",
|
||||
EntityCollapsibleState = "EntityCollapsibleState",
|
||||
EntityExplorerWidth = "EntityExplorerWidth",
|
||||
|
|
|
|||
|
|
@ -61,7 +61,10 @@ import {
|
|||
import { updateReplayEntity } from "actions/pageActions";
|
||||
import { ENTITY_TYPE } from "ee/entities/AppsmithConsole/utils";
|
||||
import type { Plugin } from "api/PluginApi";
|
||||
import { getPostBodyFormat } from "../PluginActionEditor/store";
|
||||
import {
|
||||
getPostBodyFormat,
|
||||
setExtraFormData,
|
||||
} from "../PluginActionEditor/store";
|
||||
import { apiEditorIdURL, datasourcesEditorIdURL } from "ee/RouteBuilder";
|
||||
import { getCurrentBasePageId } from "selectors/editorSelectors";
|
||||
import { validateResponse } from "./ErrorSagas";
|
||||
|
|
@ -135,10 +138,8 @@ function* syncApiParamsSaga(
|
|||
}
|
||||
}
|
||||
|
||||
function* handleUpdateBodyContentType(
|
||||
action: ReduxAction<{ title: string; apiId: string }>,
|
||||
) {
|
||||
const { apiId, title } = action.payload;
|
||||
function* handleUpdateBodyContentType(action: ReduxAction<{ title: string }>) {
|
||||
const { title } = action.payload;
|
||||
const { values } = yield select(getFormData, API_EDITOR_FORM_NAME);
|
||||
|
||||
const displayFormatValue = POST_BODY_FORMAT_OPTIONS_ARRAY.find(
|
||||
|
|
@ -216,18 +217,14 @@ function* handleUpdateBodyContentType(
|
|||
// Quick Context: The extra formadata action is responsible for updating the current multi switch mode you see on api editor body tab
|
||||
// whenever a user selects a new content type through the tab e.g application/json, this action is dispatched to update that value, which is then read in the PostDataBody file
|
||||
// to show the appropriate content type section.
|
||||
yield put({
|
||||
type: ReduxActionTypes.SET_EXTRA_FORMDATA,
|
||||
payload: {
|
||||
id: apiId,
|
||||
values: {
|
||||
displayFormat: {
|
||||
yield put(
|
||||
setExtraFormData({
|
||||
[POST_BODY_FORM_DATA_KEY]: {
|
||||
label: title,
|
||||
value: title,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
}),
|
||||
);
|
||||
|
||||
// help to prevent cyclic dependency error in case the bodyFormData is empty.
|
||||
|
||||
|
|
@ -257,7 +254,8 @@ function* updateExtraFormDataSaga() {
|
|||
const { values } = formData;
|
||||
|
||||
// when initializing, check if theres a display format present.
|
||||
const extraFormData: GetFormData = yield select(getPostBodyFormat, values.id);
|
||||
const extraFormData: { label: string; value: string } =
|
||||
yield select(getPostBodyFormat);
|
||||
|
||||
const headers: Array<{ key: string; value: string }> =
|
||||
get(values, "actionConfiguration.headers") || [];
|
||||
|
|
@ -363,15 +361,11 @@ function* setApiBodyTabHeaderFormat(apiId: string, apiContentType?: string) {
|
|||
};
|
||||
}
|
||||
|
||||
yield put({
|
||||
type: ReduxActionTypes.SET_EXTRA_FORMDATA,
|
||||
payload: {
|
||||
id: apiId,
|
||||
values: {
|
||||
yield put(
|
||||
setExtraFormData({
|
||||
[POST_BODY_FORM_DATA_KEY]: displayFormat,
|
||||
},
|
||||
},
|
||||
});
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
function* formValueChangeSaga(
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user