Skip to content

Commit 27994df

Browse files
BrunoQuaresmakylecarbs
authored andcommitted
feat: Add helpful tooltips for the key features (#2097)
1 parent dfac9a2 commit 27994df

File tree

16 files changed

+726
-395
lines changed

16 files changed

+726
-395
lines changed
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { ComponentMeta, Story } from "@storybook/react"
2+
import {
3+
HelpTooltip,
4+
HelpTooltipLink,
5+
HelpTooltipLinksGroup,
6+
HelpTooltipProps,
7+
HelpTooltipText,
8+
HelpTooltipTitle,
9+
} from "./HelpTooltip"
10+
11+
export default {
12+
title: "components/HelpTooltip",
13+
component: HelpTooltip,
14+
} as ComponentMeta<typeof HelpTooltip>
15+
16+
const Template: Story<HelpTooltipProps> = (args) => (
17+
<HelpTooltip {...args}>
18+
<HelpTooltipTitle>What is template?</HelpTooltipTitle>
19+
<HelpTooltipText>
20+
With templates you can create a common configuration for your workspaces using Terraform. So, you and your team
21+
can use the same environment to deliver great software.
22+
</HelpTooltipText>
23+
<HelpTooltipLinksGroup>
24+
<HelpTooltipLink href="https://github.com/coder/coder/">Creating a template</HelpTooltipLink>
25+
<HelpTooltipLink href="https://github.com/coder/coder/">Updating a template</HelpTooltipLink>
26+
</HelpTooltipLinksGroup>
27+
</HelpTooltip>
28+
)
29+
30+
export const Close = Template.bind({})
31+
32+
export const Open = Template.bind({})
33+
Open.args = {
34+
open: true,
35+
}
Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
import Link from "@material-ui/core/Link"
2+
import Popover from "@material-ui/core/Popover"
3+
import { makeStyles } from "@material-ui/core/styles"
4+
import HelpIcon from "@material-ui/icons/HelpOutline"
5+
import OpenInNewIcon from "@material-ui/icons/OpenInNew"
6+
import { useState } from "react"
7+
import { Stack } from "../Stack/Stack"
8+
9+
type Size = "small" | "medium"
10+
export interface HelpTooltipProps {
11+
// Useful to test on storybook
12+
open?: boolean
13+
size?: Size
14+
}
15+
16+
export const HelpTooltip: React.FC<HelpTooltipProps> = ({ children, open, size = "medium" }) => {
17+
const styles = useStyles({ size })
18+
const [anchorEl, setAnchorEl] = useState<HTMLButtonElement | null>(null)
19+
open = open ?? Boolean(anchorEl)
20+
const id = open ? "help-popover" : undefined
21+
22+
return (
23+
<>
24+
<button aria-describedby={id} className={styles.button} onClick={(event) => setAnchorEl(event.currentTarget)}>
25+
<HelpIcon className={styles.icon} />
26+
</button>
27+
<Popover
28+
classes={{ paper: styles.popoverPaper }}
29+
id={id}
30+
open={open}
31+
anchorEl={anchorEl}
32+
onClose={() => {
33+
setAnchorEl(null)
34+
}}
35+
anchorOrigin={{
36+
vertical: "bottom",
37+
horizontal: "left",
38+
}}
39+
transformOrigin={{
40+
vertical: "top",
41+
horizontal: "left",
42+
}}
43+
>
44+
{children}
45+
</Popover>
46+
</>
47+
)
48+
}
49+
50+
export const HelpTooltipTitle: React.FC = ({ children }) => {
51+
const styles = useStyles()
52+
53+
return <h4 className={styles.title}>{children}</h4>
54+
}
55+
56+
export const HelpTooltipText: React.FC = ({ children }) => {
57+
const styles = useStyles()
58+
59+
return <p className={styles.text}>{children}</p>
60+
}
61+
62+
export const HelpTooltipLink: React.FC<{ href: string }> = ({ children, href }) => {
63+
const styles = useStyles()
64+
65+
return (
66+
<Link href={href} target="_blank" rel="noreferrer" className={styles.link}>
67+
<OpenInNewIcon className={styles.linkIcon} />
68+
{children}
69+
</Link>
70+
)
71+
}
72+
73+
export const HelpTooltipLinksGroup: React.FC = ({ children }) => {
74+
const styles = useStyles()
75+
76+
return (
77+
<Stack spacing={1} className={styles.linksGroup}>
78+
{children}
79+
</Stack>
80+
)
81+
}
82+
83+
const getButtonSpacingFromSize = (size?: Size): number => {
84+
switch (size) {
85+
case "small":
86+
return 2.75
87+
case "medium":
88+
default:
89+
return 3
90+
}
91+
}
92+
93+
const getIconSpacingFromSize = (size?: Size): number => {
94+
switch (size) {
95+
case "small":
96+
return 1.75
97+
case "medium":
98+
default:
99+
return 2
100+
}
101+
}
102+
103+
const useStyles = makeStyles((theme) => ({
104+
button: {
105+
display: "flex",
106+
alignItems: "center",
107+
justifyContent: "center",
108+
width: ({ size }: { size?: Size }) => theme.spacing(getButtonSpacingFromSize(size)),
109+
height: ({ size }: { size?: Size }) => theme.spacing(getButtonSpacingFromSize(size)),
110+
padding: 0,
111+
border: 0,
112+
background: "transparent",
113+
color: theme.palette.text.secondary,
114+
cursor: "pointer",
115+
116+
"&:hover": {
117+
color: theme.palette.text.primary,
118+
},
119+
},
120+
121+
icon: {
122+
width: ({ size }: { size?: Size }) => theme.spacing(getIconSpacingFromSize(size)),
123+
height: ({ size }: { size?: Size }) => theme.spacing(getIconSpacingFromSize(size)),
124+
},
125+
126+
popoverPaper: {
127+
marginTop: theme.spacing(0.5),
128+
width: theme.spacing(38),
129+
padding: theme.spacing(2.5),
130+
color: theme.palette.text.secondary,
131+
},
132+
133+
title: {
134+
marginTop: 0,
135+
marginBottom: theme.spacing(1),
136+
color: theme.palette.text.primary,
137+
},
138+
139+
text: {
140+
marginTop: theme.spacing(0.5),
141+
marginBottom: theme.spacing(0.5),
142+
},
143+
144+
link: {
145+
display: "flex",
146+
alignItems: "center",
147+
},
148+
149+
linkIcon: {
150+
color: "inherit",
151+
width: 14,
152+
height: 14,
153+
marginRight: theme.spacing(1),
154+
},
155+
156+
linksGroup: {
157+
marginTop: theme.spacing(2),
158+
},
159+
}))
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { ComponentMeta, Story } from "@storybook/react"
2+
import { PageHeader, PageHeaderTitle } from "./PageHeader"
3+
4+
export default {
5+
title: "components/PageHeader",
6+
component: PageHeader,
7+
} as ComponentMeta<typeof PageHeader>
8+
9+
const Template: Story = () => (
10+
<PageHeader>
11+
<PageHeaderTitle>Templates</PageHeaderTitle>
12+
</PageHeader>
13+
)
14+
15+
export const Example = Template.bind({})
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import { makeStyles } from "@material-ui/core/styles"
2+
import { Stack } from "../Stack/Stack"
3+
4+
export interface PageHeaderProps {
5+
actions?: JSX.Element
6+
}
7+
8+
export const PageHeader: React.FC<PageHeaderProps> = ({ children, actions }) => {
9+
const styles = useStyles()
10+
11+
return (
12+
<div className={styles.root}>
13+
<hgroup>{children}</hgroup>
14+
<Stack direction="row" className={styles.actions}>
15+
{actions}
16+
</Stack>
17+
</div>
18+
)
19+
}
20+
21+
export const PageHeaderTitle: React.FC = ({ children }) => {
22+
const styles = useStyles()
23+
24+
return <h1 className={styles.title}>{children}</h1>
25+
}
26+
27+
export const PageHeaderSubtitle: React.FC = ({ children }) => {
28+
const styles = useStyles()
29+
30+
return <h2 className={styles.subtitle}>{children}</h2>
31+
}
32+
33+
const useStyles = makeStyles((theme) => ({
34+
root: {
35+
display: "flex",
36+
alignItems: "center",
37+
paddingTop: theme.spacing(6),
38+
paddingBottom: theme.spacing(5),
39+
},
40+
41+
title: {
42+
fontSize: theme.spacing(4),
43+
fontWeight: 400,
44+
margin: 0,
45+
display: "flex",
46+
alignItems: "center",
47+
lineHeight: "140%",
48+
},
49+
50+
subtitle: {
51+
fontSize: theme.spacing(2.5),
52+
color: theme.palette.text.secondary,
53+
fontWeight: 400,
54+
display: "block",
55+
margin: 0,
56+
marginTop: theme.spacing(1),
57+
},
58+
59+
actions: {
60+
marginLeft: "auto",
61+
},
62+
}))

site/src/components/Resources/Resources.tsx

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,13 @@ import { FC } from "react"
99
import { Workspace, WorkspaceResource } from "../../api/typesGenerated"
1010
import { getDisplayAgentStatus } from "../../util/workspace"
1111
import { AppLink } from "../AppLink/AppLink"
12+
import {
13+
HelpTooltip,
14+
HelpTooltipLink,
15+
HelpTooltipLinksGroup,
16+
HelpTooltipText,
17+
HelpTooltipTitle,
18+
} from "../HelpTooltip/HelpTooltip"
1219
import { Stack } from "../Stack/Stack"
1320
import { TableHeaderRow } from "../TableHeaders/TableHeaders"
1421
import { TerminalLink } from "../TerminalLink/TerminalLink"
@@ -21,6 +28,35 @@ const Language = {
2128
agentLabel: "Agent",
2229
statusLabel: "Status",
2330
accessLabel: "Access",
31+
resourceTooltipTitle: "What is a resource?",
32+
resourceTooltipText: "A resource is an infrastructure object that is create when the workspace is provisioned.",
33+
resourceTooltipLink: "Persistent and ephemeral resources",
34+
agentTooltipTitle: "What is an agent?",
35+
agentTooltipText:
36+
"The Coder agent runs inside your resource and gives you direct access to the shell via the UI or CLI.",
37+
}
38+
39+
const ResourcesHelpTooltip: React.FC = () => {
40+
return (
41+
<HelpTooltip size="small">
42+
<HelpTooltipTitle>{Language.resourceTooltipTitle}</HelpTooltipTitle>
43+
<HelpTooltipText>{Language.resourceTooltipText}</HelpTooltipText>
44+
<HelpTooltipLinksGroup>
45+
<HelpTooltipLink href="https://github.com/coder/coder/blob/main/docs/templates.md#persistent-and-ephemeral-resources">
46+
{Language.resourceTooltipLink}
47+
</HelpTooltipLink>
48+
</HelpTooltipLinksGroup>
49+
</HelpTooltip>
50+
)
51+
}
52+
53+
const AgentHelpTooltip: React.FC = () => {
54+
return (
55+
<HelpTooltip size="small">
56+
<HelpTooltipTitle>{Language.agentTooltipTitle}</HelpTooltipTitle>
57+
<HelpTooltipText>{Language.agentTooltipTitle}</HelpTooltipText>
58+
</HelpTooltip>
59+
)
2460
}
2561

2662
interface ResourcesProps {
@@ -41,8 +77,18 @@ export const Resources: FC<ResourcesProps> = ({ resources, getResourcesError, wo
4177
<Table className={styles.table}>
4278
<TableHead>
4379
<TableHeaderRow>
44-
<TableCell>{Language.resourceLabel}</TableCell>
45-
<TableCell className={styles.agentColumn}>{Language.agentLabel}</TableCell>
80+
<TableCell>
81+
<Stack direction="row" spacing={0.5} alignItems="center">
82+
{Language.resourceLabel}
83+
<ResourcesHelpTooltip />
84+
</Stack>
85+
</TableCell>
86+
<TableCell className={styles.agentColumn}>
87+
<Stack direction="row" spacing={0.5} alignItems="center">
88+
{Language.agentLabel}
89+
<AgentHelpTooltip />
90+
</Stack>
91+
</TableCell>
4692
<TableCell>{Language.accessLabel}</TableCell>
4793
<TableCell>{Language.statusLabel}</TableCell>
4894
</TableHeaderRow>

site/src/components/Stack/Stack.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { makeStyles } from "@material-ui/core/styles"
2+
import { CSSProperties } from "@material-ui/core/styles/withStyles"
23
import { FC } from "react"
34
import { combineClasses } from "../../util/combineClasses"
45

@@ -7,24 +8,27 @@ type Direction = "column" | "row"
78
interface StyleProps {
89
direction: Direction
910
spacing: number
11+
alignItems?: CSSProperties["alignItems"]
1012
}
1113

1214
const useStyles = makeStyles((theme) => ({
1315
stack: {
1416
display: "flex",
1517
flexDirection: ({ direction }: StyleProps) => direction,
1618
gap: ({ spacing }: StyleProps) => theme.spacing(spacing),
19+
alignItems: ({ alignItems }: StyleProps) => alignItems,
1720
},
1821
}))
1922

2023
export interface StackProps {
2124
className?: string
2225
direction?: Direction
2326
spacing?: number
27+
alignItems?: CSSProperties["alignItems"]
2428
}
2529

26-
export const Stack: FC<StackProps> = ({ children, className, direction = "column", spacing = 2 }) => {
27-
const styles = useStyles({ spacing, direction })
30+
export const Stack: FC<StackProps> = ({ children, className, direction = "column", spacing = 2, alignItems }) => {
31+
const styles = useStyles({ spacing, direction, alignItems })
2832

2933
return <div className={combineClasses([styles.stack, className])}>{children}</div>
3034
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy