Skip to content
2 changes: 1 addition & 1 deletion app/client/cypress/support/Pages/DebuggerHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export class DebuggerHelper {
_debuggerToggle: "[data-testid=t--debugger-toggle]",
_debuggerDownStreamErrMsg: "[data-testid=t--debugger-downStreamErrorMsg]",
_tabsContainer: ".t--debugger-tabs-container",
_closeButton: ".t--close-debugger",
_closeButton: ".t--view-hide-button",
_logMessage: ".t--debugger-log-message",
_logEntityLink: ".t--debugger-log-entity-link",
_logState: ".t--debugger-log-state",
Expand Down
150 changes: 150 additions & 0 deletions app/client/src/IDE/Components/BottomView.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
import React, {
Comment thread
hetunandu marked this conversation as resolved.
Outdated
type CSSProperties,
type RefObject,
useEffect,
useRef,
useState,
} from "react";
import styled from "styled-components";
import Resizer, {
ResizerCSS,
} from "components/editorComponents/Debugger/Resizer";
import { ActionExecutionResizerHeight } from "pages/Editor/APIEditor/constants";
import { CodeEditorWithGutterStyles } from "pages/Editor/JSEditor/constants";
import { ViewHideBehaviour } from "../Interfaces/ViewHideBehaviour";
import { Button } from "design-system";

const VIEW_MIN_HEIGHT = "38px";
Comment thread
hetunandu marked this conversation as resolved.
Outdated

const Container = styled.div`
${ResizerCSS};
width: 100%;
// Minimum height of bottom tabs as it can be resized
min-height: ${VIEW_MIN_HEIGHT};
background-color: var(--ads-v2-color-bg);
height: ${ActionExecutionResizerHeight}px;
border-top: 1px solid var(--ads-v2-color-border);
`;

const ViewWrapper = styled.div`
height: 100%;
&&& {
ul.ads-v2-tabs__list {
margin: 0 ${(props) => props.theme.spaces[11]}px;
height: ${VIEW_MIN_HEIGHT};
}
}

& {
.ads-v2-tabs__list {
padding: var(--ads-v2-spaces-1) var(--ads-v2-spaces-7);
}
}

& {
.ads-v2-tabs__panel {
${CodeEditorWithGutterStyles};
overflow-y: auto;
height: calc(100% - ${VIEW_MIN_HEIGHT});
}
}
`;

const MIN_HEIGHT = {
[ViewHideBehaviour.COLLAPSE]: VIEW_MIN_HEIGHT,
[ViewHideBehaviour.CLOSE]: "0px",
};

interface Props {
className?: string;
behaviour: ViewHideBehaviour;
height: number;
setHeight: (height: number) => void;
hidden: boolean;
onHideClick: () => void;
children: React.ReactNode;
additionalContainerStyles?: CSSProperties;
}

const ViewHideButton = styled(Button)`
&.view-hide-button {
position: absolute;
top: 3px;
right: 0;
padding: 9px 11px;
}
`;

const ViewHide = (props: {
hideBehaviour: ViewHideBehaviour;
isHidden: boolean;
onToggle: () => void;
}) => {
Comment thread
hetunandu marked this conversation as resolved.
Outdated
const [icon, setIcon] = useState(() => {
return props.hideBehaviour === ViewHideBehaviour.CLOSE
? "close-modal"
: "arrow-down-s-line";
});

useEffect(() => {
if (props.hideBehaviour === ViewHideBehaviour.COLLAPSE) {
if (props.isHidden) {
setIcon("arrow-up-s-line");
} else {
setIcon("arrow-down-s-line");
}
}
}, [props.isHidden]);

return (
<ViewHideButton
className="view-hide-button t--view-hide-button"
Comment thread
hetunandu marked this conversation as resolved.
Outdated
isIconButton
kind="tertiary"
onClick={props.onToggle}
size="md"
startIcon={icon}
/>
);
};

const BottomView = (props: Props) => {
const panelRef: RefObject<HTMLDivElement> = useRef(null);
Comment thread
hetunandu marked this conversation as resolved.
Outdated

// Handle the height of the tabs when toggling the hidden state
useEffect(() => {
const panel = panelRef.current;
if (!panel) return;
if (props.hidden) {
panel.style.height = MIN_HEIGHT[props.behaviour];
} else {
panel.style.height = `${props.height}px`;
}
}, [props.hidden]);
Comment thread
hetunandu marked this conversation as resolved.
Outdated

return (
<Container
className={`select-text ${props.className}`}
ref={panelRef}
style={props.additionalContainerStyles}
Comment thread
hetunandu marked this conversation as resolved.
Outdated
>
{!props.hidden && (
<Resizer
initialHeight={props.height}
onResizeComplete={props.setHeight}
panelRef={panelRef}
/>
)}
<ViewWrapper>
{props.children}
<ViewHide
hideBehaviour={props.behaviour}
isHidden={props.hidden}
onToggle={props.onHideClick}
/>
</ViewWrapper>
</Container>
);
};

export default BottomView;
14 changes: 14 additions & 0 deletions app/client/src/IDE/Interfaces/ViewHideBehaviour.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/**
* Close:
* - View is not visible at all
* - View is not interactable till opened again
*
* Collapse:
* - View is visible but can not be resized
* - Top part of the view is visible with open to reopen it
* - Children can have extra view that changes when hidden that can have different behaviours
*/
export enum ViewHideBehaviour {
CLOSE = "CLOSE",
COLLAPSE = "COLLAPSE",
}
36 changes: 23 additions & 13 deletions app/client/src/IDE/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/**
**** Structural Components ****
* Components that are part of the main structure of the IDE experience
**/
/* ====================================================
**** Structural Components ****
Components that are part of the main structure of the IDE experience
====================================================**/

/**
* The IDEHeader gets exported with 3 layout subsections.
Expand All @@ -11,18 +11,18 @@
*/
export { default as IDEHeader } from "./Structure/Header";

/**
**** UI Components ****
* Components that are smaller UI abstractions for easy use and standardisation within the IDE
**/
/* ====================================================
**** UI Components ****
Components that are smaller UI abstractions for easy use and standardisation within the IDE
=======================================================**/

/**
* IDEHeaderTitle is a small text styled wrapper that is suitable to be used inside IDEHeader
*/
export { default as IDEHeaderTitle } from "./Components/HeaderTitle";
/**
* IDEHeaderEditorSwitcher can be used for trigger component to show a drop down for pages, modules
* or any list of elements in the header. Eg. Pages / Page 1
* IDEHeaderEditorSwitcher can be used for a trigger component to show a dropdown for pages, modules
* or any list of elements in the header E.g., Pages / Page 1
*/
export { default as IDEHeaderEditorSwitcher } from "./Components/HeaderEditorSwitcher";
/**
Expand All @@ -33,6 +33,16 @@ export { default as IDEHeaderEditorSwitcher } from "./Components/HeaderEditorSwi
*/
export { default as IDEHeaderDropdown } from "./Components/HeaderDropdown";
/**
**** Interfaces ****
* Common types that are used by the different components of the IDE
**/
* IDEBottomView is a versatile view meant to be at the bottom of the screen.
* It is resizable and can be hidden or collapsed based on the behavior configured
* The view is designed for showing tabs, but this component does not assume this can
* accept any view to be rendered
*/
export { default as IDEBottomView } from "./Components/BottomView";

/* ====================================================
**** Interfaces ****
Common types that are used by the different components of the IDE
=======================================================**/

export { ViewHideBehaviour } from "./Interfaces/ViewHideBehaviour";
94 changes: 22 additions & 72 deletions app/client/src/components/editorComponents/ApiResponseView.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import type { RefObject } from "react";
import React, { useCallback, useRef, useState } from "react";
import React, { useCallback, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import ReactJson from "react-json-view";
import styled from "styled-components";
Expand All @@ -22,9 +21,8 @@ import { EditorTheme } from "./CodeEditor/EditorConfig";
import NoResponseSVG from "assets/images/no-response.svg";
import DebuggerLogs from "./Debugger/DebuggerLogs";
import ErrorLogs from "./Debugger/Errors";
import Resizer, { ResizerCSS } from "./Debugger/Resizer";
import AnalyticsUtil from "@appsmith/utils/AnalyticsUtil";
import { Classes, TAB_MIN_HEIGHT, Text, TextType } from "design-system-old";
import { Classes, Text, TextType } from "design-system-old";
import { Button, Callout, Flex, SegmentedControl } from "design-system";
import type { BottomTab } from "./EntityBottomTabs";
import EntityBottomTabs from "./EntityBottomTabs";
Expand All @@ -45,26 +43,14 @@ import { getUpdateTimestamp } from "./Debugger/ErrorLogs/ErrorLogItem";
import type { Action } from "entities/Action";
import { SegmentedControlContainer } from "../../pages/Editor/QueryEditor/EditorJSONtoForm";
import ActionExecutionInProgressView from "./ActionExecutionInProgressView";
import { CloseDebugger } from "./Debugger/DebuggerTabs";
import { EMPTY_RESPONSE } from "./emptyResponse";
import { setApiPaneDebuggerState } from "actions/apiPaneActions";
import { getApiPaneDebuggerState } from "selectors/apiPaneSelectors";
import { getIDEViewMode } from "selectors/ideSelectors";
import { EditorViewMode } from "@appsmith/entities/IDE/constants";
import ApiResponseMeta from "./ApiResponseMeta";
import useDebuggerTriggerClick from "./Debugger/hooks/useDebuggerTriggerClick";

const ResponseContainer = styled.div`
${ResizerCSS};
width: 100%;
// Minimum height of bottom tabs as it can be resized
min-height: 36px;
background-color: var(--ads-v2-color-bg);
border-top: 1px solid var(--ads-v2-color-border);
.CodeMirror-code {
font-size: 12px;
}
`;
import { IDEBottomView, ViewHideBehaviour } from "IDE";

const ResponseTabWrapper = styled.div`
display: flex;
Expand All @@ -77,28 +63,6 @@ const ResponseTabWrapper = styled.div`
}
`;

const TabbedViewWrapper = styled.div`
height: 100%;
&&& {
ul.ads-v2-tabs__list {
margin: 0 ${(props) => props.theme.spaces[11]}px;
height: ${TAB_MIN_HEIGHT};
}
}

& {
.ads-v2-tabs__list {
padding: var(--ads-v2-spaces-1) var(--ads-v2-spaces-7);
}
}

& {
.ads-v2-tabs__panel {
height: calc(100% - ${TAB_MIN_HEIGHT});
}
}
`;

const NoResponseContainer = styled.div`
flex: 1;
width: 100%;
Expand Down Expand Up @@ -250,7 +214,6 @@ function ApiResponseView(props: Props) {
? actionResponse.statusCode[0] !== "2"
: false;

const panelRef: RefObject<HTMLDivElement> = useRef(null);
const dispatch = useDispatch();
const errorCount = useSelector(getErrorCount);
const { open, responseTabHeight, selectedTab } = useSelector(
Expand Down Expand Up @@ -349,7 +312,7 @@ function ApiResponseView(props: Props) {
source: "API_PANE",
});
}
dispatch(setApiPaneDebuggerState({ selectedTab: tabKey }));
dispatch(setApiPaneDebuggerState({ open: true, selectedTab: tabKey }));
}, []);

