Skip to content

Commit e51eeb6

Browse files
authored
refactor: improve settings sidebar components (#10801)
1 parent 7fa70ce commit e51eeb6

File tree

9 files changed

+382
-603
lines changed

9 files changed

+382
-603
lines changed

site/src/components/DeploySettingsLayout/Sidebar.tsx

Lines changed: 19 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -7,135 +7,53 @@ import Globe from "@mui/icons-material/PublicOutlined";
77
import HubOutlinedIcon from "@mui/icons-material/HubOutlined";
88
import VpnKeyOutlined from "@mui/icons-material/VpnKeyOutlined";
99
import MonitorHeartOutlined from "@mui/icons-material/MonitorHeartOutlined";
10-
import { GitIcon } from "components/Icons/GitIcon";
11-
import { Stack } from "components/Stack/Stack";
12-
import type { ElementType, FC, PropsWithChildren, ReactNode } from "react";
13-
import { NavLink } from "react-router-dom";
10+
import { type FC } from "react";
1411
import { useDashboard } from "components/Dashboard/DashboardProvider";
15-
import { css } from "@emotion/css";
16-
import { useTheme } from "@emotion/react";
17-
18-
const SidebarNavItem: FC<
19-
PropsWithChildren<{ href: string; icon: ReactNode }>
20-
> = ({ children, href, icon }) => {
21-
const theme = useTheme();
22-
23-
const activeStyles = css`
24-
background-color: ${theme.palette.action.hover};
25-
26-
&::before {
27-
content: "";
28-
display: block;
29-
width: 3px;
30-
height: 100%;
31-
position: absolute;
32-
left: 0;
33-
top: 0;
34-
background-color: ${theme.palette.primary.main};
35-
border-top-left-radius: 8px;
36-
border-bottom-left-radius: 8px;
37-
}
38-
`;
39-
40-
return (
41-
<NavLink
42-
to={href}
43-
className={({ isActive }) => css`
44-
${isActive && activeStyles}
45-
46-
color: inherit;
47-
display: block;
48-
font-size: 14px;
49-
text-decoration: none;
50-
padding: 12px 12px 12px 16px;
51-
border-radius: 4px;
52-
transition: background-color 0.15s ease-in-out;
53-
margin-bottom: 1;
54-
position: relative;
55-
56-
&:hover {
57-
background-color: ${theme.palette.action.hover};
58-
}
59-
`}
60-
>
61-
<Stack alignItems="center" spacing={1.5} direction="row">
62-
{icon}
63-
{children}
64-
</Stack>
65-
</NavLink>
66-
);
67-
};
68-
69-
const SidebarNavItemIcon: FC<{ icon: ElementType }> = ({ icon: Icon }) => {
70-
return <Icon css={{ width: 16, height: 16 }} />;
71-
};
12+
import { GitIcon } from "components/Icons/GitIcon";
13+
import {
14+
Sidebar as BaseSidebar,
15+
SidebarNavItem,
16+
} from "components/Sidebar/Sidebar";
7217

73-
export const Sidebar: React.FC = () => {
18+
export const Sidebar: FC = () => {
7419
const dashboard = useDashboard();
7520

7621
return (
77-
<nav css={{ width: 245 }}>
78-
<SidebarNavItem
79-
href="general"
80-
icon={<SidebarNavItemIcon icon={LaunchOutlined} />}
81-
>
22+
<BaseSidebar>
23+
<SidebarNavItem href="general" icon={LaunchOutlined}>
8224
General
8325
</SidebarNavItem>
84-
<SidebarNavItem
85-
href="licenses"
86-
icon={<SidebarNavItemIcon icon={ApprovalIcon} />}
87-
>
26+
<SidebarNavItem href="licenses" icon={ApprovalIcon}>
8827
Licenses
8928
</SidebarNavItem>
90-
<SidebarNavItem
91-
href="appearance"
92-
icon={<SidebarNavItemIcon icon={Brush} />}
93-
>
29+
<SidebarNavItem href="appearance" icon={Brush}>
9430
Appearance
9531
</SidebarNavItem>
96-
<SidebarNavItem
97-
href="userauth"
98-
icon={<SidebarNavItemIcon icon={VpnKeyOutlined} />}
99-
>
32+
<SidebarNavItem href="userauth" icon={VpnKeyOutlined}>
10033
User Authentication
10134
</SidebarNavItem>
102-
<SidebarNavItem
103-
href="external-auth"
104-
icon={<SidebarNavItemIcon icon={GitIcon} />}
105-
>
35+
<SidebarNavItem href="external-auth" icon={GitIcon}>
10636
External Authentication
10737
</SidebarNavItem>
108-
<SidebarNavItem href="network" icon={<SidebarNavItemIcon icon={Globe} />}>
38+
<SidebarNavItem href="network" icon={Globe}>
10939
Network
11040
</SidebarNavItem>
11141
{dashboard.experiments.includes("moons") && (
112-
<SidebarNavItem
113-
href="workspace-proxies"
114-
icon={<SidebarNavItemIcon icon={HubOutlinedIcon} />}
115-
>
42+
<SidebarNavItem href="workspace-proxies" icon={HubOutlinedIcon}>
11643
Workspace Proxies
11744
</SidebarNavItem>
11845
)}
119-
<SidebarNavItem
120-
href="security"
121-
icon={<SidebarNavItemIcon icon={LockRounded} />}
122-
>
46+
<SidebarNavItem href="security" icon={LockRounded}>
12347
Security
12448
</SidebarNavItem>
125-
<SidebarNavItem
126-
href="observability"
127-
icon={<SidebarNavItemIcon icon={InsertChartIcon} />}
128-
>
49+
<SidebarNavItem href="observability" icon={InsertChartIcon}>
12950
Observability
13051
</SidebarNavItem>
13152
{dashboard.experiments.includes("deployment_health_page") && (
132-
<SidebarNavItem
133-
href="/health"
134-
icon={<SidebarNavItemIcon icon={MonitorHeartOutlined} />}
135-
>
53+
<SidebarNavItem href="/health" icon={MonitorHeartOutlined}>
13654
Health
13755
</SidebarNavItem>
13856
)}
139-
</nav>
57+
</BaseSidebar>
14058
);
14159
};

site/src/components/SettingsLayout/Section.tsx

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { type FC, type ReactNode, type PropsWithChildren } from "react";
1+
import { type FC, type ReactNode } from "react";
22
import { type Interpolation, type Theme } from "@emotion/react";
33

44
type SectionLayout = "fixed" | "fluid";
@@ -15,9 +15,7 @@ export interface SectionProps {
1515
children?: ReactNode;
1616
}
1717

18-
type SectionFC = FC<PropsWithChildren<SectionProps>>;
19-
20-
export const Section: SectionFC = ({
18+
export const Section: FC<SectionProps> = ({
2119
id,
2220
title,
2321
description,
Lines changed: 20 additions & 134 deletions
Original file line numberDiff line numberDiff line change
@@ -1,163 +1,49 @@
1-
import { css } from "@emotion/css";
2-
import {
3-
type CSSObject,
4-
type Interpolation,
5-
type Theme,
6-
useTheme,
7-
} from "@emotion/react";
81
import VpnKeyOutlined from "@mui/icons-material/VpnKeyOutlined";
92
import FingerprintOutlinedIcon from "@mui/icons-material/FingerprintOutlined";
10-
import {
11-
type FC,
12-
type ComponentType,
13-
type PropsWithChildren,
14-
type ReactNode,
15-
} from "react";
16-
import { NavLink } from "react-router-dom";
173
import AccountIcon from "@mui/icons-material/Person";
184
import ScheduleIcon from "@mui/icons-material/EditCalendarOutlined";
195
import SecurityIcon from "@mui/icons-material/LockOutlined";
206
import type { User } from "api/typesGenerated";
21-
import { Stack } from "components/Stack/Stack";
227
import { UserAvatar } from "components/UserAvatar/UserAvatar";
238
import { useDashboard } from "components/Dashboard/DashboardProvider";
24-
import { combineClasses } from "utils/combineClasses";
25-
26-
const SidebarNavItem: FC<
27-
PropsWithChildren<{ href: string; icon: ReactNode }>
28-
> = ({ children, href, icon }) => {
29-
const theme = useTheme();
30-
31-
const sidebarNavItemStyles = css`
32-
color: inherit;
33-
display: block;
34-
font-size: 14px;
35-
text-decoration: none;
36-
padding: 12px 12px 12px 16px;
37-
border-radius: 4px;
38-
transition: background-color 0.15s ease-in-out;
39-
margin-bottom: 1px;
40-
position: relative;
41-
42-
&:hover {
43-
background-color: theme.palette.action.hover;
44-
}
45-
`;
46-
47-
const sidebarNavItemActiveStyles = css`
48-
background-color: ${theme.palette.action.hover};
49-
50-
&:before {
51-
content: "";
52-
display: block;
53-
width: 3px;
54-
height: 100%;
55-
position: absolute;
56-
left: 0;
57-
top: 0;
58-
background-color: ${theme.palette.primary.main};
59-
border-top-left-radius: 8px;
60-
border-bottom-left-radius: 8px;
61-
}
62-
`;
63-
64-
return (
65-
<NavLink
66-
to={href}
67-
className={({ isActive }) =>
68-
combineClasses([
69-
sidebarNavItemStyles,
70-
isActive ? sidebarNavItemActiveStyles : undefined,
71-
])
72-
}
73-
>
74-
<Stack alignItems="center" spacing={1.5} direction="row">
75-
{icon}
76-
{children}
77-
</Stack>
78-
</NavLink>
79-
);
80-
};
81-
82-
const SidebarNavItemIcon: React.FC<{
83-
icon: ComponentType<{ className?: string }>;
84-
}> = ({ icon: Icon }) => {
85-
return <Icon css={{ width: 16, height: 16 }} />;
86-
};
9+
import {
10+
Sidebar as BaseSidebar,
11+
SidebarHeader,
12+
SidebarNavItem,
13+
} from "components/Sidebar/Sidebar";
8714

8815
export const Sidebar: React.FC<{ user: User }> = ({ user }) => {
8916
const { entitlements } = useDashboard();
9017
const allowAutostopRequirement =
9118
entitlements.features.template_autostop_requirement.enabled;
9219

9320
return (
94-
<nav css={styles.sidebar}>
95-
<Stack direction="row" alignItems="center" css={styles.userInfo}>
96-
<UserAvatar username={user.username} avatarURL={user.avatar_url} />
97-
<Stack spacing={0} css={styles.userData}>
98-
<span css={styles.username}>{user.username}</span>
99-
<span css={styles.email}>{user.email}</span>
100-
</Stack>
101-
</Stack>
102-
103-
<SidebarNavItem
104-
href="account"
105-
icon={<SidebarNavItemIcon icon={AccountIcon} />}
106-
>
21+
<BaseSidebar>
22+
<SidebarHeader
23+
avatar={
24+
<UserAvatar username={user.username} avatarURL={user.avatar_url} />
25+
}
26+
title={user.username}
27+
subtitle={user.email}
28+
/>
29+
;
30+
<SidebarNavItem href="account" icon={AccountIcon}>
10731
Account
10832
</SidebarNavItem>
10933
{allowAutostopRequirement && (
110-
<SidebarNavItem
111-
href="schedule"
112-
icon={<SidebarNavItemIcon icon={ScheduleIcon} />}
113-
>
34+
<SidebarNavItem href="schedule" icon={ScheduleIcon}>
11435
Schedule
11536
</SidebarNavItem>
11637
)}
117-
<SidebarNavItem
118-
href="security"
119-
icon={<SidebarNavItemIcon icon={SecurityIcon} />}
120-
>
38+
<SidebarNavItem href="security" icon={SecurityIcon}>
12139
Security
12240
</SidebarNavItem>
123-
<SidebarNavItem
124-
href="ssh-keys"
125-
icon={<SidebarNavItemIcon icon={FingerprintOutlinedIcon} />}
126-
>
41+
<SidebarNavItem href="ssh-keys" icon={FingerprintOutlinedIcon}>
12742
SSH Keys
12843
</SidebarNavItem>
129-
<SidebarNavItem
130-
href="tokens"
131-
icon={<SidebarNavItemIcon icon={VpnKeyOutlined} />}
132-
>
44+
<SidebarNavItem href="tokens" icon={VpnKeyOutlined}>
13345
Tokens
13446
</SidebarNavItem>
135-
</nav>
47+
</BaseSidebar>
13648
);
13749
};
138-
139-
const styles = {
140-
sidebar: {
141-
width: 245,
142-
flexShrink: 0,
143-
},
144-
userInfo: (theme) => ({
145-
...(theme.typography.body2 as CSSObject),
146-
marginBottom: 16,
147-
}),
148-
userData: {
149-
overflow: "hidden",
150-
},
151-
username: {
152-
fontWeight: 600,
153-
overflow: "hidden",
154-
textOverflow: "ellipsis",
155-
whiteSpace: "nowrap",
156-
},
157-
email: (theme) => ({
158-
color: theme.palette.text.secondary,
159-
fontSize: 12,
160-
overflow: "hidden",
161-
textOverflow: "ellipsis",
162-
}),
163-
} satisfies Record<string, Interpolation<Theme>>;

0 commit comments

Comments
 (0)