Skip to content

Commit 5fa3fde

Browse files
refactor: Improve empty views (coder#5134)
1 parent a477d90 commit 5fa3fde

File tree

6 files changed

+122
-55
lines changed

6 files changed

+122
-55
lines changed

site/src/components/EmptyState/EmptyState.tsx

Lines changed: 31 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@ export interface EmptyStateProps {
99
message: string
1010
/** Longer optional description to display below the message */
1111
description?: string | React.ReactNode
12-
descriptionClassName?: string
1312
cta?: ReactNode
1413
className?: string
14+
image?: ReactNode
1515
}
1616

1717
/**
@@ -25,14 +25,7 @@ export interface EmptyStateProps {
2525
export const EmptyState: FC<React.PropsWithChildren<EmptyStateProps>> = (
2626
props,
2727
) => {
28-
const {
29-
message,
30-
description,
31-
cta,
32-
descriptionClassName,
33-
className,
34-
...boxProps
35-
} = props
28+
const { message, description, cta, className, image, ...boxProps } = props
3629
const styles = useStyles()
3730

3831
return (
@@ -44,38 +37,42 @@ export const EmptyState: FC<React.PropsWithChildren<EmptyStateProps>> = (
4437
<Typography
4538
variant="body2"
4639
color="textSecondary"
47-
className={combineClasses([styles.description, descriptionClassName])}
40+
className={styles.description}
4841
>
4942
{description}
5043
</Typography>
5144
)}
5245
{cta && <div className={styles.cta}>{cta}</div>}
46+
{image}
5347
</Box>
5448
)
5549
}
5650

57-
const useStyles = makeStyles(
58-
(theme) => ({
59-
root: {
60-
display: "flex",
61-
flexDirection: "column",
62-
justifyContent: "center",
63-
alignItems: "center",
64-
textAlign: "center",
65-
minHeight: 300,
66-
padding: theme.spacing(3),
67-
},
51+
const useStyles = makeStyles((theme) => ({
52+
root: {
53+
overflow: "hidden",
54+
display: "flex",
55+
flexDirection: "column",
56+
justifyContent: "center",
57+
alignItems: "center",
58+
textAlign: "center",
59+
minHeight: 360,
60+
padding: theme.spacing(10, 5),
61+
position: "relative",
62+
},
6863

69-
title: {
70-
fontSize: theme.spacing(3),
71-
},
72-
description: {
73-
marginTop: theme.spacing(1.5),
74-
fontSize: theme.spacing(2),
75-
},
76-
cta: {
77-
marginTop: theme.spacing(4),
78-
},
79-
}),
80-
{ name: "EmptyState" },
81-
)
64+
title: {
65+
fontSize: theme.spacing(3),
66+
},
67+
68+
description: {
69+
marginTop: theme.spacing(1.5),
70+
fontSize: theme.spacing(2),
71+
lineHeight: "140%",
72+
maxWidth: theme.spacing(60),
73+
},
74+
75+
cta: {
76+
marginTop: theme.spacing(4),
77+
},
78+
}))
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { makeStyles } from "@material-ui/core/styles"
2+
import TableCell from "@material-ui/core/TableCell"
3+
import TableRow from "@material-ui/core/TableRow"
4+
import { FC } from "react"
5+
import {
6+
EmptyState,
7+
EmptyStateProps,
8+
} from "../../components/EmptyState/EmptyState"
9+
10+
export type TableEmptyProps = EmptyStateProps
11+
12+
export const TableEmpty: FC<TableEmptyProps> = (props) => {
13+
const styles = useStyles()
14+
15+
return (
16+
<TableRow>
17+
<TableCell colSpan={999} className={styles.tableCell}>
18+
<EmptyState {...props} />
19+
</TableCell>
20+
</TableRow>
21+
)
22+
}
23+
24+
const useStyles = makeStyles(() => ({
25+
tableCell: {
26+
padding: "0 !important",
27+
},
28+
}))

site/src/components/WorkspacesTable/WorkspacesTableBody.tsx

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import Button from "@material-ui/core/Button"
22
import Link from "@material-ui/core/Link"
3+
import { makeStyles } from "@material-ui/core/styles"
34
import TableCell from "@material-ui/core/TableCell"
45
import TableRow from "@material-ui/core/TableRow"
5-
import AddCircleOutline from "@material-ui/icons/AddCircleOutline"
6+
import AddOutlined from "@material-ui/icons/AddOutlined"
67
import { ChooseOne, Cond } from "components/Conditionals/ChooseOne"
78
import { FC } from "react"
89
import { useTranslation } from "react-i18next"
@@ -24,6 +25,7 @@ export const WorkspacesTableBody: FC<
2425
React.PropsWithChildren<TableBodyProps>
2526
> = ({ isLoading, workspaceRefs, filter, isNonInitialPage }) => {
2627
const { t } = useTranslation("workspacesPage")
28+
const styles = useStyles()
2729

2830
return (
2931
<ChooseOne>
@@ -32,7 +34,7 @@ export const WorkspacesTableBody: FC<
3234
</Cond>
3335
<Cond condition={!workspaceRefs || workspaceRefs.length === 0}>
3436
<TableRow>
35-
<TableCell colSpan={999}>
37+
<TableCell colSpan={999} className={styles.emptyTableCell}>
3638
<ChooseOne>
3739
<Cond condition={isNonInitialPage}>
3840
<EmptyState message={t("emptyPageMessage")} />
@@ -44,6 +46,7 @@ export const WorkspacesTableBody: FC<
4446
}
4547
>
4648
<EmptyState
49+
className={styles.empty}
4750
message={t("emptyCreateWorkspaceMessage")}
4851
description={t("emptyCreateWorkspaceDescription")}
4952
cta={
@@ -52,11 +55,16 @@ export const WorkspacesTableBody: FC<
5255
component={RouterLink}
5356
to="/templates"
5457
>
55-
<Button startIcon={<AddCircleOutline />}>
58+
<Button startIcon={<AddOutlined />}>
5659
{t("createFromTemplateButton")}
5760
</Button>
5861
</Link>
5962
}
63+
image={
64+
<div className={styles.emptyImage}>
65+
<img src="/empty/workspaces.webp" alt="" />
66+
</div>
67+
}
6068
/>
6169
</Cond>
6270
<Cond>
@@ -75,3 +83,25 @@ export const WorkspacesTableBody: FC<
7583
</ChooseOne>
7684
)
7785
}
86+
87+
const useStyles = makeStyles((theme) => ({
88+
emptyTableCell: {
89+
padding: "0 !important",
90+
},
91+
92+
empty: {
93+
paddingBottom: 0,
94+
},
95+
96+
emptyImage: {
97+
maxWidth: "50%",
98+
height: theme.spacing(34),
99+
overflow: "hidden",
100+
marginTop: theme.spacing(6),
101+
opacity: 0.85,
102+
103+
"& img": {
104+
maxWidth: "100%",
105+
},
106+
},
107+
}))

site/src/pages/TemplatesPage/TemplatesPageView.tsx

Lines changed: 30 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import useTheme from "@material-ui/styles/useTheme"
1111
import { AlertBanner } from "components/AlertBanner/AlertBanner"
1212
import { ChooseOne, Cond } from "components/Conditionals/ChooseOne"
1313
import { Maybe } from "components/Conditionals/Maybe"
14+
import { TableEmpty } from "components/TableEmpty/TableEmpty"
1415
import { FC } from "react"
1516
import { useTranslation } from "react-i18next"
1617
import { useNavigate } from "react-router-dom"
@@ -22,7 +23,6 @@ import {
2223
import * as TypesGen from "../../api/typesGenerated"
2324
import { AvatarData } from "../../components/AvatarData/AvatarData"
2425
import { CodeExample } from "../../components/CodeExample/CodeExample"
25-
import { EmptyState } from "../../components/EmptyState/EmptyState"
2626
import { Margins } from "../../components/Margins/Margins"
2727
import {
2828
PageHeader,
@@ -174,20 +174,21 @@ export const TemplatesPageView: FC<
174174

175175
<ChooseOne>
176176
<Cond condition={empty}>
177-
<TableRow>
178-
<TableCell colSpan={999}>
179-
<EmptyState
180-
message={Language.emptyMessage}
181-
description={
182-
props.canCreateTemplate
183-
? Language.emptyDescription
184-
: Language.emptyViewNoPerms
185-
}
186-
descriptionClassName={styles.emptyDescription}
187-
cta={<CodeExample code="coder templates init" />}
188-
/>
189-
</TableCell>
190-
</TableRow>
177+
<TableEmpty
178+
className={styles.empty}
179+
message={Language.emptyMessage}
180+
description={
181+
props.canCreateTemplate
182+
? Language.emptyDescription
183+
: Language.emptyViewNoPerms
184+
}
185+
cta={<CodeExample code="coder templates init" />}
186+
image={
187+
<div className={styles.emptyImage}>
188+
<img src="/empty/templates.webp" alt="" />
189+
</div>
190+
}
191+
/>
191192
</Cond>
192193
<Cond>
193194
{props.templates?.map((template) => {
@@ -287,9 +288,6 @@ export const TemplatesPageView: FC<
287288
}
288289

289290
const useStyles = makeStyles((theme) => ({
290-
emptyDescription: {
291-
maxWidth: theme.spacing(62),
292-
},
293291
clickableTableRow: {
294292
"&:hover td": {
295293
backgroundColor: theme.palette.action.hover,
@@ -321,4 +319,18 @@ const useStyles = makeStyles((theme) => ({
321319
width: "100%",
322320
},
323321
},
322+
empty: {
323+
paddingBottom: 0,
324+
},
325+
326+
emptyImage: {
327+
maxWidth: "50%",
328+
height: theme.spacing(40),
329+
overflow: "hidden",
330+
opacity: 0.85,
331+
332+
"& img": {
333+
maxWidth: "100%",
334+
},
335+
},
324336
}))

site/static/empty/templates.webp

107 KB
Binary file not shown.

site/static/empty/workspaces.webp

58.2 KB
Binary file not shown.

0 commit comments

Comments
 (0)