Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit eedd293

Browse files
feat: Add helpful tooltips for the key features (#2097)
1 parent 6d96696 commit eedd293

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)