## 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
171 lines
5.7 KiB
TypeScript
171 lines
5.7 KiB
TypeScript
import type { Log } from "entities/AppsmithConsole";
|
|
import type { WidgetEntity } from "@appsmith/entities/DataTree/types";
|
|
import type { DataTree } from "entities/DataTree/dataTreeTypes";
|
|
import { isEmpty } from "lodash";
|
|
import type { AppState } from "@appsmith/reducers";
|
|
import type { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer";
|
|
import { createSelector } from "reselect";
|
|
import { getWidgets } from "sagas/selectors";
|
|
import {
|
|
shouldSuppressDebuggerError,
|
|
isWidget,
|
|
} 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;
|
|
}
|
|
|
|
export const getDebuggerErrors = (state: AppState) => state.ui.debugger.errors;
|
|
export const hideErrors = (state: AppState) => state.ui.debugger.hideErrors;
|
|
const emptyErrorObejct: ErrorObejct = {};
|
|
|
|
export const getFilteredErrors = createSelector(
|
|
getDebuggerErrors,
|
|
hideErrors,
|
|
getWidgets,
|
|
getDataTree,
|
|
(errors, hideErrors, canvasWidgets, dataTree: DataTree) => {
|
|
if (hideErrors) return emptyErrorObejct;
|
|
if (isEmpty(errors)) return emptyErrorObejct;
|
|
|
|
const alwaysShowEntities: Record<string, boolean> = {};
|
|
Object.entries(errors).forEach(([, error]) => {
|
|
const entity = error?.source?.name && dataTree[error.source.name];
|
|
if (
|
|
entity &&
|
|
isWidget(entity) &&
|
|
error.source?.propertyPath === "isVisible"
|
|
) {
|
|
alwaysShowEntities[error.source.id] = true;
|
|
}
|
|
});
|
|
const filteredErrors = Object.fromEntries(
|
|
Object.entries(errors).filter(([, error]) => {
|
|
const entity = error?.source?.name && dataTree[error.source.name];
|
|
// filter error - when widget or parent widget is hidden
|
|
// parent widgets e.g. modal, tab, container
|
|
if (entity && isWidget(entity)) {
|
|
const widgetEntity = entity as WidgetEntity;
|
|
if (shouldSuppressDebuggerError(widgetEntity)) {
|
|
return false;
|
|
}
|
|
if (!hasParentWidget(widgetEntity)) {
|
|
return widgetEntity.isVisible
|
|
? true
|
|
: alwaysShowEntities[widgetEntity.widgetId];
|
|
} else {
|
|
const isParentWidgetVisible = isParentVisible(
|
|
widgetEntity,
|
|
canvasWidgets,
|
|
dataTree,
|
|
);
|
|
return widgetEntity.isVisible
|
|
? isParentWidgetVisible
|
|
: isParentWidgetVisible &&
|
|
alwaysShowEntities[widgetEntity.widgetId];
|
|
}
|
|
}
|
|
return true;
|
|
}),
|
|
);
|
|
return filteredErrors;
|
|
},
|
|
);
|
|
|
|
export const isParentVisible = (
|
|
currentWidgetData: WidgetEntity,
|
|
canvasWidgets: CanvasWidgetsReduxState,
|
|
dataTree: DataTree,
|
|
): boolean => {
|
|
const isWidgetVisible = !!currentWidgetData.isVisible;
|
|
if (!hasParentWidget(currentWidgetData)) {
|
|
return isWidgetVisible;
|
|
}
|
|
const parentWidget = canvasWidgets[currentWidgetData.parentId as string];
|
|
if (!parentWidget) return isWidgetVisible;
|
|
|
|
const parentWidgetData = dataTree[parentWidget.widgetName] as WidgetEntity;
|
|
if (!parentWidgetData) return isWidgetVisible;
|
|
|
|
switch (parentWidgetData.type) {
|
|
// check for widget types instead of harcoded string
|
|
case "TABS_WIDGET":
|
|
// need type for selectedTab and tabName
|
|
const isTabContentVisible =
|
|
!!parentWidgetData.isVisible &&
|
|
parentWidgetData.selectedTab === currentWidgetData.tabName;
|
|
return isTabContentVisible
|
|
? isParentVisible(parentWidgetData, canvasWidgets, dataTree)
|
|
: false;
|
|
case "MODAL_WIDGET":
|
|
return !!parentWidgetData.isVisible;
|
|
default:
|
|
return parentWidgetData.isVisible
|
|
? isParentVisible(parentWidgetData, canvasWidgets, dataTree)
|
|
: false;
|
|
}
|
|
};
|
|
|
|
export const hasParentWidget = (widget: WidgetEntity) =>
|
|
widget.parentId && widget.parentId !== "0";
|
|
|
|
export const getMessageCount = createSelector(getFilteredErrors, (errors) => {
|
|
let errorsCount = 0;
|
|
|
|
// count number of messages in each error.
|
|
// This logic is required because each messages in error is rendered separately.
|
|
Object.values(errors).forEach((error) => {
|
|
if (error.messages) {
|
|
errorsCount += error.messages.length;
|
|
}
|
|
});
|
|
// count number of warnings.
|
|
const warningsCount = Object.keys(errors).filter((key: string) =>
|
|
key.includes("warning"),
|
|
).length;
|
|
errorsCount = errorsCount - warningsCount;
|
|
return { errors: errorsCount, warnings: warningsCount };
|
|
});
|
|
|
|
// get selected tab in debugger.
|
|
export const getDebuggerSelectedTab = (state: AppState) =>
|
|
state.ui.debugger.context.selectedDebuggerTab;
|
|
|
|
export const getDebuggerSelectedFilter = (state: AppState) =>
|
|
state.ui.debugger.context.selectedDebuggerFilter;
|
|
|
|
export const getResponsePaneHeight = (state: AppState) =>
|
|
state.ui.debugger.context.responseTabHeight;
|
|
|
|
export const getErrorCount = (state: AppState) =>
|
|
state.ui.debugger.context.errorCount;
|
|
|
|
export const getScrollPosition = (state: AppState) =>
|
|
state.ui.debugger.context.scrollPosition;
|
|
|
|
export const getDebuggerContext = (state: AppState) =>
|
|
state.ui.debugger.context;
|
|
|
|
export const getDebuggerOpen = (state: AppState) => state.ui.debugger.isOpen;
|
|
|
|
export const showDebuggerFlag = createSelector(
|
|
getDebuggerOpen,
|
|
combinedPreviewModeSelector,
|
|
(isOpen, isPreview) => isOpen && !isPreview,
|
|
);
|
|
|
|
export const getCanvasDebuggerState = createSelector(
|
|
showDebuggerFlag,
|
|
getDebuggerContext,
|
|
(openState, context): CanvasDebuggerState => {
|
|
return {
|
|
open: openState,
|
|
selectedTab: context.selectedDebuggerTab,
|
|
responseTabHeight: context.responseTabHeight,
|
|
};
|
|
},
|
|
);
|