// update the height of the response pane on resize.
Expand Down Expand Up @@ -536,41 +499,28 @@ function ApiResponseView(props: Props) {

// close the debugger
//TODO: move this to a common place
const onClose = () => dispatch(setApiPaneDebuggerState({ open: false }));

if (!open) return null;
const toggleHide = useCallback(
() => dispatch(setApiPaneDebuggerState({ open: !open })),
[open],
);

return (
<ResponseContainer
className="t--api-bottom-pane-container select-text"
ref={panelRef}
<IDEBottomView
behaviour={ViewHideBehaviour.COLLAPSE}
className="t--api-bottom-pane-container"
height={responseTabHeight}
hidden={!open}
onHideClick={toggleHide}
setHeight={updateResponsePaneHeight}
>
<Resizer
initialHeight={responseTabHeight}
onResizeComplete={(height: number) => {
updateResponsePaneHeight(height);
}}
openResizer={isRunning}
panelRef={panelRef}
snapToHeight={ActionExecutionResizerHeight}
<EntityBottomTabs
expandedHeight={`${ActionExecutionResizerHeight}px`}
isCollapsed={!open}
onSelect={updateSelectedResponseTab}
selectedTabKey={selectedTab || ""}
tabs={tabs}
/>
<TabbedViewWrapper>
<EntityBottomTabs
expandedHeight={`${ActionExecutionResizerHeight}px`}
onSelect={updateSelectedResponseTab}
selectedTabKey={selectedTab || ""}
tabs={tabs}
/>
<CloseDebugger
className="close-debugger t--close-debugger"
isIconButton
kind="tertiary"
onClick={onClose}
size="md"
startIcon="close-modal"
/>
</TabbedViewWrapper>
</ResponseContainer>
</IDEBottomView>
);
}

Expand Down
Loading