refactor(site): make HelpTooltip easier to reuse and compose (#11242)

This commit is contained in:
Bruno Quaresma
2023-12-19 10:43:23 -03:00
committed by GitHub
parent f2606a78dd
commit 7c4fbe5bae
19 changed files with 329 additions and 447 deletions
@@ -17,6 +17,8 @@ import {
HelpTooltip,
HelpTooltipTitle,
HelpTooltipText,
HelpTooltipContent,
HelpTooltipTrigger,
} from "components/HelpTooltip/HelpTooltip";
import dayjs from "dayjs";
import { useTheme } from "@emotion/react";
@@ -139,12 +141,15 @@ export const ActiveUsersTitle: FC = () => {
return (
<div css={{ display: "flex", alignItems: "center", gap: 8 }}>
Active Users
<HelpTooltip size="small">
<HelpTooltipTitle>How do we calculate active users?</HelpTooltipTitle>
<HelpTooltipText>
When a connection is initiated to a user&apos;s workspace they are
considered an active user. e.g. apps, web terminal, SSH
</HelpTooltipText>
<HelpTooltip>
<HelpTooltipTrigger size="small" />
<HelpTooltipContent>
<HelpTooltipTitle>How do we calculate active users?</HelpTooltipTitle>
<HelpTooltipText>
When a connection is initiated to a user&apos;s workspace they are
considered an active user. e.g. apps, web terminal, SSH
</HelpTooltipText>
</HelpTooltipContent>
</HelpTooltip>
</div>
);
+62 -151
View File
@@ -1,170 +1,107 @@
import Link from "@mui/material/Link";
// This is used as base for the main HelpTooltip component
// eslint-disable-next-line no-restricted-imports -- Read above
import Popover, { type PopoverProps } from "@mui/material/Popover";
import HelpIcon from "@mui/icons-material/HelpOutline";
import OpenInNewIcon from "@mui/icons-material/OpenInNew";
import {
createContext,
useContext,
useRef,
useState,
type FC,
type PropsWithChildren,
type HTMLAttributes,
type ReactNode,
forwardRef,
ComponentProps,
} from "react";
import { Stack } from "components/Stack/Stack";
import type { CSSObject } from "@emotion/css";
import { type CSSObject } from "@emotion/css";
import { css, type Interpolation, type Theme, useTheme } from "@emotion/react";
import { type ClassName, useClassName } from "hooks/useClassName";
import {
Popover,
PopoverContent,
PopoverTrigger,
usePopover,
} from "components/Popover/Popover";
type Icon = typeof HelpIcon;
type Size = "small" | "medium";
export const HelpTooltipContext = createContext<
{ open: boolean; onClose: () => void } | undefined
>(undefined);
export const HelpTooltipIcon = HelpIcon;
const useHelpTooltip = () => {
const helpTooltipContext = useContext(HelpTooltipContext);
if (!helpTooltipContext) {
throw new Error(
"This hook should be used in side of the HelpTooltipContext.",
);
}
return helpTooltipContext;
export const HelpTooltip: FC<ComponentProps<typeof Popover>> = (props) => {
return <Popover mode="hover" {...props} />;
};
interface HelpPopoverProps extends PopoverProps {
onOpen: () => void;
onClose: () => void;
}
export const HelpPopover: FC<HelpPopoverProps> = ({
onOpen,
onClose,
children,
...props
}) => {
const popover = useClassName(classNames.popover, []);
const paper = useClassName(classNames.paper, []);
export const HelpTooltipContent = (
props: ComponentProps<typeof PopoverContent>,
) => {
const theme = useTheme();
return (
<Popover
className={popover}
classes={{ paper }}
onClose={onClose}
anchorOrigin={{
vertical: "bottom",
horizontal: "left",
}}
transformOrigin={{
vertical: "top",
horizontal: "left",
}}
PaperProps={{
onMouseEnter: onOpen,
onMouseLeave: onClose,
}}
<PopoverContent
{...props}
>
{children}
</Popover>
css={{
"& .MuiPaper-root": {
fontSize: 14,
width: 304,
padding: 20,
color: theme.palette.text.secondary,
},
}}
/>
);
};
export interface HelpTooltipProps {
// Useful to test on storybook
open?: boolean;
type HelpTooltipTriggerProps = HTMLAttributes<HTMLButtonElement> & {
size?: Size;
icon?: Icon;
buttonStyles?: Interpolation<Theme>;
iconStyles?: Interpolation<Theme>;
children?: ReactNode;
}
hoverEffect?: boolean;
};
export const HelpTooltip: FC<HelpTooltipProps> = ({
children,
open = false,
size = "medium",
icon: Icon = HelpIcon,
buttonStyles,
iconStyles,
}) => {
const theme = useTheme();
const anchorRef = useRef<HTMLButtonElement>(null);
const [isOpen, setIsOpen] = useState(open);
const id = isOpen ? "help-popover" : undefined;
export const HelpTooltipTrigger = forwardRef<
HTMLButtonElement,
HelpTooltipTriggerProps
>((props, ref) => {
const {
size = "medium",
children = <HelpTooltipIcon />,
hoverEffect = true,
...buttonProps
} = props;
const onClose = () => {
setIsOpen(false);
};
const hoverEffectStyles = css({
opacity: 0.5,
"&:hover": {
opacity: 0.75,
},
});
return (
<>
<PopoverTrigger>
<button
ref={anchorRef}
aria-describedby={id}
{...buttonProps}
aria-label="More info"
ref={ref}
css={[
css`
display: flex;
align-items: center;
justify-content: center;
width: ${theme.spacing(getButtonSpacingFromSize(size))};
height: ${theme.spacing(getButtonSpacingFromSize(size))};
padding: 0;
padding: 4px 0;
border: 0;
background: transparent;
color: ${theme.palette.text.primary};
opacity: 0.5;
cursor: pointer;
color: inherit;
&:hover {
opacity: 0.75;
& svg {
width: ${getIconSpacingFromSize(size)}px;
height: ${getIconSpacingFromSize(size)}px;
}
`,
buttonStyles,
hoverEffect ? hoverEffectStyles : null,
]}
onClick={(event) => {
event.stopPropagation();
setIsOpen(true);
}}
onMouseEnter={() => {
setIsOpen(true);
}}
onMouseLeave={() => {
setIsOpen(false);
}}
aria-label="More info"
>
<Icon
css={[
{
width: theme.spacing(getIconSpacingFromSize(size)),
height: theme.spacing(getIconSpacingFromSize(size)),
},
iconStyles,
]}
/>
{children}
</button>
<HelpPopover
id={id}
open={isOpen}
anchorEl={anchorRef.current}
onOpen={() => setIsOpen(true)}
onClose={() => setIsOpen(false)}
>
<HelpTooltipContext.Provider value={{ open: isOpen, onClose }}>
{children}
</HelpTooltipContext.Provider>
</HelpPopover>
</>
</PopoverTrigger>
);
};
});
export const HelpTooltipTitle: FC<HTMLAttributes<HTMLHeadingElement>> = ({
children,
@@ -213,7 +150,7 @@ export const HelpTooltipAction: FC<HelpTooltipActionProps> = ({
onClick,
ariaLabel,
}) => {
const tooltip = useHelpTooltip();
const popover = usePopover();
return (
<button
@@ -222,7 +159,7 @@ export const HelpTooltipAction: FC<HelpTooltipActionProps> = ({
onClick={(event) => {
event.stopPropagation();
onClick();
tooltip.onClose();
popover.setIsOpen(false);
}}
>
<Icon css={styles.actionIcon} />
@@ -239,42 +176,16 @@ export const HelpTooltipLinksGroup: FC<PropsWithChildren> = ({ children }) => {
);
};
const getButtonSpacingFromSize = (size?: Size): number => {
switch (size) {
case "small":
return 2.5;
case "medium":
default:
return 3;
}
};
const getIconSpacingFromSize = (size?: Size): number => {
switch (size) {
case "small":
return 1.5;
return 12;
case "medium":
default:
return 2;
return 16;
}
};
const classNames = {
popover: (css) => css`
pointer-events: none;
`,
paper: (css, theme) => css`
${theme.typography.body2 as CSSObject}
margin-top: 4px;
width: 304px;
padding: 20px;
color: ${theme.palette.text.secondary};
pointer-events: auto;
`,
} satisfies Record<string, ClassName>;
const styles = {
title: (theme) => ({
marginTop: 0,
@@ -1,10 +1,12 @@
import { type FC, type ReactNode } from "react";
import {
HelpTooltip,
HelpTooltipContent,
HelpTooltipIcon,
HelpTooltipText,
HelpTooltipTitle,
HelpTooltipTrigger,
} from "components/HelpTooltip/HelpTooltip";
import InfoIcon from "@mui/icons-material/InfoOutlined";
import { Interpolation, Theme, css, useTheme } from "@emotion/react";
interface InfoTooltipProps {
@@ -22,14 +24,16 @@ export const InfoTooltip: FC<InfoTooltipProps> = ({
const theme = useTheme();
return (
<HelpTooltip
size="small"
icon={InfoIcon}
iconStyles={{ color: theme.experimental.roles[type].outline }}
buttonStyles={styles.button}
>
<HelpTooltipTitle>{title}</HelpTooltipTitle>
<HelpTooltipText>{message}</HelpTooltipText>
<HelpTooltip>
<HelpTooltipTrigger size="small" css={styles.button}>
<HelpTooltipIcon
css={{ color: theme.experimental.roles[type].outline }}
/>
</HelpTooltipTrigger>
<HelpTooltipContent>
<HelpTooltipTitle>{title}</HelpTooltipTitle>
<HelpTooltipText>{message}</HelpTooltipText>
</HelpTooltipContent>
</HelpTooltip>
);
};
+6 -1
View File
@@ -9,6 +9,7 @@ import {
useId,
useRef,
useState,
HTMLAttributes,
} from "react";
// This is used as base for the main Popover component
// eslint-disable-next-line no-restricted-imports -- Read above
@@ -79,8 +80,11 @@ export const usePopover = () => {
return context;
};
export const PopoverTrigger = (props: { children: TriggerElement }) => {
export const PopoverTrigger = (
props: HTMLAttributes<HTMLElement> & { children: TriggerElement },
) => {
const popover = usePopover();
const { children, ...elementProps } = props;
const clickProps = {
onClick: () => {
@@ -98,6 +102,7 @@ export const PopoverTrigger = (props: { children: TriggerElement }) => {
};
return cloneElement(props.children, {
...elementProps,
...(popover.mode === "click" ? clickProps : hoverProps),
"aria-haspopup": true,
"aria-owns": popover.isOpen ? popover.id : undefined,
+17 -25
View File
@@ -1,14 +1,16 @@
import { type FC, useRef, useState } from "react";
import { type FC } from "react";
import { useTheme } from "@emotion/react";
import { Theme } from "@mui/material/styles";
import type { WorkspaceAgent, DERPRegion } from "api/typesGenerated";
import {
HelpTooltipText,
HelpPopover,
HelpTooltip,
HelpTooltipTitle,
HelpTooltipContent,
} from "components/HelpTooltip/HelpTooltip";
import { Stack } from "components/Stack/Stack";
import { getLatencyColor } from "utils/latency";
import { PopoverTrigger } from "components/Popover/Popover";
const getDisplayLatency = (theme: Theme, agent: WorkspaceAgent) => {
// Find the right latency to display
@@ -31,9 +33,6 @@ const getDisplayLatency = (theme: Theme, agent: WorkspaceAgent) => {
export const AgentLatency: FC<{ agent: WorkspaceAgent }> = ({ agent }) => {
const theme = useTheme();
const anchorRef = useRef<HTMLButtonElement>(null);
const [isOpen, setIsOpen] = useState(false);
const id = isOpen ? "latency-popover" : undefined;
const latency = getDisplayLatency(theme, agent);
if (!latency || !agent.latency) {
@@ -41,24 +40,17 @@ export const AgentLatency: FC<{ agent: WorkspaceAgent }> = ({ agent }) => {
}
return (
<>
<span
role="presentation"
aria-label="latency"
ref={anchorRef}
onMouseEnter={() => setIsOpen(true)}
onMouseLeave={() => setIsOpen(false)}
css={{ cursor: "pointer", color: latency.color }}
>
{Math.round(Math.round(latency.latency_ms))}ms
</span>
<HelpPopover
id={id}
open={isOpen}
anchorEl={anchorRef.current}
onOpen={() => setIsOpen(true)}
onClose={() => setIsOpen(false)}
>
<HelpTooltip>
<PopoverTrigger>
<span
role="presentation"
aria-label="latency"
css={{ cursor: "pointer", color: latency.color }}
>
{Math.round(latency.latency_ms)}ms
</span>
</PopoverTrigger>
<HelpTooltipContent>
<HelpTooltipTitle>Latency</HelpTooltipTitle>
<HelpTooltipText>
This is the latency overhead on non peer to peer connections. The
@@ -86,7 +78,7 @@ export const AgentLatency: FC<{ agent: WorkspaceAgent }> = ({ agent }) => {
))}
</Stack>
</HelpTooltipText>
</HelpPopover>
</>
</HelpTooltipContent>
</HelpTooltip>
);
};
@@ -1,10 +1,10 @@
import { type ComponentProps, type FC } from "react";
import { type FC } from "react";
import { useTheme } from "@emotion/react";
import RefreshIcon from "@mui/icons-material/RefreshOutlined";
import {
HelpPopover,
HelpTooltip,
HelpTooltipAction,
HelpTooltipContext,
HelpTooltipContent,
HelpTooltipLinksGroup,
HelpTooltipText,
HelpTooltipTitle,
@@ -12,8 +12,9 @@ import {
import type { WorkspaceAgent } from "api/typesGenerated";
import { Stack } from "components/Stack/Stack";
import { agentVersionStatus } from "../../utils/workspace";
import { PopoverTrigger } from "components/Popover/Popover";
type AgentOutdatedTooltipProps = ComponentProps<typeof HelpPopover> & {
type AgentOutdatedTooltipProps = {
agent: WorkspaceAgent;
serverVersion: string;
status: agentVersionStatus;
@@ -25,11 +26,6 @@ export const AgentOutdatedTooltip: FC<AgentOutdatedTooltipProps> = ({
serverVersion,
status,
onUpdate,
onOpen,
id,
open,
onClose,
anchorEl,
}) => {
const theme = useTheme();
const versionLabelStyles = {
@@ -50,14 +46,13 @@ export const AgentOutdatedTooltip: FC<AgentOutdatedTooltipProps> = ({
"To fix this, you can stop and start the workspace.";
return (
<HelpPopover
id={id}
open={open}
anchorEl={anchorEl}
onOpen={onOpen}
onClose={onClose}
>
<HelpTooltipContext.Provider value={{ open, onClose }}>
<HelpTooltip>
<PopoverTrigger>
<span role="status" css={{ cursor: "pointer" }}>
{status === agentVersionStatus.Outdated ? "Outdated" : "Deprecated"}
</span>
</PopoverTrigger>
<HelpTooltipContent>
<Stack spacing={1}>
<div>
<HelpTooltipTitle>{title}</HelpTooltipTitle>
@@ -84,7 +79,7 @@ export const AgentOutdatedTooltip: FC<AgentOutdatedTooltipProps> = ({
</HelpTooltipAction>
</HelpTooltipLinksGroup>
</Stack>
</HelpTooltipContext.Provider>
</HelpPopover>
</HelpTooltipContent>
</HelpTooltip>
);
};
+39 -112
View File
@@ -4,12 +4,13 @@ import { WorkspaceAgent } from "api/typesGenerated";
import { ChooseOne, Cond } from "components/Conditionals/ChooseOne";
import WarningRounded from "@mui/icons-material/WarningRounded";
import {
HelpPopover,
HelpTooltip,
HelpTooltipContent,
HelpTooltipText,
HelpTooltipTitle,
} from "components/HelpTooltip/HelpTooltip";
import { useRef, useState } from "react";
import Link from "@mui/material/Link";
import { PopoverTrigger } from "components/Popover/Popover";
// If we think in the agent status and lifecycle into a single enum/state I’d
// say we would have: connecting, timeout, disconnected, connected:created,
@@ -43,27 +44,13 @@ const StartingLifecycle: React.FC = () => {
const StartTimeoutLifecycle: React.FC<{
agent: WorkspaceAgent;
}> = ({ agent }) => {
const anchorRef = useRef<SVGSVGElement>(null);
const [isOpen, setIsOpen] = useState(false);
const id = isOpen ? "timeout-popover" : undefined;
return (
<>
<WarningRounded
ref={anchorRef}
onMouseEnter={() => setIsOpen(true)}
onMouseLeave={() => setIsOpen(false)}
role="status"
aria-label="Start timeout"
css={styles.timeoutWarning}
/>
<HelpPopover
id={id}
open={isOpen}
anchorEl={anchorRef.current}
onOpen={() => setIsOpen(true)}
onClose={() => setIsOpen(false)}
>
<HelpTooltip>
<PopoverTrigger role="status" aria-label="Agent timeout">
<WarningRounded css={styles.timeoutWarning} />
</PopoverTrigger>
<HelpTooltipContent>
<HelpTooltipTitle>Agent is taking too long to start</HelpTooltipTitle>
<HelpTooltipText>
We noticed this agent is taking longer than expected to start.{" "}
@@ -76,35 +63,20 @@ const StartTimeoutLifecycle: React.FC<{
</Link>
.
</HelpTooltipText>
</HelpPopover>
</>
</HelpTooltipContent>
</HelpTooltip>
);
};
const StartErrorLifecycle: React.FC<{
agent: WorkspaceAgent;
}> = ({ agent }) => {
const anchorRef = useRef<SVGSVGElement>(null);
const [isOpen, setIsOpen] = useState(false);
const id = isOpen ? "timeout-popover" : undefined;
return (
<>
<WarningRounded
ref={anchorRef}
onMouseEnter={() => setIsOpen(true)}
onMouseLeave={() => setIsOpen(false)}
role="status"
aria-label="Start error"
css={styles.errorWarning}
/>
<HelpPopover
id={id}
open={isOpen}
anchorEl={anchorRef.current}
onOpen={() => setIsOpen(true)}
onClose={() => setIsOpen(false)}
>
<HelpTooltip>
<PopoverTrigger role="status" aria-label="Start error">
<WarningRounded css={styles.errorWarning} />
</PopoverTrigger>
<HelpTooltipContent>
<HelpTooltipTitle>Error starting the agent</HelpTooltipTitle>
<HelpTooltipText>
Something went wrong during the agent startup.{" "}
@@ -117,8 +89,8 @@ const StartErrorLifecycle: React.FC<{
</Link>
.
</HelpTooltipText>
</HelpPopover>
</>
</HelpTooltipContent>
</HelpTooltip>
);
};
@@ -137,27 +109,12 @@ const ShuttingDownLifecycle: React.FC = () => {
const ShutdownTimeoutLifecycle: React.FC<{
agent: WorkspaceAgent;
}> = ({ agent }) => {
const anchorRef = useRef<SVGSVGElement>(null);
const [isOpen, setIsOpen] = useState(false);
const id = isOpen ? "timeout-popover" : undefined;
return (
<>
<WarningRounded
ref={anchorRef}
onMouseEnter={() => setIsOpen(true)}
onMouseLeave={() => setIsOpen(false)}
role="status"
aria-label="Stop timeout"
css={styles.timeoutWarning}
/>
<HelpPopover
id={id}
open={isOpen}
anchorEl={anchorRef.current}
onOpen={() => setIsOpen(true)}
onClose={() => setIsOpen(false)}
>
<HelpTooltip>
<PopoverTrigger role="status" aria-label="Stop timeout">
<WarningRounded css={styles.timeoutWarning} />
</PopoverTrigger>
<HelpTooltipContent>
<HelpTooltipTitle>Agent is taking too long to stop</HelpTooltipTitle>
<HelpTooltipText>
We noticed this agent is taking longer than expected to stop.{" "}
@@ -170,35 +127,20 @@ const ShutdownTimeoutLifecycle: React.FC<{
</Link>
.
</HelpTooltipText>
</HelpPopover>
</>
</HelpTooltipContent>
</HelpTooltip>
);
};
const ShutdownErrorLifecycle: React.FC<{
agent: WorkspaceAgent;
}> = ({ agent }) => {
const anchorRef = useRef<SVGSVGElement>(null);
const [isOpen, setIsOpen] = useState(false);
const id = isOpen ? "timeout-popover" : undefined;
return (
<>
<WarningRounded
ref={anchorRef}
onMouseEnter={() => setIsOpen(true)}
onMouseLeave={() => setIsOpen(false)}
role="status"
aria-label="Stop error"
css={styles.errorWarning}
/>
<HelpPopover
id={id}
open={isOpen}
anchorEl={anchorRef.current}
onOpen={() => setIsOpen(true)}
onClose={() => setIsOpen(false)}
>
<HelpTooltip>
<PopoverTrigger role="status" aria-label="Stop error">
<WarningRounded css={styles.errorWarning} />
</PopoverTrigger>
<HelpTooltipContent>
<HelpTooltipTitle>Error stopping the agent</HelpTooltipTitle>
<HelpTooltipText>
Something went wrong while trying to stop the agent.{" "}
@@ -211,8 +153,8 @@ const ShutdownErrorLifecycle: React.FC<{
</Link>
.
</HelpTooltipText>
</HelpPopover>
</>
</HelpTooltipContent>
</HelpTooltip>
);
};
@@ -293,27 +235,12 @@ const ConnectingStatus: React.FC = () => {
const TimeoutStatus: React.FC<{
agent: WorkspaceAgent;
}> = ({ agent }) => {
const anchorRef = useRef<SVGSVGElement>(null);
const [isOpen, setIsOpen] = useState(false);
const id = isOpen ? "timeout-popover" : undefined;
return (
<>
<WarningRounded
ref={anchorRef}
onMouseEnter={() => setIsOpen(true)}
onMouseLeave={() => setIsOpen(false)}
role="status"
aria-label="Timeout"
css={styles.timeoutWarning}
/>
<HelpPopover
id={id}
open={isOpen}
anchorEl={anchorRef.current}
onOpen={() => setIsOpen(true)}
onClose={() => setIsOpen(false)}
>
<HelpTooltip>
<PopoverTrigger role="status" aria-label="Timeout">
<WarningRounded css={styles.timeoutWarning} />
</PopoverTrigger>
<HelpTooltipContent>
<HelpTooltipTitle>Agent is taking too long to connect</HelpTooltipTitle>
<HelpTooltipText>
We noticed this agent is taking longer than expected to connect.{" "}
@@ -326,8 +253,8 @@ const TimeoutStatus: React.FC<{
</Link>
.
</HelpTooltipText>
</HelpPopover>
</>
</HelpTooltipContent>
</HelpTooltip>
);
};
+7 -27
View File
@@ -1,4 +1,4 @@
import { type FC, useRef, useState } from "react";
import { type FC } from "react";
import type { WorkspaceAgent } from "api/typesGenerated";
import { agentVersionStatus, getDisplayVersionStatus } from "utils/workspace";
import { AgentOutdatedTooltip } from "./AgentOutdatedTooltip";
@@ -9,9 +9,6 @@ export const AgentVersion: FC<{
serverAPIVersion: string;
onUpdate: () => void;
}> = ({ agent, serverVersion, serverAPIVersion, onUpdate }) => {
const anchorRef = useRef<HTMLButtonElement>(null);
const [isOpen, setIsOpen] = useState(false);
const id = isOpen ? "version-outdated-popover" : undefined;
const { status } = getDisplayVersionStatus(
agent.version,
serverVersion,
@@ -24,28 +21,11 @@ export const AgentVersion: FC<{
}
return (
<>
<span
role="presentation"
aria-label="latency"
ref={anchorRef}
onMouseEnter={() => setIsOpen(true)}
onMouseLeave={() => setIsOpen(false)}
css={{ cursor: "pointer" }}
>
{status === agentVersionStatus.Outdated ? "Outdated" : "Deprecated"}
</span>
<AgentOutdatedTooltip
id={id}
open={isOpen}
anchorEl={anchorRef.current}
onOpen={() => setIsOpen(true)}
onClose={() => setIsOpen(false)}
agent={agent}
serverVersion={serverVersion}
status={status}
onUpdate={onUpdate}
/>
</>
<AgentOutdatedTooltip
agent={agent}
serverVersion={serverVersion}
status={status}
onUpdate={onUpdate}
/>
);
};
@@ -4,15 +4,18 @@ import Skeleton from "@mui/material/Skeleton";
import Link from "@mui/material/Link";
import { type FC } from "react";
import { useQuery } from "react-query";
import { type Interpolation, type Theme, css, useTheme } from "@emotion/react";
import { type Interpolation, type Theme, useTheme } from "@emotion/react";
import { templateVersion } from "api/queries/templates";
import {
HelpTooltip,
HelpTooltipAction,
HelpTooltipContent,
HelpTooltipLinksGroup,
HelpTooltipText,
HelpTooltipTitle,
HelpTooltipTrigger,
} from "components/HelpTooltip/HelpTooltip";
import { usePopover } from "components/Popover/Popover";
export const Language = {
outdatedLabel: "Outdated",
@@ -28,22 +31,33 @@ interface TooltipProps {
ariaLabel?: string;
}
export const WorkspaceOutdatedTooltip: FC<TooltipProps> = ({
onUpdateVersion,
ariaLabel,
latestVersionId,
templateName,
}) => {
const { data: activeVersion } = useQuery(templateVersion(latestVersionId));
export const WorkspaceOutdatedTooltip: FC<TooltipProps> = (props) => {
return (
<HelpTooltip>
<HelpTooltipTrigger
size="small"
aria-label="More info"
hoverEffect={false}
>
<InfoIcon css={styles.icon} />
</HelpTooltipTrigger>
<OutdatedTooltipContent {...props} />
</HelpTooltip>
);
};
const OutdatedTooltipContent = (props: TooltipProps) => {
const popover = usePopover();
const { onUpdateVersion, ariaLabel, latestVersionId, templateName } = props;
const { data: activeVersion } = useQuery({
...templateVersion(latestVersionId),
enabled: popover.isOpen,
});
const theme = useTheme();
return (
<HelpTooltip
size="small"
icon={InfoIcon}
iconStyles={styles.icon}
buttonStyles={styles.button}
>
<HelpTooltipContent>
<HelpTooltipTitle>{Language.outdatedLabel}</HelpTooltipTitle>
<HelpTooltipText>{Language.versionTooltipText}</HelpTooltipText>
@@ -90,7 +104,7 @@ export const WorkspaceOutdatedTooltip: FC<TooltipProps> = ({
{Language.updateVersionLabel}
</HelpTooltipAction>
</HelpTooltipLinksGroup>
</HelpTooltip>
</HelpTooltipContent>
);
};
@@ -99,14 +113,6 @@ const styles = {
color: theme.experimental.roles.notice.outline,
}),
button: css`
opacity: 1;
&:hover {
opacity: 1;
}
`,
container: {
display: "flex",
flexDirection: "column",
+13 -7
View File
@@ -1,10 +1,12 @@
import { FC } from "react";
import {
HelpTooltip,
HelpTooltipContent,
HelpTooltipLink,
HelpTooltipLinksGroup,
HelpTooltipText,
HelpTooltipTitle,
HelpTooltipTrigger,
} from "components/HelpTooltip/HelpTooltip";
import { docs } from "utils/docs";
@@ -17,13 +19,17 @@ export const Language = {
export const AuditHelpTooltip: FC = () => {
return (
<HelpTooltip>
<HelpTooltipTitle>{Language.title}</HelpTooltipTitle>
<HelpTooltipText>{Language.body}</HelpTooltipText>
<HelpTooltipLinksGroup>
<HelpTooltipLink href={docs("/admin/audit-logs")}>
{Language.docs}
</HelpTooltipLink>
</HelpTooltipLinksGroup>
<HelpTooltipTrigger />
<HelpTooltipContent>
<HelpTooltipTitle>{Language.title}</HelpTooltipTitle>
<HelpTooltipText>{Language.body}</HelpTooltipText>
<HelpTooltipLinksGroup>
<HelpTooltipLink href={docs("/admin/audit-logs")}>
{Language.docs}
</HelpTooltipLink>
</HelpTooltipLinksGroup>
</HelpTooltipContent>
</HelpTooltip>
);
};
@@ -23,7 +23,9 @@ import * as Yup from "yup";
import { WorkspaceBuildLogs } from "components/WorkspaceBuildLogs/WorkspaceBuildLogs";
import {
HelpTooltip,
HelpTooltipContent,
HelpTooltipText,
HelpTooltipTrigger,
} from "components/HelpTooltip/HelpTooltip";
import { LazyIconField } from "components/IconField/LazyIconField";
import Link from "@mui/material/Link";
@@ -572,10 +574,13 @@ export const CreateTemplateForm: FC<CreateTemplateFormProps> = (props) => {
</strong>
<HelpTooltip>
<HelpTooltipText>
If checked, users may be able to corrupt their
workspace.
</HelpTooltipText>
<HelpTooltipTrigger size="small" />
<HelpTooltipContent>
<HelpTooltipText>
If checked, users may be able to corrupt their
workspace.
</HelpTooltipText>
</HelpTooltipContent>
</HelpTooltip>
</Stack>
<span css={styles.optionHelperText}>
@@ -608,16 +613,21 @@ export const CreateTemplateForm: FC<CreateTemplateFormProps> = (props) => {
<strong>Allow everyone to use the template</strong>
<HelpTooltip>
<HelpTooltipText>
If unchecked, only users with the &apos;template
admin&apos; and &apos;owner&apos; role can use this
template until the permissions are updated. Navigate to{" "}
<strong>
Templates &gt; Select a template &gt; Settings &gt;
Permissions
</strong>{" "}
to update permissions.
</HelpTooltipText>
<HelpTooltipTrigger size="small" />
<HelpTooltipContent>
<HelpTooltipText>
If unchecked, only users with the &apos;template
admin&apos; and &apos;owner&apos; role can use this
template until the permissions are updated. Navigate
to{" "}
<strong>
Templates <MenuPath /> Select a template{" "}
<MenuPath /> Settings <MenuPath />
Permissions
</strong>{" "}
to update permissions.
</HelpTooltipText>
</HelpTooltipContent>
</HelpTooltip>
</Stack>
<span css={styles.optionHelperText}>
@@ -684,6 +694,10 @@ export const CreateTemplateForm: FC<CreateTemplateFormProps> = (props) => {
);
};
const MenuPath = () => {
return <span aria-label="in">&gt;</span>;
};
const fillNameAndDisplayWithFilename = async (
filename: string,
form: ReturnType<typeof useFormik<CreateTemplateData>>,
@@ -46,6 +46,8 @@ import {
HelpTooltip,
HelpTooltipTitle,
HelpTooltipText,
HelpTooltipContent,
HelpTooltipTrigger,
} from "components/HelpTooltip/HelpTooltip";
import { UserAvatar } from "components/UserAvatar/UserAvatar";
import { getLatencyColor } from "utils/latency";
@@ -281,14 +283,18 @@ const UsersLatencyPanel: FC<UsersLatencyPanelProps> = ({
<PanelHeader>
<PanelTitle css={{ display: "flex", alignItems: "center", gap: 8 }}>
Latency by user
<HelpTooltip size="small">
<HelpTooltipTitle>How is latency calculated?</HelpTooltipTitle>
<HelpTooltipText>
The median round trip time of user connections to workspaces.
</HelpTooltipText>
<HelpTooltip>
<HelpTooltipTrigger size="small" />
<HelpTooltipContent>
<HelpTooltipTitle>How is latency calculated?</HelpTooltipTitle>
<HelpTooltipText>
The median round trip time of user connections to workspaces.
</HelpTooltipText>
</HelpTooltipContent>
</HelpTooltip>
</PanelTitle>
</PanelHeader>
<PanelContent>
{!data && <Loader css={{ height: "100%" }} />}
{users && users.length === 0 && <NoDataAvailable />}
@@ -348,12 +354,15 @@ const UsersActivityPanel: FC<UsersActivityPanelProps> = ({
<PanelHeader>
<PanelTitle css={{ display: "flex", alignItems: "center", gap: 8 }}>
Activity by user
<HelpTooltip size="small">
<HelpTooltipTitle>How is activity calculated?</HelpTooltipTitle>
<HelpTooltipText>
When a connection is initiated to a user&apos;s workspace they are
considered an active user. e.g. apps, web terminal, SSH
</HelpTooltipText>
<HelpTooltip>
<HelpTooltipTrigger size="small" />
<HelpTooltipContent>
<HelpTooltipTitle>How is activity calculated?</HelpTooltipTitle>
<HelpTooltipText>
When a connection is initiated to a user&apos;s workspace they
are considered an active user. e.g. apps, web terminal, SSH
</HelpTooltipText>
</HelpTooltipContent>
</HelpTooltip>
</PanelTitle>
</PanelHeader>
@@ -22,7 +22,9 @@ import { Stack } from "components/Stack/Stack";
import Checkbox from "@mui/material/Checkbox";
import {
HelpTooltip,
HelpTooltipContent,
HelpTooltipText,
HelpTooltipTrigger,
} from "components/HelpTooltip/HelpTooltip";
import { EnterpriseBadge } from "components/Badges/Badges";
@@ -161,9 +163,13 @@ export const TemplateSettingsForm: FC<TemplateSettingsForm> = ({
>
Allow users to cancel in-progress workspace jobs.
<HelpTooltip>
<HelpTooltipText>
If checked, users may be able to corrupt their workspace.
</HelpTooltipText>
<HelpTooltipTrigger />
<HelpTooltipContent>
<HelpTooltipText>
If checked, users may be able to corrupt their
workspace.
</HelpTooltipText>
</HelpTooltipContent>
</HelpTooltip>
</Stack>
<span css={styles.optionHelperText}>
@@ -193,9 +199,12 @@ export const TemplateSettingsForm: FC<TemplateSettingsForm> = ({
>
Require workspaces automatically update when started.
<HelpTooltip>
<HelpTooltipText>
This setting is not enforced for template admins.
</HelpTooltipText>
<HelpTooltipTrigger />
<HelpTooltipContent>
<HelpTooltipText>
This setting is not enforced for template admins.
</HelpTooltipText>
</HelpTooltipContent>
</HelpTooltip>
</Stack>
<span css={styles.optionHelperText}>
@@ -28,10 +28,12 @@ import {
} from "components/TableLoader/TableLoader";
import {
HelpTooltip,
HelpTooltipContent,
HelpTooltipLink,
HelpTooltipLinksGroup,
HelpTooltipText,
HelpTooltipTitle,
HelpTooltipTrigger,
} from "components/HelpTooltip/HelpTooltip";
import { EmptyTemplates } from "./EmptyTemplates";
import { useClickableTableRow } from "hooks/useClickableTableRow";
@@ -63,13 +65,16 @@ export const Language = {
const TemplateHelpTooltip: FC = () => {
return (
<HelpTooltip>
<HelpTooltipTitle>{Language.templateTooltipTitle}</HelpTooltipTitle>
<HelpTooltipText>{Language.templateTooltipText}</HelpTooltipText>
<HelpTooltipLinksGroup>
<HelpTooltipLink href={docs("/templates")}>
{Language.templateTooltipLink}
</HelpTooltipLink>
</HelpTooltipLinksGroup>
<HelpTooltipTrigger />
<HelpTooltipContent>
<HelpTooltipTitle>{Language.templateTooltipTitle}</HelpTooltipTitle>
<HelpTooltipText>{Language.templateTooltipText}</HelpTooltipText>
<HelpTooltipLinksGroup>
<HelpTooltipLink href={docs("/templates")}>
{Language.templateTooltipLink}
</HelpTooltipLink>
</HelpTooltipLinksGroup>
</HelpTooltipContent>
</HelpTooltip>
);
};
@@ -8,8 +8,10 @@ import UserIcon from "@mui/icons-material/PersonOutline";
import { Role } from "api/typesGenerated";
import {
HelpTooltip,
HelpTooltipContent,
HelpTooltipText,
HelpTooltipTitle,
HelpTooltipTrigger,
} from "components/HelpTooltip/HelpTooltip";
import {
Popover,
@@ -101,11 +103,14 @@ export const EditRolesButton: FC<EditRolesButtonProps> = ({
if (!canSetRoles) {
return (
<HelpTooltip size="small">
<HelpTooltipTitle>Externally controlled</HelpTooltipTitle>
<HelpTooltipText>
Roles for this user are controlled by the OIDC identity provider.
</HelpTooltipText>
<HelpTooltip>
<HelpTooltipTrigger size="small" />
<HelpTooltipContent>
<HelpTooltipTitle>Externally controlled</HelpTooltipTitle>
<HelpTooltipText>
Roles for this user are controlled by the OIDC identity provider.
</HelpTooltipText>
</HelpTooltipContent>
</HelpTooltip>
);
}
@@ -1,10 +1,12 @@
import { FC } from "react";
import {
HelpTooltip,
HelpTooltipContent,
HelpTooltipLink,
HelpTooltipLinksGroup,
HelpTooltipText,
HelpTooltipTitle,
HelpTooltipTrigger,
} from "components/HelpTooltip/HelpTooltip";
import { docs } from "utils/docs";
@@ -42,17 +44,19 @@ export const TableColumnHelpTooltip: FC<Props> = ({ variant }) => {
const variantLang = Language[variant];
return (
<HelpTooltip size="small">
<HelpTooltipTitle>{variantLang.title}</HelpTooltipTitle>
<HelpTooltipText>{variantLang.text}</HelpTooltipText>
<HelpTooltipLinksGroup>
{variantLang.links.map((link) => (
<HelpTooltipLink key={link.text} href={link.href}>
{link.text}
</HelpTooltipLink>
))}
</HelpTooltipLinksGroup>
<HelpTooltip>
<HelpTooltipTrigger size="small" />
<HelpTooltipContent>
<HelpTooltipTitle>{variantLang.title}</HelpTooltipTitle>
<HelpTooltipText>{variantLang.text}</HelpTooltipText>
<HelpTooltipLinksGroup>
{variantLang.links.map((link) => (
<HelpTooltipLink key={link.text} href={link.href}>
{link.text}
</HelpTooltipLink>
))}
</HelpTooltipLinksGroup>
</HelpTooltipContent>
</HelpTooltip>
);
};
@@ -87,7 +87,7 @@ export const WorkspaceStats: FC<WorkspaceStatsProps> = ({
css={styles.statsItem}
label="Version"
value={
<>
<span css={{ display: "flex", alignItems: "center", gap: 4 }}>
<Link
component={RouterLink}
to={`/templates/${workspace.template_name}/versions/${workspace.latest_build.template_version_name}`}
@@ -103,7 +103,7 @@ export const WorkspaceStats: FC<WorkspaceStatsProps> = ({
ariaLabel="update version"
/>
)}
</>
</span>
}
/>
@@ -1,10 +1,12 @@
import { FC } from "react";
import {
HelpTooltip,
HelpTooltipContent,
HelpTooltipLink,
HelpTooltipLinksGroup,
HelpTooltipText,
HelpTooltipTitle,
HelpTooltipTrigger,
} from "components/HelpTooltip/HelpTooltip";
import { docs } from "utils/docs";
@@ -20,16 +22,19 @@ const Language = {
export const WorkspaceHelpTooltip: FC = () => {
return (
<HelpTooltip>
<HelpTooltipTitle>{Language.workspaceTooltipTitle}</HelpTooltipTitle>
<HelpTooltipText>{Language.workspaceTooltipText}</HelpTooltipText>
<HelpTooltipLinksGroup>
<HelpTooltipLink href={docs("/workspaces")}>
{Language.workspaceTooltipLink1}
</HelpTooltipLink>
<HelpTooltipLink href={docs("/ides")}>
{Language.workspaceTooltipLink2}
</HelpTooltipLink>
</HelpTooltipLinksGroup>
<HelpTooltipTrigger />
<HelpTooltipContent>
<HelpTooltipTitle>{Language.workspaceTooltipTitle}</HelpTooltipTitle>
<HelpTooltipText>{Language.workspaceTooltipText}</HelpTooltipText>
<HelpTooltipLinksGroup>
<HelpTooltipLink href={docs("/workspaces")}>
{Language.workspaceTooltipLink1}
</HelpTooltipLink>
<HelpTooltipLink href={docs("/ides")}>
{Language.workspaceTooltipLink2}
</HelpTooltipLink>
</HelpTooltipLinksGroup>
</HelpTooltipContent>
</HelpTooltip>
);
};
@@ -146,7 +146,7 @@ export const WorkspacesTable: FC<WorkspacesTableProps> = ({
title={
<Stack
direction="row"
spacing={0}
spacing={0.5}
alignItems="center"
>
{workspace.name}