Skip to content

Commit 5b5fbbe

Browse files
authored
fix: implement proper overflow behavior for workspace history (#19340)
Before: https://github.com/user-attachments/assets/2f1ff75c-4916-4d0a-a657-004d46691ea0 After: https://github.com/user-attachments/assets/a8e575b5-84f9-4eea-b318-93d2a3d60aa5 Also converts a lot of emotion components to tailwind
1 parent 734299d commit 5b5fbbe

File tree

5 files changed

+184
-290
lines changed

5 files changed

+184
-290
lines changed
Lines changed: 26 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,28 @@
1-
import { type Interpolation, type Theme, useTheme } from "@emotion/react";
21
import type { ComponentProps, FC, HTMLAttributes } from "react";
32
import { Link, type LinkProps } from "react-router";
3+
import { cn } from "utils/cn";
44
import { TopbarIconButton } from "./Topbar";
55

66
export const Sidebar: FC<HTMLAttributes<HTMLDivElement>> = (props) => {
7-
const theme = useTheme();
87
return (
98
<div
10-
css={{
11-
width: 260,
12-
borderRight: `1px solid ${theme.palette.divider}`,
13-
height: "100%",
14-
overflow: "auto",
15-
flexShrink: 0,
16-
padding: "8px 0",
17-
display: "flex",
18-
flexDirection: "column",
19-
gap: 1,
20-
}}
9+
// TODO: Remove extra border classes once MUI is removed
10+
className="flex flex-col gap-px w-64 border-solid border-0 border-r border-r-border h-full py-2 shrink-0 overflow-y-auto"
2111
{...props}
2212
/>
2313
);
2414
};
2515

26-
export const SidebarLink: FC<LinkProps> = (props) => {
27-
return <Link css={styles.sidebarItem} {...props} />;
16+
export const SidebarLink: FC<LinkProps> = ({ className, ...props }) => {
17+
return (
18+
<Link
19+
className={cn(
20+
"text-[13px] text-content-primary py-2 px-4 text-left bg-transparent hover:divide-surface-tertiary cursor-pointer border-0 no-underline",
21+
className,
22+
)}
23+
{...props}
24+
/>
25+
);
2826
};
2927

3028
interface SidebarItemProps extends HTMLAttributes<HTMLButtonElement> {
@@ -33,21 +31,16 @@ interface SidebarItemProps extends HTMLAttributes<HTMLButtonElement> {
3331

3432
export const SidebarItem: FC<SidebarItemProps> = ({
3533
isActive,
34+
className,
3635
...buttonProps
3736
}) => {
38-
const theme = useTheme();
39-
4037
return (
4138
<button
42-
css={[
43-
styles.sidebarItem,
44-
{ opacity: "0.75", "&:hover": { opacity: 1 } },
45-
isActive && {
46-
background: theme.palette.action.selected,
47-
opacity: 1,
48-
pointerEvents: "none",
49-
},
50-
]}
39+
className={cn(
40+
"text-[13px] text-content-primary py-2 px-4 text-left bg-transparent hover:divide-surface-tertiary opacity-75 hover:opacity-100 cursor-pointer border-0",
41+
isActive && "opacity-100 bg-surface-tertiary",
42+
className,
43+
)}
5144
{...buttonProps}
5245
/>
5346
);
@@ -56,15 +49,7 @@ export const SidebarItem: FC<SidebarItemProps> = ({
5649
export const SidebarCaption: FC<HTMLAttributes<HTMLSpanElement>> = (props) => {
5750
return (
5851
<span
59-
css={{
60-
fontSize: 10,
61-
lineHeight: 1.2,
62-
padding: "12px 16px",
63-
display: "block",
64-
textTransform: "uppercase",
65-
fontWeight: 500,
66-
letterSpacing: "0.1em",
67-
}}
52+
className="text-[10px] leading-tight py-3 px-4 uppercase font-medium text-content-primary tracking-widest"
6853
{...props}
6954
/>
7055
);
@@ -76,49 +61,17 @@ interface SidebarIconButton extends ComponentProps<typeof TopbarIconButton> {
7661

7762
export const SidebarIconButton: FC<SidebarIconButton> = ({
7863
isActive,
64+
className,
7965
...buttonProps
8066
}) => {
8167
return (
8268
<TopbarIconButton
83-
css={[
84-
{ opacity: 0.75, "&:hover": { opacity: 1 } },
85-
isActive && styles.activeSidebarIconButton,
86-
]}
69+
className={cn(
70+
"opacity-75 hover:opacity-100 border-0 border-x-2 border-x-transparent border-solid",
71+
isActive && "opacity-100 relative border-l-sky-400",
72+
className,
73+
)}
8774
{...buttonProps}
8875
/>
8976
);
9077
};
91-
92-
const styles = {
93-
sidebarItem: (theme) => ({
94-
fontSize: 13,
95-
lineHeight: 1.2,
96-
color: theme.palette.text.primary,
97-
textDecoration: "none",
98-
padding: "8px 16px",
99-
display: "block",
100-
textAlign: "left",
101-
background: "none",
102-
border: 0,
103-
cursor: "pointer",
104-
105-
"&:hover": {
106-
backgroundColor: theme.palette.action.hover,
107-
},
108-
}),
109-
110-
activeSidebarIconButton: (theme) => ({
111-
opacity: 1,
112-
position: "relative",
113-
"&::before": {
114-
content: '""',
115-
position: "absolute",
116-
left: 0,
117-
top: 0,
118-
bottom: 0,
119-
width: 2,
120-
backgroundColor: theme.palette.primary.main,
121-
height: "100%",
122-
},
123-
}),
124-
} satisfies Record<string, Interpolation<Theme>>;

site/src/modules/dashboard/DashboardLayout.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,10 @@ export const DashboardLayout: FC = () => {
2323
{canViewDeployment && <LicenseBanner />}
2424
<AnnouncementBanners />
2525

26-
<div className="flex flex-col min-h-full">
26+
<div className="flex flex-col h-screen">
2727
<Navbar />
2828

29-
<div className="flex flex-col flex-1 pb-12">
29+
<div className="flex flex-col flex-1 min-h-0">
3030
<Suspense fallback={<Loader />}>
3131
<Outlet />
3232
</Suspense>

site/src/modules/dashboard/Navbar/NavbarView.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ export const NavbarView: FC<NavbarViewProps> = ({
5151
const webPush = useWebpushNotifications();
5252

5353
return (
54-
<div className="border-0 border-b border-solid h-[72px] flex items-center leading-none px-6">
54+
<div className="border-0 border-b border-solid h-[72px] min-h-[72px] flex items-center leading-none px-6">
5555
<NavLink to="/workspaces">
5656
{logo_url ? (
5757
<ExternalImage className="h-7" src={logo_url} alt="Custom Logo" />

site/src/pages/WorkspacePage/HistorySidebar.tsx

Lines changed: 34 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
SidebarItem,
99
SidebarLink,
1010
} from "components/FullPageLayout/Sidebar";
11+
import { ScrollArea } from "components/ScrollArea/ScrollArea";
1112
import { Spinner } from "components/Spinner/Spinner";
1213
import {
1314
WorkspaceBuildData,
@@ -30,36 +31,40 @@ export const HistorySidebar: FC<HistorySidebarProps> = ({ workspace }) => {
3031
return (
3132
<Sidebar>
3233
<SidebarCaption>History</SidebarCaption>
33-
{builds
34-
? builds.map((build) => (
35-
<SidebarLink
36-
target="_blank"
37-
key={build.id}
38-
to={`/@${build.workspace_owner_name}/${build.workspace_name}/builds/${build.build_number}`}
39-
>
40-
<WorkspaceBuildData build={build} />
41-
</SidebarLink>
42-
))
43-
: Array.from({ length: 15 }, (_, i) => (
44-
<SidebarItem key={i}>
45-
<WorkspaceBuildDataSkeleton />
46-
</SidebarItem>
47-
))}
48-
{buildsQuery.hasNextPage && (
49-
<div css={{ padding: 16 }}>
50-
<Button
51-
onClick={() => buildsQuery.fetchNextPage()}
52-
disabled={buildsQuery.isFetchingNextPage}
53-
variant="outline"
54-
className="w-full"
55-
>
56-
<Spinner loading={buildsQuery.isFetchingNextPage}>
57-
<ArrowDownwardOutlined />
58-
</Spinner>
59-
Show more builds
60-
</Button>
34+
<ScrollArea>
35+
<div className="flex flex-col gap-px">
36+
{builds
37+
? builds.map((build) => (
38+
<SidebarLink
39+
target="_blank"
40+
key={build.id}
41+
to={`/@${build.workspace_owner_name}/${build.workspace_name}/builds/${build.build_number}`}
42+
>
43+
<WorkspaceBuildData build={build} />
44+
</SidebarLink>
45+
))
46+
: Array.from({ length: 15 }, (_, i) => (
47+
<SidebarItem key={i}>
48+
<WorkspaceBuildDataSkeleton />
49+
</SidebarItem>
50+
))}
51+
{buildsQuery.hasNextPage && (
52+
<div css={{ padding: 16 }}>
53+
<Button
54+
onClick={() => buildsQuery.fetchNextPage()}
55+
disabled={buildsQuery.isFetchingNextPage}
56+
variant="outline"
57+
className="w-full"
58+
>
59+
<Spinner loading={buildsQuery.isFetchingNextPage}>
60+
<ArrowDownwardOutlined />
61+
</Spinner>
62+
Show more builds
63+
</Button>
64+
</div>
65+
)}
6166
</div>
62-
)}
67+
</ScrollArea>
6368
</Sidebar>
6469
);
6570
};

0 commit comments

Comments
 (0)