Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions app/client/src/IDE/Components/BottomView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import styled from "styled-components";
import Resizer, {
ResizerCSS,
} from "components/editorComponents/Debugger/Resizer";
import { CodeEditorWithGutterStyles } from "pages/Editor/JSEditor/constants";
import { ViewHideBehaviour, ViewDisplayMode } from "IDE/Interfaces/View";
import { CodeEditorWithGutterStyles } from "pages/Editor/JSEditor/styledComponents";
import { ViewDisplayMode, ViewHideBehaviour } from "IDE/Interfaces/View";
import { Button } from "design-system";

const VIEW_MIN_HEIGHT = 38;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not critical by any means, but it would be more appropriate for constants to be in a separate file.

Expand Down
25 changes: 25 additions & 0 deletions app/client/src/IDE/Components/Sidebar/SidebarButton.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { render } from "test/testUtils";
import React from "react";
import SidebarButton, { type SidebarButtonProps } from "./SidebarButton";
import { Condition } from "../../Interfaces/Condition";

const sidebarButtonProps: SidebarButtonProps = {
icon: "down-arrow",
onClick: () => {},
selected: false,
title: "Test",
};

describe("SidebarButton", () => {
it("should render the warning icon in case the datasource list is empty", () => {
const withWarningCondition = {
...sidebarButtonProps,
condition: Condition.Warn,
};

const { container } = render(<SidebarButton {...withWarningCondition} />);

const svgs = container.querySelectorAll("svg");
expect(svgs).toHaveLength(2);
});
});
Original file line number Diff line number Diff line change
@@ -1,15 +1,25 @@
import React from "react";
import { Icon, Text, Tooltip } from "design-system";
import styled from "styled-components";
import { SidebarTopButtonTitles } from "@appsmith/entities/IDE/constants";
import { Condition } from "../../Interfaces/Condition";

const ConditionConfig: Record<Condition, { icon: string; color: string }> = {
[Condition.Warn]: {
icon: "warning",
color: "#ffe283",
},
// TODO add this information for further conditions
// Error: { color: "", icon: "" },
// Success: { color: "", icon: "" },
};

export interface SidebarButtonProps {
title?: string;
selected: boolean;
icon: string;
onClick: () => void;
tooltip?: string;
conditionIcon?: string;
condition?: Condition;
}

