refactor: use a global tooltip provider with a consistent 100 millisecond delay duration (#20869)

Part 1 of 2

- this sets up the TooltipProvider for Storybook in preview.tsx and for
the app in App.tsx along with removing TooltipProvider in some of the
usages of Tooltip
- I tested existing components that haven't had the TooltipProvider
removed and they still function correctly. So should be fine until the
2nd PR to complete the migration.

---------

Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
Jaayden Halko
2025-11-24 23:22:54 +00:00
committed by GitHub
parent f08cb2f059
commit 6c2900f138
37 changed files with 636 additions and 744 deletions
+5 -2
View File
@@ -11,6 +11,7 @@ import isChromatic from "chromatic/isChromatic";
import { StrictMode } from "react";
import { QueryClient, QueryClientProvider } from "react-query";
import { withRouter } from "storybook-addon-remix-react-router";
import { TooltipProvider } from "../src/components/Tooltip/Tooltip";
import "theme/globalFonts";
import type { Decorator, Loader, Parameters } from "@storybook/react-vite";
import themes from "../src/theme";
@@ -100,8 +101,10 @@ const withTheme: Decorator = (Story, context) => {
<StyledEngineProvider injectFirst>
<MuiThemeProvider theme={themes[selected]}>
<EmotionThemeProvider theme={themes[selected]}>
<CssBaseline />
<Story />
<TooltipProvider delayDuration={100}>
<CssBaseline />
<Story />
</TooltipProvider>
</EmotionThemeProvider>
</MuiThemeProvider>
</StyledEngineProvider>
+5 -2
View File
@@ -1,5 +1,6 @@
import "./theme/globalFonts";
import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
import { TooltipProvider } from "components/Tooltip/Tooltip";
import {
type FC,
type ReactNode,
@@ -53,8 +54,10 @@ export const AppProviders: FC<AppProvidersProps> = ({
<QueryClientProvider client={queryClient}>
<AuthProvider>
<ThemeProvider>
{children}
<GlobalSnackbar />
<TooltipProvider delayDuration={100}>
{children}
<GlobalSnackbar />
</TooltipProvider>
</ThemeProvider>
</AuthProvider>
{showDevtools && <ReactQueryDevtools initialIsOpen={showDevtools} />}
+13 -16
View File
@@ -3,7 +3,6 @@ import { Button } from "components/Button/Button";
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "components/Tooltip/Tooltip";
import { EyeIcon, EyeOffIcon } from "lucide-react";
@@ -77,21 +76,19 @@ export const CodeExample: FC<CodeExampleProps> = ({
<div className="flex items-center gap-1">
{showRevealButton && redactPattern && !secret && (
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<Button
size="icon"
variant="subtle"
onClick={() => setShowFullValue(!showFullValue)}
>
{icon}
<span className="sr-only">{showButtonLabel}</span>
</Button>
</TooltipTrigger>
<TooltipContent>{showButtonLabel}</TooltipContent>
</Tooltip>
</TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<Button
size="icon"
variant="subtle"
onClick={() => setShowFullValue(!showFullValue)}
>
{icon}
<span className="sr-only">{showButtonLabel}</span>
</Button>
</TooltipTrigger>
<TooltipContent>{showButtonLabel}</TooltipContent>
</Tooltip>
)}
<CopyButton text={code} label="Copy code" />
</div>
+13 -16
View File
@@ -15,7 +15,6 @@ import {
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "components/Tooltip/Tooltip";
import { Check, ChevronDown, CornerDownLeft, Info } from "lucide-react";
@@ -138,21 +137,19 @@ export const Combobox: FC<ComboboxProps> = ({
<Check className="size-icon-sm" />
)}
{option.description && (
<TooltipProvider delayDuration={100}>
<Tooltip>
<TooltipTrigger asChild>
<span
className="flex"
onMouseEnter={(e) => e.stopPropagation()}
>
<Info className="w-3.5 h-3.5 text-content-secondary" />
</span>
</TooltipTrigger>
<TooltipContent side="right" sideOffset={10}>
{option.description}
</TooltipContent>
</Tooltip>
</TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<span
className="flex"
onMouseEnter={(e) => e.stopPropagation()}
>
<Info className="w-3.5 h-3.5 text-content-secondary" />
</span>
</TooltipTrigger>
<TooltipContent side="right" sideOffset={10}>
{option.description}
</TooltipContent>
</Tooltip>
)}
</div>
</CommandItem>
+14 -17
View File
@@ -2,7 +2,6 @@ import { Button, type ButtonProps } from "components/Button/Button";
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "components/Tooltip/Tooltip";
import { useClipboard } from "hooks/useClipboard";
@@ -22,21 +21,19 @@ export const CopyButton: FC<CopyButtonProps> = ({
const { showCopiedSuccess, copyToClipboard } = useClipboard();
return (
<TooltipProvider delayDuration={100}>
<Tooltip>
<TooltipTrigger asChild>
<Button
size="icon"
variant="subtle"
onClick={() => copyToClipboard(text)}
{...buttonProps}
>
{showCopiedSuccess ? <CheckIcon /> : <CopyIcon />}
<span className="sr-only">{label}</span>
</Button>
</TooltipTrigger>
<TooltipContent>{label}</TooltipContent>
</Tooltip>
</TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<Button
size="icon"
variant="subtle"
onClick={() => copyToClipboard(text)}
{...buttonProps}
>
{showCopiedSuccess ? <CheckIcon /> : <CopyIcon />}
<span className="sr-only">{label}</span>
</Button>
</TooltipTrigger>
<TooltipContent>{label}</TooltipContent>
</Tooltip>
);
};
@@ -1,7 +1,6 @@
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "components/Tooltip/Tooltip";
import { useClickable } from "hooks/useClickable";
@@ -37,55 +36,53 @@ export const CopyableValue: FC<CopyableValueProps> = ({
});
return (
<TooltipProvider delayDuration={100}>
<Tooltip
open={tooltipOpen}
onOpenChange={(shouldBeOpen) => {
// Always keep the tooltip open when in focus to handle issues when onOpenChange is unexpectedly false
if (!shouldBeOpen && isFocused) return;
setTooltipOpen(shouldBeOpen);
}}
>
<TooltipTrigger asChild>
<span
ref={clickableProps.ref}
{...attrs}
className={cn("cursor-pointer", className)}
role={role ?? clickableProps.role}
tabIndex={tabIndex ?? clickableProps.tabIndex}
onClick={(event) => {
clickableProps.onClick(event);
onClick?.(event);
}}
onKeyDown={(event) => {
clickableProps.onKeyDown(event);
onKeyDown?.(event);
}}
onKeyUp={(event) => {
clickableProps.onKeyUp(event);
onKeyUp?.(event);
}}
onMouseEnter={() => {
setIsFocused(true);
setTooltipOpen(true);
}}
onMouseLeave={() => {
setTooltipOpen(false);
}}
onFocus={() => {
setIsFocused(true);
}}
onBlur={() => {
setTooltipOpen(false);
}}
>
{children}
</span>
</TooltipTrigger>
<TooltipContent side={side}>
{showCopiedSuccess ? "Copied!" : "Click to copy"}
</TooltipContent>
</Tooltip>
</TooltipProvider>
<Tooltip
open={tooltipOpen}
onOpenChange={(shouldBeOpen) => {
// Always keep the tooltip open when in focus to handle issues when onOpenChange is unexpectedly false
if (!shouldBeOpen && isFocused) return;
setTooltipOpen(shouldBeOpen);
}}
>
<TooltipTrigger asChild>
<span
ref={clickableProps.ref}
{...attrs}
className={cn("cursor-pointer", className)}
role={role ?? clickableProps.role}
tabIndex={tabIndex ?? clickableProps.tabIndex}
onClick={(event) => {
clickableProps.onClick(event);
onClick?.(event);
}}
onKeyDown={(event) => {
clickableProps.onKeyDown(event);
onKeyDown?.(event);
}}
onKeyUp={(event) => {
clickableProps.onKeyUp(event);
onKeyUp?.(event);
}}
onMouseEnter={() => {
setIsFocused(true);
setTooltipOpen(true);
}}
onMouseLeave={() => {
setTooltipOpen(false);
}}
onFocus={() => {
setIsFocused(true);
}}
onBlur={() => {
setTooltipOpen(false);
}}
>
{children}
</span>
</TooltipTrigger>
<TooltipContent side={side}>
{showCopiedSuccess ? "Copied!" : "Click to copy"}
</TooltipContent>
</Tooltip>
);
};
@@ -2,7 +2,6 @@ import { Link } from "components/Link/Link";
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "components/Tooltip/Tooltip";
import type { FC, HTMLAttributes, ReactNode } from "react";
@@ -47,40 +46,38 @@ export const FeatureStageBadge: FC<FeatureStageBadgeProps> = ({
const sizeClasses = badgeSizeClasses[size];
return (
<TooltipProvider delayDuration={100}>
<Tooltip>
<TooltipTrigger asChild>
<span
className={cn(
"block max-w-fit cursor-default flex-shrink-0 leading-none whitespace-nowrap border rounded-md transition-colors duration-200 ease-in-out bg-transparent border-solid border-transparent",
sizeClasses,
colorClasses,
className,
)}
{...delegatedProps}
>
<span className="sr-only"> (This is a</span>
<span className="first-letter:uppercase">
{labelText && `${labelText} `}
{featureStageBadgeTypes[contentType]}
</span>
<span className="sr-only"> feature)</span>
<Tooltip>
<TooltipTrigger asChild>
<span
className={cn(
"block max-w-fit cursor-default flex-shrink-0 leading-none whitespace-nowrap border rounded-md transition-colors duration-200 ease-in-out bg-transparent border-solid border-transparent",
sizeClasses,
colorClasses,
className,
)}
{...delegatedProps}
>
<span className="sr-only"> (This is a</span>
<span className="first-letter:uppercase">
{labelText && `${labelText} `}
{featureStageBadgeTypes[contentType]}
</span>
</TooltipTrigger>
<TooltipContent align="start" className="max-w-xs text-sm">
<p className="m-0">
This feature has not yet reached general availability (GA).
</p>
<span className="sr-only"> feature)</span>
</span>
</TooltipTrigger>
<TooltipContent align="start" className="max-w-xs text-sm">
<p className="m-0">
This feature has not yet reached general availability (GA).
</p>
<Link
href={docs("/install/releases/feature-stages")}
className="font-semibold"
>
Learn about feature stages
<span className="sr-only"> (link opens in new tab)</span>
</Link>
</TooltipContent>
</Tooltip>
</TooltipProvider>
<Link
href={docs("/install/releases/feature-stages")}
className="font-semibold"
>
Learn about feature stages
<span className="sr-only"> (link opens in new tab)</span>
</Link>
</TooltipContent>
</Tooltip>
);
};
@@ -11,7 +11,6 @@ import {
TooltipContent,
type TooltipContentProps,
type TooltipProps,
TooltipProvider,
TooltipTrigger,
} from "components/Tooltip/Tooltip";
import { CircleHelpIcon, ExternalLinkIcon } from "lucide-react";
@@ -33,11 +32,7 @@ export const HelpTooltipTrigger = TooltipTrigger;
export const HelpTooltipIcon = CircleHelpIcon;
export const HelpTooltip: FC<TooltipProps> = (props) => {
return (
<TooltipProvider>
<Tooltip delayDuration={0} {...props} />
</TooltipProvider>
);
return <Tooltip {...props} />;
};
export const HelpTooltipContent: FC<TooltipContentProps> = ({
@@ -14,7 +14,6 @@ import {
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "components/Tooltip/Tooltip";
import { useDebouncedValue } from "hooks/debounce";
@@ -684,21 +683,19 @@ export const MultiSelectCombobox = forwardRef<
)}
{option.label}
{option.description && (
<TooltipProvider delayDuration={100}>
<Tooltip>
<TooltipTrigger asChild>
<span className="flex items-center pointer-events-auto">
<Info className="!w-3.5 !h-3.5 text-content-secondary" />
</span>
</TooltipTrigger>
<TooltipContent
side="right"
sideOffset={10}
>
{option.description}
</TooltipContent>
</Tooltip>
</TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<span className="flex items-center pointer-events-auto">
<Info className="!w-3.5 !h-3.5 text-content-secondary" />
</span>
</TooltipTrigger>
<TooltipContent
side="right"
sideOffset={10}
>
{option.description}
</TooltipContent>
</Tooltip>
)}
</div>
</CommandItem>
@@ -2,7 +2,6 @@ import { Pill } from "components/Pill/Pill";
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "components/Tooltip/Tooltip";
import type { FC } from "react";
@@ -33,11 +32,9 @@ export const StatusPill: FC<StatusPillProps> = ({
return pill;
}
return (
<TooltipProvider>
<Tooltip delayDuration={150}>
<TooltipTrigger asChild>{pill}</TooltipTrigger>
<TooltipContent>{label}</TooltipContent>
</Tooltip>
</TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>{pill}</TooltipTrigger>
<TooltipContent>{label}</TooltipContent>
</Tooltip>
);
};
@@ -7,7 +7,6 @@ import { CoderIcon } from "components/Icons/CoderIcon";
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "components/Tooltip/Tooltip";
import type { ProxyContextValue } from "contexts/ProxyContext";
@@ -230,21 +229,19 @@ const TasksNavItem: FC<TasksNavItemProps> = ({ user }) => {
>
Tasks
{idleCount > 0 && (
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<Badge
variant="info"
size="xs"
className="ml-2"
aria-label={idleTasksLabel(idleCount)}
>
{idleCount}
</Badge>
</TooltipTrigger>
<TooltipContent>{idleTasksLabel(idleCount)}</TooltipContent>
</Tooltip>
</TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<Badge
variant="info"
size="xs"
className="ml-2"
aria-label={idleTasksLabel(idleCount)}
>
{idleCount}
</Badge>
</TooltipTrigger>
<TooltipContent>{idleTasksLabel(idleCount)}</TooltipContent>
</Tooltip>
)}
</NavLink>
);
@@ -14,7 +14,6 @@ import { Stack } from "components/Stack/Stack";
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "components/Tooltip/Tooltip";
import { useProxy } from "contexts/ProxyContext";
@@ -346,19 +345,17 @@ export const AgentDevcontainerCard: FC<AgentDevcontainerCardProps> = ({
)
: "";
return (
<TooltipProvider key={portLabel}>
<Tooltip>
<TooltipTrigger asChild>
<AgentButton disabled={!hasHostBind} asChild>
<a href={linkDest}>
<ExternalLinkIcon />
{portLabel}
</a>
</AgentButton>
</TooltipTrigger>
<TooltipContent>{helperText}</TooltipContent>
</Tooltip>
</TooltipProvider>
<Tooltip key={portLabel}>
<TooltipTrigger asChild>
<AgentButton disabled={!hasHostBind} asChild>
<a href={linkDest}>
<ExternalLinkIcon />
{portLabel}
</a>
</AgentButton>
</TooltipTrigger>
<TooltipContent>{helperText}</TooltipContent>
</Tooltip>
);
})}
</section>
@@ -5,7 +5,6 @@ import type { Line } from "components/Logs/LogLine";
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "components/Tooltip/Tooltip";
import { type ComponentProps, forwardRef, type JSX } from "react";
@@ -148,33 +147,31 @@ export const AgentLogs = forwardRef<List, AgentLogsProps>(
</List>
{overflowed && (
<TooltipProvider delayDuration={100}>
<Tooltip>
<TooltipTrigger asChild>
<Badge
asChild
className="max-w-fit py-1.5 px-3 absolute bottom-3 left-1/2 -translate-x-1/2"
>
<span>Logs overflowed</span>
</Badge>
</TooltipTrigger>
<TooltipContent
<Tooltip>
<TooltipTrigger asChild>
<Badge
asChild
className="w-full text-sm text-content-secondary bg-surface-primary max-w-prose leading-relaxed m-0 p-4"
className="max-w-fit py-1.5 px-3 absolute bottom-3 left-1/2 -translate-x-1/2"
>
<p>
Startup logs exceeded the max size of{" "}
<span className="tracking-wide font-mono">1MB</span>, and will
not continue to be written to the database. Logs will continue
to be written to the{" "}
<span className="font-mono bg-surface-tertiary rounded-md px-1.5 py-0.5">
/tmp/coder-startup-script.log
</span>{" "}
file in the workspace.
</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
<span>Logs overflowed</span>
</Badge>
</TooltipTrigger>
<TooltipContent
asChild
className="w-full text-sm text-content-secondary bg-surface-primary max-w-prose leading-relaxed m-0 p-4"
>
<p>
Startup logs exceeded the max size of{" "}
<span className="tracking-wide font-mono">1MB</span>, and will
not continue to be written to the database. Logs will continue
to be written to the{" "}
<span className="font-mono bg-surface-tertiary rounded-md px-1.5 py-0.5">
/tmp/coder-startup-script.log
</span>{" "}
file in the workspace.
</p>
</TooltipContent>
</Tooltip>
)}
</div>
);
+12 -15
View File
@@ -6,7 +6,6 @@ import { Spinner } from "components/Spinner/Spinner";
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "components/Tooltip/Tooltip";
import { useProxy } from "contexts/ProxyContext";
@@ -137,20 +136,18 @@ export const AppLink: FC<AppLinkProps> = ({
if (primaryTooltip || app.tooltip) {
return (
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>{button}</TooltipTrigger>
<TooltipContent>
{primaryTooltip ? (
primaryTooltip
) : app.tooltip ? (
<Markdown className="text-content-secondary prose-sm font-medium wrap-anywhere">
{app.tooltip}
</Markdown>
) : null}
</TooltipContent>
</Tooltip>
</TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>{button}</TooltipTrigger>
<TooltipContent>
{primaryTooltip ? (
primaryTooltip
) : app.tooltip ? (
<Markdown className="text-content-secondary prose-sm font-medium wrap-anywhere">
{app.tooltip}
</Markdown>
) : null}
</TooltipContent>
</Tooltip>
);
}
@@ -36,7 +36,6 @@ import { Spinner } from "components/Spinner/Spinner";
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "components/Tooltip/Tooltip";
import { useFormik } from "formik";
@@ -218,40 +217,36 @@ export const PortForwardPopoverView: FC<PortForwardPopoverViewProps> = ({
: "authenticated";
const disabledPublicMenuItem = (
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
{/* Tooltips don't work directly on disabled MenuItem components so you must wrap in div. */}
<div>
<MenuItem value="public" disabled>
Public
</MenuItem>
</div>
</TooltipTrigger>
<TooltipContent disablePortal>
This workspace template does not allow sharing ports publicly.
</TooltipContent>
</Tooltip>
</TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
{/* Tooltips don't work directly on disabled MenuItem components so you must wrap in div. */}
<div>
<MenuItem value="public" disabled>
Public
</MenuItem>
</div>
</TooltipTrigger>
<TooltipContent disablePortal>
This workspace template does not allow sharing ports publicly.
</TooltipContent>
</Tooltip>
);
const disabledAuthenticatedMenuItem = (
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
{/* Tooltips don't work directly on disabled MenuItem components so you must wrap in div. */}
<div>
<MenuItem value="authenticated" disabled>
Authenticated
</MenuItem>
</div>
</TooltipTrigger>
<TooltipContent disablePortal>
This workspace template does not allow sharing ports outside of its
organization.
</TooltipContent>
</Tooltip>
</TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
{/* Tooltips don't work directly on disabled MenuItem components so you must wrap in div. */}
<div>
<MenuItem value="authenticated" disabled>
Authenticated
</MenuItem>
</div>
</TooltipTrigger>
<TooltipContent disablePortal>
This workspace template does not allow sharing ports outside of its
organization.
</TooltipContent>
</Tooltip>
);
return (
@@ -338,19 +333,15 @@ export const PortForwardPopoverView: FC<PortForwardPopoverViewProps> = ({
required
css={styles.newPortInput}
/>
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<Button type="submit" size="icon" variant="subtle">
<ExternalLinkIcon />
<span className="sr-only">Connect to port</span>
</Button>
</TooltipTrigger>
<TooltipContent disablePortal>
Connect to port
</TooltipContent>
</Tooltip>
</TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<Button type="submit" size="icon" variant="subtle">
<ExternalLinkIcon />
<span className="sr-only">Connect to port</span>
</Button>
</TooltipTrigger>
<TooltipContent disablePortal>Connect to port</TooltipContent>
</Tooltip>
</form>
</Stack>
</Stack>
@@ -405,30 +396,28 @@ export const PortForwardPopoverView: FC<PortForwardPopoverViewProps> = ({
alignItems="center"
>
{canSharePorts && (
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<Button
size="icon"
variant="subtle"
onClick={async () => {
await upsertSharedPortMutation.mutateAsync({
agent_name: agent.name,
port: port.port,
protocol: listeningPortProtocol,
share_level: defaultShareLevel,
});
}}
>
<ShareIcon />
<span className="sr-only">Share</span>
</Button>
</TooltipTrigger>
<TooltipContent disablePortal>
Share this port
</TooltipContent>
</Tooltip>
</TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<Button
size="icon"
variant="subtle"
onClick={async () => {
await upsertSharedPortMutation.mutateAsync({
agent_name: agent.name,
port: port.port,
protocol: listeningPortProtocol,
share_level: defaultShareLevel,
});
}}
>
<ShareIcon />
<span className="sr-only">Share</span>
</Button>
</TooltipTrigger>
<TooltipContent disablePortal>
Share this port
</TooltipContent>
</Tooltip>
)}
</Stack>
</Stack>
@@ -24,7 +24,6 @@ import { Spinner } from "components/Spinner/Spinner";
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "components/Tooltip/Tooltip";
import { useAuthenticated } from "hooks/useAuthenticated";
@@ -414,23 +413,21 @@ const ExternalAuthButtons: FC<ExternalAuthButtonProps> = ({
</Button>
{shouldRetry && !auth.authenticated && (
<TooltipProvider>
<Tooltip delayDuration={100}>
<TooltipTrigger asChild>
<Button
variant="outline"
size="icon"
onClick={startPollingExternalAuth}
>
<RedoIcon />
<span className="sr-only">Refresh external auth</span>
</Button>
</TooltipTrigger>
<TooltipContent>
Retry connecting to {auth.display_name}
</TooltipContent>
</Tooltip>
</TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="outline"
size="icon"
onClick={startPollingExternalAuth}
>
<RedoIcon />
<span className="sr-only">Refresh external auth</span>
</Button>
</TooltipTrigger>
<TooltipContent>
Retry connecting to {auth.display_name}
</TooltipContent>
</Tooltip>
)}
</div>
);
@@ -2,7 +2,6 @@ import type { WorkspaceAppStatus as APIWorkspaceAppStatus } from "api/typesGener
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "components/Tooltip/Tooltip";
import capitalize from "lodash/capitalize";
@@ -28,23 +27,21 @@ export const WorkspaceAppStatus = ({
const message = status.message || capitalize(status.state);
return (
<div className="flex flex-col text-content-secondary">
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<div className="flex items-center gap-2">
<AppStatusStateIcon
latest
disabled={disabled}
state={status.state}
/>
<span className="whitespace-nowrap max-w-72 overflow-hidden text-ellipsis text-sm text-content-primary font-medium">
{message}
</span>
</div>
</TooltipTrigger>
<TooltipContent>{message}</TooltipContent>
</Tooltip>
</TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<div className="flex items-center gap-2">
<AppStatusStateIcon
latest
disabled={disabled}
state={status.state}
/>
<span className="whitespace-nowrap max-w-72 overflow-hidden text-ellipsis text-sm text-content-primary font-medium">
{message}
</span>
</div>
</TooltipTrigger>
<TooltipContent>{message}</TooltipContent>
</Tooltip>
<span className="text-xs first-letter:uppercase block pl-6">
{status.state}
</span>
@@ -7,7 +7,6 @@ import {
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "components/Tooltip/Tooltip";
import type React from "react";
@@ -60,19 +59,17 @@ export const WorkspaceStatusIndicator: FC<WorkspaceStatusIndicatorProps> = ({
}
return (
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<StatusIndicator variant={variantByStatusType[type]}>
<StatusIndicatorDot />
<span className="sr-only">Workspace status:</span> {text}
{children}
</StatusIndicator>
</TooltipTrigger>
<TooltipContent>
Your workspace is running but some agents are unhealthy.
</TooltipContent>
</Tooltip>
</TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<StatusIndicator variant={variantByStatusType[type]}>
<StatusIndicatorDot />
<span className="sr-only">Workspace status:</span> {text}
{children}
</StatusIndicator>
</TooltipTrigger>
<TooltipContent>
Your workspace is running but some agents are unhealthy.
</TooltipContent>
</Tooltip>
);
};
@@ -14,7 +14,6 @@ import { Switch } from "components/Switch/Switch";
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "components/Tooltip/Tooltip";
import { UserAutocomplete } from "components/UserAutocomplete/UserAutocomplete";
@@ -386,26 +385,24 @@ export const CreateWorkspacePageViewExperimental: FC<
<span className="flex flex-row items-center gap-2">
<h1 className="text-3xl font-semibold m-0">New workspace</h1>
<TooltipProvider delayDuration={100}>
<Tooltip>
<TooltipTrigger asChild>
<CircleHelp className="size-icon-xs text-content-secondary" />
</TooltipTrigger>
<TooltipContent className="max-w-xs text-sm">
Dynamic Parameters enhances Coder's existing parameter system
with real-time validation, conditional parameter behavior, and
richer input types.
<br />
<Link
href={docs(
"/admin/templates/extending-templates/dynamic-parameters",
)}
>
View docs
</Link>
</TooltipContent>
</Tooltip>
</TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<CircleHelp className="size-icon-xs text-content-secondary" />
</TooltipTrigger>
<TooltipContent className="max-w-xs text-sm">
Dynamic Parameters enhances Coder's existing parameter system
with real-time validation, conditional parameter behavior, and
richer input types.
<br />
<Link
href={docs(
"/admin/templates/extending-templates/dynamic-parameters",
)}
>
View docs
</Link>
</TooltipContent>
</Tooltip>
</span>
</header>
@@ -6,7 +6,6 @@ import { Spinner } from "components/Spinner/Spinner";
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "components/Tooltip/Tooltip";
import { Check, Redo } from "lucide-react";
@@ -77,19 +76,17 @@ export const ExternalAuthButton: FC<ExternalAuthButtonProps> = ({
)}
{displayRetry && !auth.authenticated && (
<TooltipProvider>
<Tooltip delayDuration={100}>
<TooltipTrigger asChild>
<Button variant="outline" size="icon" onClick={onStartPolling}>
<Redo />
<span className="sr-only">Refresh external auth</span>
</Button>
</TooltipTrigger>
<TooltipContent>
Retry login with {auth.display_name}
</TooltipContent>
</Tooltip>
</TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<Button variant="outline" size="icon" onClick={onStartPolling}>
<Redo />
<span className="sr-only">Refresh external auth</span>
</Button>
</TooltipTrigger>
<TooltipContent>
Retry login with {auth.display_name}
</TooltipContent>
</Tooltip>
)}
</span>
</div>
@@ -16,7 +16,6 @@ import {
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "components/Tooltip/Tooltip";
import { useFormik } from "formik";
@@ -67,31 +66,29 @@ export const AppearanceSettingsPageView: FC<
</SettingsHeader>
<Badges>
<TooltipProvider>
<Tooltip delayDuration={0}>
{isEntitled && !isPremium ? (
<EnterpriseBadge />
) : (
<TooltipTrigger asChild>
<span>
<PremiumBadge />
</span>
</TooltipTrigger>
)}
<Tooltip>
{isEntitled && !isPremium ? (
<EnterpriseBadge />
) : (
<TooltipTrigger asChild>
<span>
<PremiumBadge />
</span>
</TooltipTrigger>
)}
<TooltipContent
sideOffset={-28}
collisionPadding={16}
className="p-0"
>
<PopoverPaywall
message="Appearance"
description="With a Premium license, you can customize the appearance and branding of your deployment."
documentationLink="https://coder.com/docs/admin/appearance"
/>
</TooltipContent>
</Tooltip>
</TooltipProvider>
<TooltipContent
sideOffset={-28}
collisionPadding={16}
className="p-0"
>
<PopoverPaywall
message="Appearance"
description="With a Premium license, you can customize the appearance and branding of your deployment."
documentationLink="https://coder.com/docs/admin/appearance"
/>
</TooltipContent>
</Tooltip>
</Badges>
<Fieldset
@@ -41,7 +41,6 @@ import {
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "components/Tooltip/Tooltip";
import { useFormik } from "formik";
@@ -417,23 +416,21 @@ const OrganizationRow: FC<OrganizationRowProps> = ({
<div className="flex flex-row items-center gap-2 text-content-primary">
{idpOrg}
{!exists && (
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<TriangleAlert className="size-icon-xs cursor-pointer text-content-warning" />
</TooltipTrigger>
<TooltipContent
align="start"
alignOffset={-8}
sideOffset={8}
className="p-2 text-xs text-content-secondary max-w-sm"
>
This value has not be seen in the specified claim field
before. You might want to check your IdP configuration and
ensure that this value is not misspelled.
</TooltipContent>
</Tooltip>
</TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<TriangleAlert className="size-icon-xs cursor-pointer text-content-warning" />
</TooltipTrigger>
<TooltipContent
align="start"
alignOffset={-8}
sideOffset={8}
className="p-2 text-xs text-content-secondary max-w-sm"
>
This value has not be seen in the specified claim field before.
You might want to check your IdP configuration and ensure that
this value is not misspelled.
</TooltipContent>
</Tooltip>
)}
</div>
</TableCell>
@@ -2,7 +2,6 @@ import { Pill } from "components/Pill/Pill";
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "components/Tooltip/Tooltip";
import type { FC } from "react";
@@ -47,36 +46,34 @@ interface OverflowPillProps {
const OverflowPillList: FC<OverflowPillProps> = ({ organizations }) => {
return (
<TooltipProvider>
<Tooltip delayDuration={0}>
<TooltipTrigger asChild>
<Pill
className="min-h-4 min-w-6 bg-surface-secondary border-none px-3 py-1"
data-testid="overflow-pill"
>
+{organizations.length}
</Pill>
</TooltipTrigger>
<Tooltip>
<TooltipTrigger asChild>
<Pill
className="min-h-4 min-w-6 bg-surface-secondary border-none px-3 py-1"
data-testid="overflow-pill"
>
+{organizations.length}
</Pill>
</TooltipTrigger>
<TooltipContent className="px-4 py-3 border-surface-quaternary">
<ul className="flex flex-col gap-2 list-none my-0 pl-0">
{organizations.map((organization) => (
<li key={organization.name}>
<Pill
className={cn(
"border-none w-fit",
organization.isUUID
? "bg-surface-destructive"
: "bg-surface-secondary",
)}
>
{organization.name}
</Pill>
</li>
))}
</ul>
</TooltipContent>
</Tooltip>
</TooltipProvider>
<TooltipContent className="px-4 py-3 border-surface-quaternary">
<ul className="flex flex-col gap-2 list-none my-0 pl-0">
{organizations.map((organization) => (
<li key={organization.name}>
<Pill
className={cn(
"border-none w-fit",
organization.isUUID
? "bg-surface-destructive"
: "bg-surface-secondary",
)}
>
{organization.name}
</Pill>
</li>
))}
</ul>
</TooltipContent>
</Tooltip>
);
};
@@ -15,7 +15,6 @@ import { Stack } from "components/Stack/Stack";
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "components/Tooltip/Tooltip";
import type { FC } from "react";
@@ -53,31 +52,29 @@ export const ObservabilitySettingsPageView: FC<
</SettingsHeader>
<Badges>
<TooltipProvider>
<Tooltip delayDuration={0}>
{featureAuditLogEnabled && !isPremium ? (
<EnterpriseBadge />
) : (
<TooltipTrigger asChild>
<span>
<PremiumBadge />
</span>
</TooltipTrigger>
)}
<Tooltip>
{featureAuditLogEnabled && !isPremium ? (
<EnterpriseBadge />
) : (
<TooltipTrigger asChild>
<span>
<PremiumBadge />
</span>
</TooltipTrigger>
)}
<TooltipContent
sideOffset={-28}
collisionPadding={16}
className="p-0"
>
<PopoverPaywall
message="Observability"
description="With a Premium license, you can monitor your application with logs and metrics."
documentationLink="https://coder.com/docs/admin/appearance"
/>
</TooltipContent>
</Tooltip>
</TooltipProvider>
<TooltipContent
sideOffset={-28}
collisionPadding={16}
className="p-0"
>
<PopoverPaywall
message="Observability"
description="With a Premium license, you can monitor your application with logs and metrics."
documentationLink="https://coder.com/docs/admin/appearance"
/>
</TooltipContent>
</Tooltip>
</Badges>
</div>
@@ -12,7 +12,6 @@ import { Spinner } from "components/Spinner/Spinner";
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "components/Tooltip/Tooltip";
import { useFormik } from "formik";
@@ -82,29 +81,27 @@ export const CreateOrganizationPageView: FC<
)}
<Badges>
<TooltipProvider>
<Tooltip delayDuration={0}>
{isEntitled && (
<TooltipTrigger asChild>
<span>
<PremiumBadge />
</span>
</TooltipTrigger>
)}
<Tooltip>
{isEntitled && (
<TooltipTrigger asChild>
<span>
<PremiumBadge />
</span>
</TooltipTrigger>
)}
<TooltipContent
sideOffset={-28}
collisionPadding={16}
className="p-0"
>
<PopoverPaywall
message="Organizations"
description="Create multiple organizations within a single Coder deployment, allowing several platform teams to operate with isolated users, templates, and distinct underlying infrastructure."
documentationLink={docs("/admin/users/organizations")}
/>
</TooltipContent>
</Tooltip>
</TooltipProvider>
<TooltipContent
sideOffset={-28}
collisionPadding={16}
className="p-0"
>
<PopoverPaywall
message="Organizations"
description="Create multiple organizations within a single Coder deployment, allowing several platform teams to operate with isolated users, templates, and distinct underlying infrastructure."
documentationLink={docs("/admin/users/organizations")}
/>
</TooltipContent>
</Tooltip>
</Badges>
<header className="flex flex-col items-center">
@@ -5,7 +5,6 @@ import { Pill } from "components/Pill/Pill";
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "components/Tooltip/Tooltip";
import type { FC } from "react";
@@ -77,34 +76,29 @@ const OverflowPermissionPill: FC<OverflowPermissionPillProps> = ({
const theme = useTheme();
return (
<TooltipProvider>
<Tooltip delayDuration={0}>
<TooltipTrigger asChild>
<Pill
css={{
backgroundColor: theme.palette.background.paper,
borderColor: theme.palette.divider,
}}
data-testid="overflow-permissions-pill"
>
+{resources.length} more
</Pill>
</TooltipTrigger>
<Tooltip>
<TooltipTrigger asChild>
<Pill
css={{
backgroundColor: theme.palette.background.paper,
borderColor: theme.palette.divider,
}}
data-testid="overflow-permissions-pill"
>
+{resources.length} more
</Pill>
</TooltipTrigger>
<TooltipContent className="px-4 py-3 border-surface-quaternary">
<ul className="flex flex-col gap-2 list-none my-0 pl-0">
{resources.map((resource) => (
<li key={resource}>
<PermissionsPill
resource={resource}
permissions={permissions}
/>
</li>
))}
</ul>
</TooltipContent>
</Tooltip>
</TooltipProvider>
<TooltipContent className="px-4 py-3 border-surface-quaternary">
<ul className="flex flex-col gap-2 list-none my-0 pl-0">
{resources.map((resource) => (
<li key={resource}>
<PermissionsPill resource={resource} permissions={permissions} />
</li>
))}
</ul>
</TooltipContent>
</Tooltip>
);
};
@@ -25,7 +25,6 @@ import { TableCell, TableRow } from "components/Table/Table";
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "components/Tooltip/Tooltip";
import { useFormik } from "formik";
@@ -370,23 +369,21 @@ const GroupRow: FC<GroupRowProps> = ({
<div className="flex flex-row items-center gap-2 text-content-primary">
{idpGroup}
{!exists && (
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<TriangleAlert className="size-icon-xs cursor-pointer text-content-warning" />
</TooltipTrigger>
<TooltipContent
align="start"
alignOffset={-8}
sideOffset={8}
className="p-2 text-xs text-content-secondary max-w-sm"
>
This value has not be seen in the specified claim field
before. You might want to check your IdP configuration and
ensure that this value is not misspelled.
</TooltipContent>
</Tooltip>
</TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<TriangleAlert className="size-icon-xs cursor-pointer text-content-warning" />
</TooltipTrigger>
<TooltipContent
align="start"
alignOffset={-8}
sideOffset={8}
className="p-2 text-xs text-content-secondary max-w-sm"
>
This value has not be seen in the specified claim field before.
You might want to check your IdP configuration and ensure that
this value is not misspelled.
</TooltipContent>
</Tooltip>
)}
</div>
</TableCell>
@@ -4,7 +4,6 @@ import { Pill } from "components/Pill/Pill";
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "components/Tooltip/Tooltip";
import type { FC } from "react";
@@ -36,25 +35,23 @@ interface OverflowPillProps {
const OverflowPill: FC<OverflowPillProps> = ({ roles }) => {
return (
<TooltipProvider>
<Tooltip delayDuration={0}>
<TooltipTrigger asChild>
<Pill data-testid="overflow-pill">+{roles.length} more</Pill>
</TooltipTrigger>
<Tooltip>
<TooltipTrigger asChild>
<Pill data-testid="overflow-pill">+{roles.length} more</Pill>
</TooltipTrigger>
<TooltipContent className="px-4 py-3 border-surface-quaternary">
<ul className="flex flex-col gap-2 list-none my-0 pl-0">
{roles.map((role) => (
<li key={role}>
<Pill css={isUUID(role) ? styles.errorPill : styles.pill}>
{role}
</Pill>
</li>
))}
</ul>
</TooltipContent>
</Tooltip>
</TooltipProvider>
<TooltipContent className="px-4 py-3 border-surface-quaternary">
<ul className="flex flex-col gap-2 list-none my-0 pl-0">
{roles.map((role) => (
<li key={role}>
<Pill css={isUUID(role) ? styles.errorPill : styles.pill}>
{role}
</Pill>
</li>
))}
</ul>
</TooltipContent>
</Tooltip>
);
};
@@ -12,7 +12,6 @@ import { TableCell, TableRow } from "components/Table/Table";
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "components/Tooltip/Tooltip";
import { useFormik } from "formik";
@@ -285,23 +284,21 @@ const RoleRow: FC<RoleRowProps> = ({
<div className="flex flex-row items-center gap-2 text-content-primary">
{idpRole}
{!exists && (
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<TriangleAlert className="size-icon-xs cursor-pointer text-content-warning" />
</TooltipTrigger>
<TooltipContent
align="start"
alignOffset={-8}
sideOffset={8}
className="p-2 text-xs text-content-secondary max-w-sm"
>
This value has not be seen in the specified claim field
before. You might want to check your IdP configuration and
ensure that this value is not misspelled.
</TooltipContent>
</Tooltip>
</TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<TriangleAlert className="size-icon-xs cursor-pointer text-content-warning" />
</TooltipTrigger>
<TooltipContent
align="start"
alignOffset={-8}
sideOffset={8}
className="p-2 text-xs text-content-secondary max-w-sm"
>
This value has not be seen in the specified claim field before.
You might want to check your IdP configuration and ensure that
this value is not misspelled.
</TooltipContent>
</Tooltip>
)}
</div>
</TableCell>
@@ -3,7 +3,6 @@ import { Button } from "components/Button/Button";
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "components/Tooltip/Tooltip";
import { BanIcon } from "lucide-react";
@@ -22,24 +21,22 @@ export const CancelJobButton: FC<CancelJobButtonProps> = ({ job }) => {
return (
<>
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<Button
disabled={!isCancellable}
aria-label="Cancel job"
size="icon"
variant="outline"
onClick={() => {
setIsDialogOpen(true);
}}
>
<BanIcon />
</Button>
</TooltipTrigger>
<TooltipContent>Cancel job</TooltipContent>
</Tooltip>
</TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<Button
disabled={!isCancellable}
aria-label="Cancel job"
size="icon"
variant="outline"
onClick={() => {
setIsDialogOpen(true);
}}
>
<BanIcon />
</Button>
</TooltipTrigger>
<TooltipContent>Cancel job</TooltipContent>
</Tooltip>
<CancelJobConfirmationDialog
open={isDialogOpen}
@@ -37,7 +37,6 @@ import {
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "components/Tooltip/Tooltip";
import { XIcon } from "lucide-react";
@@ -122,23 +121,21 @@ const OrganizationProvisionerJobsPageView: FC<
{filter.ids}
</Badge>
<div className="size-10 flex items-center justify-center absolute top-0 right-0">
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<Button
size="icon"
variant="subtle"
onClick={() => {
onFilterChange({ ...filter, ids: "" });
}}
>
<span className="sr-only">Clear ID</span>
<XIcon />
</Button>
</TooltipTrigger>
<TooltipContent>Clear ID</TooltipContent>
</Tooltip>
</TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<Button
size="icon"
variant="subtle"
onClick={() => {
onFilterChange({ ...filter, ids: "" });
}}
>
<span className="sr-only">Clear ID</span>
<XIcon />
</Button>
</TooltipTrigger>
<TooltipContent>Clear ID</TooltipContent>
</Tooltip>
</div>
</div>
)}
@@ -1,7 +1,6 @@
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "components/Tooltip/Tooltip";
import { InfoIcon } from "lucide-react";
@@ -10,22 +9,20 @@ export const LastConnectionHead: FC = () => {
return (
<span className="flex items-center gap-1 whitespace-nowrap text-xs font-medium text-content-secondary">
Last connection
<TooltipProvider>
<Tooltip delayDuration={0}>
<TooltipTrigger asChild>
<span className="flex items-center">
<span className="sr-only">More info</span>
<InfoIcon
tabIndex={0}
className="cursor-pointer size-icon-xs p-0.5"
/>
</span>
</TooltipTrigger>
<TooltipContent className="max-w-xs">
Last time the provisioner connected to the control plane
</TooltipContent>
</Tooltip>
</TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<span className="flex items-center">
<span className="sr-only">More info</span>
<InfoIcon
tabIndex={0}
className="cursor-pointer size-icon-xs p-0.5"
/>
</span>
</TooltipTrigger>
<TooltipContent className="max-w-xs">
Last time the provisioner connected to the control plane
</TooltipContent>
</Tooltip>
</span>
);
};
@@ -22,7 +22,6 @@ import {
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "components/Tooltip/Tooltip";
import { XIcon } from "lucide-react";
@@ -75,23 +74,21 @@ export const OrganizationProvisionersPageView: FC<
{filter.ids}
</Badge>
<div className="size-10 flex items-center justify-center absolute top-0 right-0">
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<Button
size="icon"
variant="subtle"
onClick={() => {
onFilterChange({ ...filter, ids: "" });
}}
>
<span className="sr-only">Clear ID</span>
<XIcon />
</Button>
</TooltipTrigger>
<TooltipContent>Clear ID</TooltipContent>
</Tooltip>
</TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<Button
size="icon"
variant="subtle"
onClick={() => {
onFilterChange({ ...filter, ids: "" });
}}
>
<span className="sr-only">Clear ID</span>
<XIcon />
</Button>
</TooltipTrigger>
<TooltipContent>Clear ID</TooltipContent>
</Tooltip>
</div>
</div>
</div>
@@ -6,7 +6,6 @@ import {
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "components/Tooltip/Tooltip";
import { InfoIcon } from "lucide-react";
@@ -63,22 +62,20 @@ export const ProvisionerKey: FC<ProvisionerKeyProps> = ({ name }) => {
<span className="flex items-center gap-1 whitespace-nowrap text-xs font-medium text-content-secondary">
{name}
{info && (
<TooltipProvider>
<Tooltip delayDuration={0}>
<TooltipTrigger asChild>
<span className="flex items-center">
<span className="sr-only">More info</span>
<InfoIcon
tabIndex={0}
className="cursor-pointer size-icon-xs p-0.5"
/>
</span>
</TooltipTrigger>
<TooltipContent className="max-w-xs">
{infoByType[type]}
</TooltipContent>
</Tooltip>
</TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<span className="flex items-center">
<span className="sr-only">More info</span>
<InfoIcon
tabIndex={0}
className="cursor-pointer size-icon-xs p-0.5"
/>
</span>
</TooltipTrigger>
<TooltipContent className="max-w-xs">
{infoByType[type]}
</TooltipContent>
</Tooltip>
)}
</span>
);
@@ -2,7 +2,6 @@ import { StatusIndicator } from "components/StatusIndicator/StatusIndicator";
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "components/Tooltip/Tooltip";
import { TriangleAlertIcon } from "lucide-react";
@@ -22,27 +21,25 @@ export const ProvisionerVersion: FC<ProvisionerVersionProps> = ({
Up to date
</span>
) : (
<TooltipProvider>
<Tooltip delayDuration={0}>
<TooltipTrigger asChild>
<StatusIndicator
variant="warning"
size="sm"
className="cursor-pointer"
tabIndex={0}
>
<TriangleAlertIcon className="size-icon-xs" />
Outdated
</StatusIndicator>
</TooltipTrigger>
<TooltipContent className="max-w-xs">
<p className="m-0">
This provisioner is out of date. You may experience issues when
using a provisioner version that doesn't match your Coder
deployment. Please upgrade to a newer version.
</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<StatusIndicator
variant="warning"
size="sm"
className="cursor-pointer"
tabIndex={0}
>
<TriangleAlertIcon className="size-icon-xs" />
Outdated
</StatusIndicator>
</TooltipTrigger>
<TooltipContent className="max-w-xs">
<p className="m-0">
This provisioner is out of date. You may experience issues when using
a provisioner version that doesn't match your Coder deployment. Please
upgrade to a newer version.
</p>
</TooltipContent>
</Tooltip>
);
};
+28 -33
View File
@@ -10,7 +10,6 @@ import { ScrollArea } from "components/ScrollArea/ScrollArea";
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "components/Tooltip/Tooltip";
import capitalize from "lodash/capitalize";
@@ -99,19 +98,17 @@ export const AppStatuses: FC<AppStatusesProps> = ({
{latestStatus.uri &&
(latestStatus.uri.startsWith("file://") ? (
<TooltipProvider>
<Tooltip>
<TooltipTrigger>
<span className="flex items-center gap-1">
<FileIcon className="size-icon-xs" />
{truncateURI(latestStatus.uri)}
</span>
</TooltipTrigger>
<TooltipContent>
This file is located in your workspace
</TooltipContent>
</Tooltip>
</TooltipProvider>
<Tooltip>
<TooltipTrigger>
<span className="flex items-center gap-1">
<FileIcon className="size-icon-xs" />
{truncateURI(latestStatus.uri)}
</span>
</TooltipTrigger>
<TooltipContent>
This file is located in your workspace
</TooltipContent>
</Tooltip>
) : (
<Button asChild variant="outline" size="sm">
<a href={latestStatus.uri} target="_blank" rel="noreferrer">
@@ -132,25 +129,23 @@ export const AppStatuses: FC<AppStatusesProps> = ({
</Button>
)}
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<Button
disabled={otherStatuses.length === 0}
size="icon"
variant="subtle"
onClick={() => {
setDisplayStatuses((display) => !display);
}}
>
{displayStatuses ? <ChevronUpIcon /> : <ChevronDownIcon />}
</Button>
</TooltipTrigger>
<TooltipContent>
{displayStatuses ? "Hide statuses" : "Show statuses"}
</TooltipContent>
</Tooltip>
</TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<Button
disabled={otherStatuses.length === 0}
size="icon"
variant="subtle"
onClick={() => {
setDisplayStatuses((display) => !display);
}}
>
{displayStatuses ? <ChevronUpIcon /> : <ChevronDownIcon />}
</Button>
</TooltipTrigger>
<TooltipContent>
{displayStatuses ? "Hide statuses" : "Show statuses"}
</TooltipContent>
</Tooltip>
</div>
</div>
+4 -1
View File
@@ -4,6 +4,7 @@ import {
render as testingLibraryRender,
waitFor,
} from "@testing-library/react";
import { TooltipProvider } from "components/Tooltip/Tooltip";
import { RequireAuth } from "contexts/auth/RequireAuth";
import type { ProxyProvider } from "contexts/ProxyContext";
import { ThemeOverride } from "contexts/ThemeProvider";
@@ -239,7 +240,9 @@ export const waitForLoaderToBeRemoved = async (): Promise<void> => {
export const renderComponent = (component: React.ReactNode) => {
return testingLibraryRender(component, {
wrapper: ({ children }) => (
<ThemeOverride theme={themes[DEFAULT_THEME]}>{children}</ThemeOverride>
<ThemeOverride theme={themes[DEFAULT_THEME]}>
<TooltipProvider delayDuration={100}>{children}</TooltipProvider>
</ThemeOverride>
),
});
};