-
Notifications
You must be signed in to change notification settings - Fork 3.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
chore: Move Sidebar to IDE/Components #34487
Changes from all commits
86392ea
c796df7
cad6f3b
9b4c703
7a017e0
c86818a
1156e73
0d560d1
d3d23fe
4c14038
c88428c
79a9358
71e5795
e26ee7a
f2d0e9f
0b685d4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
import React from "react"; | ||
import styled from "styled-components"; | ||
import SidebarButton from "./SidebarButton"; | ||
import type { EditorState } from "@appsmith/entities/IDE/constants"; | ||
import type { SidebarButtonProps } from "./SidebarButton/SidebarButton"; | ||
import { Flex } from "design-system"; | ||
|
||
const Container = styled(Flex)` | ||
width: 50px; | ||
border-right: 1px solid var(--ads-v2-color-border); | ||
height: 100%; | ||
flex-direction: column; | ||
justify-content: space-between; | ||
background-color: var(--ads-v2-color-bg); | ||
position: relative; | ||
`; | ||
|
||
// Sidebar handles the correct handling of sidebar button. It will check if | ||
// the button should be selected and only handle calling the onClick | ||
export interface IDESidebarButton | ||
extends Omit<SidebarButtonProps, "onClick" | "selected"> { | ||
state: EditorState; | ||
urlSuffix: string; | ||
} | ||
|
||
interface IDESidebarProps { | ||
id?: string; | ||
topButtons: IDESidebarButton[]; | ||
bottomButtons: IDESidebarButton[]; | ||
editorState: EditorState; | ||
onClick: (suffix: string) => void; | ||
} | ||
|
||
function IDESidebar(props: IDESidebarProps) { | ||
const { bottomButtons, editorState, onClick, topButtons } = props; | ||
|
||
return ( | ||
<Container className="t--sidebar" id={props.id}> | ||
<div> | ||
{topButtons.map((button) => ( | ||
<SidebarButton | ||
icon={button.icon} | ||
key={button.state} | ||
onClick={onClick} | ||
selected={editorState === button.state} | ||
title={button.title} | ||
tooltip={button.tooltip} | ||
urlSuffix={button.urlSuffix} | ||
/> | ||
))} | ||
</div> | ||
<div> | ||
{bottomButtons.map((button) => ( | ||
<SidebarButton | ||
icon={button.icon} | ||
key={button.state} | ||
onClick={onClick} | ||
selected={editorState === button.state} | ||
title={button.title} | ||
tooltip={button.tooltip} | ||
urlSuffix={button.urlSuffix} | ||
/> | ||
))} | ||
</div> | ||
</Container> | ||
); | ||
} | ||
|
||
export default IDESidebar; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
import { render } from "test/testUtils"; | ||
import React from "react"; | ||
import SidebarButton, { type SidebarButtonProps } from "./SidebarButton"; | ||
|
||
import { Condition } from "../../../enums"; | ||
import userEvent from "@testing-library/user-event"; | ||
|
||
const sidebarButtonProps: SidebarButtonProps = { | ||
icon: "down-arrow", | ||
onClick: () => {}, | ||
selected: false, | ||
title: "Test", | ||
urlSuffix: "/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); | ||
}); | ||
|
||
it("should call onClick with urlSuffix", async () => { | ||
const checkOnClick = { | ||
...sidebarButtonProps, | ||
onClick: jest.fn(), | ||
}; | ||
const { getByRole } = render(<SidebarButton {...checkOnClick} />); | ||
|
||
await userEvent.click(getByRole("button")); | ||
expect(checkOnClick.onClick).toHaveBeenCalledWith(checkOnClick.urlSuffix); | ||
}); | ||
|
||
it("should not call onClick when button is already selected", async () => { | ||
const withSelected = { | ||
...sidebarButtonProps, | ||
selected: true, | ||
onClick: jest.fn(), | ||
}; | ||
const { getByRole } = render(<SidebarButton {...withSelected} />); | ||
|
||
await userEvent.click(getByRole("button")); | ||
expect(withSelected.onClick).not.toHaveBeenCalled(); | ||
}); | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
import React, { useCallback } from "react"; | ||
import { Flex, Icon, Text, Tooltip } from "design-system"; | ||
import styled from "styled-components"; | ||
|
||
import { Condition } from "../../../enums"; | ||
|
||
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: (urlSuffix: string) => void; | ||
urlSuffix: string; | ||
tooltip?: string; | ||
condition?: Condition; | ||
} | ||
|
||
const Container = styled(Flex)` | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. All these can be passed to the Flex component as prop. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
justify-content: center; | ||
flex-direction: column; | ||
width: 50px; | ||
text-align: center; | ||
align-items: center; | ||
padding: 8px 0; | ||
`; | ||
|
||
const IconContainer = styled.div<{ selected: boolean }>` | ||
padding: 2px; | ||
background-color: ${(props) => | ||
props.selected ? "var(--colors-raw-orange-100, #fbe6dc)" : "white"}; | ||
border-radius: 3px; | ||
width: 32px; | ||
height: 32px; | ||
display: flex; | ||
align-items: center; | ||
justify-content: center; | ||
cursor: pointer; | ||
position: relative; | ||
|
||
&:hover { | ||
background: ${(props) => | ||
props.selected | ||
? "var(--colors-raw-orange-100, #fbe6dc)" | ||
: "var(--ads-v2-color-bg-subtle, #f1f5f9);"}; | ||
} | ||
`; | ||
|
||
const ConditionIcon = styled(Icon)` | ||
position: absolute; | ||
bottom: 3px; | ||
right: -1px; | ||
|
||
&.t--sidebar-${Condition.Warn}-condition-icon { | ||
color: ${ConditionConfig[Condition.Warn].color}; | ||
} | ||
|
||
// TODO add more condition colors here | ||
`; | ||
|
||
function SidebarButton(props: SidebarButtonProps) { | ||
const { condition, icon, onClick, selected, title, tooltip, urlSuffix } = | ||
props; | ||
const handleOnClick = useCallback(() => { | ||
if (!selected) { | ||
onClick(urlSuffix); | ||
} | ||
}, [selected, onClick, urlSuffix]); | ||
return ( | ||
<Container> | ||
<Tooltip | ||
content={tooltip} | ||
isDisabled={!!title && !tooltip} | ||
placement={"right"} | ||
> | ||
<IconContainer | ||
className={`t--sidebar-${title || tooltip}`} | ||
data-selected={selected} | ||
onClick={handleOnClick} | ||
role="button" | ||
selected={selected} | ||
> | ||
<Icon name={icon} size="lg" /> | ||
{condition && ( | ||
<ConditionIcon | ||
className={`t--sidebar-${condition}-condition-icon`} | ||
name={ConditionConfig[condition].icon} | ||
size="md" | ||
/> | ||
)} | ||
</IconContainer> | ||
</Tooltip> | ||
{title ? <Text kind="body-s">{title}</Text> : null} | ||
</Container> | ||
); | ||
} | ||
|
||
export default SidebarButton; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export { default } from "./SidebarButton"; |
alex-golovanov marked this conversation as resolved.
Show resolved
Hide resolved
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
export { default } from "./Sidebar"; | ||
export type { IDESidebarButton } from "./Sidebar"; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
export enum Condition { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Because it is being exported for outside consumption, maybe a more descriptive name should be considered. |
||
Warn = "Warn", | ||
// Error = "Error", | ||
// Success = "Success", | ||
} |
There was a problem hiding this comment.
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.