From 1d4198048c8ed7284859ba92b7af5be318289c5c Mon Sep 17 00:00:00 2001 From: Hetu Nandu Date: Thu, 29 Feb 2024 11:53:57 +0530 Subject: [PATCH] chore: Debugger Split states (#31043) ## Description Creates local states for the debugger for Query Pane, Api Pane and JS Pane and separates it from the main Canvas Debugger state. This is done so that in Split pane, the states of Action Pane debugger can be different from the Canvas Debugger state. To keep handling the Fullscreen Debugger experience, a new hook `useDebuggerTriggerClick` is introduced which opens the correct debugger based on the IDE state. Also removes the Error and Logs from the Query / Api / JS Debuggers when in split screen mode for a cleaner debugging experience ##### This change removes the expectation of having a common debugger state that follows around as the user navigates in the IDE. Instead it create a new debugger state per entity item. The tests have been updated to reflect this #### PR fixes following issue(s) Fixes #30836 Fixes #30342 #### Media #### Type of change - Breaking change (fix or feature that would cause existing functionality to not work as expected) ## Testing #### How Has This Been Tested? - [ ] Manual - [ ] JUnit - [ ] Jest - [ ] Cypress > > #### Test Plan > Add Testsmith test cases links that relate to this PR > > #### Issues raised during DP testing > Link issues raised during DP testing for better visiblity and tracking (copy link from comments dropped on this PR) > > > ## Checklist: #### Dev activity - [ ] My code follows the style guidelines of this project - [ ] I have performed a self-review of my own code - [ ] I have commented my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [ ] My changes generate no new warnings - [ ] I have added tests that prove my fix is effective or that my feature works - [ ] New and existing unit tests pass locally with my changes - [ ] PR is being merged under a feature flag #### QA activity: - [ ] [Speedbreak features](https://github.com/appsmithorg/TestSmith/wiki/Guidelines-for-test-plans#speedbreakers-) have been covered - [ ] Test plan covers all impacted features and [areas of interest](https://github.com/appsmithorg/TestSmith/wiki/Guidelines-for-test-plans#areas-of-interest-) - [ ] Test plan has been peer reviewed by project stakeholders and other QA members - [ ] Manually tested functionality on DP - [ ] We had an implementation alignment call with stakeholders post QA Round 2 - [ ] Cypress test cases have been added and approved by SDET/manual QA - [ ] Added `Test Plan Approved` label after Cypress tests were reviewed - [ ] Added `Test Plan Approved` label after JUnit tests were reviewed --- .../Debugger/Api_pane_navigation_spec.ts | 6 +- .../OtherUIFeatures/EntityBottomBar_spec.ts | 14 +- .../ClientSide/OtherUIFeatures/Logs2_spec.js | 5 +- app/client/src/actions/apiPaneActions.ts | 42 ++---- app/client/src/actions/debuggerActions.ts | 14 +- app/client/src/actions/jsPaneActions.ts | 12 +- app/client/src/actions/queryPaneActions.ts | 10 ++ .../src/ce/constants/ReduxActionConstants.tsx | 5 +- .../src/ce/navigation/FocusElements/AppIDE.ts | 48 ++++++- .../ce/reducers/uiReducers/apiPaneReducer.ts | 42 ++++-- .../reducers/uiReducers/queryPaneReducer.ts | 39 ++++-- .../editorComponents/ApiResponseView.tsx | 84 +++++------ .../editorComponents/Debugger/Schema.tsx | 8 +- .../Debugger/hooks/useDebuggerTriggerClick.ts | 96 +++++++++++++ .../editorComponents/Debugger/index.tsx | 38 +---- .../editorComponents/JSResponseView.tsx | 59 ++++---- app/client/src/navigation/FocusElements.ts | 3 + .../Editor/APIEditor/CommonEditorForm.tsx | 28 ++-- .../pages/Editor/DataSourceEditor/hooks.ts | 2 +- .../DatasourceInfo/DatasourceStructure.tsx | 46 ++---- .../EditorPane/components/ActionDrawer.tsx | 6 +- .../EditorPane/components/ActionEditor.tsx | 8 +- .../CreateNewDatasourceTab.tsx | 132 +++++++++--------- app/client/src/pages/Editor/JSEditor/Form.tsx | 44 +++--- .../Editor/QueryEditor/EditorJSONtoForm.tsx | 83 ++--------- .../Editor/QueryEditor/QueryDebuggerTabs.tsx | 124 +++++++++++----- .../Editor/QueryEditor/QueryResponseTab.tsx | 8 +- .../reducers/uiReducers/debuggerReducer.ts | 26 ++++ .../src/reducers/uiReducers/jsPaneReducer.ts | 27 +++- .../sagas/ActionExecution/PluginActionSaga.ts | 62 +++++--- app/client/src/sagas/JSPaneSagas.ts | 25 ++-- app/client/src/selectors/apiPaneSelectors.ts | 11 ++ .../src/selectors/debuggerSelectors.tsx | 17 ++- app/client/src/selectors/jsPaneSelectors.ts | 3 + .../src/selectors/queryPaneSelectors.ts | 3 + 35 files changed, 696 insertions(+), 484 deletions(-) create mode 100644 app/client/src/components/editorComponents/Debugger/hooks/useDebuggerTriggerClick.ts diff --git a/app/client/cypress/e2e/Regression/ClientSide/Debugger/Api_pane_navigation_spec.ts b/app/client/cypress/e2e/Regression/ClientSide/Debugger/Api_pane_navigation_spec.ts index baea494db5..fe5229868f 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/Debugger/Api_pane_navigation_spec.ts +++ b/app/client/cypress/e2e/Regression/ClientSide/Debugger/Api_pane_navigation_spec.ts @@ -31,7 +31,6 @@ describe( debuggerHelper.ClicklogEntityLink(); agHelper.AssertElementVisibility(apiPage._nextCursorValue); - debuggerHelper.CloseBottomBar(); entityExplorer.ActionContextMenuByEntityName({ entityNameinLeftSidebar: "Api1", entityType: entityItems.Api, @@ -51,11 +50,9 @@ describe( apiPage.SelectPaneTab("Headers"); EditorNavigation.ShowCanvas(); debuggerHelper.AssertErrorCount(1); - debuggerHelper.ClickDebuggerIcon(); debuggerHelper.ClicklogEntityLink(); agHelper.AssertElementVisibility(apiPage._bodyValue(0)); - debuggerHelper.CloseBottomBar(); entityExplorer.ActionContextMenuByEntityName({ entityNameinLeftSidebar: "Api2", entityType: entityItems.Api, @@ -69,12 +66,11 @@ describe( apiPage.SelectPaneTab("Pagination"); EditorNavigation.ShowCanvas(); - debuggerHelper.ClickDebuggerIcon(); + // The Debugger should already be open here as it was opened before debuggerHelper.ClickLogsTab(); debuggerHelper.ClicklogEntityLink(true); agHelper.AssertElementVisibility(dataSources._queryTimeout); - debuggerHelper.CloseBottomBar(); entityExplorer.ActionContextMenuByEntityName({ entityNameinLeftSidebar: "Api3", entityType: entityItems.Api, diff --git a/app/client/cypress/e2e/Regression/ClientSide/OtherUIFeatures/EntityBottomBar_spec.ts b/app/client/cypress/e2e/Regression/ClientSide/OtherUIFeatures/EntityBottomBar_spec.ts index 377f4a0a4b..9eabb6ac94 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/OtherUIFeatures/EntityBottomBar_spec.ts +++ b/app/client/cypress/e2e/Regression/ClientSide/OtherUIFeatures/EntityBottomBar_spec.ts @@ -2,7 +2,7 @@ import * as _ from "../../../../support/Objects/ObjectsCore"; import { PageType } from "../../../../support/Pages/DebuggerHelper"; import EditorNavigation from "../../../../support/Pages/EditorNavigation"; -describe("Entity bottom bar", () => { +describe("Entity bottom bar", { tags: ["@tag.Debugger"] }, () => { it("1. Debugger should be closable", () => { //Verify if bottom bar is closed. _.debuggerHelper.AssertClosed(); @@ -32,17 +32,14 @@ describe("Entity bottom bar", () => { _.debuggerHelper.AssertSelectedTab("Response"); //verify if bottom bar is closed on switching to canvas page. EditorNavigation.ShowCanvas(); - _.debuggerHelper.AssertSelectedTab("Errors"); + _.debuggerHelper.AssertClosed(); }); it("3. Api bottom pane should be collapsable", () => { _.apiPage.CreateAndFillApi( _.dataManager.dsValues[_.dataManager.defaultEnviorment].mockApiUrl, ); - //Verify that the errors tab is still open. - _.debuggerHelper.AssertSelectedTab("Errors"); - //Verify if bottom bar is closed on clicking close icon in API page. - _.debuggerHelper.CloseBottomBar(); + //Verify that the errors tab is still closed. _.debuggerHelper.AssertClosed(); //Verify if bottom bar opens on clicking debugger icon in api page. _.debuggerHelper.ClickDebuggerIcon(); @@ -60,12 +57,11 @@ describe("Entity bottom bar", () => { it("4. Bottom bar in Datasource", () => { //Verify if bottom bar remain open on shifting to create new datasource page. _.dataSources.NavigateToDSCreateNew(); - //Expecting errors tab to be closed as previous selected tab was response. - //And response tab is not part of datasource page. + //Expecting errors tab to be closed as this is now a datasource _.debuggerHelper.AssertClosed(); //Verify if bottom bar opens on clicking debugger icon in datasource page. _.debuggerHelper.ClickDebuggerIcon(); - _.debuggerHelper.AssertClosed(); + _.debuggerHelper.AssertOpen(PageType.DataSources); }); it( diff --git a/app/client/cypress/e2e/Regression/ClientSide/OtherUIFeatures/Logs2_spec.js b/app/client/cypress/e2e/Regression/ClientSide/OtherUIFeatures/Logs2_spec.js index e0851d9a00..1c39aedda1 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/OtherUIFeatures/Logs2_spec.js +++ b/app/client/cypress/e2e/Regression/ClientSide/OtherUIFeatures/Logs2_spec.js @@ -14,7 +14,7 @@ const generateTestLogString = () => { return logString; }; -describe("Debugger logs", function () { +describe("Debugger logs", { tags: ["@tag.Debugger"] }, function () { this.beforeEach(() => { logString = generateTestLogString(); }); @@ -285,7 +285,8 @@ describe("Debugger logs", function () { }); EditorNavigation.SelectEntityByName("Page1", EntityType.Page); - _.agHelper.GetNClick(_.locators._errorTab); + _.agHelper.AssertElementVisibility(".t--debugger-count"); + _.debuggerHelper.ClickDebuggerIcon(); _.debuggerHelper.ClicklogEntityLink(); diff --git a/app/client/src/actions/apiPaneActions.ts b/app/client/src/actions/apiPaneActions.ts index 1c58a6864a..6d6a2dff92 100644 --- a/app/client/src/actions/apiPaneActions.ts +++ b/app/client/src/actions/apiPaneActions.ts @@ -2,6 +2,7 @@ import type { ReduxAction } from "@appsmith/constants/ReduxActionConstants"; import { ReduxActionTypes } from "@appsmith/constants/ReduxActionConstants"; import type { EventLocation } from "@appsmith/utils/analyticsUtilTypes"; import type { SlashCommandPayload } from "entities/Action"; +import type { ApiPaneDebuggerState } from "@appsmith/reducers/uiReducers/apiPaneReducer"; export const changeApi = ( id: string, @@ -14,40 +15,6 @@ export const changeApi = ( }; }; -export const initApiPane = (urlId?: string): ReduxAction<{ id?: string }> => { - return { - type: ReduxActionTypes.INIT_API_PANE, - payload: { id: urlId }, - }; -}; - -export const setCurrentCategory = ( - category: string, -): ReduxAction<{ category: string }> => { - return { - type: ReduxActionTypes.SET_CURRENT_CATEGORY, - payload: { category }, - }; -}; - -export const setLastUsedEditorPage = ( - path: string, -): ReduxAction<{ path: string }> => { - return { - type: ReduxActionTypes.SET_LAST_USED_EDITOR_PAGE, - payload: { path }, - }; -}; - -export const setLastSelectedPage = ( - selectedPageId: string, -): ReduxAction<{ selectedPageId: string }> => { - return { - type: ReduxActionTypes.SET_LAST_SELECTED_PAGE_PAGE, - payload: { selectedPageId }, - }; -}; - export const createNewApiAction = ( pageId: string, from: EventLocation, @@ -109,3 +76,10 @@ export const setApiRightPaneSelectedTab: ( type: ReduxActionTypes.SET_API_RIGHT_PANE_SELECTED_TAB, payload: { selectedTab: payload }, }); + +export const setApiPaneDebuggerState = ( + payload: Partial, +) => ({ + type: ReduxActionTypes.SET_API_PANE_DEBUGGER_STATE, + payload, +}); diff --git a/app/client/src/actions/debuggerActions.ts b/app/client/src/actions/debuggerActions.ts index d34292ca60..3f58f4c476 100644 --- a/app/client/src/actions/debuggerActions.ts +++ b/app/client/src/actions/debuggerActions.ts @@ -1,7 +1,10 @@ import { ReduxActionTypes } from "@appsmith/constants/ReduxActionConstants"; import type { Log, Message, SourceEntity } from "entities/AppsmithConsole"; import type { ENTITY_TYPE } from "@appsmith/entities/AppsmithConsole/utils"; -import type { DebuggerContext } from "reducers/uiReducers/debuggerReducer"; +import type { + CanvasDebuggerState, + DebuggerContext, +} from "reducers/uiReducers/debuggerReducer"; import type { EventName } from "@appsmith/utils/analyticsUtilTypes"; import type { APP_MODE } from "entities/App"; @@ -126,3 +129,12 @@ export const setDebuggerContext = (context: DebuggerContext) => { payload: { context }, }; }; + +export const setCanvasDebuggerState = ( + payload: Partial, +) => { + return { + type: ReduxActionTypes.SET_CANVAS_DEBUGGER_STATE, + payload, + }; +}; diff --git a/app/client/src/actions/jsPaneActions.ts b/app/client/src/actions/jsPaneActions.ts index f1eda98680..7ea2c9da47 100644 --- a/app/client/src/actions/jsPaneActions.ts +++ b/app/client/src/actions/jsPaneActions.ts @@ -6,7 +6,10 @@ import type { SetFunctionPropertyPayload, } from "@appsmith/api/JSActionAPI"; import type { EventLocation } from "@appsmith/utils/analyticsUtilTypes"; -import type { JSEditorTab } from "reducers/uiReducers/jsPaneReducer"; +import type { + JSEditorTab, + JSPaneDebuggerState, +} from "reducers/uiReducers/jsPaneReducer"; export const createNewJSCollection = ( pageId: string, @@ -111,3 +114,10 @@ export const setJsPaneConfigSelectedTab: ( type: ReduxActionTypes.SET_JS_PANE_CONFIG_SELECTED_TAB, payload: { selectedTab: payload }, }); + +export const setJsPaneDebuggerState = ( + payload: Partial, +) => ({ + type: ReduxActionTypes.SET_JS_PANE_DEBUGGER_STATE, + payload, +}); diff --git a/app/client/src/actions/queryPaneActions.ts b/app/client/src/actions/queryPaneActions.ts index 55f87e7724..7bfe58cc56 100644 --- a/app/client/src/actions/queryPaneActions.ts +++ b/app/client/src/actions/queryPaneActions.ts @@ -1,6 +1,7 @@ import type { ReduxAction } from "@appsmith/constants/ReduxActionConstants"; import { ReduxActionTypes } from "@appsmith/constants/ReduxActionConstants"; import type { Action } from "entities/Action"; +import type { QueryPaneDebuggerState } from "@appsmith/reducers/uiReducers/queryPaneReducer"; export interface ChangeQueryPayload { id: string; @@ -26,3 +27,12 @@ export const setQueryPaneConfigSelectedTabIndex: ( type: ReduxActionTypes.SET_QUERY_PANE_CONFIG_SELECTED_TAB, payload: { selectedTabIndex: payload }, }); + +export const setQueryPaneDebuggerState = ( + payload: Partial, +) => { + return { + type: ReduxActionTypes.SET_QUERY_PANE_DEBUGGER_STATE, + payload, + }; +}; diff --git a/app/client/src/ce/constants/ReduxActionConstants.tsx b/app/client/src/ce/constants/ReduxActionConstants.tsx index e8f201a29e..a626d4265b 100644 --- a/app/client/src/ce/constants/ReduxActionConstants.tsx +++ b/app/client/src/ce/constants/ReduxActionConstants.tsx @@ -463,7 +463,6 @@ const ActionTypes = { FETCH_ALL_PUBLISHED_PAGES: "FETCH_ALL_PUBLISHED_PAGES", CREATE_NEW_API_ACTION: "CREATE_NEW_API_ACTION", CREATE_NEW_QUERY_ACTION: "CREATE_NEW_QUERY_ACTION", - SET_CURRENT_CATEGORY: "SET_CURRENT_CATEGORY", SET_LAST_USED_EDITOR_PAGE: "SET_LAST_USED_EDITOR_PAGE", SET_LAST_SELECTED_PAGE_PAGE: "SET_LAST_SELECTED_PAGE_PAGE", FETCH_PROVIDER_DETAILS_BY_PROVIDER_ID_INIT: @@ -911,6 +910,10 @@ const ActionTypes = { END_CONSOLIDATED_PAGE_LOAD: "END_CONSOLIDATED_PAGE_LOAD", FETCH_ENVIRONMENT_SUCCESS: "FETCH_ENVIRONMENT_SUCCESS", SET_SHOW_CREATE_NEW_MODAL: "SET_SHOW_CREATE_NEW_MODAL", + SET_QUERY_PANE_DEBUGGER_STATE: "SET_QUERY_PANE_DEBUGGER_STATE", + SET_API_PANE_DEBUGGER_STATE: "SET_API_PANE_DEBUGGER_STATE", + SET_JS_PANE_DEBUGGER_STATE: "SET_JS_PANE_DEBUGGER_STATE", + SET_CANVAS_DEBUGGER_STATE: "SET_CANVAS_DEBUGGER_STATE", }; export const ReduxActionTypes = { diff --git a/app/client/src/ce/navigation/FocusElements/AppIDE.ts b/app/client/src/ce/navigation/FocusElements/AppIDE.ts index 87c8f1c1a5..e6b94fcf44 100644 --- a/app/client/src/ce/navigation/FocusElements/AppIDE.ts +++ b/app/client/src/ce/navigation/FocusElements/AppIDE.ts @@ -1,5 +1,6 @@ import { setApiPaneConfigSelectedTabIndex, + setApiPaneDebuggerState, setApiRightPaneSelectedTab, } from "actions/apiPaneActions"; import { @@ -13,6 +14,7 @@ import { } from "actions/editorContextActions"; import { getApiPaneConfigSelectedTabIndex, + getApiPaneDebuggerState, getApiRightPaneSelectedTab, } from "selectors/apiPaneSelectors"; import { @@ -33,14 +35,20 @@ import { import { setDatasourceViewMode } from "actions/datasourceActions"; import { updateExplorerWidthAction } from "actions/explorerActions"; -import { setJsPaneConfigSelectedTab } from "actions/jsPaneActions"; +import { + setJsPaneConfigSelectedTab, + setJsPaneDebuggerState, +} from "actions/jsPaneActions"; import { setAllPropertySectionState, setFocusablePropertyPaneField, setPropertyPaneWidthAction, setSelectedPropertyPanels, } from "actions/propertyPaneActions"; -import { setQueryPaneConfigSelectedTabIndex } from "actions/queryPaneActions"; +import { + setQueryPaneConfigSelectedTabIndex, + setQueryPaneDebuggerState, +} from "actions/queryPaneActions"; import { selectWidgetInitAction } from "actions/widgetSelectionActions"; import { DEFAULT_ENTITY_EXPLORER_WIDTH, @@ -53,6 +61,7 @@ import { getExplorerWidth } from "selectors/explorerSelector"; import { getFirstJSObject, getJSPaneConfigSelectedTab, + getJsPaneDebuggerState, } from "selectors/jsPaneSelectors"; import { getFocusablePropertyPaneField, @@ -62,6 +71,7 @@ import { import { getFirstQuery, getQueryPaneConfigSelectedTabIndex, + getQueryPaneDebuggerState, } from "selectors/queryPaneSelectors"; import { getDebuggerContext } from "selectors/debuggerSelectors"; import { setDebuggerContext } from "actions/debuggerActions"; @@ -83,6 +93,7 @@ import { FocusElement, FocusElementConfigType } from "navigation/FocusElements"; import type { FocusElementsConfigList } from "sagas/FocusRetentionSaga"; import { getJSTabs, getQueryTabs } from "selectors/ideSelectors"; import { setJSTabs, setQueryTabs } from "actions/ideActions"; +import { ActionExecutionResizerHeight } from "pages/Editor/APIEditor/constants"; export const AppIDEFocusElements: FocusElementsConfigList = { [FocusEntity.DATASOURCE_LIST]: [ @@ -117,6 +128,17 @@ export const AppIDEFocusElements: FocusElementsConfigList = { setter: setJsPaneConfigSelectedTab, defaultValue: JSEditorTab.CODE, }, + { + type: FocusElementConfigType.Redux, + name: FocusElement.JSDebugger, + selector: getJsPaneDebuggerState, + setter: setJsPaneDebuggerState, + defaultValue: { + open: false, + responseTabHeight: ActionExecutionResizerHeight, + selectedTab: undefined, + }, + }, ], [FocusEntity.QUERY]: [ { @@ -156,6 +178,28 @@ export const AppIDEFocusElements: FocusElementsConfigList = { selector: getApiRightPaneSelectedTab, setter: setApiRightPaneSelectedTab, }, + { + type: FocusElementConfigType.Redux, + name: FocusElement.QueryDebugger, + selector: getQueryPaneDebuggerState, + setter: setQueryPaneDebuggerState, + defaultValue: { + open: false, + responseTabHeight: ActionExecutionResizerHeight, + selectedTab: undefined, + }, + }, + { + type: FocusElementConfigType.Redux, + name: FocusElement.ApiDebugger, + selector: getApiPaneDebuggerState, + setter: setApiPaneDebuggerState, + defaultValue: { + open: false, + responseTabHeight: ActionExecutionResizerHeight, + selectedTab: undefined, + }, + }, ], [FocusEntity.PROPERTY_PANE]: [ { diff --git a/app/client/src/ce/reducers/uiReducers/apiPaneReducer.ts b/app/client/src/ce/reducers/uiReducers/apiPaneReducer.ts index 996da6dd11..f5d6afd19d 100644 --- a/app/client/src/ce/reducers/uiReducers/apiPaneReducer.ts +++ b/app/client/src/ce/reducers/uiReducers/apiPaneReducer.ts @@ -6,32 +6,38 @@ import { } from "@appsmith/constants/ReduxActionConstants"; import type { Action } from "entities/Action"; import type { UpdateActionPropertyActionPayload } from "actions/pluginActionActions"; +import { ActionExecutionResizerHeight } from "pages/Editor/APIEditor/constants"; export const initialState: ApiPaneReduxState = { isCreating: false, - isFetching: false, isRunning: {}, isSaving: {}, isDeleting: {}, isDirty: {}, - currentCategory: "", extraformData: {}, selectedConfigTabIndex: 0, - selectedResponseTab: "", + debugger: { + open: false, + responseTabHeight: ActionExecutionResizerHeight, + }, }; +export interface ApiPaneDebuggerState { + open: boolean; + responseTabHeight: number; + selectedTab?: string; +} + export interface ApiPaneReduxState { isCreating: boolean; // RR - isFetching: boolean; // RR isRunning: Record; isSaving: Record; // RN isDeleting: Record; isDirty: Record; - currentCategory: string; extraformData: Record; selectedConfigTabIndex: number; - selectedResponseTab: string; selectedRightPaneTab?: string; + debugger: ApiPaneDebuggerState; } export const handlers = { @@ -74,6 +80,10 @@ export const handlers = { ...state.isRunning, [action.payload.id]: true, }, + debugger: { + ...state.debugger, + open: true, + }, }), [ReduxActionTypes.RUN_ACTION_SUCCESS]: ( state: ApiPaneReduxState, @@ -183,14 +193,6 @@ export const handlers = { }, }), - [ReduxActionTypes.SET_CURRENT_CATEGORY]: ( - state: ApiPaneReduxState, - action: ReduxAction<{ category: string }>, - ) => ({ - ...state, - currentCategory: action.payload.category, - }), - /** * This redux action sets the extraformData field for an action. This is used to select the * appropriate body type tab selection in the Rest API plugin based on the content-type. @@ -233,6 +235,18 @@ export const handlers = { selectedRightPaneTab: selectedTab, }; }, + [ReduxActionTypes.SET_API_PANE_DEBUGGER_STATE]: ( + state: ApiPaneReduxState, + action: ReduxAction>, + ) => { + return { + ...state, + debugger: { + ...state.debugger, + ...action.payload, + }, + }; + }, }; const apiPaneReducer = createReducer(initialState, handlers); diff --git a/app/client/src/ce/reducers/uiReducers/queryPaneReducer.ts b/app/client/src/ce/reducers/uiReducers/queryPaneReducer.ts index 6d49fa9beb..8fc2dcaea4 100644 --- a/app/client/src/ce/reducers/uiReducers/queryPaneReducer.ts +++ b/app/client/src/ce/reducers/uiReducers/queryPaneReducer.ts @@ -8,29 +8,33 @@ import { omit } from "lodash"; import type { Action } from "entities/Action"; import type { ActionResponse } from "api/ActionAPI"; import { ActionExecutionResizerHeight } from "pages/Editor/APIEditor/constants"; +import { DEBUGGER_TAB_KEYS } from "../../../components/editorComponents/Debugger/helpers"; export const initialState: QueryPaneReduxState = { - isFetching: false, - isCreating: false, isRunning: {}, isSaving: {}, isDeleting: {}, runErrorMessage: {}, - lastUsed: "", // NR - responseTabHeight: ActionExecutionResizerHeight, + debugger: { + open: false, + responseTabHeight: ActionExecutionResizerHeight, + }, selectedConfigTabIndex: "0", }; +export interface QueryPaneDebuggerState { + open: boolean; + responseTabHeight: number; + selectedTab?: string; +} + export interface QueryPaneReduxState { - isFetching: boolean; // RR isRunning: Record; isSaving: Record; // RR isDeleting: Record; runErrorMessage: Record; - lastUsed: string; // NR - isCreating: boolean; // RR selectedConfigTabIndex: string; - responseTabHeight: number; + debugger: QueryPaneDebuggerState; } export const handlers = { @@ -122,13 +126,18 @@ export const handlers = { [ReduxActionTypes.RUN_ACTION_REQUEST]: ( state: any, action: ReduxAction<{ id: string }>, - ) => { + ): QueryPaneReduxState => { return { ...state, isRunning: { ...state.isRunning, [action.payload.id]: true, }, + debugger: { + ...state.debugger, + selectedTab: DEBUGGER_TAB_KEYS.RESPONSE_TAB, + open: true, + }, }; }, @@ -187,6 +196,18 @@ export const handlers = { selectedConfigTabIndex: selectedTabIndex, }; }, + [ReduxActionTypes.SET_QUERY_PANE_DEBUGGER_STATE]: ( + state: QueryPaneReduxState, + action: ReduxAction>, + ) => { + return { + ...state, + debugger: { + ...state.debugger, + ...action.payload, + }, + }; + }, }; const queryPaneReducer = createReducer(initialState, handlers); diff --git a/app/client/src/components/editorComponents/ApiResponseView.tsx b/app/client/src/components/editorComponents/ApiResponseView.tsx index 6d1f3d6697..be37c4aa7b 100644 --- a/app/client/src/components/editorComponents/ApiResponseView.tsx +++ b/app/client/src/components/editorComponents/ApiResponseView.tsx @@ -13,11 +13,10 @@ import { isArray, isEmpty, isString } from "lodash"; import { CHECK_REQUEST_BODY, createMessage, + DEBUGGER_ERRORS, DEBUGGER_LOGS, EMPTY_RESPONSE_FIRST_HALF, EMPTY_RESPONSE_LAST_HALF, - INSPECT_ENTITY, - DEBUGGER_ERRORS, } from "@appsmith/constants/messages"; import { Text as BlueprintText } from "@blueprintjs/core"; import { EditorTheme } from "./CodeEditor/EditorConfig"; @@ -26,26 +25,17 @@ import DebuggerLogs from "./Debugger/DebuggerLogs"; import ErrorLogs from "./Debugger/Errors"; import Resizer, { ResizerCSS } from "./Debugger/Resizer"; import AnalyticsUtil from "utils/AnalyticsUtil"; -import EntityDeps from "./Debugger/EntityDependecies"; import { Classes, TAB_MIN_HEIGHT, Text, TextType } from "design-system-old"; import { Button, Callout, Flex, SegmentedControl } from "design-system"; +import type { BottomTab } from "./EntityBottomTabs"; import EntityBottomTabs from "./EntityBottomTabs"; import { DEBUGGER_TAB_KEYS } from "./Debugger/helpers"; import Table from "pages/Editor/QueryEditor/Table"; import { API_RESPONSE_TYPE_OPTIONS } from "constants/ApiEditorConstants/CommonApiConstants"; import { setActionResponseDisplayFormat } from "actions/pluginActionActions"; import { isHtml } from "./utils"; -import { - getDebuggerSelectedTab, - getErrorCount, - getResponsePaneHeight, -} from "selectors/debuggerSelectors"; +import { getErrorCount } from "selectors/debuggerSelectors"; import { ActionExecutionResizerHeight } from "pages/Editor/APIEditor/constants"; -import { - setDebuggerSelectedTab, - setResponsePaneHeight, - showDebugger, -} from "actions/debuggerActions"; import LogAdditionalInfo from "./Debugger/ErrorLogs/components/LogAdditionalInfo"; import { JsonWrapper, @@ -59,6 +49,10 @@ import ActionExecutionInProgressView from "./ActionExecutionInProgressView"; import { CloseDebugger } from "./Debugger/DebuggerTabs"; import { EMPTY_RESPONSE } from "./emptyResponse"; import BindDataButton from "../../pages/Editor/QueryEditor/BindDataButton"; +import { setApiPaneDebuggerState } from "actions/apiPaneActions"; +import { getApiPaneDebuggerState } from "selectors/apiPaneSelectors"; +import { getIDEViewMode } from "selectors/ideSelectors"; +import { EditorViewMode } from "@appsmith/entities/IDE/constants"; interface TextStyleProps { accent: "primary" | "secondary" | "error"; @@ -312,12 +306,19 @@ function ApiResponseView(props: Props) { const panelRef: RefObject = useRef(null); const dispatch = useDispatch(); const errorCount = useSelector(getErrorCount); + const { open, responseTabHeight, selectedTab } = useSelector( + getApiPaneDebuggerState, + ); + + const ideViewMode = useSelector(getIDEViewMode); const onDebugClick = useCallback(() => { AnalyticsUtil.logEvent("OPEN_DEBUGGER", { source: "API", }); - dispatch(setDebuggerSelectedTab(DEBUGGER_TAB_KEYS.ERROR_TAB)); + dispatch( + setApiPaneDebuggerState({ selectedTab: DEBUGGER_TAB_KEYS.ERROR_TAB }), + ); }, []); const onRunClick = () => { @@ -380,7 +381,7 @@ function ApiResponseView(props: Props) { panelComponent: responseTabComponent( dataType.key, actionResponse?.body, - responsePaneHeight, + responseTabHeight, ), }; }); @@ -401,8 +402,6 @@ function ApiResponseView(props: Props) { (dataType) => dataType.title === responseDisplayFormat?.title, ); - // get the selected tab in the response pane. - const selectedResponseTab = useSelector(getDebuggerSelectedTab); // update the selected tab in the response pane. const updateSelectedResponseTab = useCallback((tabKey: string) => { if (tabKey === DEBUGGER_TAB_KEYS.ERROR_TAB) { @@ -410,13 +409,12 @@ function ApiResponseView(props: Props) { source: "API_PANE", }); } - dispatch(setDebuggerSelectedTab(tabKey)); + dispatch(setApiPaneDebuggerState({ selectedTab: tabKey })); }, []); - // get the height of the response pane. - const responsePaneHeight = useSelector(getResponsePaneHeight); + // update the height of the response pane on resize. const updateResponsePaneHeight = useCallback((height: number) => { - dispatch(setResponsePaneHeight(height)); + dispatch(setApiPaneDebuggerState({ responseTabHeight: height })); }, []); // get request timestamp formatted to human readable format. @@ -427,7 +425,7 @@ function ApiResponseView(props: Props) { name: currentActionConfig ? currentActionConfig.name : "API", id: currentActionConfig?.id || "", }; - const tabs = [ + const tabs: BottomTab[] = [ { key: "response", title: "Response", @@ -526,7 +524,7 @@ function ApiResponseView(props: Props) { {responseTabComponent( selectedControl || segmentedControlOptions[0]?.value, actionResponse?.body, - responsePaneHeight, + responseTabHeight, )} ) : null} @@ -579,32 +577,34 @@ function ApiResponseView(props: Props) { ), }, - { - key: DEBUGGER_TAB_KEYS.ERROR_TAB, - title: createMessage(DEBUGGER_ERRORS), - count: errorCount, - panelComponent: , - }, - { - key: DEBUGGER_TAB_KEYS.LOGS_TAB, - title: createMessage(DEBUGGER_LOGS), - panelComponent: , - }, - { - key: DEBUGGER_TAB_KEYS.INSPECT_TAB, - title: createMessage(INSPECT_ENTITY), - panelComponent: , - }, ]; + if (ideViewMode === EditorViewMode.FullScreen) { + tabs.push( + { + key: DEBUGGER_TAB_KEYS.ERROR_TAB, + title: createMessage(DEBUGGER_ERRORS), + count: errorCount, + panelComponent: , + }, + { + key: DEBUGGER_TAB_KEYS.LOGS_TAB, + title: createMessage(DEBUGGER_LOGS), + panelComponent: , + }, + ); + } + // close the debugger //TODO: move this to a common place - const onClose = () => dispatch(showDebugger(false)); + const onClose = () => dispatch(setApiPaneDebuggerState({ open: false })); + + if (!open) return null; return ( { updateResponsePaneHeight(height); }} @@ -659,7 +659,7 @@ function ApiResponseView(props: Props) { { const datasourceStructure = useSelector((state) => getDatasourceStructureById(state, props.datasourceId), ); - const responsePaneHeight = useSelector(getResponsePaneHeight); + const { responseTabHeight } = useSelector(getQueryPaneDebuggerState); const [selectedTable, setSelectedTable] = useState(); const selectedTableItems = find(datasourceStructure?.tables, [ @@ -69,7 +69,7 @@ const Schema = (props: Props) => { @@ -94,7 +94,7 @@ const Schema = (props: Props) => { borderLeft="1px solid var(--ads-v2-color-border)" flex="1" flexDirection="column" - height={`${responsePaneHeight - 45}px`} + height={`${responseTabHeight - 45}px`} justifyContent={ isLoading || columns.length === 0 ? "center" : "flex-start" } diff --git a/app/client/src/components/editorComponents/Debugger/hooks/useDebuggerTriggerClick.ts b/app/client/src/components/editorComponents/Debugger/hooks/useDebuggerTriggerClick.ts new file mode 100644 index 0000000000..f186805c33 --- /dev/null +++ b/app/client/src/components/editorComponents/Debugger/hooks/useDebuggerTriggerClick.ts @@ -0,0 +1,96 @@ +import { useLocation } from "react-router"; +import { DEBUGGER_TAB_KEYS } from "../helpers"; +import { setCanvasDebuggerState } from "actions/debuggerActions"; +import AnalyticsUtil from "utils/AnalyticsUtil"; +import type { FocusEntityInfo } from "navigation/FocusEntity"; +import { FocusEntity, identifyEntityFromPath } from "navigation/FocusEntity"; +import { setJsPaneDebuggerState } from "actions/jsPaneActions"; +import { setApiPaneDebuggerState } from "actions/apiPaneActions"; +import { setQueryPaneDebuggerState } from "actions/queryPaneActions"; +import { getJsPaneDebuggerState } from "selectors/jsPaneSelectors"; +import { getApiPaneDebuggerState } from "selectors/apiPaneSelectors"; +import { getQueryPaneDebuggerState } from "selectors/queryPaneSelectors"; +import { getCanvasDebuggerState } from "selectors/debuggerSelectors"; +import { getIDEViewMode } from "selectors/ideSelectors"; +import { useDispatch, useSelector } from "react-redux"; +import { EditorViewMode } from "@appsmith/entities/IDE/constants"; +import type { ReduxAction } from "@appsmith/constants/ReduxActionConstants"; +import type { CanvasDebuggerState } from "reducers/uiReducers/debuggerReducer"; +import type { AppState } from "@appsmith/reducers"; + +interface Config { + set: ( + payload: Partial, + ) => ReduxAction>; + get: (state: AppState) => CanvasDebuggerState; +} + +const canvasDebuggerConfig: Config = { + set: setCanvasDebuggerState, + get: getCanvasDebuggerState, +}; + +const queryDebuggerConfig: Config = { + set: setQueryPaneDebuggerState, + get: getQueryPaneDebuggerState, +}; + +const getConfig = (focusInfo: FocusEntityInfo): Config => { + switch (focusInfo.entity) { + case FocusEntity.QUERY: + if (focusInfo.params.apiId) { + if (focusInfo.params.pluginPackageName) { + return queryDebuggerConfig; + } + return { + set: setApiPaneDebuggerState, + get: getApiPaneDebuggerState, + }; + } + return queryDebuggerConfig; + case FocusEntity.JS_OBJECT: + return { + set: setJsPaneDebuggerState, + get: getJsPaneDebuggerState, + }; + default: + return canvasDebuggerConfig; + } +}; + +const useDebuggerTriggerClick = () => { + const location = useLocation(); + const currentFocus = identifyEntityFromPath(location.pathname); + const ideState = useSelector(getIDEViewMode); + const dispatch = useDispatch(); + + const config = + ideState === EditorViewMode.FullScreen + ? getConfig(currentFocus) + : canvasDebuggerConfig; + + const state = useSelector(config.get); + return () => { + // If debugger is already open and selected tab is error tab then we will close debugger. + if (state.open && state.selectedTab === DEBUGGER_TAB_KEYS.ERROR_TAB) { + dispatch(config.set({ open: false })); + } else { + // If debugger is not open then we will open debugger and show error tab. + if (!state.open) { + dispatch( + config.set({ open: true, selectedTab: DEBUGGER_TAB_KEYS.ERROR_TAB }), + ); + } + // Select error tab if debugger is open and selected tab is not error tab. + // And also when we are opening debugger. + dispatch(config.set({ selectedTab: DEBUGGER_TAB_KEYS.ERROR_TAB })); + } + if (!state.open) { + AnalyticsUtil.logEvent("OPEN_DEBUGGER", { + source: "TRIGGER", + }); + } + }; +}; + +export default useDebuggerTriggerClick; diff --git a/app/client/src/components/editorComponents/Debugger/index.tsx b/app/client/src/components/editorComponents/Debugger/index.tsx index cc1d767861..2cf4fa833c 100644 --- a/app/client/src/components/editorComponents/Debugger/index.tsx +++ b/app/client/src/components/editorComponents/Debugger/index.tsx @@ -1,20 +1,10 @@ import React, { useEffect } from "react"; import { useDispatch, useSelector } from "react-redux"; import DebuggerTabs from "./DebuggerTabs"; -import { - setDebuggerSelectedTab, - setErrorCount, - showDebugger as showDebuggerAction, -} from "actions/debuggerActions"; -import AnalyticsUtil from "utils/AnalyticsUtil"; -import { stopEventPropagation } from "utils/AppsmithUtils"; -import { - getDebuggerSelectedTab, - getMessageCount, - showDebuggerFlag, -} from "selectors/debuggerSelectors"; -import { DEBUGGER_TAB_KEYS } from "./helpers"; +import { setErrorCount } from "actions/debuggerActions"; +import { getMessageCount, showDebuggerFlag } from "selectors/debuggerSelectors"; import { Button, Tooltip } from "design-system"; +import useDebuggerTriggerClick from "./hooks/useDebuggerTriggerClick"; function Debugger() { // Debugger render flag @@ -25,33 +15,13 @@ function Debugger() { export function DebuggerTrigger() { const dispatch = useDispatch(); - const showDebugger = useSelector(showDebuggerFlag); - const selectedTab = useSelector(getDebuggerSelectedTab); const messageCounters = useSelector(getMessageCount); useEffect(() => { dispatch(setErrorCount(messageCounters.errors)); }); - const onClick = (e: any) => { - // If debugger is already open and selected tab is error tab then we will close debugger. - if (showDebugger && selectedTab === DEBUGGER_TAB_KEYS.ERROR_TAB) { - dispatch(showDebuggerAction(false)); - } else { - // If debugger is not open then we will open debugger and show error tab. - if (!showDebugger) { - dispatch(showDebuggerAction(true)); - } - // Select error tab if debugger is open and selected tab is not error tab. - // And also when we are opening debugger. - dispatch(setDebuggerSelectedTab(DEBUGGER_TAB_KEYS.ERROR_TAB)); - } - if (!showDebugger) - AnalyticsUtil.logEvent("OPEN_DEBUGGER", { - source: "CANVAS", - }); - stopEventPropagation(e); - }; + const onClick = useDebuggerTriggerClick(); //tooltip will always show error count as we are opening error tab on click of debugger. const tooltipContent = diff --git a/app/client/src/components/editorComponents/JSResponseView.tsx b/app/client/src/components/editorComponents/JSResponseView.tsx index 915f218d29..8f82dd21d6 100644 --- a/app/client/src/components/editorComponents/JSResponseView.tsx +++ b/app/client/src/components/editorComponents/JSResponseView.tsx @@ -1,5 +1,5 @@ import type { RefObject } from "react"; -import React, { useEffect, useRef, useCallback, useState } from "react"; +import React, { useCallback, useEffect, useRef, useState } from "react"; import { connect, useDispatch, useSelector } from "react-redux"; import type { RouteComponentProps } from "react-router"; import { withRouter } from "react-router"; @@ -9,15 +9,14 @@ import type { AppState } from "@appsmith/reducers"; import type { JSEditorRouteParams } from "constants/routes"; import { createMessage, - DEBUGGER_LOGS, DEBUGGER_ERRORS, + DEBUGGER_LOGS, EXECUTING_FUNCTION, NO_JS_FUNCTION_RETURN_VALUE, UPDATING_JS_COLLECTION, } from "@appsmith/constants/messages"; import type { EditorTheme } from "./CodeEditor/EditorConfig"; import DebuggerLogs from "./Debugger/DebuggerLogs"; -import ErrorLogs from "./Debugger/Errors"; import Resizer, { ResizerCSS } from "./Debugger/Resizer"; import type { JSAction } from "entities/JSCollection"; import ReadOnlyEditor from "components/editorComponents/ReadOnlyEditor"; @@ -26,22 +25,14 @@ import LoadingOverlayScreen from "components/editorComponents/LoadingOverlayScre import type { JSCollectionData } from "@appsmith/reducers/entityReducers/jsActionsReducer"; import type { EvaluationError } from "utils/DynamicBindingUtils"; import { DEBUGGER_TAB_KEYS } from "./Debugger/helpers"; +import type { BottomTab } from "./EntityBottomTabs"; import EntityBottomTabs from "./EntityBottomTabs"; import { TAB_MIN_HEIGHT } from "design-system-old"; import { CodeEditorWithGutterStyles } from "pages/Editor/JSEditor/constants"; import { getIsSavingEntity } from "selectors/editorSelectors"; import { getJSResponseViewState } from "./utils"; -import { - getDebuggerSelectedTab, - getFilteredErrors, - getResponsePaneHeight, -} from "selectors/debuggerSelectors"; +import { getFilteredErrors } from "selectors/debuggerSelectors"; import { ActionExecutionResizerHeight } from "pages/Editor/APIEditor/constants"; -import { - setDebuggerSelectedTab, - setResponsePaneHeight, - showDebugger, -} from "actions/debuggerActions"; import { NoResponse, ResponseTabErrorContainer, @@ -52,6 +43,11 @@ import LOG_TYPE from "entities/AppsmithConsole/logtype"; import type { SourceEntity, Log } from "entities/AppsmithConsole"; import { ENTITY_TYPE } from "@appsmith/entities/AppsmithConsole/utils"; import { CloseDebugger } from "./Debugger/DebuggerTabs"; +import { getJsPaneDebuggerState } from "selectors/jsPaneSelectors"; +import { setJsPaneDebuggerState } from "actions/jsPaneActions"; +import { getIDEViewMode } from "selectors/ideSelectors"; +import { EditorViewMode } from "@appsmith/entities/IDE/constants"; +import ErrorLogs from "./Debugger/Errors"; const ResponseContainer = styled.div` ${ResizerCSS}; @@ -210,7 +206,10 @@ function JSResponseView(props: Props) { } } } catch (e) {} - const tabs = [ + + const ideViewMode = useSelector(getIDEViewMode); + + const tabs: BottomTab[] = [ { key: "response", title: "Response", @@ -279,12 +278,6 @@ function JSResponseView(props: Props) { ), }, - { - key: DEBUGGER_TAB_KEYS.ERROR_TAB, - title: createMessage(DEBUGGER_ERRORS), - count: errorCount, - panelComponent: , - }, { key: DEBUGGER_TAB_KEYS.LOGS_TAB, title: createMessage(DEBUGGER_LOGS), @@ -292,24 +285,34 @@ function JSResponseView(props: Props) { }, ]; + if (ideViewMode === EditorViewMode.FullScreen) { + tabs.push({ + key: DEBUGGER_TAB_KEYS.ERROR_TAB, + title: createMessage(DEBUGGER_ERRORS), + count: errorCount, + panelComponent: , + }); + } + // get the selected tab from the store. - const selectedResponseTab = useSelector(getDebuggerSelectedTab); + const { open, responseTabHeight, selectedTab } = useSelector( + getJsPaneDebuggerState, + ); + // set the selected tab in the store. const setSelectedResponseTab = useCallback((selectedTab: string) => { - dispatch(setDebuggerSelectedTab(selectedTab)); + dispatch(setJsPaneDebuggerState({ selectedTab })); }, []); - // get the height of the response pane. - const responseTabHeight = useSelector(getResponsePaneHeight); // set the height of the response pane on resize. const setResponseHeight = useCallback((height: number) => { - dispatch(setResponsePaneHeight(height)); + dispatch(setJsPaneDebuggerState({ responseTabHeight: height })); }, []); // close the debugger - const onClose = () => dispatch(showDebugger(false)); + const onClose = () => dispatch(setJsPaneDebuggerState({ open: false })); // Do not render if header tab is selected in the bottom bar. - return !(selectedResponseTab === DEBUGGER_TAB_KEYS.HEADER_TAB) ? ( + return open && selectedTab ? ( diff --git a/app/client/src/navigation/FocusElements.ts b/app/client/src/navigation/FocusElements.ts index c6356c5072..61441253c2 100644 --- a/app/client/src/navigation/FocusElements.ts +++ b/app/client/src/navigation/FocusElements.ts @@ -26,6 +26,9 @@ export enum FocusElement { SelectedJSObject = "SelectedJSObject", SelectedSegment = "SelectedSegment", IDETabs = "IDETabs", + QueryDebugger = "QueryDebugger", + ApiDebugger = "ApiDebugger", + JSDebugger = "JSDebugger", } export enum FocusElementConfigType { diff --git a/app/client/src/pages/Editor/APIEditor/CommonEditorForm.tsx b/app/client/src/pages/Editor/APIEditor/CommonEditorForm.tsx index ee25dfa67d..eacb7df5a0 100644 --- a/app/client/src/pages/Editor/APIEditor/CommonEditorForm.tsx +++ b/app/client/src/pages/Editor/APIEditor/CommonEditorForm.tsx @@ -50,7 +50,6 @@ import { replayHighlightClass } from "globalStyles/portals"; import { getPlugin } from "@appsmith/selectors/entitiesSelector"; import { setApiPaneConfigSelectedTabIndex } from "actions/apiPaneActions"; import type { AutoGeneratedHeader } from "./helpers"; -import { showDebuggerFlag } from "selectors/debuggerSelectors"; import { getApiPaneConfigSelectedTabIndex } from "selectors/apiPaneSelectors"; import { noop } from "lodash"; import { DEFAULT_DATASOURCE_NAME } from "constants/ApiEditorConstants/ApiEditorConstants"; @@ -573,9 +572,6 @@ function CommonEditorForm(props: CommonFormPropsWithExtraParams) { actionDatasourceName === DEFAULT_DATASOURCE_NAME) || !isExecutePermitted; - // Debugger render flag - const showDebugger = useSelector(showDebuggerFlag); - const isGraphql = isGraphqlPlugin(plugin); const theme = EditorTheme.LIGHT; @@ -732,19 +728,17 @@ function CommonEditorForm(props: CommonFormPropsWithExtraParams) { - {showDebugger && ( - - )} + { @@ -160,33 +160,6 @@ type DatasourceStructureProps = Partial & { }; const DatasourceStructure = (props: DatasourceStructureProps) => { - const [containerHeight, setContainerHeight] = useState(); - const containerRef = useRef(null); - - const updateContainerHeight = () => { - if (containerRef.current?.offsetHeight) { - setContainerHeight(containerRef.current?.offsetHeight); - } - }; - - const resizeObserver = useRef( - new ResizeObserver(() => { - updateContainerHeight(); - }), - ); - - useEffect(() => { - updateContainerHeight(); - if (containerRef.current) { - resizeObserver.current.observe(containerRef.current); - } - return () => { - if (containerRef.current) { - resizeObserver.current.unobserve(containerRef.current); - } - }; - }, []); - const Row = (index: number) => { const structure = props.tables[index]; @@ -201,15 +174,12 @@ const DatasourceStructure = (props: DatasourceStructureProps) => { }; return ( - - {containerHeight && ( - - )} + + ); }; diff --git a/app/client/src/pages/Editor/IDE/EditorPane/components/ActionDrawer.tsx b/app/client/src/pages/Editor/IDE/EditorPane/components/ActionDrawer.tsx index 28f67cafa6..af612152be 100644 --- a/app/client/src/pages/Editor/IDE/EditorPane/components/ActionDrawer.tsx +++ b/app/client/src/pages/Editor/IDE/EditorPane/components/ActionDrawer.tsx @@ -2,8 +2,6 @@ import React, { useEffect, useRef, useState } from "react"; import styled from "styled-components"; import { ResizerCSS } from "components/editorComponents/Debugger/Resizer"; import Resizable from "components/editorComponents/Debugger/Resizer"; -import { useSelector } from "react-redux"; -import { getResponsePaneHeight } from "selectors/debuggerSelectors"; import { ActionExecutionResizerHeight } from "pages/Editor/APIEditor/constants"; import type { BottomTab } from "components/editorComponents/EntityBottomTabs"; import EntityBottomTabs from "components/editorComponents/EntityBottomTabs"; @@ -23,11 +21,11 @@ interface Props { onResizeComplete: (height: number) => void; isRunning: boolean; tabs: BottomTab[]; + height?: number; } const ActionDrawer = (props: Props) => { const panelRef = useRef(null); - const responsePaneHeight = useSelector(getResponsePaneHeight); const [selectedTab, setSelectedTab] = useState(""); useEffect(() => { if (props.tabs.length) { @@ -38,7 +36,7 @@ const ActionDrawer = (props: Props) => { return ( void; } const ActionEditor = (props: Props) => { const dispatch = useDispatch(); const setDrawerHeight = useCallback((height: number) => { - dispatch(setResponsePaneHeight(height)); + if (props.onSetHeight) { + dispatch(props.onSetHeight(height)); + } }, []); const [settingsOpen, setSettingsOpen] = useState(false); @@ -42,6 +45,7 @@ const ActionEditor = (props: Props) => { /> ) : null; return ( - - {dataSources.length === 0 && } - {dataSources.length === 0 && this.props.mockDatasources.length > 0 && ( - <> - {mockDataSection} - - - )} - {isEnabledForCreateNew && ( - <> - - - - )} - - - - - - {dataSources.length > 0 && this.props.mockDatasources.length > 0 && ( - <> - - {mockDataSection} - - )} - + <> + + {dataSources.length === 0 && } + {dataSources.length === 0 && + this.props.mockDatasources.length > 0 && ( + <> + {mockDataSection} + + + )} + {isEnabledForCreateNew && ( + <> + + + + )} + + + + + + {dataSources.length > 0 && this.props.mockDatasources.length > 0 && ( + <> + + {mockDataSection} + + )} + + {showDebugger && } + ); } } diff --git a/app/client/src/pages/Editor/JSEditor/Form.tsx b/app/client/src/pages/Editor/JSEditor/Form.tsx index e6b5cb5281..1972b5a1b5 100644 --- a/app/client/src/pages/Editor/JSEditor/Form.tsx +++ b/app/client/src/pages/Editor/JSEditor/Form.tsx @@ -14,6 +14,7 @@ import JSObjectNameEditor from "./JSObjectNameEditor"; import { setActiveJSAction, setJsPaneConfigSelectedTab, + setJsPaneDebuggerState, startExecutingJSFunction, updateJSCollectionBody, } from "actions/jsPaneActions"; @@ -60,7 +61,6 @@ import history from "utils/history"; import { CursorPositionOrigin } from "@appsmith/reducers/uiReducers/editorContextReducer"; import LazyCodeEditor from "components/editorComponents/LazyCodeEditor"; import styled from "styled-components"; -import { showDebuggerFlag } from "selectors/debuggerSelectors"; import { Tab, TabPanel, Tabs, TabsList } from "design-system"; import { JSEditorTab } from "reducers/uiReducers/jsPaneReducer"; import { useFeatureFlag } from "utils/hooks/useFeatureFlag"; @@ -70,6 +70,7 @@ import { getHasManageActionPermission, } from "@appsmith/utils/BusinessFeatures/permissionPageHelpers"; import type { JSCollectionData } from "@appsmith/reducers/entityReducers/jsActionsReducer"; +import { DEBUGGER_TAB_KEYS } from "../../../components/editorComponents/Debugger/helpers"; interface JSFormProps { jsCollectionData: JSCollectionData; @@ -202,6 +203,12 @@ function JSEditorForm({ // Executes JS action const executeJSAction = (jsAction: JSAction, from: EventLocation) => { + dispatch( + setJsPaneDebuggerState({ + open: true, + selectedTab: DEBUGGER_TAB_KEYS.RESPONSE_TAB, + }), + ); setActiveResponse(jsAction); if (jsAction.id !== selectedJSActionOption.data?.id) setSelectedJSActionOption(convertJSActionToDropdownOption(jsAction)); @@ -303,9 +310,6 @@ function JSEditorForm({ const selectedConfigTab = useSelector(getJSPaneConfigSelectedTab); - // Debugger render flag - const showDebugger = useSelector(showDebuggerFlag); - const setSelectedConfigTab = useCallback((selectedTab: JSEditorTab) => { dispatch(setJsPaneConfigSelectedTab(selectedTab)); }, []); @@ -418,23 +422,21 @@ function JSEditorForm({ )} - {showDebugger ? ( - - | KeyboardEvent, - ) => { - handleRunAction(event, "JS_OBJECT_RESPONSE_RUN_BUTTON"); - }} - theme={theme} - /> - ) : null} + + | KeyboardEvent, + ) => { + handleRunAction(event, "JS_OBJECT_RESPONSE_RUN_BUTTON"); + }} + theme={theme} + /> diff --git a/app/client/src/pages/Editor/QueryEditor/EditorJSONtoForm.tsx b/app/client/src/pages/Editor/QueryEditor/EditorJSONtoForm.tsx index 717ec8c330..3434429581 100644 --- a/app/client/src/pages/Editor/QueryEditor/EditorJSONtoForm.tsx +++ b/app/client/src/pages/Editor/QueryEditor/EditorJSONtoForm.tsx @@ -1,4 +1,4 @@ -import { useContext, useEffect, useState } from "react"; +import { useContext } from "react"; import React, { useCallback } from "react"; import type { InjectedFormProps } from "redux-form"; import { noop } from "lodash"; @@ -15,7 +15,6 @@ import ActionSettings from "pages/Editor/ActionSettings"; import { Button, Tab, TabPanel, Tabs, TabsList, Tooltip } from "design-system"; import styled from "styled-components"; import FormRow from "components/editorComponents/FormRow"; -import { ResizerCSS } from "components/editorComponents/Debugger/Resizer"; import { createMessage, DOCUMENTATION, @@ -30,16 +29,10 @@ import ActionRightPane, { import type { ActionResponse } from "api/ActionAPI"; import type { Plugin } from "api/PluginApi"; import type { UIComponentTypes } from "api/PluginApi"; -import { DEBUGGER_TAB_KEYS } from "components/editorComponents/Debugger/helpers"; import { EDITOR_TABS } from "constants/QueryEditorConstants"; import type { FormEvalOutput } from "reducers/evaluationReducers/formEvaluationReducer"; import { getQueryPaneConfigSelectedTabIndex } from "selectors/queryPaneSelectors"; import { setQueryPaneConfigSelectedTabIndex } from "actions/queryPaneActions"; -import { ActionExecutionResizerHeight } from "pages/Editor/APIEditor/constants"; -import { - getDebuggerSelectedTab, - showDebuggerFlag, -} from "selectors/debuggerSelectors"; import type { SourceEntity } from "entities/AppsmithConsole"; import { ENTITY_TYPE as SOURCE_ENTITY_TYPE } from "@appsmith/entities/AppsmithConsole/utils"; import { DocsLink, openDoc } from "../../../constants/DocumentationLinks"; @@ -47,7 +40,6 @@ import { useFeatureFlag } from "utils/hooks/useFeatureFlag"; import { FEATURE_FLAG } from "@appsmith/entities/FeatureFlag"; import { QueryEditorContext } from "./QueryEditorContext"; import QueryDebuggerTabs from "./QueryDebuggerTabs"; -import { setDebuggerSelectedTab, showDebugger } from "actions/debuggerActions"; import useShowSchema from "components/editorComponents/ActionRightPane/useShowSchema"; import { doesPluginRequireDatasource } from "@appsmith/entities/Engine/actionHelpers"; import FormRender from "./FormRender"; @@ -79,16 +71,6 @@ const QueryFormContainer = styled.form` } `; -export const TabbedViewContainer = styled.div` - ${ResizerCSS}; - height: ${ActionExecutionResizerHeight}px; - // Minimum height of bottom tabs as it can be resized - min-height: 36px; - width: 100%; - background-color: var(--ads-v2-color-bg); - border-top: 1px solid var(--ads-v2-color-border); -`; - const SettingsWrapper = styled.div` ${thinScrollbar}; height: 100%; @@ -221,7 +203,6 @@ export function EditorJSONtoForm(props: Props) { onCreateDatasourceClick, onRunClick, plugin, - responseDisplayFormat, runErrorMessage, settingConfig, uiComponent, @@ -242,8 +223,6 @@ export function EditorJSONtoForm(props: Props) { const currentActionConfig: Action | undefined = actions.find( (action) => action.id === params.apiId || action.id === params.queryId, ); - const [showResponseOnFirstLoad, setShowResponseOnFirstLoad] = - useState(false); const pluginRequireDatasource = doesPluginRequireDatasource(plugin); @@ -264,38 +243,6 @@ export function EditorJSONtoForm(props: Props) { const dispatch = useDispatch(); - // These useEffects are used to open the response tab by default for page load queries - // as for page load queries, query response is available and can be shown in response tab - useEffect(() => { - // actionResponse and responseDisplayFormat is present only when query has response available - if ( - responseDisplayFormat && - !!responseDisplayFormat?.title && - actionResponse && - actionResponse.isExecutionSuccess && - !showResponseOnFirstLoad - ) { - dispatch(showDebugger(true)); - dispatch(setDebuggerSelectedTab(DEBUGGER_TAB_KEYS.RESPONSE_TAB)); - setShowResponseOnFirstLoad(true); - } - }, [responseDisplayFormat, actionResponse, showResponseOnFirstLoad]); - - useEffect(() => { - if (showSchema) { - dispatch(showDebugger(true)); - dispatch(setDebuggerSelectedTab(DEBUGGER_TAB_KEYS.SCHEMA_TAB)); - } - }, [showSchema]); - - // When multiple page load queries exist, we want to response tab by default for all of them - // Hence this useEffect will reset showResponseOnFirstLoad flag used to track whether to show response tab or not - useEffect(() => { - if (!!currentActionConfig?.id) { - setShowResponseOnFirstLoad(false); - } - }, [currentActionConfig?.id]); - const handleDocumentationClick = () => { openDoc(DocsLink.QUERY, plugin?.documentationLink, plugin?.name); }; @@ -311,15 +258,10 @@ export function EditorJSONtoForm(props: Props) { const selectedConfigTab = useSelector(getQueryPaneConfigSelectedTabIndex); - // Debugger render flag - const renderDebugger = useSelector(showDebuggerFlag); - const setSelectedConfigTab = useCallback((selectedIndex: string) => { dispatch(setQueryPaneConfigSelectedTabIndex(selectedIndex)); }, []); - const selectedResponseTab = useSelector(getDebuggerSelectedTab); - // here we check for normal conditions for opening action pane // or if any of the flags are true, We should open the actionpane by default. const shouldOpenActionPaneByDefault = @@ -465,19 +407,16 @@ export function EditorJSONtoForm(props: Props) { )} - {renderDebugger && - selectedResponseTab !== DEBUGGER_TAB_KEYS.HEADER_TAB && ( - - )} + {showRightPane && ( diff --git a/app/client/src/pages/Editor/QueryEditor/QueryDebuggerTabs.tsx b/app/client/src/pages/Editor/QueryEditor/QueryDebuggerTabs.tsx index ea82cda35a..701584f8b4 100644 --- a/app/client/src/pages/Editor/QueryEditor/QueryDebuggerTabs.tsx +++ b/app/client/src/pages/Editor/QueryEditor/QueryDebuggerTabs.tsx @@ -1,20 +1,11 @@ -import { - setDebuggerSelectedTab, - setResponsePaneHeight, - showDebugger, -} from "actions/debuggerActions"; import { CloseDebugger } from "components/editorComponents/Debugger/DebuggerTabs"; import type { BottomTab } from "components/editorComponents/EntityBottomTabs"; import EntityBottomTabs from "components/editorComponents/EntityBottomTabs"; -import React, { useCallback, useEffect, useRef } from "react"; +import React, { useCallback, useEffect, useRef, useState } from "react"; import { useDispatch, useSelector } from "react-redux"; import styled from "styled-components"; import { ActionExecutionResizerHeight } from "pages/Editor/APIEditor/constants"; -import { - getDebuggerSelectedTab, - getErrorCount, - getResponsePaneHeight, -} from "selectors/debuggerSelectors"; +import { getErrorCount } from "selectors/debuggerSelectors"; import { Text, TextType } from "design-system-old"; import Resizable, { ResizerCSS, @@ -23,12 +14,10 @@ import { DEBUGGER_TAB_KEYS } from "components/editorComponents/Debugger/helpers" import { DEBUGGER_ERRORS, DEBUGGER_LOGS, - INSPECT_ENTITY, createMessage, } from "@appsmith/constants/messages"; import DebuggerLogs from "components/editorComponents/Debugger/DebuggerLogs"; import ErrorLogs from "components/editorComponents/Debugger/Errors"; -import EntityDeps from "components/editorComponents/Debugger/EntityDependecies"; import Schema from "components/editorComponents/Debugger/Schema"; import type { ActionResponse } from "api/ActionAPI"; import { isString } from "lodash"; @@ -42,6 +31,11 @@ import { import { DatasourceComponentTypes } from "api/PluginApi"; import { fetchDatasourceStructure } from "actions/datasourceActions"; import { DatasourceStructureContext } from "entities/Datasource"; +import { getQueryPaneDebuggerState } from "selectors/queryPaneSelectors"; +import { setQueryPaneDebuggerState } from "actions/queryPaneActions"; +import { actionResponseDisplayDataFormats } from "../utils"; +import { getIDEViewMode } from "selectors/ideSelectors"; +import { EditorViewMode } from "@appsmith/entities/IDE/constants"; const ResultsCount = styled.div` position: absolute; @@ -86,8 +80,16 @@ function QueryDebuggerTabs({ const panelRef = useRef(null); const dispatch = useDispatch(); - const selectedResponseTab = useSelector(getDebuggerSelectedTab); - const responsePaneHeight = useSelector(getResponsePaneHeight); + const { open, responseTabHeight, selectedTab } = useSelector( + getQueryPaneDebuggerState, + ); + + const { responseDisplayFormat } = + actionResponseDisplayDataFormats(actionResponse); + + const [showResponseOnFirstLoad, setShowResponseOnFirstLoad] = + useState(false); + const errorCount = useSelector(getErrorCount); const pluginDatasourceForm = useSelector((state) => @@ -120,6 +122,46 @@ function QueryDebuggerTabs({ } }, []); + // These useEffects are used to open the response tab by default for page load queries + // as for page load queries, query response is available and can be shown in response tab + useEffect(() => { + // actionResponse and responseDisplayFormat is present only when query has response available + if ( + responseDisplayFormat && + !!responseDisplayFormat?.title && + actionResponse && + actionResponse.isExecutionSuccess && + !showResponseOnFirstLoad + ) { + dispatch( + setQueryPaneDebuggerState({ + open: true, + selectedTab: DEBUGGER_TAB_KEYS.RESPONSE_TAB, + }), + ); + setShowResponseOnFirstLoad(true); + } + }, [responseDisplayFormat, actionResponse, showResponseOnFirstLoad]); + + useEffect(() => { + if (showSchema && !selectedTab) { + dispatch( + setQueryPaneDebuggerState({ + open: true, + selectedTab: DEBUGGER_TAB_KEYS.SCHEMA_TAB, + }), + ); + } + }, [showSchema, currentActionConfig?.id, selectedTab]); + + // When multiple page load queries exist, we want to response tab by default for all of them + // Hence this useEffect will reset showResponseOnFirstLoad flag used to track whether to show response tab or not + useEffect(() => { + if (!!currentActionConfig?.id) { + setShowResponseOnFirstLoad(false); + } + }, [currentActionConfig?.id]); + // Query is executed even once during the session, show the response data. if (actionResponse) { if (isString(actionResponse.body)) { @@ -140,32 +182,36 @@ function QueryDebuggerTabs({ } const setQueryResponsePaneHeight = useCallback((height: number) => { - dispatch(setResponsePaneHeight(height)); + dispatch(setQueryPaneDebuggerState({ responseTabHeight: height })); + }, []); + + const onClose = useCallback(() => { + dispatch(setQueryPaneDebuggerState({ open: false })); }, []); - const onClose = () => dispatch(showDebugger(false)); const setSelectedResponseTab = useCallback((tabKey: string) => { - dispatch(setDebuggerSelectedTab(tabKey)); + dispatch(setQueryPaneDebuggerState({ selectedTab: tabKey })); }, []); - const responseTabs: BottomTab[] = [ - { - key: DEBUGGER_TAB_KEYS.ERROR_TAB, - title: createMessage(DEBUGGER_ERRORS), - count: errorCount, - panelComponent: , - }, - { - key: DEBUGGER_TAB_KEYS.LOGS_TAB, - title: createMessage(DEBUGGER_LOGS), - panelComponent: , - }, - { - key: DEBUGGER_TAB_KEYS.INSPECT_TAB, - title: createMessage(INSPECT_ENTITY), - panelComponent: , - }, - ]; + const ideViewMode = useSelector(getIDEViewMode); + + const responseTabs: BottomTab[] = []; + + if (ideViewMode === EditorViewMode.FullScreen) { + responseTabs.push( + { + key: DEBUGGER_TAB_KEYS.ERROR_TAB, + title: createMessage(DEBUGGER_ERRORS), + count: errorCount, + panelComponent: , + }, + { + key: DEBUGGER_TAB_KEYS.LOGS_TAB, + title: createMessage(DEBUGGER_LOGS), + panelComponent: , + }, + ); + } if (currentActionConfig) { responseTabs.unshift({ @@ -197,13 +243,15 @@ function QueryDebuggerTabs({ }); } + if (!open) return null; + return ( setQueryResponsePaneHeight(height) } @@ -226,7 +274,7 @@ function QueryDebuggerTabs({ { const actionResponse = useSelector((state) => getActionData(state, currentActionConfig.id), ); - const responsePaneHeight = useSelector(getResponsePaneHeight); + const { responseTabHeight } = useSelector(getQueryPaneDebuggerState); const { responseDataTypes, responseDisplayFormat } = actionResponseDisplayDataFormats(actionResponse); @@ -99,7 +99,7 @@ const QueryResponseTab = (props: Props) => { panelComponent: responseTabComponent( dataType.key, output, - responsePaneHeight, + responseTabHeight, ), }; }); @@ -278,7 +278,7 @@ const QueryResponseTab = (props: Props) => { {responseTabComponent( selectedControl || segmentedControlOptions[0]?.value, output, - responsePaneHeight, + responseTabHeight, )} )} diff --git a/app/client/src/reducers/uiReducers/debuggerReducer.ts b/app/client/src/reducers/uiReducers/debuggerReducer.ts index 9ac2c506ec..4b7eea94f8 100644 --- a/app/client/src/reducers/uiReducers/debuggerReducer.ts +++ b/app/client/src/reducers/uiReducers/debuggerReducer.ts @@ -160,6 +160,26 @@ const debuggerReducer = createImmerReducer(initialState, { ) => { state.context = action.context; }, + [ReduxActionTypes.SET_CANVAS_DEBUGGER_STATE]: ( + state: DebuggerReduxState, + action: { payload: Partial }, + ): DebuggerReduxState => { + return { + ...state, + isOpen: "open" in action.payload ? !!action.payload.open : state.isOpen, + context: { + ...state.context, + responseTabHeight: + "responseTabHeight" in action.payload + ? Number(action.payload.responseTabHeight) + : state.context.responseTabHeight, + selectedDebuggerTab: + "selectedTab" in action.payload + ? String(action.payload.selectedTab) + : state.context.selectedDebuggerTab, + }, + }; + }, // Resetting debugger state after env switch [ReduxActionTypes.SWITCH_ENVIRONMENT_SUCCESS]: () => { return klona(initialState); @@ -183,4 +203,10 @@ export interface DebuggerContext { selectedDebuggerFilter: string; } +export interface CanvasDebuggerState { + open: boolean; + responseTabHeight: number; + selectedTab?: string; +} + export default debuggerReducer; diff --git a/app/client/src/reducers/uiReducers/jsPaneReducer.ts b/app/client/src/reducers/uiReducers/jsPaneReducer.ts index 7553af7c5c..0403411d87 100644 --- a/app/client/src/reducers/uiReducers/jsPaneReducer.ts +++ b/app/client/src/reducers/uiReducers/jsPaneReducer.ts @@ -12,24 +12,31 @@ export enum JSEditorTab { SETTINGS = "SETTINGS", } +export interface JSPaneDebuggerState { + open: boolean; + responseTabHeight: number; + selectedTab?: string; +} + export interface JsPaneReduxState { isCreating: boolean; - isFetching: boolean; isSaving: Record; isDeleting: Record; isDirty: Record; selectedConfigTab: JSEditorTab; - responseTabHeight: number; + debugger: JSPaneDebuggerState; } const initialState: JsPaneReduxState = { isCreating: false, - isFetching: false, isSaving: {}, isDeleting: {}, isDirty: {}, - responseTabHeight: ActionExecutionResizerHeight, selectedConfigTab: JSEditorTab.CODE, + debugger: { + open: false, + responseTabHeight: ActionExecutionResizerHeight, + }, }; const jsPaneReducer = createReducer(initialState, { @@ -184,6 +191,18 @@ const jsPaneReducer = createReducer(initialState, { selectedConfigTab: selectedTab, }; }, + [ReduxActionTypes.SET_JS_PANE_DEBUGGER_STATE]: ( + state: JsPaneReduxState, + action: ReduxAction>, + ) => { + return { + ...state, + debugger: { + ...state.debugger, + ...action.payload, + }, + }; + }, }); export default jsPaneReducer; diff --git a/app/client/src/sagas/ActionExecution/PluginActionSaga.ts b/app/client/src/sagas/ActionExecution/PluginActionSaga.ts index 5cfc7abbdd..83f4c7c43f 100644 --- a/app/client/src/sagas/ActionExecution/PluginActionSaga.ts +++ b/app/client/src/sagas/ActionExecution/PluginActionSaga.ts @@ -18,9 +18,11 @@ import { updateAction, updateActionData, } from "actions/pluginActionActions"; -import { makeUpdateJSCollection } from "sagas/JSPaneSagas"; +import { + handleExecuteJSFunctionSaga, + makeUpdateJSCollection, +} from "sagas/JSPaneSagas"; -import { setDebuggerSelectedTab, showDebugger } from "actions/debuggerActions"; import type { ApplicationPayload, ReduxAction, @@ -38,12 +40,13 @@ import type { import ActionAPI from "api/ActionAPI"; import { getAction, + getCurrentActions, getCurrentPageNameByActionId, + getDatasource, + getJSCollectionFromAllEntities, getPlugin, isActionDirty, isActionSaving, - getDatasource, - getJSCollectionFromAllEntities, } from "@appsmith/selectors/entitiesSelector"; import { getIsGitSyncModalOpen } from "selectors/gitSyncSelectors"; import { @@ -51,15 +54,15 @@ import { getCurrentApplication, } from "@appsmith/selectors/applicationSelectors"; import { + find, + flatten, get, isArray, - isString, - set, - find, - isNil, - flatten, isArrayBuffer, isEmpty, + isNil, + isString, + set, unset, } from "lodash"; import AppsmithConsole from "utils/AppsmithConsole"; @@ -77,12 +80,12 @@ import type { Action } from "entities/Action"; import { PluginType } from "entities/Action"; import LOG_TYPE from "entities/AppsmithConsole/logtype"; import { + ACTION_EXECUTION_CANCELLED, + ACTION_EXECUTION_FAILED, createMessage, ERROR_ACTION_EXECUTE_FAIL, ERROR_FAIL_ON_PAGE_LOAD_ACTIONS, ERROR_PLUGIN_ACTION_EXECUTE, - ACTION_EXECUTION_CANCELLED, - ACTION_EXECUTION_FAILED, SWITCH_ENVIRONMENT_SUCCESS, } from "@appsmith/constants/messages"; import type { @@ -107,7 +110,7 @@ import * as log from "loglevel"; import { EMPTY_RESPONSE } from "components/editorComponents/emptyResponse"; import type { AppState } from "@appsmith/reducers"; import { DEFAULT_EXECUTE_ACTION_TIMEOUT_MS } from "@appsmith/constants/ApiConstants"; -import { evalWorker, evaluateActionBindings } from "sagas/EvaluationsSaga"; +import { evaluateActionBindings, evalWorker } from "sagas/EvaluationsSaga"; import { isBlobUrl, parseBlobUrl } from "utils/AppsmithUtils"; import { getType, Types } from "utils/TypeHelpers"; import { matchPath } from "react-router"; @@ -115,11 +118,11 @@ import { API_EDITOR_BASE_PATH, API_EDITOR_ID_PATH, API_EDITOR_PATH_WITH_SELECTED_PAGE_ID, + CURL_IMPORT_PAGE_PATH, INTEGRATION_EDITOR_PATH, + matchQueryBuilderPath, QUERIES_EDITOR_BASE_PATH, QUERIES_EDITOR_ID_PATH, - CURL_IMPORT_PAGE_PATH, - matchQueryBuilderPath, } from "constants/routes"; import { SAAS_EDITOR_API_ID_PATH } from "pages/Editor/SaaSEditor/constants"; import { APP_MODE } from "entities/App"; @@ -141,10 +144,9 @@ import { submitCurlImportForm } from "actions/importActions"; import type { curlImportFormValues } from "pages/Editor/APIEditor/helpers"; import { matchBasePath } from "@appsmith/pages/Editor/Explorer/helpers"; import { - isTrueObject, findDatatype, + isTrueObject, } from "@appsmith/workers/Evaluation/evaluationUtils"; -import { handleExecuteJSFunctionSaga } from "sagas/JSPaneSagas"; import type { Plugin } from "api/PluginApi"; import { setDefaultActionDisplayFormat } from "./PluginActionSagaUtils"; import { checkAndLogErrorsIfCyclicDependency } from "sagas/helper"; @@ -152,13 +154,15 @@ import { toast } from "design-system"; import type { TRunDescription } from "workers/Evaluation/fns/actionFns"; import { DEBUGGER_TAB_KEYS } from "components/editorComponents/Debugger/helpers"; import { FILE_SIZE_LIMIT_FOR_BLOBS } from "constants/WidgetConstants"; -import { getCurrentActions } from "@appsmith/selectors/entitiesSelector"; import type { ActionData } from "@appsmith/reducers/entityReducers/actionsReducer"; import { handleStoreOperations } from "./StoreActionSaga"; import { fetchPage } from "actions/pageActions"; import type { Datasource } from "entities/Datasource"; import { softRefreshDatasourceStructure } from "actions/datasourceActions"; -import { changeQuery } from "actions/queryPaneActions"; +import { + changeQuery, + setQueryPaneDebuggerState, +} from "actions/queryPaneActions"; import { getCurrentEnvironmentDetails, getCurrentEnvironmentName, @@ -179,6 +183,7 @@ import { } from "@appsmith/utils/actionExecutionUtils"; import type { JSAction, JSCollection } from "entities/JSCollection"; import { getAllowedActionAnalyticsKeys } from "constants/AppsmithActionConstants/formConfig/ActionAnalyticsConfig"; +import { setApiPaneDebuggerState } from "../../actions/apiPaneActions"; enum ActionResponseDataTypes { BINARY = "BINARY", @@ -792,7 +797,7 @@ export function* runActionSaga( const { paginationField } = reduxAction.payload; // open response tab in debugger on exection of action. if (!reduxAction.payload.skipOpeningDebugger) { - yield call(openDebugger); + yield call(openDebugger, plugin.type); } let payload = EMPTY_RESPONSE; @@ -1170,7 +1175,7 @@ function* executePageLoadAction(pageAction: PageAction, span?: OtlpSpan) { // open response tab in debugger on exection of action on page load. // Only if current page is the page on which the action is executed. if (window.location.pathname.includes(pageAction.id)) - yield call(openDebugger); + yield call(openDebugger, plugin.type); if (isError) { AppsmithConsole.addErrors([ @@ -1563,9 +1568,20 @@ function triggerFileUploadInstrumentation( } //Open debugger with response tab selected. -function* openDebugger() { - yield put(showDebugger(true)); - yield put(setDebuggerSelectedTab(DEBUGGER_TAB_KEYS.RESPONSE_TAB)); +function* openDebugger(pluginType: PluginType) { + if (pluginType === PluginType.API) { + yield put( + setApiPaneDebuggerState({ + open: true, + selectedTab: DEBUGGER_TAB_KEYS.RESPONSE_TAB, + }), + ); + } else { + setQueryPaneDebuggerState({ + open: true, + selectedTab: DEBUGGER_TAB_KEYS.RESPONSE_TAB, + }); + } } // Function to clear the action responses for the actions which are not executeOnLoad. diff --git a/app/client/src/sagas/JSPaneSagas.ts b/app/client/src/sagas/JSPaneSagas.ts index 79777228e2..b1c0a4de73 100644 --- a/app/client/src/sagas/JSPaneSagas.ts +++ b/app/client/src/sagas/JSPaneSagas.ts @@ -53,6 +53,7 @@ import { updateJSCollectionBodySuccess, updateJSFunction, executeJSFunctionInit, + setJsPaneDebuggerState, } from "actions/jsPaneActions"; import { getCurrentWorkspaceId } from "@appsmith/selectors/selectedWorkspaceSelectors"; import { getPluginIdOfPackageName } from "sagas/selectors"; @@ -91,14 +92,13 @@ import type { EventLocation } from "@appsmith/utils/analyticsUtilTypes"; import AnalyticsUtil from "utils/AnalyticsUtil"; import { checkAndLogErrorsIfCyclicDependency } from "./helper"; import { toast } from "design-system"; -import { setDebuggerSelectedTab, showDebugger } from "actions/debuggerActions"; import { DEBUGGER_TAB_KEYS } from "components/editorComponents/Debugger/helpers"; -import { getDebuggerSelectedTab } from "selectors/debuggerSelectors"; import { getIsServerDSLMigrationsEnabled } from "selectors/pageSelectors"; import { getJSActionNameToDisplay, getJSActionPathNameToDisplay, } from "@appsmith/utils/actionExecutionUtils"; +import { getJsPaneDebuggerState } from "selectors/jsPaneSelectors"; export interface GenerateDefaultJSObjectProps { name: string; @@ -434,15 +434,16 @@ export function* handleExecuteJSFunctionSaga(data: { // open response tab in debugger on runnning or page load js action. if (doesURLPathContainCollectionId || openDebugger) { - yield put(showDebugger(true)); + yield put(setJsPaneDebuggerState({ open: true })); - const debuggerSelectedTab: ReturnType = - yield select(getDebuggerSelectedTab); + const { selectedTab: debuggerSelectedTab } = yield select( + getJsPaneDebuggerState, + ); yield put( - setDebuggerSelectedTab( - debuggerSelectedTab || DEBUGGER_TAB_KEYS.RESPONSE_TAB, - ), + setJsPaneDebuggerState({ + selectedTab: debuggerSelectedTab || DEBUGGER_TAB_KEYS.RESPONSE_TAB, + }), ); } yield put({ @@ -481,8 +482,12 @@ export function* handleExecuteJSFunctionSaga(data: { } catch (error) { // open response tab in debugger on runnning js action. if (doesURLPathContainCollectionId) { - yield put(showDebugger(true)); - yield put(setDebuggerSelectedTab(DEBUGGER_TAB_KEYS.RESPONSE_TAB)); + yield put( + setJsPaneDebuggerState({ + open: true, + selectedTab: DEBUGGER_TAB_KEYS.RESPONSE_TAB, + }), + ); } AppsmithConsole.addErrors([ { diff --git a/app/client/src/selectors/apiPaneSelectors.ts b/app/client/src/selectors/apiPaneSelectors.ts index df0dd35505..9ec0ee0f5e 100644 --- a/app/client/src/selectors/apiPaneSelectors.ts +++ b/app/client/src/selectors/apiPaneSelectors.ts @@ -1,4 +1,6 @@ import type { AppState } from "@appsmith/reducers"; +import { createSelector } from "reselect"; +import { combinedPreviewModeSelector } from "./editorSelectors"; type GetFormData = ( state: AppState, @@ -18,3 +20,12 @@ export const getApiRightPaneSelectedTab = (state: AppState) => export const getIsRunning = (state: AppState, apiId: string) => state.ui.apiPane.isRunning[apiId]; + +export const getApiPaneDebuggerState = (state: AppState) => + state.ui.apiPane.debugger; + +export const showApiPaneDebugger = createSelector( + (state) => state.ui.apiPane.debugger.open, + combinedPreviewModeSelector, + (isOpen, isPreview) => isOpen && !isPreview, +); diff --git a/app/client/src/selectors/debuggerSelectors.tsx b/app/client/src/selectors/debuggerSelectors.tsx index 39ee035925..1fea171dd4 100644 --- a/app/client/src/selectors/debuggerSelectors.tsx +++ b/app/client/src/selectors/debuggerSelectors.tsx @@ -12,6 +12,7 @@ import { } from "@appsmith/workers/Evaluation/evaluationUtils"; import { getDataTree } from "./dataTreeSelectors"; import { combinedPreviewModeSelector } from "./editorSelectors"; +import type { CanvasDebuggerState } from "reducers/uiReducers/debuggerReducer"; interface ErrorObejct { [k: string]: Log; @@ -148,8 +149,22 @@ export const getScrollPosition = (state: AppState) => export const getDebuggerContext = (state: AppState) => state.ui.debugger.context; +export const getDebuggerOpen = (state: AppState) => state.ui.debugger.isOpen; + export const showDebuggerFlag = createSelector( - (state) => state.ui.debugger.isOpen, + getDebuggerOpen, combinedPreviewModeSelector, (isOpen, isPreview) => isOpen && !isPreview, ); + +export const getCanvasDebuggerState = createSelector( + showDebuggerFlag, + getDebuggerContext, + (openState, context): CanvasDebuggerState => { + return { + open: openState, + selectedTab: context.selectedDebuggerTab, + responseTabHeight: context.responseTabHeight, + }; + }, +); diff --git a/app/client/src/selectors/jsPaneSelectors.ts b/app/client/src/selectors/jsPaneSelectors.ts index 179cfecb3f..e425fe9c82 100644 --- a/app/client/src/selectors/jsPaneSelectors.ts +++ b/app/client/src/selectors/jsPaneSelectors.ts @@ -21,3 +21,6 @@ export const getFirstJSObject = ( return identifyEntityFromPath(urlWithoutQueryParams); } }; + +export const getJsPaneDebuggerState = (state: AppState) => + state.ui.jsPane.debugger; diff --git a/app/client/src/selectors/queryPaneSelectors.ts b/app/client/src/selectors/queryPaneSelectors.ts index 5ef7491c6e..651452bd59 100644 --- a/app/client/src/selectors/queryPaneSelectors.ts +++ b/app/client/src/selectors/queryPaneSelectors.ts @@ -18,6 +18,9 @@ export const getFirstQuery = (state: AppState): FocusEntityInfo | undefined => { } }; +export const getQueryPaneDebuggerState = (state: AppState) => + state.ui.queryPane.debugger; + export const getQueryRunErrorMessage = (state: AppState, id: string) => { const { runErrorMessage } = state.ui.queryPane; return runErrorMessage[id];