const Container = styled.div`
Expand Down Expand Up @@ -46,15 +56,15 @@ const ConditionIcon = styled(Icon)`
position: absolute;
bottom: 3px;
right: -1px;
&.t--sidebar-${SidebarTopButtonTitles.DATA}-condition-icon {
color: #ffe283;
&.t--sidebar-${Condition.Warn}-condition-icon {
color: ${ConditionConfig[Condition.Warn].color};
}
// TODO add more condition colors here
Comment thread
hetunandu marked this conversation as resolved.
`;

function SidebarButton(props: SidebarButtonProps) {
return (
<Container>
{props.title === SidebarTopButtonTitles.DATA}
<Tooltip
content={props.tooltip}
isDisabled={!!props.title && !props.tooltip}
Expand All @@ -67,10 +77,10 @@ function SidebarButton(props: SidebarButtonProps) {
selected={props.selected}
>
<Icon name={props.icon} size="lg" />
{props.conditionIcon && (
{props.condition && (
<ConditionIcon
className={`t--sidebar-${props.title}-condition-icon`}
name={props.conditionIcon}
className={`t--sidebar-${props.condition}-condition-icon`}
name={ConditionConfig[props.condition].icon}
size="md"
/>
)}
Expand Down
72 changes: 72 additions & 0 deletions app/client/src/IDE/Components/Sidebar/index.tsx
Comment thread
alex-golovanov marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import React, { useCallback } from "react";
import styled from "styled-components";
import SidebarButton from "./SidebarButton";
import type { EditorState } from "@appsmith/entities/IDE/constants";
import type { ISidebarButton } from "../../Interfaces/ISidebarButton";

const Container = styled.div`
width: 50px;
border-right: 1px solid var(--ads-v2-color-border);
height: 100%;
display: flex;
flex-direction: column;
justify-content: space-between;
background-color: var(--ads-v2-color-bg);
position: relative;
`;

interface IDESidebarProps {
id?: string;
topButtons: ISidebarButton[];
bottomButtons: ISidebarButton[];
appState: EditorState;
Comment thread
alex-golovanov marked this conversation as resolved.
Outdated
onClick: (suffix: string) => void;
}

function IDESidebar(props: IDESidebarProps) {
const { appState, bottomButtons, onClick, topButtons } = props;

const handleOnClick = useCallback(
(button: ISidebarButton) => {
if (appState !== button.state) {
onClick(button.urlSuffix);
}
},
[appState],
);

return (
<Container className="t--sidebar" id={props.id}>
<div>
{topButtons.map((b) => (
Comment thread
alex-golovanov marked this conversation as resolved.
Outdated
<SidebarButton
icon={b.icon}
key={b.state}
onClick={() => {
handleOnClick(b);
}}
selected={appState === b.state}
title={b.title}
tooltip={b.tooltip}
/>
))}
</div>
<div>
{bottomButtons.map((b) => (
<SidebarButton
icon={b.icon}
key={b.state}
onClick={() => {
Comment thread
alex-golovanov marked this conversation as resolved.
Outdated
handleOnClick(b);
}}
selected={appState === b.state}
title={b.title}
tooltip={b.tooltip}
/>
))}
</div>
</Container>
);
}

export default IDESidebar;
5 changes: 5 additions & 0 deletions app/client/src/IDE/Interfaces/Condition.ts
Comment thread
alex-golovanov marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export enum Condition {
Warn = "Warn",
// Error = "Error",
// Success = "Success",
}
12 changes: 12 additions & 0 deletions app/client/src/IDE/Interfaces/ISidebarButton.ts
Comment thread
alex-golovanov marked this conversation as resolved.
Outdated
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import type { EditorState } from "@appsmith/entities/IDE/constants";

import type { Condition } from "./Condition";

export interface ISidebarButton {
Comment thread
alex-golovanov marked this conversation as resolved.
Outdated
state: EditorState;
icon: string;
title?: string;
urlSuffix: string;
condition?: Condition;
tooltip?: string;
}
8 changes: 8 additions & 0 deletions app/client/src/IDE/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,17 @@ export { default as IDEHeaderDropdown } from "./Components/HeaderDropdown";
*/
export { default as IDEBottomView } from "./Components/BottomView";

/**
* IDESidebar is used inside the IDE to have a navigation menu on the left side of the screen.
* It switches between different editor states
*/
export { default as IDESidebar } from "./Components/Sidebar";

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

export { ViewHideBehaviour, ViewDisplayMode } from "./Interfaces/View";
export type { ISidebarButton } from "./Interfaces/ISidebarButton";
export { Condition } from "./Interfaces/Condition";
2 changes: 1 addition & 1 deletion app/client/src/api/LibraryAPI.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export default class LibraryApi extends Api {
library: Partial<JSLibrary>,
) {
const url = LibraryApi.getUpdateLibraryBaseURL(applicationId) + "/remove";
return Api.patch(url, { accessor: library.accessor, url: library.url });
return Api.patch(url, library);
Comment thread
hetunandu marked this conversation as resolved.
}

static async getLibraries(applicationId: string, mode: APP_MODE) {
Expand Down
30 changes: 6 additions & 24 deletions app/client/src/ce/entities/IDE/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,8 @@ import {
SAAS_EDITOR_DATASOURCE_ID_PATH,
} from "pages/Editor/SaaSEditor/constants";
import type { PluginType } from "entities/Action";
import type { ReactNode, ComponentType } from "react";
import {
EMPTY_DATASOURCE_TOOLTIP_SIDEBUTTON,
createMessage,
} from "@appsmith/constants/messages";
import type { ComponentType, ReactNode } from "react";
import type { ISidebarButton } from "IDE";

export enum EditorState {
DATA = "DATA",
Expand Down Expand Up @@ -61,20 +58,7 @@ export enum EditorViewMode {
SplitScreen = "SplitScreen",
}

export enum SideButtonType {
DATSOURCE = "DATASOURCE",
}

export interface SidebarButton {
state: EditorState;
icon: string;
title?: string;
urlSuffix: string;
conditionType?: SideButtonType;
conditionTooltip?: string;
}

export const TopButtons: SidebarButton[] = [
export const TopButtons: ISidebarButton[] = [
{
state: EditorState.EDITOR,
icon: "editor-v3",
Expand All @@ -86,22 +70,20 @@ export const TopButtons: SidebarButton[] = [
icon: "datasource-v3",
title: SidebarTopButtonTitles.DATA,
urlSuffix: "datasource",
conditionType: SideButtonType.DATSOURCE,
conditionTooltip: createMessage(EMPTY_DATASOURCE_TOOLTIP_SIDEBUTTON),
},
];

export const BottomButtons: SidebarButton[] = [
export const BottomButtons: ISidebarButton[] = [
{
state: EditorState.LIBRARIES,
icon: "packages-v3",
title: SidebarBottomButtonTitles.LIBRARIES,
tooltip: SidebarBottomButtonTitles.LIBRARIES,
urlSuffix: "libraries",
},
{
state: EditorState.SETTINGS,
icon: "settings-v3",
title: SidebarBottomButtonTitles.SETTINGS,
tooltip: SidebarBottomButtonTitles.SETTINGS,
urlSuffix: "settings",
},
];
Expand Down
78 changes: 78 additions & 0 deletions app/client/src/pages/Editor/IDE/Sidebar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import React, { useCallback, useEffect, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import { builderURL } from "@appsmith/RouteBuilder";
import { getCurrentPageId } from "selectors/editorSelectors";
import history, { NavigationMethod } from "utils/history";
import { useCurrentAppState } from "./hooks";
import { getCurrentWorkspaceId } from "@appsmith/selectors/selectedWorkspaceSelectors";
import { fetchWorkspace } from "@appsmith/actions/workspaceActions";
import { IDESidebar, Condition } from "IDE";
import {
BottomButtons,
EditorState,
TopButtons,
} from "@appsmith/entities/IDE/constants";
import { getDatasources } from "@appsmith/selectors/entitiesSelector";
import {
createMessage,
EMPTY_DATASOURCE_TOOLTIP_SIDEBUTTON,
} from "@appsmith/constants/messages";

function Sidebar() {
const [topButtons, setTopButtons] = useState(TopButtons);
const dispatch = useDispatch();
const appState = useCurrentAppState();
const pageId = useSelector(getCurrentPageId);
const currentWorkspaceId = useSelector(getCurrentWorkspaceId);
const datasources = useSelector(getDatasources);

useEffect(() => {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no need for useEffect + setState here, as there are no effects. We do need to make this reference stable so useMemo should be used.

Something like this:

const topButtons = React.useMemo(() => {
    return datasourcesExist
      ? TopButtons
      : TopButtons.map((button) => {
          if (button.state === EditorState.DATA) {
            return {
              ...button,
              condition: Condition.Warn,
              tooltip: createMessage(EMPTY_DATASOURCE_TOOLTIP_SIDEBUTTON),
            };
          }
          return button;
        });
  }, [datasourcesExist]);

if (datasources.length === 0) {
Comment thread
alex-golovanov marked this conversation as resolved.
Outdated
setTopButtons(
TopButtons.map((button) => {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The casing here TopButtons is confusing, as it suggests that there is an array of components or classes, but in fact it's just an array of objects. It is strongly recommended to name all constants in CONSTANT_CASE.

if (button.state === EditorState.DATA) {
return {
...button,
condition: Condition.Warn,
tooltip: createMessage(EMPTY_DATASOURCE_TOOLTIP_SIDEBUTTON),
};
}
return button;
}),
);
} else {
setTopButtons(TopButtons);
}
}, [datasources]);

useEffect(() => {
dispatch(fetchWorkspace(currentWorkspaceId));
}, [currentWorkspaceId]);
Comment thread
alex-golovanov marked this conversation as resolved.
Outdated

const onClick = useCallback(
(suffix) => {
history.push(
builderURL({
pageId,
suffix,
}),
{
invokedBy: NavigationMethod.AppSidebar,
},
);
},
[pageId],
);

return (
<IDESidebar
appState={appState}
bottomButtons={BottomButtons}
id={"t--app-sidebar"}
onClick={onClick}
topButtons={topButtons}
/>
);
}

export default Sidebar;
23 changes: 0 additions & 23 deletions app/client/src/pages/Editor/IDE/Sidebar/SidebarButton.test.tsx

This file was deleted.

Loading