Skip to content

Commit b045734

Browse files
feat: Add use template button to template row (#5811)
1 parent 0e58772 commit b045734

File tree

3 files changed

+131
-120
lines changed

3 files changed

+131
-120
lines changed

site/src/hooks/useClickable.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { KeyboardEvent } from "react"
22

3-
interface UseClickableResult {
3+
export interface UseClickableResult {
44
tabIndex: 0
55
role: "button"
66
onClick: () => void
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { makeStyles } from "@material-ui/core/styles"
2+
import { useClickable, UseClickableResult } from "./useClickable"
3+
4+
interface UseClickableTableRowResult extends UseClickableResult {
5+
className: string
6+
hover: true
7+
}
8+
9+
export const useClickableTableRow = (
10+
onClick: () => void,
11+
): UseClickableTableRowResult => {
12+
const styles = useStyles()
13+
const clickable = useClickable(onClick)
14+
15+
return {
16+
...clickable,
17+
className: styles.row,
18+
hover: true,
19+
}
20+
}
21+
22+
const useStyles = makeStyles((theme) => ({
23+
row: {
24+
cursor: "pointer",
25+
26+
"&:focus": {
27+
outline: `1px solid ${theme.palette.secondary.dark}`,
28+
outlineOffset: -1,
29+
},
30+
},
31+
}))
Lines changed: 99 additions & 119 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,13 @@
11
import Button from "@material-ui/core/Button"
22
import Link from "@material-ui/core/Link"
3-
import { makeStyles, Theme } from "@material-ui/core/styles"
3+
import { makeStyles } from "@material-ui/core/styles"
44
import Table from "@material-ui/core/Table"
55
import TableBody from "@material-ui/core/TableBody"
66
import TableCell from "@material-ui/core/TableCell"
77
import TableContainer from "@material-ui/core/TableContainer"
88
import TableHead from "@material-ui/core/TableHead"
99
import TableRow from "@material-ui/core/TableRow"
10-
import KeyboardArrowRight from "@material-ui/icons/KeyboardArrowRight"
1110
import AddIcon from "@material-ui/icons/AddOutlined"
12-
import useTheme from "@material-ui/styles/useTheme"
1311
import { AlertBanner } from "components/AlertBanner/AlertBanner"
1412
import { ChooseOne, Cond } from "components/Conditionals/ChooseOne"
1513
import { Maybe } from "components/Conditionals/Maybe"
@@ -28,7 +26,6 @@ import {
2826
PageHeaderTitle,
2927
} from "../../components/PageHeader/PageHeader"
3028
import { Stack } from "../../components/Stack/Stack"
31-
import { TableCellLink } from "../../components/TableCellLink/TableCellLink"
3229
import { TableLoader } from "../../components/TableLoader/TableLoader"
3330
import {
3431
HelpTooltip,
@@ -39,6 +36,11 @@ import {
3936
} from "../../components/Tooltips/HelpTooltip/HelpTooltip"
4037
import { EmptyTemplates } from "./EmptyTemplates"
4138
import { TemplatesContext } from "xServices/templates/templatesXService"
39+
import { useClickableTableRow } from "hooks/useClickableTableRow"
40+
import { Template } from "api/typesGenerated"
41+
import { combineClasses } from "util/combineClasses"
42+
import { colors } from "theme/colors"
43+
import ArrowForwardOutlined from "@material-ui/icons/ArrowForwardOutlined"
4244

4345
export const Language = {
4446
developerCount: (activeCount: number): string => {
@@ -54,7 +56,6 @@ export const Language = {
5456
templateTooltipText:
5557
"With templates you can create a common configuration for your workspaces using Terraform.",
5658
templateTooltipLink: "Manage templates",
57-
createdByLabel: "Created by",
5859
}
5960

6061
const TemplateHelpTooltip: React.FC = () => {
@@ -71,16 +72,80 @@ const TemplateHelpTooltip: React.FC = () => {
7172
)
7273
}
7374

75+
const TemplateRow: FC<{ template: Template }> = ({ template }) => {
76+
const templatePageLink = `/templates/${template.name}`
77+
const hasIcon = template.icon && template.icon !== ""
78+
const navigate = useNavigate()
79+
const styles = useStyles()
80+
const { className: clickableClassName, ...clickableRow } =
81+
useClickableTableRow(() => {
82+
navigate(templatePageLink)
83+
})
84+
85+
return (
86+
<TableRow
87+
key={template.id}
88+
data-testid={`template-${template.id}`}
89+
{...clickableRow}
90+
className={combineClasses([clickableClassName, styles.tableRow])}
91+
>
92+
<TableCell>
93+
<AvatarData
94+
title={
95+
template.display_name.length > 0
96+
? template.display_name
97+
: template.name
98+
}
99+
subtitle={template.description}
100+
highlightTitle
101+
avatar={
102+
hasIcon && (
103+
<div className={styles.templateIconWrapper}>
104+
<img alt="" src={template.icon} />
105+
</div>
106+
)
107+
}
108+
/>
109+
</TableCell>
110+
111+
<TableCell className={styles.secondary}>
112+
{Language.developerCount(template.active_user_count)}
113+
</TableCell>
114+
115+
<TableCell className={styles.secondary}>
116+
{formatTemplateBuildTime(template.build_time_stats.start.P50)}
117+
</TableCell>
118+
119+
<TableCell data-chromatic="ignore" className={styles.secondary}>
120+
{createDayString(template.updated_at)}
121+
</TableCell>
122+
123+
<TableCell className={styles.actionCell}>
124+
<Button
125+
variant="outlined"
126+
size="small"
127+
className={styles.actionButton}
128+
startIcon={<ArrowForwardOutlined />}
129+
title={`Create a workspace using the ${template.display_name} template`}
130+
onClick={(e) => {
131+
e.stopPropagation()
132+
navigate(`/templates/${template.name}/workspace`)
133+
}}
134+
>
135+
Use template
136+
</Button>
137+
</TableCell>
138+
</TableRow>
139+
)
140+
}
141+
74142
export interface TemplatesPageViewProps {
75143
context: TemplatesContext
76144
}
77145

78146
export const TemplatesPageView: FC<
79147
React.PropsWithChildren<TemplatesPageViewProps>
80148
> = ({ context }) => {
81-
const styles = useStyles()
82-
const navigate = useNavigate()
83-
const theme: Theme = useTheme()
84149
const { templates, error, examples, permissions } = context
85150
const isLoading = !templates
86151
const isEmpty = Boolean(templates && templates.length === 0)
@@ -136,11 +201,10 @@ export const TemplatesPageView: FC<
136201
<Table>
137202
<TableHead>
138203
<TableRow>
139-
<TableCell width="34%">{Language.nameLabel}</TableCell>
140-
<TableCell width="16%">{Language.usedByLabel}</TableCell>
141-
<TableCell width="16%">{Language.buildTimeLabel}</TableCell>
142-
<TableCell width="16%">{Language.lastUpdatedLabel}</TableCell>
143-
<TableCell width="16%">{Language.createdByLabel}</TableCell>
204+
<TableCell width="35%">{Language.nameLabel}</TableCell>
205+
<TableCell width="15%">{Language.usedByLabel}</TableCell>
206+
<TableCell width="10%">{Language.buildTimeLabel}</TableCell>
207+
<TableCell width="15%">{Language.lastUpdatedLabel}</TableCell>
144208
<TableCell width="1%"></TableCell>
145209
</TableRow>
146210
</TableHead>
@@ -158,91 +222,9 @@ export const TemplatesPageView: FC<
158222
</Cond>
159223

160224
<Cond>
161-
{templates?.map((template) => {
162-
const templatePageLink = `/templates/${template.name}`
163-
const hasIcon = template.icon && template.icon !== ""
164-
165-
return (
166-
<TableRow
167-
key={template.id}
168-
hover
169-
data-testid={`template-${template.id}`}
170-
tabIndex={0}
171-
onKeyDown={(event) => {
172-
if (event.key === "Enter") {
173-
navigate(templatePageLink)
174-
}
175-
}}
176-
className={styles.clickableTableRow}
177-
>
178-
<TableCellLink to={templatePageLink}>
179-
<AvatarData
180-
title={
181-
template.display_name.length > 0
182-
? template.display_name
183-
: template.name
184-
}
185-
subtitle={template.description}
186-
highlightTitle
187-
avatar={
188-
hasIcon && (
189-
<div className={styles.templateIconWrapper}>
190-
<img alt="" src={template.icon} />
191-
</div>
192-
)
193-
}
194-
/>
195-
</TableCellLink>
196-
197-
<TableCellLink to={templatePageLink}>
198-
<span
199-
style={{ color: theme.palette.text.secondary }}
200-
>
201-
{Language.developerCount(
202-
template.active_user_count,
203-
)}
204-
</span>
205-
</TableCellLink>
206-
207-
<TableCellLink to={templatePageLink}>
208-
<span
209-
style={{ color: theme.palette.text.secondary }}
210-
>
211-
{formatTemplateBuildTime(
212-
template.build_time_stats.start.P50,
213-
)}
214-
</span>
215-
</TableCellLink>
216-
217-
<TableCellLink
218-
data-chromatic="ignore"
219-
to={templatePageLink}
220-
>
221-
<span
222-
style={{ color: theme.palette.text.secondary }}
223-
>
224-
{createDayString(template.updated_at)}
225-
</span>
226-
</TableCellLink>
227-
228-
<TableCellLink to={templatePageLink}>
229-
<span
230-
style={{ color: theme.palette.text.secondary }}
231-
>
232-
{template.created_by_name}
233-
</span>
234-
</TableCellLink>
235-
236-
<TableCellLink to={templatePageLink}>
237-
<div className={styles.arrowCell}>
238-
<KeyboardArrowRight
239-
className={styles.arrowRight}
240-
/>
241-
</div>
242-
</TableCellLink>
243-
</TableRow>
244-
)
245-
})}
225+
{templates?.map((template) => (
226+
<TemplateRow key={template.id} template={template} />
227+
))}
246228
</Cond>
247229
</ChooseOne>
248230
</TableBody>
@@ -255,27 +237,6 @@ export const TemplatesPageView: FC<
255237
}
256238

257239
const useStyles = makeStyles((theme) => ({
258-
clickableTableRow: {
259-
"&:hover td": {
260-
backgroundColor: theme.palette.action.hover,
261-
},
262-
263-
"&:focus": {
264-
outline: `1px solid ${theme.palette.secondary.dark}`,
265-
},
266-
267-
"& .MuiTableCell-root:last-child": {
268-
paddingRight: theme.spacing(2),
269-
},
270-
},
271-
arrowRight: {
272-
color: theme.palette.text.secondary,
273-
width: 20,
274-
height: 20,
275-
},
276-
arrowCell: {
277-
display: "flex",
278-
},
279240
templateIconWrapper: {
280241
// Same size then the avatar component
281242
width: 36,
@@ -286,4 +247,23 @@ const useStyles = makeStyles((theme) => ({
286247
width: "100%",
287248
},
288249
},
250+
actionCell: {
251+
whiteSpace: "nowrap",
252+
},
253+
secondary: {
254+
color: theme.palette.text.secondary,
255+
},
256+
tableRow: {
257+
"&:hover $actionButton": {
258+
color: theme.palette.text.primary,
259+
borderColor: colors.gray[11],
260+
"&:hover": {
261+
borderColor: theme.palette.text.primary,
262+
},
263+
},
264+
},
265+
actionButton: {
266+
color: theme.palette.text.secondary,
267+
transition: "none",
268+
},
289269
}))

0 commit comments

Comments
 (0)