Skip to content

Commit

Permalink
feat: Add helpful tooltips for the key features (#2097)
Browse files Browse the repository at this point in the history
  • Loading branch information
BrunoQuaresma committed Jun 7, 2022
1 parent 6d96696 commit eedd293
Show file tree
Hide file tree
Showing 16 changed files with 726 additions and 395 deletions.
35 changes: 35 additions & 0 deletions site/src/components/HelpTooltip/HelpTooltip.stories.tsx
@@ -0,0 +1,35 @@
import { ComponentMeta, Story } from "@storybook/react"
import {
HelpTooltip,
HelpTooltipLink,
HelpTooltipLinksGroup,
HelpTooltipProps,
HelpTooltipText,
HelpTooltipTitle,
} from "./HelpTooltip"

export default {
title: "components/HelpTooltip",
component: HelpTooltip,
} as ComponentMeta<typeof HelpTooltip>

const Template: Story<HelpTooltipProps> = (args) => (
<HelpTooltip {...args}>
<HelpTooltipTitle>What is template?</HelpTooltipTitle>
<HelpTooltipText>
With templates you can create a common configuration for your workspaces using Terraform. So, you and your team
can use the same environment to deliver great software.
</HelpTooltipText>
<HelpTooltipLinksGroup>
<HelpTooltipLink href="https://github.com/coder/coder/">Creating a template</HelpTooltipLink>
<HelpTooltipLink href="https://github.com/coder/coder/">Updating a template</HelpTooltipLink>
</HelpTooltipLinksGroup>
</HelpTooltip>
)

export const Close = Template.bind({})

export const Open = Template.bind({})
Open.args = {
open: true,
}
159 changes: 159 additions & 0 deletions site/src/components/HelpTooltip/HelpTooltip.tsx
@@ -0,0 +1,159 @@
import Link from "@material-ui/core/Link"
import Popover from "@material-ui/core/Popover"
import { makeStyles } from "@material-ui/core/styles"
import HelpIcon from "@material-ui/icons/HelpOutline"
import OpenInNewIcon from "@material-ui/icons/OpenInNew"
import { useState } from "react"
import { Stack } from "../Stack/Stack"

type Size = "small" | "medium"
export interface HelpTooltipProps {
// Useful to test on storybook
open?: boolean
size?: Size
}

export const HelpTooltip: React.FC<HelpTooltipProps> = ({ children, open, size = "medium" }) => {
const styles = useStyles({ size })
const [anchorEl, setAnchorEl] = useState<HTMLButtonElement | null>(null)
open = open ?? Boolean(anchorEl)
const id = open ? "help-popover" : undefined

return (
<>
<button aria-describedby={id} className={styles.button} onClick={(event) => setAnchorEl(event.currentTarget)}>
<HelpIcon className={styles.icon} />
</button>
<Popover
classes={{ paper: styles.popoverPaper }}
id={id}
open={open}
anchorEl={anchorEl}
onClose={() => {
setAnchorEl(null)
}}
anchorOrigin={{
vertical: "bottom",
horizontal: "left",
}}
transformOrigin={{
vertical: "top",
horizontal: "left",
}}
>
{children}
</Popover>
</>
)
}

export const HelpTooltipTitle: React.FC = ({ children }) => {
const styles = useStyles()

return <h4 className={styles.title}>{children}</h4>
}

export const HelpTooltipText: React.FC = ({ children }) => {
const styles = useStyles()

return <p className={styles.text}>{children}</p>
}

export const HelpTooltipLink: React.FC<{ href: string }> = ({ children, href }) => {
const styles = useStyles()

return (
<Link href={href} target="_blank" rel="noreferrer" className={styles.link}>
<OpenInNewIcon className={styles.linkIcon} />
{children}
</Link>
)
}

export const HelpTooltipLinksGroup: React.FC = ({ children }) => {
const styles = useStyles()

return (
<Stack spacing={1} className={styles.linksGroup}>
{children}
</Stack>
)
}

const getButtonSpacingFromSize = (size?: Size): number => {
switch (size) {
case "small":
return 2.75
case "medium":
default:
return 3
}
}

const getIconSpacingFromSize = (size?: Size): number => {
switch (size) {
case "small":
return 1.75
case "medium":
default:
return 2
}
}

const useStyles = makeStyles((theme) => ({
button: {
display: "flex",
alignItems: "center",
justifyContent: "center",
width: ({ size }: { size?: Size }) => theme.spacing(getButtonSpacingFromSize(size)),
height: ({ size }: { size?: Size }) => theme.spacing(getButtonSpacingFromSize(size)),
padding: 0,
border: 0,
background: "transparent",
color: theme.palette.text.secondary,
cursor: "pointer",

"&:hover": {
color: theme.palette.text.primary,
},
},

icon: {
width: ({ size }: { size?: Size }) => theme.spacing(getIconSpacingFromSize(size)),
height: ({ size }: { size?: Size }) => theme.spacing(getIconSpacingFromSize(size)),
},

popoverPaper: {
marginTop: theme.spacing(0.5),
width: theme.spacing(38),
padding: theme.spacing(2.5),
color: theme.palette.text.secondary,
},

title: {
marginTop: 0,
marginBottom: theme.spacing(1),
color: theme.palette.text.primary,
},

text: {
marginTop: theme.spacing(0.5),
marginBottom: theme.spacing(0.5),
},

link: {
display: "flex",
alignItems: "center",
},

linkIcon: {
color: "inherit",
width: 14,
height: 14,
marginRight: theme.spacing(1),
},

linksGroup: {
marginTop: theme.spacing(2),
},
}))
15 changes: 15 additions & 0 deletions site/src/components/PageHeader/PageHeader.stories.tsx
@@ -0,0 +1,15 @@
import { ComponentMeta, Story } from "@storybook/react"
import { PageHeader, PageHeaderTitle } from "./PageHeader"

export default {
title: "components/PageHeader",
component: PageHeader,
} as ComponentMeta<typeof PageHeader>

const Template: Story = () => (
<PageHeader>
<PageHeaderTitle>Templates</PageHeaderTitle>
</PageHeader>
)

export const Example = Template.bind({})
62 changes: 62 additions & 0 deletions site/src/components/PageHeader/PageHeader.tsx
@@ -0,0 +1,62 @@
import { makeStyles } from "@material-ui/core/styles"
import { Stack } from "../Stack/Stack"

export interface PageHeaderProps {
actions?: JSX.Element
}

export const PageHeader: React.FC<PageHeaderProps> = ({ children, actions }) => {
const styles = useStyles()

return (
<div className={styles.root}>
<hgroup>{children}</hgroup>
<Stack direction="row" className={styles.actions}>
{actions}
</Stack>
</div>
)
}

export const PageHeaderTitle: React.FC = ({ children }) => {
const styles = useStyles()

return <h1 className={styles.title}>{children}</h1>
}

export const PageHeaderSubtitle: React.FC = ({ children }) => {
const styles = useStyles()

return <h2 className={styles.subtitle}>{children}</h2>
}

const useStyles = makeStyles((theme) => ({
root: {
display: "flex",
alignItems: "center",
paddingTop: theme.spacing(6),
paddingBottom: theme.spacing(5),
},

title: {
fontSize: theme.spacing(4),
fontWeight: 400,
margin: 0,
display: "flex",
alignItems: "center",
lineHeight: "140%",
},

subtitle: {
fontSize: theme.spacing(2.5),
color: theme.palette.text.secondary,
fontWeight: 400,
display: "block",
margin: 0,
marginTop: theme.spacing(1),
},

actions: {
marginLeft: "auto",
},
}))
50 changes: 48 additions & 2 deletions site/src/components/Resources/Resources.tsx
Expand Up @@ -9,6 +9,13 @@ import { FC } from "react"
import { Workspace, WorkspaceResource } from "../../api/typesGenerated"
import { getDisplayAgentStatus } from "../../util/workspace"
import { AppLink } from "../AppLink/AppLink"
import {
HelpTooltip,
HelpTooltipLink,
HelpTooltipLinksGroup,
HelpTooltipText,
HelpTooltipTitle,
} from "../HelpTooltip/HelpTooltip"
import { Stack } from "../Stack/Stack"
import { TableHeaderRow } from "../TableHeaders/TableHeaders"
import { TerminalLink } from "../TerminalLink/TerminalLink"
Expand All @@ -21,6 +28,35 @@ const Language = {
agentLabel: "Agent",
statusLabel: "Status",
accessLabel: "Access",
resourceTooltipTitle: "What is a resource?",
resourceTooltipText: "A resource is an infrastructure object that is create when the workspace is provisioned.",
resourceTooltipLink: "Persistent and ephemeral resources",
agentTooltipTitle: "What is an agent?",
agentTooltipText:
"The Coder agent runs inside your resource and gives you direct access to the shell via the UI or CLI.",
}

const ResourcesHelpTooltip: React.FC = () => {
return (
<HelpTooltip size="small">
<HelpTooltipTitle>{Language.resourceTooltipTitle}</HelpTooltipTitle>
<HelpTooltipText>{Language.resourceTooltipText}</HelpTooltipText>
<HelpTooltipLinksGroup>
<HelpTooltipLink href="https://github.com/coder/coder/blob/main/docs/templates.md#persistent-and-ephemeral-resources">
{Language.resourceTooltipLink}
</HelpTooltipLink>
</HelpTooltipLinksGroup>
</HelpTooltip>
)
}

const AgentHelpTooltip: React.FC = () => {
return (
<HelpTooltip size="small">
<HelpTooltipTitle>{Language.agentTooltipTitle}</HelpTooltipTitle>
<HelpTooltipText>{Language.agentTooltipTitle}</HelpTooltipText>
</HelpTooltip>
)
}

interface ResourcesProps {
Expand All @@ -41,8 +77,18 @@ export const Resources: FC<ResourcesProps> = ({ resources, getResourcesError, wo
<Table className={styles.table}>
<TableHead>
<TableHeaderRow>
<TableCell>{Language.resourceLabel}</TableCell>
<TableCell className={styles.agentColumn}>{Language.agentLabel}</TableCell>
<TableCell>
<Stack direction="row" spacing={0.5} alignItems="center">
{Language.resourceLabel}
<ResourcesHelpTooltip />
</Stack>
</TableCell>
<TableCell className={styles.agentColumn}>
<Stack direction="row" spacing={0.5} alignItems="center">
{Language.agentLabel}
<AgentHelpTooltip />
</Stack>
</TableCell>
<TableCell>{Language.accessLabel}</TableCell>
<TableCell>{Language.statusLabel}</TableCell>
</TableHeaderRow>
Expand Down
8 changes: 6 additions & 2 deletions site/src/components/Stack/Stack.tsx
@@ -1,4 +1,5 @@
import { makeStyles } from "@material-ui/core/styles"
import { CSSProperties } from "@material-ui/core/styles/withStyles"
import { FC } from "react"
import { combineClasses } from "../../util/combineClasses"

Expand All @@ -7,24 +8,27 @@ type Direction = "column" | "row"
interface StyleProps {
direction: Direction
spacing: number
alignItems?: CSSProperties["alignItems"]
}

const useStyles = makeStyles((theme) => ({
stack: {
display: "flex",
flexDirection: ({ direction }: StyleProps) => direction,
gap: ({ spacing }: StyleProps) => theme.spacing(spacing),
alignItems: ({ alignItems }: StyleProps) => alignItems,
},
}))

export interface StackProps {
className?: string
direction?: Direction
spacing?: number
alignItems?: CSSProperties["alignItems"]
}

export const Stack: FC<StackProps> = ({ children, className, direction = "column", spacing = 2 }) => {
const styles = useStyles({ spacing, direction })
export const Stack: FC<StackProps> = ({ children, className, direction = "column", spacing = 2, alignItems }) => {
const styles = useStyles({ spacing, direction, alignItems })

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

0 comments on commit eedd293

Please sign in to comment.