fix(site): improve keyboard navigation in help popovers (#23374)

This commit is contained in:
code-qtzl
2026-03-31 11:10:33 -06:00
committed by GitHub
parent 84b94a8376
commit f44a8994da
39 changed files with 590 additions and 584 deletions
@@ -7,12 +7,12 @@ import {
ChartTooltipContent,
} from "#/components/Chart/Chart";
import {
HelpTooltip,
HelpTooltipContent,
HelpTooltipIconTrigger,
HelpTooltipText,
HelpTooltipTitle,
} from "#/components/HelpTooltip/HelpTooltip";
HelpPopover,
HelpPopoverContent,
HelpPopoverIconTrigger,
HelpPopoverText,
HelpPopoverTitle,
} from "#/components/HelpPopover/HelpPopover";
import { formatDate } from "#/utils/time";
const chartConfig = {
@@ -120,18 +120,18 @@ export const ActiveUsersTitle: FC<ActiveUsersTitleProps> = ({ interval }) => {
return (
<div className="flex items-center gap-2">
{interval === "day" ? "Daily" : "Weekly"} Active Users
<HelpTooltip>
<HelpTooltipIconTrigger size="small" />
<HelpTooltipContent>
<HelpTooltipTitle>How do we calculate active users?</HelpTooltipTitle>
<HelpTooltipText>
<HelpPopover>
<HelpPopoverIconTrigger size="small" />
<HelpPopoverContent>
<HelpPopoverTitle>How do we calculate active users?</HelpPopoverTitle>
<HelpPopoverText>
When a connection is initiated to a user&apos;s workspace they are
considered an active user. e.g. apps, web terminal, SSH. This is for
measuring user activity and has no connection to license
consumption.
</HelpTooltipText>
</HelpTooltipContent>
</HelpTooltip>
</HelpPopoverText>
</HelpPopoverContent>
</HelpPopover>
</div>
);
};
@@ -0,0 +1,36 @@
import type { Meta, StoryObj } from "@storybook/react-vite";
import {
HelpPopover,
HelpPopoverLink,
HelpPopoverLinksGroup,
HelpPopoverText,
HelpPopoverTitle,
} from "./HelpPopover";
const meta: Meta<typeof HelpPopover> = {
title: "components/HelpPopover",
component: HelpPopover,
args: {
children: (
<>
<HelpPopoverTitle>What is a template?</HelpPopoverTitle>
<HelpPopoverText>
A template is a common configuration for your team&apos;s workspaces.
</HelpPopoverText>
<HelpPopoverLinksGroup>
<HelpPopoverLink href="https://github.com/coder/coder/">
Creating a template
</HelpPopoverLink>
<HelpPopoverLink href="https://github.com/coder/coder/">
Updating a template
</HelpPopoverLink>
</HelpPopoverLinksGroup>
</>
),
},
};
export default meta;
type Story = StoryObj<typeof HelpPopover>;
export const Example: Story = {};
@@ -1,32 +1,29 @@
import { CircleHelpIcon, ExternalLinkIcon } from "lucide-react";
import type { FC, HTMLAttributes, PropsWithChildren, ReactNode } from "react";
import {
Tooltip,
TooltipContent,
type TooltipContentProps,
type TooltipProps,
TooltipTrigger,
} from "#/components/Tooltip/Tooltip";
Popover,
PopoverContent,
type PopoverContentProps,
PopoverTrigger,
} from "#/components/Popover/Popover";
import { cn } from "#/utils/cn";
type Icon = typeof CircleHelpIcon;
type Size = "small" | "medium";
export const HelpTooltipTrigger = TooltipTrigger;
export const HelpPopoverTrigger = PopoverTrigger;
export const HelpTooltipIcon = CircleHelpIcon;
export const HelpPopoverIcon = CircleHelpIcon;
export const HelpTooltip: FC<TooltipProps> = (props) => {
return <Tooltip {...props} />;
};
export const HelpPopover = Popover;
export const HelpTooltipContent: FC<TooltipContentProps> = ({
export const HelpPopoverContent: FC<PopoverContentProps> = ({
className,
...props
}) => {
return (
<TooltipContent
<PopoverContent
side="bottom"
align="start"
collisionPadding={16}
@@ -39,22 +36,23 @@ export const HelpTooltipContent: FC<TooltipContentProps> = ({
);
};
type HelpTooltipIconTriggerProps = React.ComponentPropsWithRef<"button"> & {
type HelpPopoverIconTriggerProps = React.ComponentPropsWithRef<"button"> & {
size?: Size;
hoverEffect?: boolean;
};
export const HelpTooltipIconTrigger: React.FC<HelpTooltipIconTriggerProps> = ({
export const HelpPopoverIconTrigger: React.FC<HelpPopoverIconTriggerProps> = ({
size = "medium",
children = <HelpTooltipIcon />,
children = <HelpPopoverIcon />,
hoverEffect = true,
className,
...buttonProps
}) => {
return (
<HelpTooltipTrigger asChild>
<HelpPopoverTrigger asChild>
<button
{...buttonProps}
type="button"
aria-label="More info"
className={cn(
"flex items-center justify-center px-0 py-1",
@@ -66,11 +64,11 @@ export const HelpTooltipIconTrigger: React.FC<HelpTooltipIconTriggerProps> = ({
>
{children}
</button>
</HelpTooltipTrigger>
</HelpPopoverTrigger>
);
};
export const HelpTooltipTitle: FC<HTMLAttributes<HTMLHeadingElement>> = ({
export const HelpPopoverTitle: FC<HTMLAttributes<HTMLHeadingElement>> = ({
children,
className,
...attrs
@@ -88,7 +86,7 @@ export const HelpTooltipTitle: FC<HTMLAttributes<HTMLHeadingElement>> = ({
);
};
export const HelpTooltipText: FC<HTMLAttributes<HTMLParagraphElement>> = ({
export const HelpPopoverText: FC<HTMLAttributes<HTMLParagraphElement>> = ({
children,
className,
...attrs
@@ -106,12 +104,12 @@ export const HelpTooltipText: FC<HTMLAttributes<HTMLParagraphElement>> = ({
);
};
interface HelpTooltipLink {
interface HelpPopoverLink {
children?: ReactNode;
href: string;
}
export const HelpTooltipLink: FC<HelpTooltipLink> = ({ children, href }) => {
export const HelpPopoverLink: FC<HelpPopoverLink> = ({ children, href }) => {
return (
<a
href={href}
@@ -125,14 +123,14 @@ export const HelpTooltipLink: FC<HelpTooltipLink> = ({ children, href }) => {
);
};
interface HelpTooltipActionProps {
interface HelpPopoverActionProps {
children?: ReactNode;
icon: Icon;
onClick: () => void;
ariaLabel?: string;
}
export const HelpTooltipAction: FC<HelpTooltipActionProps> = ({
export const HelpPopoverAction: FC<HelpPopoverActionProps> = ({
children,
icon: Icon,
onClick,
@@ -151,6 +149,6 @@ export const HelpTooltipAction: FC<HelpTooltipActionProps> = ({
);
};
export const HelpTooltipLinksGroup: FC<PropsWithChildren> = ({ children }) => {
export const HelpPopoverLinksGroup: FC<PropsWithChildren> = ({ children }) => {
return <div className="flex flex-col gap-2 mt-4">{children}</div>;
};
@@ -1,38 +0,0 @@
import type { Meta, StoryObj } from "@storybook/react-vite";
import {
HelpTooltip,
HelpTooltipLink,
HelpTooltipLinksGroup,
HelpTooltipText,
HelpTooltipTitle,
} from "./HelpTooltip";
const meta: Meta<typeof HelpTooltip> = {
title: "components/HelpTooltip",
component: HelpTooltip,
args: {
children: (
<>
<HelpTooltipTitle>What is a template?</HelpTooltipTitle>
<HelpTooltipText>
A template is a common configuration for your team&apos;s workspaces.
</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>
</>
),
},
};
export default meta;
type Story = StoryObj<typeof HelpTooltip>;
const Example: Story = {};
export { Example as HelpTooltip };
+16 -16
View File
@@ -1,12 +1,12 @@
import type { FC, ReactNode } from "react";
import {
HelpTooltip,
HelpTooltipContent,
HelpTooltipIcon,
HelpTooltipIconTrigger,
HelpTooltipText,
HelpTooltipTitle,
} from "#/components/HelpTooltip/HelpTooltip";
HelpPopover,
HelpPopoverContent,
HelpPopoverIcon,
HelpPopoverIconTrigger,
HelpPopoverText,
HelpPopoverTitle,
} from "#/components/HelpPopover/HelpPopover";
import type { ThemeRole } from "#/theme/roles";
import { cn } from "#/utils/cn";
@@ -34,14 +34,14 @@ export const InfoTooltip: FC<InfoTooltipProps> = ({
type = "info",
}) => {
return (
<HelpTooltip>
<HelpTooltipIconTrigger size="small" hoverEffect={false}>
<HelpTooltipIcon className={cn(tooltipColorClasses[type])} />
</HelpTooltipIconTrigger>
<HelpTooltipContent>
{title && <HelpTooltipTitle>{title}</HelpTooltipTitle>}
<HelpTooltipText>{message}</HelpTooltipText>
</HelpTooltipContent>
</HelpTooltip>
<HelpPopover>
<HelpPopoverIconTrigger size="small" hoverEffect={false}>
<HelpPopoverIcon className={cn(tooltipColorClasses[type])} />
</HelpPopoverIconTrigger>
<HelpPopoverContent>
{title && <HelpPopoverTitle>{title}</HelpPopoverTitle>}
<HelpPopoverText>{message}</HelpPopoverText>
</HelpPopoverContent>
</HelpPopover>
);
};
+36 -24
View File
@@ -5,7 +5,11 @@
import * as PopoverPrimitive from "@radix-ui/react-popover";
import { cn } from "#/utils/cn";
export type PopoverContentProps = PopoverPrimitive.PopoverContentProps;
export type PopoverContentProps = React.ComponentPropsWithRef<
typeof PopoverPrimitive.Content
> & {
disablePortal?: boolean;
};
export type PopoverTriggerProps = PopoverPrimitive.PopoverTriggerProps;
@@ -13,28 +17,36 @@ export const Popover = PopoverPrimitive.Root;
export const PopoverTrigger = PopoverPrimitive.Trigger;
export const PopoverContent: React.FC<
React.ComponentPropsWithRef<typeof PopoverPrimitive.Content>
> = ({ className, align = "center", sideOffset = 4, ...props }) => {
return (
<PopoverPrimitive.Portal>
<PopoverPrimitive.Content
align={align}
sideOffset={sideOffset}
collisionPadding={16}
className={cn(
`z-50 w-72 rounded-md border border-solid bg-surface-primary
text-content-primary shadow-md outline-none
max-h-[var(--radix-popper-available-height)] overflow-y-auto
data-[state=open]:animate-in data-[state=closed]:animate-out
data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0
data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95
data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2
data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2`,
className,
)}
{...props}
/>
</PopoverPrimitive.Portal>
export const PopoverContent: React.FC<PopoverContentProps> = ({
className,
align = "center",
sideOffset = 4,
disablePortal,
...props
}) => {
const content = (
<PopoverPrimitive.Content
align={align}
sideOffset={sideOffset}
collisionPadding={16}
className={cn(
`z-50 w-72 rounded-md border border-solid bg-surface-primary
text-content-primary shadow-md outline-none
max-h-[var(--radix-popper-available-height)] overflow-y-auto
data-[state=open]:animate-in data-[state=closed]:animate-out
data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0
data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95
data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2
data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2`,
className,
)}
{...props}
/>
);
return disablePortal ? (
content
) : (
<PopoverPrimitive.Portal>{content}</PopoverPrimitive.Portal>
);
};
+1 -3
View File
@@ -7,15 +7,13 @@ import { cn } from "#/utils/cn";
export const TooltipProvider = TooltipPrimitive.Provider;
export type TooltipProps = TooltipPrimitive.TooltipProps;
export const Tooltip = TooltipPrimitive.Root;
export const TooltipTrigger = TooltipPrimitive.Trigger;
export const TooltipArrow = TooltipPrimitive.Arrow;
export type TooltipContentProps = React.ComponentPropsWithRef<
type TooltipContentProps = React.ComponentPropsWithRef<
typeof TooltipPrimitive.Content
> & {
disablePortal?: boolean;
@@ -24,7 +24,7 @@ import type {
WorkspaceStatus,
} from "#/api/typesGenerated";
import { Button } from "#/components/Button/Button";
import { HelpTooltipTitle } from "#/components/HelpTooltip/HelpTooltip";
import { HelpPopoverTitle } from "#/components/HelpPopover/HelpPopover";
import { JetBrainsIcon } from "#/components/Icons/JetBrainsIcon";
import { RocketIcon } from "#/components/Icons/RocketIcon";
import { TerminalIcon } from "#/components/Icons/TerminalIcon";
@@ -137,9 +137,9 @@ export const DeploymentBannerView: FC<DeploymentBannerViewProps> = ({
>
{healthErrors.length > 0 ? (
<>
<HelpTooltipTitle>
<HelpPopoverTitle>
We have detected problems with your Coder deployment.
</HelpTooltipTitle>
</HelpPopoverTitle>
<div className="flex flex-col gap-1">
{healthErrors.map((error) => (
<HealthIssue key={error}>{error}</HealthIssue>
+15 -15
View File
@@ -1,12 +1,12 @@
import type { FC } from "react";
import type { DERPRegion, WorkspaceAgent } from "#/api/typesGenerated";
import {
HelpTooltip,
HelpTooltipContent,
HelpTooltipText,
HelpTooltipTitle,
HelpTooltipTrigger,
} from "#/components/HelpTooltip/HelpTooltip";
HelpPopover,
HelpPopoverContent,
HelpPopoverText,
HelpPopoverTitle,
HelpPopoverTrigger,
} from "#/components/HelpPopover/HelpPopover";
import { cn } from "#/utils/cn";
import { getLatencyColor } from "#/utils/latency";
@@ -41,8 +41,8 @@ export const AgentLatency: FC<AgentLatencyProps> = ({ agent }) => {
}
return (
<HelpTooltip>
<HelpTooltipTrigger asChild>
<HelpPopover>
<HelpPopoverTrigger asChild>
<span
role="presentation"
aria-label="latency"
@@ -50,13 +50,13 @@ export const AgentLatency: FC<AgentLatencyProps> = ({ agent }) => {
>
{Math.round(latency.latency_ms)}ms
</span>
</HelpTooltipTrigger>
<HelpTooltipContent>
<HelpTooltipTitle>Latency</HelpTooltipTitle>
<HelpTooltipText>
</HelpPopoverTrigger>
<HelpPopoverContent>
<HelpPopoverTitle>Latency</HelpPopoverTitle>
<HelpPopoverText>
This is the latency overhead on non peer to peer connections. The
first row is the preferred relay.
</HelpTooltipText>
</HelpPopoverText>
<div className="flex-col gap-1 mt-4">
{Object.entries(agent.latency)
.sort(([, a], [, b]) => a.latency_ms - b.latency_ms)
@@ -73,7 +73,7 @@ export const AgentLatency: FC<AgentLatencyProps> = ({ agent }) => {
</div>
))}
</div>
</HelpTooltipContent>
</HelpTooltip>
</HelpPopoverContent>
</HelpPopover>
);
};
@@ -2,14 +2,14 @@ import { RotateCcwIcon } from "lucide-react";
import { type FC, useState } from "react";
import type { WorkspaceAgent } from "#/api/typesGenerated";
import {
HelpTooltip,
HelpTooltipAction,
HelpTooltipContent,
HelpTooltipLinksGroup,
HelpTooltipText,
HelpTooltipTitle,
HelpTooltipTrigger,
} from "#/components/HelpTooltip/HelpTooltip";
HelpPopover,
HelpPopoverAction,
HelpPopoverContent,
HelpPopoverLinksGroup,
HelpPopoverText,
HelpPopoverTitle,
HelpPopoverTrigger,
} from "#/components/HelpPopover/HelpPopover";
import { Stack } from "#/components/Stack/Stack";
import { agentVersionStatus } from "../../utils/workspace";
@@ -39,17 +39,17 @@ export const AgentOutdatedTooltip: FC<AgentOutdatedTooltipProps> = ({
const text = `${opener} This can happen after you update Coder with running workspaces. To fix this, you can stop and start the workspace.`;
return (
<HelpTooltip open={isOpen} onOpenChange={setIsOpen}>
<HelpTooltipTrigger asChild>
<HelpPopover open={isOpen} onOpenChange={setIsOpen}>
<HelpPopoverTrigger asChild>
<span role="status" className="cursor-pointer">
{status === agentVersionStatus.Outdated ? "Outdated" : "Deprecated"}
</span>
</HelpTooltipTrigger>
<HelpTooltipContent>
</HelpPopoverTrigger>
<HelpPopoverContent>
<Stack spacing={1}>
<div>
<HelpTooltipTitle>{title}</HelpTooltipTitle>
<HelpTooltipText>{text}</HelpTooltipText>
<HelpPopoverTitle>{title}</HelpPopoverTitle>
<HelpPopoverText>{text}</HelpPopoverText>
</div>
<Stack spacing={0.5}>
@@ -66,8 +66,8 @@ export const AgentOutdatedTooltip: FC<AgentOutdatedTooltipProps> = ({
<span>{serverVersion}</span>
</Stack>
<HelpTooltipLinksGroup>
<HelpTooltipAction
<HelpPopoverLinksGroup>
<HelpPopoverAction
icon={RotateCcwIcon}
onClick={() => {
onUpdate();
@@ -76,10 +76,10 @@ export const AgentOutdatedTooltip: FC<AgentOutdatedTooltipProps> = ({
ariaLabel="Update workspace"
>
Update workspace
</HelpTooltipAction>
</HelpTooltipLinksGroup>
</HelpPopoverAction>
</HelpPopoverLinksGroup>
</Stack>
</HelpTooltipContent>
</HelpTooltip>
</HelpPopoverContent>
</HelpPopover>
);
};
+15 -15
View File
@@ -8,12 +8,12 @@ import type {
} from "#/api/typesGenerated";
import { ChooseOne, Cond } from "#/components/Conditionals/ChooseOne";
import {
HelpTooltip,
HelpTooltipContent,
HelpTooltipText,
HelpTooltipTitle,
HelpTooltipTrigger,
} from "#/components/HelpTooltip/HelpTooltip";
HelpPopover,
HelpPopoverContent,
HelpPopoverText,
HelpPopoverTitle,
HelpPopoverTrigger,
} from "#/components/HelpPopover/HelpPopover";
import {
Tooltip,
TooltipContent,
@@ -52,8 +52,8 @@ const AgentWarningTooltip: FC<AgentWarningTooltipProps> = ({
variant = "warning",
}) => {
return (
<HelpTooltip>
<HelpTooltipTrigger asChild role="status" aria-label={ariaLabel}>
<HelpPopover>
<HelpPopoverTrigger asChild role="status" aria-label={ariaLabel}>
<TriangleAlertIcon
className={cn(
"relative size-3.5",
@@ -62,10 +62,10 @@ const AgentWarningTooltip: FC<AgentWarningTooltipProps> = ({
: "text-content-destructive",
)}
/>
</HelpTooltipTrigger>
<HelpTooltipContent>
<HelpTooltipTitle>{title}</HelpTooltipTitle>
<HelpTooltipText>
</HelpPopoverTrigger>
<HelpPopoverContent>
<HelpPopoverTitle>{title}</HelpPopoverTitle>
<HelpPopoverText>
{detail}
{troubleshootingURL && (
<>
@@ -75,9 +75,9 @@ const AgentWarningTooltip: FC<AgentWarningTooltipProps> = ({
</Link>
</>
)}
</HelpTooltipText>
</HelpTooltipContent>
</HelpTooltip>
</HelpPopoverText>
</HelpPopoverContent>
</HelpPopover>
);
};
@@ -37,10 +37,10 @@ import {
import { ChevronDownIcon } from "#/components/AnimatedIcons/ChevronDown";
import { Button } from "#/components/Button/Button";
import {
HelpTooltipLink,
HelpTooltipText,
HelpTooltipTitle,
} from "#/components/HelpTooltip/HelpTooltip";
HelpPopoverLink,
HelpPopoverText,
HelpPopoverTitle,
} from "#/components/HelpPopover/HelpPopover";
import {
Popover,
PopoverContent,
@@ -258,18 +258,18 @@ export const PortForwardPopoverView: FC<PortForwardPopoverViewProps> = ({
justifyContent="space-between"
alignItems="start"
>
<HelpTooltipTitle>Listening Ports</HelpTooltipTitle>
<HelpTooltipLink
<HelpPopoverTitle>Listening Ports</HelpPopoverTitle>
<HelpPopoverLink
href={docs("/admin/networking/port-forwarding#dashboard")}
>
Learn more
</HelpTooltipLink>
</HelpPopoverLink>
</Stack>
<Stack direction="column" gap={1}>
<HelpTooltipText css={{ color: theme.palette.text.secondary }}>
<HelpPopoverText css={{ color: theme.palette.text.secondary }}>
The listening ports are exclusively accessible to you. Selecting
HTTP/S will change the protocol for all listening ports.
</HelpTooltipText>
</HelpPopoverText>
<Stack direction="row" gap={2} className="pb-2">
<FormControl size="small" css={styles.protocolFormControl}>
<Select
@@ -330,9 +330,9 @@ export const PortForwardPopoverView: FC<PortForwardPopoverViewProps> = ({
</Stack>
</Stack>
{filteredListeningPorts.length === 0 && (
<HelpTooltipText css={styles.noPortText}>
<HelpPopoverText css={styles.noPortText}>
No open ports were detected.
</HelpTooltipText>
</HelpPopoverText>
)}
{filteredListeningPorts.map((port) => {
const url = portForwardURL(
@@ -415,12 +415,12 @@ export const PortForwardPopoverView: FC<PortForwardPopoverViewProps> = ({
borderTop: `1px solid ${theme.palette.divider}`,
}}
>
<HelpTooltipTitle>Shared Ports</HelpTooltipTitle>
<HelpTooltipText css={{ color: theme.palette.text.secondary }}>
<HelpPopoverTitle>Shared Ports</HelpPopoverTitle>
<HelpPopoverText css={{ color: theme.palette.text.secondary }}>
{canSharePorts
? "Ports can be shared with organization members, other Coder users, or with the public."
: "This workspace template does not allow sharing ports. Contact a template administrator to enable port sharing."}
</HelpTooltipText>
</HelpPopoverText>
{canSharePorts && (
<div>
{filteredSharedPorts?.map((share) => {
@@ -5,10 +5,10 @@ import { ChevronDownIcon } from "#/components/AnimatedIcons/ChevronDown";
import { Button } from "#/components/Button/Button";
import { CodeExample } from "#/components/CodeExample/CodeExample";
import {
HelpTooltipLink,
HelpTooltipLinksGroup,
HelpTooltipText,
} from "#/components/HelpTooltip/HelpTooltip";
HelpPopoverLink,
HelpPopoverLinksGroup,
HelpPopoverText,
} from "#/components/HelpPopover/HelpPopover";
import {
Popover,
PopoverContent,
@@ -44,9 +44,9 @@ export const AgentSSHButton: FC<AgentSSHButtonProps> = ({
align="end"
className="py-4 px-6 w-80 text-content-secondary mt-[2px] bg-surface-secondary"
>
<HelpTooltipText>
<HelpPopoverText>
Run the following commands to connect with SSH:
</HelpTooltipText>
</HelpPopoverText>
<ol style={{ margin: 0, padding: 0 }}>
<Stack spacing={0.5} className="mt-3">
@@ -61,25 +61,25 @@ export const AgentSSHButton: FC<AgentSSHButtonProps> = ({
</Stack>
</ol>
<HelpTooltipLinksGroup>
<HelpTooltipLink href={docs("/install")}>
<HelpPopoverLinksGroup>
<HelpPopoverLink href={docs("/install")}>
Install Coder CLI
</HelpTooltipLink>
<HelpTooltipLink href={docs("/user-guides/workspace-access/vscode")}>
</HelpPopoverLink>
<HelpPopoverLink href={docs("/user-guides/workspace-access/vscode")}>
Connect via VS Code Remote SSH
</HelpTooltipLink>
<HelpTooltipLink
</HelpPopoverLink>
<HelpPopoverLink
href={docs("/user-guides/workspace-access/jetbrains")}
>
Connect via JetBrains IDEs
</HelpTooltipLink>
<HelpTooltipLink href={docs("/user-guides/desktop")}>
</HelpPopoverLink>
<HelpPopoverLink href={docs("/user-guides/desktop")}>
Connect via Coder Desktop
</HelpTooltipLink>
<HelpTooltipLink href={docs("/user-guides/workspace-access#ssh")}>
</HelpPopoverLink>
<HelpPopoverLink href={docs("/user-guides/workspace-access#ssh")}>
SSH configuration
</HelpTooltipLink>
</HelpTooltipLinksGroup>
</HelpPopoverLink>
</HelpPopoverLinksGroup>
</PopoverContent>
</Popover>
);
@@ -92,9 +92,9 @@ interface SSHStepProps {
const SSHStep: FC<SSHStepProps> = ({ helpText, codeExample }) => (
<li style={{ listStylePosition: "inside" }}>
<HelpTooltipText style={{ display: "inline" }}>
<HelpPopoverText style={{ display: "inline" }}>
<strong className="text-xs">{helpText}</strong>
</HelpTooltipText>
</HelpPopoverText>
<CodeExample secret={false} code={codeExample} />
</li>
);
@@ -5,13 +5,13 @@ import type {
WorkspaceAgentDevcontainer,
} from "#/api/typesGenerated";
import {
HelpTooltip,
HelpTooltipAction,
HelpTooltipContent,
HelpTooltipLinksGroup,
HelpTooltipText,
HelpTooltipTitle,
} from "#/components/HelpTooltip/HelpTooltip";
HelpPopover,
HelpPopoverAction,
HelpPopoverContent,
HelpPopoverLinksGroup,
HelpPopoverText,
HelpPopoverTitle,
} from "#/components/HelpPopover/HelpPopover";
import { TooltipTrigger } from "#/components/Tooltip/Tooltip";
type SubAgentOutdatedTooltipProps = {
@@ -33,34 +33,34 @@ export const SubAgentOutdatedTooltip: FC<SubAgentOutdatedTooltipProps> = ({
}
return (
<HelpTooltip>
<HelpPopover>
<TooltipTrigger className="px-0 py-1 bg-transparent text-inherit border-none opacity-50 hover:opacity-100">
<span role="status" className="cursor-pointer">
Outdated
</span>
</TooltipTrigger>
<HelpTooltipContent>
<HelpPopoverContent>
<div className="flex flex-col gap-2">
<div>
<HelpTooltipTitle>Dev Container Outdated</HelpTooltipTitle>
<HelpTooltipText>
<HelpPopoverTitle>Dev Container Outdated</HelpPopoverTitle>
<HelpPopoverText>
This Dev Container is outdated. This can happen if you modify your
devcontainer.json file after the Dev Container has been created.
To fix this, you can rebuild the Dev Container.
</HelpTooltipText>
</HelpPopoverText>
</div>
<HelpTooltipLinksGroup>
<HelpTooltipAction
<HelpPopoverLinksGroup>
<HelpPopoverAction
icon={RotateCcwIcon}
onClick={onUpdate}
ariaLabel="Rebuild Dev Container"
>
Rebuild Dev Container
</HelpTooltipAction>
</HelpTooltipLinksGroup>
</HelpPopoverAction>
</HelpPopoverLinksGroup>
</div>
</HelpTooltipContent>
</HelpTooltip>
</HelpPopoverContent>
</HelpPopover>
);
};
@@ -9,15 +9,15 @@ import { getErrorDetail, getErrorMessage } from "#/api/errors";
import { templateVersion } from "#/api/queries/templates";
import type { Workspace } from "#/api/typesGenerated";
import {
HelpTooltip,
HelpTooltipAction,
HelpTooltipContent,
HelpTooltipIconTrigger,
HelpTooltipLinksGroup,
HelpTooltipText,
HelpTooltipTitle,
HelpTooltipTrigger,
} from "#/components/HelpTooltip/HelpTooltip";
HelpPopover,
HelpPopoverAction,
HelpPopoverContent,
HelpPopoverIconTrigger,
HelpPopoverLinksGroup,
HelpPopoverText,
HelpPopoverTitle,
HelpPopoverTrigger,
} from "#/components/HelpPopover/HelpPopover";
import { linkToTemplate, useLinks } from "#/modules/navigation";
import {
useWorkspaceUpdate,
@@ -36,22 +36,22 @@ export const WorkspaceOutdatedTooltip: FC<WorkspaceOutdatedTooltipProps> = ({
const [isOpen, setIsOpen] = useState(false);
return (
<HelpTooltip open={isOpen} onOpenChange={setIsOpen}>
<HelpPopover open={isOpen} onOpenChange={setIsOpen}>
{children ? (
<HelpTooltipTrigger asChild>
<HelpPopoverTrigger asChild>
<span className="flex items-center gap-1.5 cursor-help">
<InfoIcon css={styles.icon} size={14} />
<span>{children}</span>
</span>
</HelpTooltipTrigger>
</HelpPopoverTrigger>
) : (
<HelpTooltipIconTrigger size="small" hoverEffect={false}>
<HelpPopoverIconTrigger size="small" hoverEffect={false}>
<InfoIcon css={styles.icon} />
<span className="sr-only">Outdated info</span>
</HelpTooltipIconTrigger>
</HelpPopoverIconTrigger>
)}
<WorkspaceOutdatedTooltipContent isOpen={isOpen} workspace={workspace} />
</HelpTooltip>
</HelpPopover>
);
};
@@ -86,11 +86,11 @@ const WorkspaceOutdatedTooltipContent: FC<TooltipContentProps> = ({
return (
<>
<HelpTooltipContent disablePortal={false}>
<HelpTooltipTitle>Outdated</HelpTooltipTitle>
<HelpTooltipText>
<HelpPopoverContent disablePortal={false}>
<HelpPopoverTitle>Outdated</HelpPopoverTitle>
<HelpPopoverText>
This workspace version is outdated and a newer version is available.
</HelpTooltipText>
</HelpPopoverText>
<div css={styles.container}>
<div className="leading-[1.6]">
@@ -122,15 +122,15 @@ const WorkspaceOutdatedTooltipContent: FC<TooltipContentProps> = ({
</div>
</div>
<HelpTooltipLinksGroup>
<HelpTooltipAction
<HelpPopoverLinksGroup>
<HelpPopoverAction
icon={RotateCcwIcon}
onClick={updateWorkspace.update}
>
Update
</HelpTooltipAction>
</HelpTooltipLinksGroup>
</HelpTooltipContent>
</HelpPopoverAction>
</HelpPopoverLinksGroup>
</HelpPopoverContent>
<WorkspaceUpdateDialogs {...updateWorkspace.dialogs} />
</>
);
@@ -0,0 +1,32 @@
import type { FC } from "react";
import {
HelpPopover,
HelpPopoverContent,
HelpPopoverIconTrigger,
HelpPopoverLink,
HelpPopoverLinksGroup,
HelpPopoverText,
HelpPopoverTitle,
} from "#/components/HelpPopover/HelpPopover";
import { docs } from "#/utils/docs";
export const AIBridgeHelpPopover: FC = () => {
return (
<HelpPopover>
<HelpPopoverIconTrigger />
<HelpPopoverContent>
<HelpPopoverTitle>What is AI Bridge?</HelpPopoverTitle>
<HelpPopoverText>
AI Bridge is a smart gateway for AI that provides centralized
management, auditing, and attribution for LLM usage.
</HelpPopoverText>
<HelpPopoverLinksGroup>
<HelpPopoverLink href={docs("/ai-coder/ai-bridge")}>
Read the docs
</HelpPopoverLink>
</HelpPopoverLinksGroup>
</HelpPopoverContent>
</HelpPopover>
);
};
@@ -1,32 +0,0 @@
import type { FC } from "react";
import {
HelpTooltip,
HelpTooltipContent,
HelpTooltipIconTrigger,
HelpTooltipLink,
HelpTooltipLinksGroup,
HelpTooltipText,
HelpTooltipTitle,
} from "#/components/HelpTooltip/HelpTooltip";
import { docs } from "#/utils/docs";
export const AIBridgeHelpTooltip: FC = () => {
return (
<HelpTooltip>
<HelpTooltipIconTrigger />
<HelpTooltipContent>
<HelpTooltipTitle>What is AI Bridge?</HelpTooltipTitle>
<HelpTooltipText>
AI Bridge is a smart gateway for AI that provides centralized
management, auditing, and attribution for LLM usage.
</HelpTooltipText>
<HelpTooltipLinksGroup>
<HelpTooltipLink href={docs("/ai-coder/ai-bridge")}>
Read the docs
</HelpTooltipLink>
</HelpTooltipLinksGroup>
</HelpTooltipContent>
</HelpTooltip>
);
};
@@ -6,7 +6,7 @@ import {
PageHeaderSubtitle,
PageHeaderTitle,
} from "#/components/PageHeader/PageHeader";
import { AIBridgeHelpTooltip } from "./AIBridgeHelpTooltip";
import { AIBridgeHelpPopover } from "./AIBridgeHelpPopover";
const AIBridgeLayout: FC<PropsWithChildren> = () => {
return (
@@ -15,7 +15,7 @@ const AIBridgeLayout: FC<PropsWithChildren> = () => {
<PageHeaderTitle>
<div className="flex items-center gap-2">
<span>AI Bridge Logs</span>
<AIBridgeHelpTooltip />
<AIBridgeHelpPopover />
</div>
</PageHeaderTitle>
<PageHeaderSubtitle>
@@ -0,0 +1,35 @@
import type { FC } from "react";
import {
HelpPopover,
HelpPopoverContent,
HelpPopoverIconTrigger,
HelpPopoverLink,
HelpPopoverLinksGroup,
HelpPopoverText,
HelpPopoverTitle,
} from "#/components/HelpPopover/HelpPopover";
import { docs } from "#/utils/docs";
const Language = {
title: "What is an audit log?",
body: "An audit log is a record of events and changes made throughout a system.",
docs: "Events we track",
};
export const AuditHelpPopover: FC = () => {
return (
<HelpPopover>
<HelpPopoverIconTrigger />
<HelpPopoverContent>
<HelpPopoverTitle>{Language.title}</HelpPopoverTitle>
<HelpPopoverText>{Language.body}</HelpPopoverText>
<HelpPopoverLinksGroup>
<HelpPopoverLink href={docs("/admin/security/audit-logs")}>
{Language.docs}
</HelpPopoverLink>
</HelpPopoverLinksGroup>
</HelpPopoverContent>
</HelpPopover>
);
};
@@ -1,35 +0,0 @@
import type { FC } from "react";
import {
HelpTooltip,
HelpTooltipContent,
HelpTooltipIconTrigger,
HelpTooltipLink,
HelpTooltipLinksGroup,
HelpTooltipText,
HelpTooltipTitle,
} from "#/components/HelpTooltip/HelpTooltip";
import { docs } from "#/utils/docs";
const Language = {
title: "What is an audit log?",
body: "An audit log is a record of events and changes made throughout a system.",
docs: "Events we track",
};
export const AuditHelpTooltip: FC = () => {
return (
<HelpTooltip>
<HelpTooltipIconTrigger />
<HelpTooltipContent>
<HelpTooltipTitle>{Language.title}</HelpTooltipTitle>
<HelpTooltipText>{Language.body}</HelpTooltipText>
<HelpTooltipLinksGroup>
<HelpTooltipLink href={docs("/admin/security/audit-logs")}>
{Language.docs}
</HelpTooltipLink>
</HelpTooltipLinksGroup>
</HelpTooltipContent>
</HelpTooltip>
);
};
+2 -2
View File
@@ -24,7 +24,7 @@ import { TableLoader } from "#/components/TableLoader/TableLoader";
import { Timeline } from "#/components/Timeline/Timeline";
import { docs } from "#/utils/docs";
import { AuditFilter } from "./AuditFilter";
import { AuditHelpTooltip } from "./AuditHelpTooltip";
import { AuditHelpPopover } from "./AuditHelpPopover";
import { AuditLogRow } from "./AuditLogRow/AuditLogRow";
const Language = {
@@ -63,7 +63,7 @@ export const AuditPageView: FC<AuditPageViewProps> = ({
<PageHeaderTitle>
<Stack direction="row" spacing={1} alignItems="center">
<span>{Language.title}</span>
<AuditHelpTooltip />
<AuditHelpPopover />
</Stack>
</PageHeaderTitle>
<PageHeaderSubtitle>{Language.subtitle}</PageHeaderSubtitle>
@@ -0,0 +1,35 @@
import type { FC } from "react";
import {
HelpPopover,
HelpPopoverContent,
HelpPopoverIconTrigger,
HelpPopoverLink,
HelpPopoverLinksGroup,
HelpPopoverText,
HelpPopoverTitle,
} from "#/components/HelpPopover/HelpPopover";
import { docs } from "#/utils/docs";
const Language = {
title: "Why are some events missing?",
body: "The connection log is a best-effort log of workspace access. Some events are reported by workspace agents, and receipt of these events by the server is not guaranteed.",
docs: "Connection log documentation",
};
export const ConnectionLogHelpPopover: FC = () => {
return (
<HelpPopover>
<HelpPopoverIconTrigger />
<HelpPopoverContent>
<HelpPopoverTitle>{Language.title}</HelpPopoverTitle>
<HelpPopoverText>{Language.body}</HelpPopoverText>
<HelpPopoverLinksGroup>
<HelpPopoverLink href={docs("/admin/monitoring/connection-logs")}>
{Language.docs}
</HelpPopoverLink>
</HelpPopoverLinksGroup>
</HelpPopoverContent>
</HelpPopover>
);
};
@@ -1,35 +0,0 @@
import type { FC } from "react";
import {
HelpTooltip,
HelpTooltipContent,
HelpTooltipIconTrigger,
HelpTooltipLink,
HelpTooltipLinksGroup,
HelpTooltipText,
HelpTooltipTitle,
} from "#/components/HelpTooltip/HelpTooltip";
import { docs } from "#/utils/docs";
const Language = {
title: "Why are some events missing?",
body: "The connection log is a best-effort log of workspace access. Some events are reported by workspace agents, and receipt of these events by the server is not guaranteed.",
docs: "Connection log documentation",
};
export const ConnectionLogHelpTooltip: FC = () => {
return (
<HelpTooltip>
<HelpTooltipIconTrigger />
<HelpTooltipContent>
<HelpTooltipTitle>{Language.title}</HelpTooltipTitle>
<HelpTooltipText>{Language.body}</HelpTooltipText>
<HelpTooltipLinksGroup>
<HelpTooltipLink href={docs("/admin/monitoring/connection-logs")}>
{Language.docs}
</HelpTooltipLink>
</HelpTooltipLinksGroup>
</HelpTooltipContent>
</HelpTooltip>
);
};
@@ -24,7 +24,7 @@ import { TableLoader } from "#/components/TableLoader/TableLoader";
import { Timeline } from "#/components/Timeline/Timeline";
import { docs } from "#/utils/docs";
import { ConnectionLogFilter } from "./ConnectionLogFilter";
import { ConnectionLogHelpTooltip } from "./ConnectionLogHelpTooltip";
import { ConnectionLogHelpPopover } from "./ConnectionLogHelpPopover";
import { ConnectionLogRow } from "./ConnectionLogRow/ConnectionLogRow";
const Language = {
@@ -62,7 +62,7 @@ export const ConnectionLogPageView: FC<ConnectionLogPageViewProps> = ({
<PageHeaderTitle>
<Stack direction="row" spacing={1} alignItems="center">
<span>{Language.title}</span>
<ConnectionLogHelpTooltip />
<ConnectionLogHelpPopover />
</Stack>
</PageHeaderTitle>
<PageHeaderSubtitle>{Language.subtitle}</PageHeaderSubtitle>
@@ -28,11 +28,11 @@ import {
} from "#/components/Dialog/Dialog";
import { EmptyState } from "#/components/EmptyState/EmptyState";
import {
HelpTooltip,
HelpTooltipContent,
HelpTooltipIconTrigger,
HelpTooltipText,
} from "#/components/HelpTooltip/HelpTooltip";
HelpPopover,
HelpPopoverContent,
HelpPopoverIconTrigger,
HelpPopoverText,
} from "#/components/HelpPopover/HelpPopover";
import { Input } from "#/components/Input/Input";
import { Label } from "#/components/Label/Label";
import { Link } from "#/components/Link/Link";
@@ -206,7 +206,7 @@ export const IdpOrgSyncPageView: FC<IdpSyncPageViewProps> = ({
<Label htmlFor={`${id}-assign-default-org`}>
Assign Default Organization
</Label>
<AssignDefaultOrgHelpTooltip />
<AssignDefaultOrgHelpPopover />
</span>
</div>
</div>
@@ -490,16 +490,16 @@ const OrganizationRow: FC<OrganizationRowProps> = ({
);
};
const AssignDefaultOrgHelpTooltip: FC = () => {
const AssignDefaultOrgHelpPopover: FC = () => {
return (
<HelpTooltip>
<HelpTooltipIconTrigger />
<HelpTooltipContent>
<HelpTooltipText>
<HelpPopover>
<HelpPopoverIconTrigger />
<HelpPopoverContent>
<HelpPopoverText>
Disabling will remove all users from the default organization if a
mapping for the default organization is not defined.
</HelpTooltipText>
</HelpTooltipContent>
</HelpTooltip>
</HelpPopoverText>
</HelpPopoverContent>
</HelpPopover>
);
};
@@ -18,12 +18,12 @@ import {
ComboboxTrigger,
} from "#/components/Combobox/Combobox";
import {
HelpTooltip,
HelpTooltipContent,
HelpTooltipIconTrigger,
HelpTooltipText,
HelpTooltipTitle,
} from "#/components/HelpTooltip/HelpTooltip";
HelpPopover,
HelpPopoverContent,
HelpPopoverIconTrigger,
HelpPopoverText,
HelpPopoverTitle,
} from "#/components/HelpPopover/HelpPopover";
import { Input } from "#/components/Input/Input";
import { Label } from "#/components/Label/Label";
import { Link } from "#/components/Link/Link";
@@ -222,7 +222,7 @@ export const IdpGroupSyncForm: FC<IdpGroupSyncFormProps> = ({
<Label htmlFor={`${id}-auto-create-missing-groups`}>
Auto create missing groups
</Label>
<AutoCreateMissingGroupsHelpTooltip />
<AutoCreateMissingGroupsHelpPopover />
</span>
</div>
<div className="flex flex-row gap-2 justify-between items-start">
@@ -445,17 +445,17 @@ const GroupRow: FC<GroupRowProps> = ({
);
};
const AutoCreateMissingGroupsHelpTooltip: FC = () => {
const AutoCreateMissingGroupsHelpPopover: FC = () => {
return (
<HelpTooltip>
<HelpTooltipIconTrigger />
<HelpTooltipContent>
<HelpTooltipText>
<HelpPopover>
<HelpPopoverIconTrigger />
<HelpPopoverContent>
<HelpPopoverText>
Enabling auto create missing groups will automatically create groups
returned by the OIDC provider if they do not exist in Coder.
</HelpTooltipText>
</HelpTooltipContent>
</HelpTooltip>
</HelpPopoverText>
</HelpPopoverContent>
</HelpPopover>
);
};
@@ -464,11 +464,11 @@ const LegacyGroupSyncHeader: FC = () => {
<h4 className="text-xl font-medium">
<div className="flex items-end gap-2">
<span>Legacy group sync settings</span>
<HelpTooltip>
<HelpTooltipIconTrigger />
<HelpTooltipContent>
<HelpTooltipTitle>Legacy group sync settings</HelpTooltipTitle>
<HelpTooltipText>
<HelpPopover>
<HelpPopoverIconTrigger />
<HelpPopoverContent>
<HelpPopoverTitle>Legacy group sync settings</HelpPopoverTitle>
<HelpPopoverText>
These settings were configured using environment variables, and
only apply to the default organization. It is now recommended to
configure IdP sync via the CLI or the UI, which enables sync to be
@@ -477,9 +477,9 @@ const LegacyGroupSyncHeader: FC = () => {
<Link href={docs("/admin/users/idp-sync")}>
Learn more&hellip;
</Link>
</HelpTooltipText>
</HelpTooltipContent>
</HelpTooltip>
</HelpPopoverText>
</HelpPopoverContent>
</HelpPopover>
</div>
</h4>
);
@@ -38,7 +38,7 @@ import { UserAutocomplete } from "#/components/UserAutocomplete/UserAutocomplete
import type { PaginationResultInfo } from "#/hooks/usePaginatedQuery";
import { AISeatCell } from "#/modules/users/AISeatCell";
import { UserGroupsCell } from "#/pages/UsersPage/UsersTable/UserGroupsCell";
import { TableColumnHelpTooltip } from "./UserTable/TableColumnHelpTooltip";
import { TableColumnHelpPopover } from "./UserTable/TableColumnHelpPopover";
import { UserRoleCell } from "./UserTable/UserRoleCell";
interface OrganizationMembersPageViewProps {
@@ -115,20 +115,20 @@ export const OrganizationMembersPageView: FC<
<TableHead className="w-2/6">
<Stack direction="row" spacing={1} alignItems="center">
<span>Roles</span>
<TableColumnHelpTooltip variant="roles" />
<TableColumnHelpPopover variant="roles" />
</Stack>
</TableHead>
<TableHead className={showAISeatColumn ? "w-1/6" : "w-2/6"}>
<Stack direction="row" spacing={1} alignItems="center">
<span>Groups</span>
<TableColumnHelpTooltip variant="groups" />
<TableColumnHelpPopover variant="groups" />
</Stack>
</TableHead>
{showAISeatColumn && (
<TableHead className="w-1/6">
<Stack direction="row" spacing={1} alignItems="center">
<span>AI add-on</span>
<TableColumnHelpTooltip variant="ai_addon" />
<TableColumnHelpPopover variant="ai_addon" />
</Stack>
</TableHead>
)}
@@ -5,12 +5,12 @@ import type { SlimRole } from "#/api/typesGenerated";
import { Button } from "#/components/Button/Button";
import { CollapsibleSummary } from "#/components/CollapsibleSummary/CollapsibleSummary";
import {
HelpTooltip,
HelpTooltipContent,
HelpTooltipIconTrigger,
HelpTooltipText,
HelpTooltipTitle,
} from "#/components/HelpTooltip/HelpTooltip";
HelpPopover,
HelpPopoverContent,
HelpPopoverIconTrigger,
HelpPopoverText,
HelpPopoverTitle,
} from "#/components/HelpPopover/HelpPopover";
import { EditSquare } from "#/components/Icons/EditSquare";
import {
Popover,
@@ -87,15 +87,15 @@ export const EditRolesButton: FC<EditRolesButtonProps> = (props) => {
if (!canSetRoles) {
return (
<HelpTooltip>
<HelpTooltipIconTrigger size="small" />
<HelpTooltipContent>
<HelpTooltipTitle>Externally controlled</HelpTooltipTitle>
<HelpTooltipText>
<HelpPopover>
<HelpPopoverIconTrigger size="small" />
<HelpPopoverContent>
<HelpPopoverTitle>Externally controlled</HelpPopoverTitle>
<HelpPopoverText>
Roles for this user are controlled by the OIDC identity provider.
</HelpTooltipText>
</HelpTooltipContent>
</HelpTooltip>
</HelpPopoverText>
</HelpPopoverContent>
</HelpPopover>
);
}
@@ -1,13 +1,13 @@
import type { FC } from "react";
import {
HelpTooltip,
HelpTooltipContent,
HelpTooltipIconTrigger,
HelpTooltipLink,
HelpTooltipLinksGroup,
HelpTooltipText,
HelpTooltipTitle,
} from "#/components/HelpTooltip/HelpTooltip";
HelpPopover,
HelpPopoverContent,
HelpPopoverIconTrigger,
HelpPopoverLink,
HelpPopoverLinksGroup,
HelpPopoverText,
HelpPopoverTitle,
} from "#/components/HelpPopover/HelpPopover";
import { docs } from "#/utils/docs";
type ColumnHeader = "roles" | "groups" | "ai_addon";
@@ -48,25 +48,25 @@ type Props = {
variant: ColumnHeader;
};
export const TableColumnHelpTooltip: FC<Props> = ({ variant }) => {
export const TableColumnHelpPopover: FC<Props> = ({ variant }) => {
const variantLang = Language[variant];
return (
<HelpTooltip>
<HelpTooltipIconTrigger size="small" />
<HelpTooltipContent>
<HelpTooltipTitle>{variantLang.title}</HelpTooltipTitle>
<HelpTooltipText>{variantLang.text}</HelpTooltipText>
<HelpPopover>
<HelpPopoverIconTrigger size="small" />
<HelpPopoverContent>
<HelpPopoverTitle>{variantLang.title}</HelpPopoverTitle>
<HelpPopoverText>{variantLang.text}</HelpPopoverText>
{variantLang.links.length > 0 && (
<HelpTooltipLinksGroup>
<HelpPopoverLinksGroup>
{variantLang.links.map((link) => (
<HelpTooltipLink key={link.text} href={link.href}>
<HelpPopoverLink key={link.text} href={link.href}>
{link.text}
</HelpTooltipLink>
</HelpPopoverLink>
))}
</HelpTooltipLinksGroup>
</HelpPopoverLinksGroup>
)}
</HelpTooltipContent>
</HelpTooltip>
</HelpPopoverContent>
</HelpPopover>
);
};
@@ -39,12 +39,12 @@ import {
type DateRangeValue,
} from "#/components/DateRangePicker/DateRangePicker";
import {
HelpTooltip,
HelpTooltipContent,
HelpTooltipIconTrigger,
HelpTooltipText,
HelpTooltipTitle,
} from "#/components/HelpTooltip/HelpTooltip";
HelpPopover,
HelpPopoverContent,
HelpPopoverIconTrigger,
HelpPopoverText,
HelpPopoverTitle,
} from "#/components/HelpPopover/HelpPopover";
import { Link } from "#/components/Link/Link";
import { Loader } from "#/components/Loader/Loader";
import { Stack } from "#/components/Stack/Stack";
@@ -318,15 +318,15 @@ const UsersLatencyPanel: FC<UsersLatencyPanelProps> = ({
<PanelHeader>
<PanelTitle className="flex items-center gap-2">
Latency by user
<HelpTooltip>
<HelpTooltipIconTrigger size="small" />
<HelpTooltipContent>
<HelpTooltipTitle>How is latency calculated?</HelpTooltipTitle>
<HelpTooltipText>
<HelpPopover>
<HelpPopoverIconTrigger size="small" />
<HelpPopoverContent>
<HelpPopoverTitle>How is latency calculated?</HelpPopoverTitle>
<HelpPopoverText>
The median round trip time of user connections to workspaces.
</HelpTooltipText>
</HelpTooltipContent>
</HelpTooltip>
</HelpPopoverText>
</HelpPopoverContent>
</HelpPopover>
</PanelTitle>
</PanelHeader>
<PanelContent error={error} data={data?.report.users}>
@@ -373,16 +373,16 @@ const UsersActivityPanel: FC<UsersActivityPanelProps> = ({
<PanelHeader>
<PanelTitle className="flex items-center gap-2">
Activity by user
<HelpTooltip>
<HelpTooltipIconTrigger size="small" />
<HelpTooltipContent>
<HelpTooltipTitle>How is activity calculated?</HelpTooltipTitle>
<HelpTooltipText>
<HelpPopover>
<HelpPopoverIconTrigger size="small" />
<HelpPopoverContent>
<HelpPopoverTitle>How is activity calculated?</HelpPopoverTitle>
<HelpPopoverText>
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>
</HelpPopoverText>
</HelpPopoverContent>
</HelpPopover>
</PanelTitle>
</PanelHeader>
<PanelContent error={error} data={data?.report.users}>
@@ -7,18 +7,18 @@ import * as Yup from "yup";
import { ConfirmDialog } from "#/components/Dialogs/ConfirmDialog/ConfirmDialog";
import type { DialogProps } from "#/components/Dialogs/Dialog";
import { FormFields } from "#/components/Form/Form";
import {
HelpPopover,
HelpPopoverContent,
HelpPopoverIconTrigger,
HelpPopoverLink,
HelpPopoverLinksGroup,
HelpPopoverText,
HelpPopoverTitle,
} from "#/components/HelpPopover/HelpPopover";
import { Stack } from "#/components/Stack/Stack";
import type { PublishVersionData } from "#/pages/TemplateVersionEditorPage/types";
import { getFormHelpers } from "#/utils/formUtils";
import {
HelpTooltip,
HelpTooltipContent,
HelpTooltipIconTrigger,
HelpTooltipLink,
HelpTooltipLinksGroup,
HelpTooltipText,
HelpTooltipTitle,
} from "../../components/HelpTooltip/HelpTooltip";
import { docs } from "../../utils/docs";
export const Language = {
@@ -120,31 +120,31 @@ export const PublishTemplateVersionDialog: FC<
}
/>
<HelpTooltip>
<HelpTooltipIconTrigger />
<HelpPopover>
<HelpPopoverIconTrigger />
{/**
* 2025-09-03 - Without disablePortal, the tooltip will render under the dialog;
* this prop may not need to be set when we switch away from MuiDialog
*/}
<HelpTooltipContent disablePortal>
<HelpTooltipTitle>
<HelpPopoverContent disablePortal>
<HelpPopoverTitle>
{Language.activeVersionHelpTitle}
</HelpTooltipTitle>
<HelpTooltipText>
</HelpPopoverTitle>
<HelpPopoverText>
{Language.activeVersionHelpText}
</HelpTooltipText>
<HelpTooltipLinksGroup>
<HelpTooltipLink
</HelpPopoverText>
<HelpPopoverLinksGroup>
<HelpPopoverLink
href={docs(
"/admin/templates/managing-templates#template-update-policies",
)}
>
{Language.activeVersionHelpBody}
</HelpTooltipLink>
</HelpTooltipLinksGroup>
</HelpTooltipContent>
</HelpTooltip>
</HelpPopoverLink>
</HelpPopoverLinksGroup>
</HelpPopoverContent>
</HelpPopover>
</Stack>
</FormFields>
</Stack>
@@ -12,14 +12,14 @@ import { AvatarDataSkeleton } from "#/components/Avatar/AvatarDataSkeleton";
import { DeprecatedBadge } from "#/components/Badges/Badges";
import { Button } from "#/components/Button/Button";
import {
HelpTooltip,
HelpTooltipContent,
HelpTooltipIconTrigger,
HelpTooltipLink,
HelpTooltipLinksGroup,
HelpTooltipText,
HelpTooltipTitle,
} from "#/components/HelpTooltip/HelpTooltip";
HelpPopover,
HelpPopoverContent,
HelpPopoverIconTrigger,
HelpPopoverLink,
HelpPopoverLinksGroup,
HelpPopoverText,
HelpPopoverTitle,
} from "#/components/HelpPopover/HelpPopover";
import { Margins } from "#/components/Margins/Margins";
import {
PageHeader,
@@ -68,20 +68,20 @@ const Language = {
templateTooltipLink: "Manage templates",
};
const TemplateHelpTooltip: FC = () => {
const TemplateHelpPopover: FC = () => {
return (
<HelpTooltip>
<HelpTooltipIconTrigger />
<HelpTooltipContent>
<HelpTooltipTitle>{Language.templateTooltipTitle}</HelpTooltipTitle>
<HelpTooltipText>{Language.templateTooltipText}</HelpTooltipText>
<HelpTooltipLinksGroup>
<HelpTooltipLink href={docs("/admin/templates")}>
<HelpPopover>
<HelpPopoverIconTrigger />
<HelpPopoverContent>
<HelpPopoverTitle>{Language.templateTooltipTitle}</HelpPopoverTitle>
<HelpPopoverText>{Language.templateTooltipText}</HelpPopoverText>
<HelpPopoverLinksGroup>
<HelpPopoverLink href={docs("/admin/templates")}>
{Language.templateTooltipLink}
</HelpTooltipLink>
</HelpTooltipLinksGroup>
</HelpTooltipContent>
</HelpTooltip>
</HelpPopoverLink>
</HelpPopoverLinksGroup>
</HelpPopoverContent>
</HelpPopover>
);
};
@@ -241,7 +241,7 @@ export const TemplatesPageView: FC<TemplatesPageViewProps> = ({
<PageHeaderTitle>
<Stack spacing={1} direction="row" alignItems="center">
Templates
<TemplateHelpTooltip />
<TemplateHelpPopover />
</Stack>
</PageHeaderTitle>
<PageHeaderSubtitle>
@@ -9,7 +9,7 @@ import {
TableHeader,
TableRow,
} from "#/components/Table/Table";
import { TableColumnHelpTooltip } from "../../OrganizationSettingsPage/UserTable/TableColumnHelpTooltip";
import { TableColumnHelpPopover } from "../../OrganizationSettingsPage/UserTable/TableColumnHelpPopover";
import { UsersTableBody } from "./UsersTableBody";
const Language = {
@@ -76,20 +76,20 @@ export const UsersTable: FC<UsersTableProps> = ({
<TableHead className="w-2/6">
<Stack direction="row" spacing={1} alignItems="center">
<span>{Language.rolesLabel}</span>
<TableColumnHelpTooltip variant="roles" />
<TableColumnHelpPopover variant="roles" />
</Stack>
</TableHead>
<TableHead className="w-1/6">
<Stack direction="row" spacing={1} alignItems="center">
<span>{Language.groupsLabel}</span>
<TableColumnHelpTooltip variant="groups" />
<TableColumnHelpPopover variant="groups" />
</Stack>
</TableHead>
{showAISeatColumn && (
<TableHead className="w-1/6">
<Stack direction="row" spacing={1} alignItems="center">
<span>{Language.aiAddonLabel}</span>
<TableColumnHelpTooltip variant="ai_addon" />
<TableColumnHelpPopover variant="ai_addon" />
</Stack>
</TableHead>
)}
@@ -13,11 +13,11 @@ import { Button } from "#/components/Button/Button";
import { FormFields } from "#/components/Form/Form";
import { TopbarButton } from "#/components/FullPageLayout/Topbar";
import {
HelpTooltipLink,
HelpTooltipLinksGroup,
HelpTooltipText,
HelpTooltipTitle,
} from "#/components/HelpTooltip/HelpTooltip";
HelpPopoverLink,
HelpPopoverLinksGroup,
HelpPopoverText,
HelpPopoverTitle,
} from "#/components/HelpPopover/HelpPopover";
import { Link } from "#/components/Link/Link";
import { Loader } from "#/components/Loader/Loader";
import {
@@ -147,10 +147,10 @@ const BuildParametersPopoverContent: FC<BuildParametersPopoverContentProps> = ({
ephemeralParameters.length > 0 ? (
<div className="divide-y">
<div className="p-5 text-content-secondary">
<HelpTooltipTitle>Build Options</HelpTooltipTitle>
<HelpTooltipText>
<HelpPopoverTitle>Build Options</HelpPopoverTitle>
<HelpPopoverText>
These parameters only apply for a single workspace start.
</HelpTooltipText>
</HelpPopoverText>
</div>
<div className="border-0 border-solid p-5">
<Form
@@ -170,19 +170,19 @@ const BuildParametersPopoverContent: FC<BuildParametersPopoverContentProps> = ({
</div>
) : (
<div className="p-5 text-content-secondary">
<HelpTooltipTitle>Build Options</HelpTooltipTitle>
<HelpTooltipText>
<HelpPopoverTitle>Build Options</HelpPopoverTitle>
<HelpPopoverText>
This template has no ephemeral build options.
</HelpTooltipText>
<HelpTooltipLinksGroup>
<HelpTooltipLink
</HelpPopoverText>
<HelpPopoverLinksGroup>
<HelpPopoverLink
href={docs(
"/admin/templates/extending-templates/parameters#ephemeral-parameters",
)}
>
Read the docs
</HelpTooltipLink>
</HelpTooltipLinksGroup>
</HelpPopoverLink>
</HelpPopoverLinksGroup>
</div>
)
) : (
@@ -18,10 +18,10 @@ import {
TopbarIconButton,
} from "#/components/FullPageLayout/Topbar";
import {
HelpTooltip,
HelpTooltipContent,
HelpTooltipTrigger,
} from "#/components/HelpTooltip/HelpTooltip";
HelpPopover,
HelpPopoverContent,
HelpPopoverTrigger,
} from "#/components/HelpPopover/HelpPopover";
import {
Tooltip,
TooltipContent,
@@ -259,18 +259,18 @@ const OwnerBreadcrumb: FC<OwnerBreadcrumbProps> = ({
ownerAvatarUrl,
}) => {
return (
<HelpTooltip>
<HelpTooltipTrigger asChild>
<HelpPopover>
<HelpPopoverTrigger asChild>
<span css={styles.breadcrumbSegment}>
<Avatar size="sm" fallback={ownerName} src={ownerAvatarUrl} />
<span css={styles.breadcrumbText}>{ownerName}</span>
</span>
</HelpTooltipTrigger>
</HelpPopoverTrigger>
<HelpTooltipContent align="center">
<HelpPopoverContent align="center">
<AvatarData title={ownerName} subtitle="Owner" src={ownerAvatarUrl} />
</HelpTooltipContent>
</HelpTooltip>
</HelpPopoverContent>
</HelpPopover>
);
};
@@ -286,8 +286,8 @@ const OrganizationBreadcrumb: FC<OrganizationBreadcrumbProps> = ({
orgIconUrl,
}) => {
return (
<HelpTooltip>
<HelpTooltipTrigger asChild>
<HelpPopover>
<HelpPopoverTrigger asChild>
<span css={styles.breadcrumbSegment}>
<Avatar
size="sm"
@@ -297,9 +297,9 @@ const OrganizationBreadcrumb: FC<OrganizationBreadcrumbProps> = ({
/>
<span css={styles.breadcrumbText}>{orgName}</span>
</span>
</HelpTooltipTrigger>
</HelpPopoverTrigger>
<HelpTooltipContent align="center">
<HelpPopoverContent align="center">
<AvatarData
title={
orgPageUrl ? (
@@ -327,8 +327,8 @@ const OrganizationBreadcrumb: FC<OrganizationBreadcrumbProps> = ({
}
imgFallbackText={orgName}
/>
</HelpTooltipContent>
</HelpTooltip>
</HelpPopoverContent>
</HelpPopover>
);
};
@@ -351,8 +351,8 @@ const WorkspaceBreadcrumb: FC<WorkspaceBreadcrumbProps> = ({
}) => {
return (
<div className="flex items-center">
<HelpTooltip>
<HelpTooltipTrigger asChild>
<HelpPopover>
<HelpPopoverTrigger asChild>
<span css={styles.breadcrumbSegment}>
<TopbarAvatar
src={templateIconUrl}
@@ -363,9 +363,9 @@ const WorkspaceBreadcrumb: FC<WorkspaceBreadcrumbProps> = ({
{workspaceName}
</span>
</span>
</HelpTooltipTrigger>
</HelpPopoverTrigger>
<HelpTooltipContent align="center">
<HelpPopoverContent align="center">
<AvatarData
title={
<Link
@@ -395,8 +395,8 @@ const WorkspaceBreadcrumb: FC<WorkspaceBreadcrumbProps> = ({
}
imgFallbackText={templateDisplayName}
/>
</HelpTooltipContent>
</HelpTooltip>
</HelpPopoverContent>
</HelpPopover>
<CopyButton text={workspaceName} label="Copy workspace name" />
</div>
);
@@ -0,0 +1,40 @@
import type { FC } from "react";
import {
HelpPopover,
HelpPopoverContent,
HelpPopoverIconTrigger,
HelpPopoverLink,
HelpPopoverLinksGroup,
HelpPopoverText,
HelpPopoverTitle,
} from "#/components/HelpPopover/HelpPopover";
import { docs } from "#/utils/docs";
const Language = {
workspaceTooltipTitle: "What is a workspace?",
workspaceTooltipText:
"A workspace is your development environment in the cloud. It includes the infrastructure and tools you need to work on your project.",
workspaceTooltipLink1: "Create Workspaces",
workspaceTooltipLink2: "Connect with SSH",
workspaceTooltipLink3: "Editors and IDEs",
};
export const WorkspaceHelpPopover: FC = () => {
return (
<HelpPopover>
<HelpPopoverIconTrigger />
<HelpPopoverContent>
<HelpPopoverTitle>{Language.workspaceTooltipTitle}</HelpPopoverTitle>
<HelpPopoverText>{Language.workspaceTooltipText}</HelpPopoverText>
<HelpPopoverLinksGroup>
<HelpPopoverLink href={docs("/user-guides")}>
{Language.workspaceTooltipLink1}
</HelpPopoverLink>
<HelpPopoverLink href={docs("/user-guides/workspace-access")}>
{Language.workspaceTooltipLink2}
</HelpPopoverLink>
</HelpPopoverLinksGroup>
</HelpPopoverContent>
</HelpPopover>
);
};
@@ -1,40 +0,0 @@
import type { FC } from "react";
import {
HelpTooltip,
HelpTooltipContent,
HelpTooltipIconTrigger,
HelpTooltipLink,
HelpTooltipLinksGroup,
HelpTooltipText,
HelpTooltipTitle,
} from "#/components/HelpTooltip/HelpTooltip";
import { docs } from "#/utils/docs";
const Language = {
workspaceTooltipTitle: "What is a workspace?",
workspaceTooltipText:
"A workspace is your development environment in the cloud. It includes the infrastructure and tools you need to work on your project.",
workspaceTooltipLink1: "Create Workspaces",
workspaceTooltipLink2: "Connect with SSH",
workspaceTooltipLink3: "Editors and IDEs",
};
export const WorkspaceHelpTooltip: FC = () => {
return (
<HelpTooltip>
<HelpTooltipIconTrigger />
<HelpTooltipContent>
<HelpTooltipTitle>{Language.workspaceTooltipTitle}</HelpTooltipTitle>
<HelpTooltipText>{Language.workspaceTooltipText}</HelpTooltipText>
<HelpTooltipLinksGroup>
<HelpTooltipLink href={docs("/user-guides")}>
{Language.workspaceTooltipLink1}
</HelpTooltipLink>
<HelpTooltipLink href={docs("/user-guides/workspace-access")}>
{Language.workspaceTooltipLink2}
</HelpTooltipLink>
</HelpTooltipLinksGroup>
</HelpTooltipContent>
</HelpTooltip>
);
};
@@ -30,7 +30,7 @@ import {
type WorkspaceFilterState,
WorkspacesFilter,
} from "./filter/WorkspacesFilter";
import { WorkspaceHelpTooltip } from "./WorkspaceHelpTooltip";
import { WorkspaceHelpPopover } from "./WorkspaceHelpPopover";
import { WorkspacesButton } from "./WorkspacesButton";
const Language = {
@@ -110,7 +110,7 @@ export const WorkspacesPageView: FC<WorkspacesPageViewProps> = ({
<PageHeaderTitle>
<Stack direction="row" spacing={1} alignItems="center">
<span>{Language.pageTitle}</span>
<WorkspaceHelpTooltip />
<WorkspaceHelpPopover />
</Stack>
</PageHeaderTitle>
</PageHeader>
+4 -4
View File
@@ -9,7 +9,7 @@ import utc from "dayjs/plugin/utc";
import type { ReactNode } from "react";
import { Link as RouterLink } from "react-router";
import type { Template, Workspace } from "#/api/typesGenerated";
import { HelpTooltipTitle } from "#/components/HelpTooltip/HelpTooltip";
import { HelpPopoverTitle } from "#/components/HelpPopover/HelpPopover";
import type { WorkspaceActivityStatus } from "#/modules/workspaces/activity";
import { isWorkspaceOn } from "./workspace";
@@ -119,7 +119,7 @@ export const autostopDisplay = (
message: "Required to stop soon",
tooltip: (
<>
<HelpTooltipTitle>Upcoming stop required</HelpTooltipTitle>
<HelpPopoverTitle>Upcoming stop required</HelpPopoverTitle>
This workspace will be required to stop by{" "}
{dayjs(workspace.latest_build.max_deadline).format(
"MMMM D [at] h:mm A",
@@ -139,11 +139,11 @@ export const autostopDisplay = (
};
}
let title = (
<HelpTooltipTitle>Template Autostop requirement</HelpTooltipTitle>
<HelpPopoverTitle>Template Autostop requirement</HelpPopoverTitle>
);
let reason: ReactNode = ` because the ${template.display_name} template has an autostop requirement.`;
if (template.autostop_requirement && template.allow_user_autostop) {
title = <HelpTooltipTitle>Autostop schedule</HelpTooltipTitle>;
title = <HelpPopoverTitle>Autostop schedule</HelpPopoverTitle>;
reason = (
<span data-chromatic="ignore">
{" "}