mirror of
https://github.com/coder/coder.git
synced 2026-09-21 20:51:01 +08:00
refactor: replace Popover with Tooltip in HelpTooltip (#19635)
for #19397 Currently there are 24 files that import bindings from the deprecated `Popover` component. One of those is `HelpTooltip`, which is instantiated in 24 other files. After this PR, the remaining files that import the deprecated `Popover` should be able to be migrated in just 1-2 more PRs. 🤞🏽 I opted for `Tooltip` as a replacement because it's triggered on hover, unlike our new `Popover` which is triggered on click.
This commit is contained in:
@@ -7,9 +7,9 @@ import {
|
||||
import {
|
||||
HelpTooltip,
|
||||
HelpTooltipContent,
|
||||
HelpTooltipIconTrigger,
|
||||
HelpTooltipText,
|
||||
HelpTooltipTitle,
|
||||
HelpTooltipTrigger,
|
||||
} from "components/HelpTooltip/HelpTooltip";
|
||||
import type { FC } from "react";
|
||||
import { Area, AreaChart, CartesianGrid, XAxis, YAxis } from "recharts";
|
||||
@@ -116,7 +116,7 @@ export const ActiveUsersTitle: FC<ActiveUsersTitleProps> = ({ interval }) => {
|
||||
<div className="flex items-center gap-2">
|
||||
{interval === "day" ? "Daily" : "Weekly"} Active Users
|
||||
<HelpTooltip>
|
||||
<HelpTooltipTrigger size="small" />
|
||||
<HelpTooltipIconTrigger size="small" />
|
||||
<HelpTooltipContent>
|
||||
<HelpTooltipTitle>How do we calculate active users?</HelpTooltipTitle>
|
||||
<HelpTooltipText>
|
||||
|
||||
@@ -3,18 +3,17 @@ import {
|
||||
css,
|
||||
type Interpolation,
|
||||
type Theme,
|
||||
useTheme,
|
||||
} from "@emotion/react";
|
||||
import Link from "@mui/material/Link";
|
||||
import {
|
||||
Popover,
|
||||
PopoverContent,
|
||||
type PopoverContentProps,
|
||||
type PopoverProps,
|
||||
PopoverTrigger,
|
||||
usePopover,
|
||||
} from "components/deprecated/Popover/Popover";
|
||||
import { Stack } from "components/Stack/Stack";
|
||||
import {
|
||||
Tooltip,
|
||||
TooltipContent,
|
||||
type TooltipContentProps,
|
||||
type TooltipProps,
|
||||
TooltipProvider,
|
||||
TooltipTrigger,
|
||||
} from "components/Tooltip/Tooltip";
|
||||
import { CircleHelpIcon, ExternalLinkIcon } from "lucide-react";
|
||||
import {
|
||||
type FC,
|
||||
@@ -23,43 +22,50 @@ import {
|
||||
type PropsWithChildren,
|
||||
type ReactNode,
|
||||
} from "react";
|
||||
import { cn } from "utils/cn";
|
||||
|
||||
type Icon = typeof CircleHelpIcon;
|
||||
|
||||
type Size = "small" | "medium";
|
||||
|
||||
export const HelpTooltipTrigger = TooltipTrigger;
|
||||
|
||||
export const HelpTooltipIcon = CircleHelpIcon;
|
||||
|
||||
export const HelpTooltip: FC<PopoverProps> = (props) => {
|
||||
return <Popover mode="hover" {...props} />;
|
||||
export const HelpTooltip: FC<TooltipProps> = (props) => {
|
||||
return (
|
||||
<TooltipProvider>
|
||||
<Tooltip delayDuration={0} {...props} />
|
||||
</TooltipProvider>
|
||||
);
|
||||
};
|
||||
|
||||
export const HelpTooltipContent: FC<PopoverContentProps> = (props) => {
|
||||
const theme = useTheme();
|
||||
|
||||
export const HelpTooltipContent: FC<TooltipContentProps> = ({
|
||||
className,
|
||||
...props
|
||||
}) => {
|
||||
return (
|
||||
<PopoverContent
|
||||
<TooltipContent
|
||||
side="bottom"
|
||||
align="start"
|
||||
collisionPadding={16}
|
||||
{...props}
|
||||
css={{
|
||||
"& .MuiPaper-root": {
|
||||
fontSize: 14,
|
||||
width: 304,
|
||||
padding: 20,
|
||||
color: theme.palette.text.secondary,
|
||||
},
|
||||
}}
|
||||
className={cn(
|
||||
"w-[320px] p-5 bg-surface-secondary border-surface-quaternary text-sm",
|
||||
className,
|
||||
)}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
type HelpTooltipTriggerProps = HTMLAttributes<HTMLButtonElement> & {
|
||||
type HelpTooltipIconTriggerProps = HTMLAttributes<HTMLButtonElement> & {
|
||||
size?: Size;
|
||||
hoverEffect?: boolean;
|
||||
};
|
||||
|
||||
export const HelpTooltipTrigger = forwardRef<
|
||||
export const HelpTooltipIconTrigger = forwardRef<
|
||||
HTMLButtonElement,
|
||||
HelpTooltipTriggerProps
|
||||
HelpTooltipIconTriggerProps
|
||||
>((props, ref) => {
|
||||
const {
|
||||
size = "medium",
|
||||
@@ -76,7 +82,7 @@ export const HelpTooltipTrigger = forwardRef<
|
||||
});
|
||||
|
||||
return (
|
||||
<PopoverTrigger>
|
||||
<HelpTooltipTrigger asChild>
|
||||
<button
|
||||
{...buttonProps}
|
||||
aria-label="More info"
|
||||
@@ -102,7 +108,7 @@ export const HelpTooltipTrigger = forwardRef<
|
||||
>
|
||||
{children}
|
||||
</button>
|
||||
</PopoverTrigger>
|
||||
</HelpTooltipTrigger>
|
||||
);
|
||||
});
|
||||
|
||||
@@ -155,18 +161,12 @@ export const HelpTooltipAction: FC<HelpTooltipActionProps> = ({
|
||||
onClick,
|
||||
ariaLabel,
|
||||
}) => {
|
||||
const popover = usePopover();
|
||||
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
aria-label={ariaLabel ?? ""}
|
||||
css={styles.action}
|
||||
onClick={(event) => {
|
||||
event.stopPropagation();
|
||||
onClick();
|
||||
popover.setOpen(false);
|
||||
}}
|
||||
onClick={onClick}
|
||||
>
|
||||
<Icon css={styles.actionIcon} />
|
||||
{children}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { Meta, StoryObj } from "@storybook/react-vite";
|
||||
import { expect, userEvent, waitFor, within } from "storybook/test";
|
||||
import { expect, screen, userEvent, waitFor } from "storybook/test";
|
||||
import { InfoTooltip } from "./InfoTooltip";
|
||||
|
||||
const meta = {
|
||||
@@ -16,13 +16,13 @@ export default meta;
|
||||
type Story = StoryObj<typeof InfoTooltip>;
|
||||
|
||||
export const Example: Story = {
|
||||
play: async ({ canvasElement, step }) => {
|
||||
const screen = within(canvasElement);
|
||||
|
||||
play: async ({ step }) => {
|
||||
await step("activate hover trigger", async () => {
|
||||
await userEvent.hover(screen.getByRole("button"));
|
||||
await waitFor(() =>
|
||||
expect(screen.getByText(meta.args.message)).toBeInTheDocument(),
|
||||
expect(screen.getByRole("tooltip")).toHaveTextContent(
|
||||
meta.args.message,
|
||||
),
|
||||
);
|
||||
});
|
||||
},
|
||||
@@ -33,13 +33,13 @@ export const Notice = {
|
||||
type: "notice",
|
||||
message: "Unfortunately, there's a radio connected to my brain",
|
||||
},
|
||||
play: async ({ canvasElement, step }) => {
|
||||
const screen = within(canvasElement);
|
||||
|
||||
play: async ({ step }) => {
|
||||
await step("activate hover trigger", async () => {
|
||||
await userEvent.hover(screen.getByRole("button"));
|
||||
await waitFor(() =>
|
||||
expect(screen.getByText(Notice.args.message)).toBeInTheDocument(),
|
||||
expect(screen.getByRole("tooltip")).toHaveTextContent(
|
||||
Notice.args.message,
|
||||
),
|
||||
);
|
||||
});
|
||||
},
|
||||
@@ -50,13 +50,13 @@ export const Warning = {
|
||||
type: "warning",
|
||||
message: "Unfortunately, there's a radio connected to my brain",
|
||||
},
|
||||
play: async ({ canvasElement, step }) => {
|
||||
const screen = within(canvasElement);
|
||||
|
||||
play: async ({ step }) => {
|
||||
await step("activate hover trigger", async () => {
|
||||
await userEvent.hover(screen.getByRole("button"));
|
||||
await waitFor(() =>
|
||||
expect(screen.getByText(Warning.args.message)).toBeInTheDocument(),
|
||||
expect(screen.getByRole("tooltip")).toHaveTextContent(
|
||||
Warning.args.message,
|
||||
),
|
||||
);
|
||||
});
|
||||
},
|
||||
|
||||
@@ -3,9 +3,9 @@ import {
|
||||
HelpTooltip,
|
||||
HelpTooltipContent,
|
||||
HelpTooltipIcon,
|
||||
HelpTooltipIconTrigger,
|
||||
HelpTooltipText,
|
||||
HelpTooltipTitle,
|
||||
HelpTooltipTrigger,
|
||||
} from "components/HelpTooltip/HelpTooltip";
|
||||
import type { FC, ReactNode } from "react";
|
||||
import type { ThemeRole } from "theme/roles";
|
||||
@@ -26,9 +26,9 @@ export const InfoTooltip: FC<InfoTooltipProps> = ({
|
||||
|
||||
return (
|
||||
<HelpTooltip>
|
||||
<HelpTooltipTrigger size="small" css={styles.button}>
|
||||
<HelpTooltipIconTrigger size="small" css={styles.button}>
|
||||
<HelpTooltipIcon css={{ color: iconColor }} />
|
||||
</HelpTooltipTrigger>
|
||||
</HelpTooltipIconTrigger>
|
||||
<HelpTooltipContent>
|
||||
<HelpTooltipTitle>{title}</HelpTooltipTitle>
|
||||
<HelpTooltipText>{message}</HelpTooltipText>
|
||||
@@ -39,10 +39,10 @@ export const InfoTooltip: FC<InfoTooltipProps> = ({
|
||||
|
||||
const styles = {
|
||||
button: css`
|
||||
opacity: 1;
|
||||
opacity: 1;
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
`,
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
`,
|
||||
} satisfies Record<string, Interpolation<Theme>>;
|
||||
|
||||
@@ -8,15 +8,21 @@ 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 type TooltipContentProps = React.ComponentPropsWithoutRef<
|
||||
typeof TooltipPrimitive.Content
|
||||
> & {
|
||||
disablePortal?: boolean;
|
||||
};
|
||||
|
||||
export const TooltipContent = React.forwardRef<
|
||||
React.ElementRef<typeof TooltipPrimitive.Content>,
|
||||
React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content> & {
|
||||
disablePortal?: boolean;
|
||||
}
|
||||
TooltipContentProps
|
||||
>(({ className, sideOffset = 4, disablePortal, ...props }, ref) => {
|
||||
const content = (
|
||||
<TooltipPrimitive.Content
|
||||
|
||||
@@ -60,7 +60,7 @@ type ControlledPopoverProps = BasePopoverProps & {
|
||||
onOpenChange: (open: boolean) => void;
|
||||
};
|
||||
|
||||
export type PopoverProps = UncontrolledPopoverProps | ControlledPopoverProps;
|
||||
type PopoverProps = UncontrolledPopoverProps | ControlledPopoverProps;
|
||||
|
||||
/** @deprecated prefer `components.Popover` */
|
||||
export const Popover: FC<PopoverProps> = (props) => {
|
||||
@@ -155,7 +155,7 @@ export const PopoverTrigger: FC<PopoverTriggerProps> = (props) => {
|
||||
|
||||
type Horizontal = "left" | "right";
|
||||
|
||||
export type PopoverContentProps = Omit<
|
||||
type PopoverContentProps = Omit<
|
||||
MuiPopoverProps,
|
||||
"open" | "onClose" | "anchorEl"
|
||||
> & {
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { type Theme, useTheme } from "@emotion/react";
|
||||
import type { DERPRegion, WorkspaceAgent } from "api/typesGenerated";
|
||||
import { PopoverTrigger } from "components/deprecated/Popover/Popover";
|
||||
import {
|
||||
HelpTooltip,
|
||||
HelpTooltipContent,
|
||||
HelpTooltipText,
|
||||
HelpTooltipTitle,
|
||||
HelpTooltipTrigger,
|
||||
} from "components/HelpTooltip/HelpTooltip";
|
||||
import { Stack } from "components/Stack/Stack";
|
||||
import type { FC } from "react";
|
||||
@@ -44,7 +44,7 @@ export const AgentLatency: FC<AgentLatencyProps> = ({ agent }) => {
|
||||
|
||||
return (
|
||||
<HelpTooltip>
|
||||
<PopoverTrigger>
|
||||
<HelpTooltipTrigger asChild>
|
||||
<span
|
||||
role="presentation"
|
||||
aria-label="latency"
|
||||
@@ -52,35 +52,33 @@ export const AgentLatency: FC<AgentLatencyProps> = ({ agent }) => {
|
||||
>
|
||||
{Math.round(latency.latency_ms)}ms
|
||||
</span>
|
||||
</PopoverTrigger>
|
||||
</HelpTooltipTrigger>
|
||||
<HelpTooltipContent>
|
||||
<HelpTooltipTitle>Latency</HelpTooltipTitle>
|
||||
<HelpTooltipText>
|
||||
This is the latency overhead on non peer to peer connections. The
|
||||
first row is the preferred relay.
|
||||
</HelpTooltipText>
|
||||
<HelpTooltipText>
|
||||
<Stack direction="column" spacing={1} css={{ marginTop: 16 }}>
|
||||
{Object.entries(agent.latency)
|
||||
.sort(([, a], [, b]) => a.latency_ms - b.latency_ms)
|
||||
.map(([regionName, region]) => (
|
||||
<Stack
|
||||
direction="row"
|
||||
key={regionName}
|
||||
spacing={0.5}
|
||||
justifyContent="space-between"
|
||||
css={
|
||||
region.preferred && {
|
||||
color: theme.palette.text.primary,
|
||||
}
|
||||
<Stack direction="column" spacing={1} css={{ marginTop: 16 }}>
|
||||
{Object.entries(agent.latency)
|
||||
.sort(([, a], [, b]) => a.latency_ms - b.latency_ms)
|
||||
.map(([regionName, region]) => (
|
||||
<Stack
|
||||
direction="row"
|
||||
key={regionName}
|
||||
spacing={0.5}
|
||||
justifyContent="space-between"
|
||||
css={
|
||||
region.preferred && {
|
||||
color: theme.palette.text.primary,
|
||||
}
|
||||
>
|
||||
<strong>{regionName}</strong>
|
||||
{Math.round(region.latency_ms)}ms
|
||||
</Stack>
|
||||
))}
|
||||
</Stack>
|
||||
</HelpTooltipText>
|
||||
}
|
||||
>
|
||||
<strong>{regionName}</strong>
|
||||
{Math.round(region.latency_ms)}ms
|
||||
</Stack>
|
||||
))}
|
||||
</Stack>
|
||||
</HelpTooltipContent>
|
||||
</HelpTooltip>
|
||||
);
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import type { WorkspaceAgent } from "api/typesGenerated";
|
||||
import { PopoverTrigger } from "components/deprecated/Popover/Popover";
|
||||
import {
|
||||
HelpTooltip,
|
||||
HelpTooltipAction,
|
||||
@@ -7,10 +6,11 @@ import {
|
||||
HelpTooltipLinksGroup,
|
||||
HelpTooltipText,
|
||||
HelpTooltipTitle,
|
||||
HelpTooltipTrigger,
|
||||
} from "components/HelpTooltip/HelpTooltip";
|
||||
import { Stack } from "components/Stack/Stack";
|
||||
import { RotateCcwIcon } from "lucide-react";
|
||||
import type { FC } from "react";
|
||||
import { type FC, useState } from "react";
|
||||
import { agentVersionStatus } from "../../utils/workspace";
|
||||
|
||||
type AgentOutdatedTooltipProps = {
|
||||
@@ -26,6 +26,8 @@ export const AgentOutdatedTooltip: FC<AgentOutdatedTooltipProps> = ({
|
||||
status,
|
||||
onUpdate,
|
||||
}) => {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
|
||||
const title =
|
||||
status === agentVersionStatus.Outdated
|
||||
? "Agent Outdated"
|
||||
@@ -37,12 +39,12 @@ 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>
|
||||
<PopoverTrigger>
|
||||
<HelpTooltip open={isOpen} onOpenChange={setIsOpen}>
|
||||
<HelpTooltipTrigger asChild>
|
||||
<span role="status" className="cursor-pointer">
|
||||
{status === agentVersionStatus.Outdated ? "Outdated" : "Deprecated"}
|
||||
</span>
|
||||
</PopoverTrigger>
|
||||
</HelpTooltipTrigger>
|
||||
<HelpTooltipContent>
|
||||
<Stack spacing={1}>
|
||||
<div>
|
||||
@@ -67,7 +69,10 @@ export const AgentOutdatedTooltip: FC<AgentOutdatedTooltipProps> = ({
|
||||
<HelpTooltipLinksGroup>
|
||||
<HelpTooltipAction
|
||||
icon={RotateCcwIcon}
|
||||
onClick={onUpdate}
|
||||
onClick={() => {
|
||||
onUpdate();
|
||||
setIsOpen(false);
|
||||
}}
|
||||
ariaLabel="Update workspace"
|
||||
>
|
||||
Update workspace
|
||||
|
||||
@@ -6,12 +6,12 @@ import type {
|
||||
WorkspaceAgentDevcontainer,
|
||||
} from "api/typesGenerated";
|
||||
import { ChooseOne, Cond } from "components/Conditionals/ChooseOne";
|
||||
import { PopoverTrigger } from "components/deprecated/Popover/Popover";
|
||||
import {
|
||||
HelpTooltip,
|
||||
HelpTooltipContent,
|
||||
HelpTooltipText,
|
||||
HelpTooltipTitle,
|
||||
HelpTooltipTrigger,
|
||||
} from "components/HelpTooltip/HelpTooltip";
|
||||
import { TriangleAlertIcon } from "lucide-react";
|
||||
import type { FC } from "react";
|
||||
@@ -62,9 +62,9 @@ interface DevcontainerStatusProps {
|
||||
const StartTimeoutLifecycle: FC<AgentStatusProps> = ({ agent }) => {
|
||||
return (
|
||||
<HelpTooltip>
|
||||
<PopoverTrigger role="status" aria-label="Agent timeout">
|
||||
<HelpTooltipTrigger asChild role="status" aria-label="Agent timeout">
|
||||
<TriangleAlertIcon css={styles.timeoutWarning} />
|
||||
</PopoverTrigger>
|
||||
</HelpTooltipTrigger>
|
||||
|
||||
<HelpTooltipContent>
|
||||
<HelpTooltipTitle>Agent is taking too long to start</HelpTooltipTitle>
|
||||
@@ -87,9 +87,9 @@ const StartTimeoutLifecycle: FC<AgentStatusProps> = ({ agent }) => {
|
||||
const StartErrorLifecycle: FC<AgentStatusProps> = ({ agent }) => {
|
||||
return (
|
||||
<HelpTooltip>
|
||||
<PopoverTrigger role="status" aria-label="Start error">
|
||||
<HelpTooltipTrigger asChild role="status" aria-label="Start error">
|
||||
<TriangleAlertIcon css={styles.errorWarning} />
|
||||
</PopoverTrigger>
|
||||
</HelpTooltipTrigger>
|
||||
<HelpTooltipContent>
|
||||
<HelpTooltipTitle>Error starting the agent</HelpTooltipTitle>
|
||||
<HelpTooltipText>
|
||||
@@ -123,9 +123,9 @@ const ShuttingDownLifecycle: FC = () => {
|
||||
const ShutdownTimeoutLifecycle: FC<AgentStatusProps> = ({ agent }) => {
|
||||
return (
|
||||
<HelpTooltip>
|
||||
<PopoverTrigger role="status" aria-label="Stop timeout">
|
||||
<HelpTooltipTrigger asChild role="status" aria-label="Stop timeout">
|
||||
<TriangleAlertIcon css={styles.timeoutWarning} />
|
||||
</PopoverTrigger>
|
||||
</HelpTooltipTrigger>
|
||||
<HelpTooltipContent>
|
||||
<HelpTooltipTitle>Agent is taking too long to stop</HelpTooltipTitle>
|
||||
<HelpTooltipText>
|
||||
@@ -147,9 +147,9 @@ const ShutdownTimeoutLifecycle: FC<AgentStatusProps> = ({ agent }) => {
|
||||
const ShutdownErrorLifecycle: FC<AgentStatusProps> = ({ agent }) => {
|
||||
return (
|
||||
<HelpTooltip>
|
||||
<PopoverTrigger role="status" aria-label="Stop error">
|
||||
<HelpTooltipTrigger asChild role="status" aria-label="Stop error">
|
||||
<TriangleAlertIcon css={styles.errorWarning} />
|
||||
</PopoverTrigger>
|
||||
</HelpTooltipTrigger>
|
||||
<HelpTooltipContent>
|
||||
<HelpTooltipTitle>Error stopping the agent</HelpTooltipTitle>
|
||||
<HelpTooltipText>
|
||||
@@ -243,9 +243,9 @@ const ConnectingStatus: FC = () => {
|
||||
const TimeoutStatus: FC<AgentStatusProps> = ({ agent }) => {
|
||||
return (
|
||||
<HelpTooltip>
|
||||
<PopoverTrigger role="status" aria-label="Timeout">
|
||||
<HelpTooltipTrigger asChild role="status" aria-label="Timeout">
|
||||
<TriangleAlertIcon css={styles.timeoutWarning} />
|
||||
</PopoverTrigger>
|
||||
</HelpTooltipTrigger>
|
||||
<HelpTooltipContent>
|
||||
<HelpTooltipTitle>Agent is taking too long to connect</HelpTooltipTitle>
|
||||
<HelpTooltipText>
|
||||
@@ -308,9 +308,9 @@ const SubAgentStatus: FC<SubAgentStatusProps> = ({ agent }) => {
|
||||
const DevcontainerStartError: FC<AgentStatusProps> = ({ agent }) => {
|
||||
return (
|
||||
<HelpTooltip>
|
||||
<PopoverTrigger role="status" aria-label="Start error">
|
||||
<HelpTooltipTrigger asChild role="status" aria-label="Start error">
|
||||
<TriangleAlertIcon css={styles.errorWarning} />
|
||||
</PopoverTrigger>
|
||||
</HelpTooltipTrigger>
|
||||
<HelpTooltipContent>
|
||||
<HelpTooltipTitle>
|
||||
Error starting the devcontainer agent
|
||||
|
||||
@@ -9,9 +9,9 @@ import {
|
||||
HelpTooltipLinksGroup,
|
||||
HelpTooltipText,
|
||||
HelpTooltipTitle,
|
||||
HelpTooltipTrigger,
|
||||
} from "components/HelpTooltip/HelpTooltip";
|
||||
import { Stack } from "components/Stack/Stack";
|
||||
import { TooltipTrigger } from "components/Tooltip/Tooltip";
|
||||
import { RotateCcwIcon } from "lucide-react";
|
||||
import type { FC } from "react";
|
||||
|
||||
@@ -39,11 +39,11 @@ export const SubAgentOutdatedTooltip: FC<SubAgentOutdatedTooltipProps> = ({
|
||||
|
||||
return (
|
||||
<HelpTooltip>
|
||||
<HelpTooltipTrigger>
|
||||
<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>
|
||||
</HelpTooltipTrigger>
|
||||
</TooltipTrigger>
|
||||
<HelpTooltipContent>
|
||||
<Stack spacing={1}>
|
||||
<div>
|
||||
|
||||
+4
-2
@@ -5,7 +5,7 @@ import {
|
||||
} from "testHelpers/entities";
|
||||
import { withDashboardProvider } from "testHelpers/storybook";
|
||||
import type { Meta, StoryObj } from "@storybook/react-vite";
|
||||
import { expect, userEvent, waitFor, within } from "storybook/test";
|
||||
import { expect, screen, userEvent, waitFor, within } from "storybook/test";
|
||||
import { WorkspaceOutdatedTooltip } from "./WorkspaceOutdatedTooltip";
|
||||
|
||||
const meta: Meta<typeof WorkspaceOutdatedTooltip> = {
|
||||
@@ -39,7 +39,9 @@ const Example: Story = {
|
||||
await step("activate hover trigger", async () => {
|
||||
await userEvent.hover(body.getByRole("button"));
|
||||
await waitFor(() =>
|
||||
expect(body.getByText(MockTemplateVersion.message)).toBeInTheDocument(),
|
||||
expect(screen.getByRole("tooltip")).toHaveTextContent(
|
||||
MockTemplateVersion.message,
|
||||
),
|
||||
);
|
||||
});
|
||||
},
|
||||
|
||||
@@ -4,49 +4,56 @@ import Skeleton from "@mui/material/Skeleton";
|
||||
import { getErrorDetail, getErrorMessage } from "api/errors";
|
||||
import { templateVersion } from "api/queries/templates";
|
||||
import type { Workspace } from "api/typesGenerated";
|
||||
import { usePopover } from "components/deprecated/Popover/Popover";
|
||||
import { displayError } from "components/GlobalSnackbar/utils";
|
||||
import {
|
||||
HelpTooltip,
|
||||
HelpTooltipAction,
|
||||
HelpTooltipContent,
|
||||
HelpTooltipIconTrigger,
|
||||
HelpTooltipLinksGroup,
|
||||
HelpTooltipText,
|
||||
HelpTooltipTitle,
|
||||
HelpTooltipTrigger,
|
||||
} from "components/HelpTooltip/HelpTooltip";
|
||||
import { InfoIcon, RotateCcwIcon } from "lucide-react";
|
||||
import { linkToTemplate, useLinks } from "modules/navigation";
|
||||
import type { FC } from "react";
|
||||
import { type FC, useState } from "react";
|
||||
import { useQuery } from "react-query";
|
||||
import {
|
||||
useWorkspaceUpdate,
|
||||
WorkspaceUpdateDialogs,
|
||||
} from "../WorkspaceUpdateDialogs";
|
||||
|
||||
interface TooltipProps {
|
||||
interface WorkspaceOutdatedTooltipProps {
|
||||
workspace: Workspace;
|
||||
}
|
||||
|
||||
export const WorkspaceOutdatedTooltip: FC<TooltipProps> = (props) => {
|
||||
export const WorkspaceOutdatedTooltip: FC<WorkspaceOutdatedTooltipProps> = (
|
||||
props,
|
||||
) => {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<HelpTooltip>
|
||||
<HelpTooltipTrigger size="small" hoverEffect={false}>
|
||||
<HelpTooltip open={isOpen} onOpenChange={setIsOpen}>
|
||||
<HelpTooltipIconTrigger size="small" hoverEffect={false}>
|
||||
<InfoIcon css={styles.icon} />
|
||||
<span className="sr-only">Outdated info</span>
|
||||
</HelpTooltipTrigger>
|
||||
<WorkspaceOutdatedTooltipContent {...props} />
|
||||
</HelpTooltipIconTrigger>
|
||||
<WorkspaceOutdatedTooltipContent isOpen={isOpen} {...props} />
|
||||
</HelpTooltip>
|
||||
);
|
||||
};
|
||||
|
||||
const WorkspaceOutdatedTooltipContent: FC<TooltipProps> = ({ workspace }) => {
|
||||
type TooltipContentProps = WorkspaceOutdatedTooltipProps & { isOpen: boolean };
|
||||
|
||||
const WorkspaceOutdatedTooltipContent: FC<TooltipContentProps> = ({
|
||||
workspace,
|
||||
isOpen,
|
||||
}) => {
|
||||
const getLink = useLinks();
|
||||
const theme = useTheme();
|
||||
const popover = usePopover();
|
||||
const { data: activeVersion } = useQuery({
|
||||
...templateVersion(workspace.template_active_version_id),
|
||||
enabled: popover.open,
|
||||
enabled: isOpen,
|
||||
});
|
||||
const updateWorkspace = useWorkspaceUpdate({
|
||||
workspace,
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import {
|
||||
HelpTooltip,
|
||||
HelpTooltipContent,
|
||||
HelpTooltipIconTrigger,
|
||||
HelpTooltipLink,
|
||||
HelpTooltipLinksGroup,
|
||||
HelpTooltipText,
|
||||
HelpTooltipTitle,
|
||||
HelpTooltipTrigger,
|
||||
} from "components/HelpTooltip/HelpTooltip";
|
||||
import type { FC } from "react";
|
||||
import { docs } from "utils/docs";
|
||||
@@ -19,7 +19,7 @@ const Language = {
|
||||
export const AuditHelpTooltip: FC = () => {
|
||||
return (
|
||||
<HelpTooltip>
|
||||
<HelpTooltipTrigger />
|
||||
<HelpTooltipIconTrigger />
|
||||
|
||||
<HelpTooltipContent>
|
||||
<HelpTooltipTitle>{Language.title}</HelpTooltipTitle>
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import {
|
||||
HelpTooltip,
|
||||
HelpTooltipContent,
|
||||
HelpTooltipIconTrigger,
|
||||
HelpTooltipLink,
|
||||
HelpTooltipLinksGroup,
|
||||
HelpTooltipText,
|
||||
HelpTooltipTitle,
|
||||
HelpTooltipTrigger,
|
||||
} from "components/HelpTooltip/HelpTooltip";
|
||||
import type { FC } from "react";
|
||||
import { docs } from "utils/docs";
|
||||
@@ -19,7 +19,7 @@ const Language = {
|
||||
export const ConnectionLogHelpTooltip: FC = () => {
|
||||
return (
|
||||
<HelpTooltip>
|
||||
<HelpTooltipTrigger />
|
||||
<HelpTooltipIconTrigger />
|
||||
|
||||
<HelpTooltipContent>
|
||||
<HelpTooltipTitle>{Language.title}</HelpTooltipTitle>
|
||||
|
||||
@@ -18,8 +18,8 @@ import { EmptyState } from "components/EmptyState/EmptyState";
|
||||
import {
|
||||
HelpTooltip,
|
||||
HelpTooltipContent,
|
||||
HelpTooltipIconTrigger,
|
||||
HelpTooltipText,
|
||||
HelpTooltipTrigger,
|
||||
} from "components/HelpTooltip/HelpTooltip";
|
||||
import { Input } from "components/Input/Input";
|
||||
import { Label } from "components/Label/Label";
|
||||
@@ -459,7 +459,7 @@ const OrganizationRow: FC<OrganizationRowProps> = ({
|
||||
const AssignDefaultOrgHelpTooltip: FC = () => {
|
||||
return (
|
||||
<HelpTooltip>
|
||||
<HelpTooltipTrigger />
|
||||
<HelpTooltipIconTrigger />
|
||||
<HelpTooltipContent>
|
||||
<HelpTooltipText>
|
||||
Disabling will remove all users from the default organization if a
|
||||
|
||||
@@ -8,9 +8,9 @@ import { Combobox } from "components/Combobox/Combobox";
|
||||
import {
|
||||
HelpTooltip,
|
||||
HelpTooltipContent,
|
||||
HelpTooltipIconTrigger,
|
||||
HelpTooltipText,
|
||||
HelpTooltipTitle,
|
||||
HelpTooltipTrigger,
|
||||
} from "components/HelpTooltip/HelpTooltip";
|
||||
import { Input } from "components/Input/Input";
|
||||
import { Label } from "components/Label/Label";
|
||||
@@ -414,7 +414,7 @@ const GroupRow: FC<GroupRowProps> = ({
|
||||
const AutoCreateMissingGroupsHelpTooltip: FC = () => {
|
||||
return (
|
||||
<HelpTooltip>
|
||||
<HelpTooltipTrigger />
|
||||
<HelpTooltipIconTrigger />
|
||||
<HelpTooltipContent>
|
||||
<HelpTooltipText>
|
||||
Enabling auto create missing groups will automatically create groups
|
||||
@@ -431,7 +431,7 @@ const LegacyGroupSyncHeader: FC = () => {
|
||||
<div className="flex items-end gap-2">
|
||||
<span>Legacy group sync settings</span>
|
||||
<HelpTooltip>
|
||||
<HelpTooltipTrigger />
|
||||
<HelpTooltipIconTrigger />
|
||||
<HelpTooltipContent>
|
||||
<HelpTooltipTitle>Legacy group sync settings</HelpTooltipTitle>
|
||||
<HelpTooltipText>
|
||||
|
||||
@@ -43,6 +43,17 @@ export const Loading: Story = {
|
||||
},
|
||||
};
|
||||
|
||||
export const CannotSetRoles: Story = {
|
||||
args: {
|
||||
userLoginType: "oidc",
|
||||
oidcRoleSync: true,
|
||||
},
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
await userEvent.hover(canvas.getByLabelText("More info"));
|
||||
},
|
||||
};
|
||||
|
||||
export const AdvancedOpen: Story = {
|
||||
args: {
|
||||
selectedRoleNames: new Set([MockWorkspaceCreationBanRole.name]),
|
||||
|
||||
@@ -11,9 +11,9 @@ import {
|
||||
import {
|
||||
HelpTooltip,
|
||||
HelpTooltipContent,
|
||||
HelpTooltipIconTrigger,
|
||||
HelpTooltipText,
|
||||
HelpTooltipTitle,
|
||||
HelpTooltipTrigger,
|
||||
} from "components/HelpTooltip/HelpTooltip";
|
||||
import { EditSquare } from "components/Icons/EditSquare";
|
||||
import { UserIcon } from "lucide-react";
|
||||
@@ -83,7 +83,7 @@ export const EditRolesButton: FC<EditRolesButtonProps> = (props) => {
|
||||
if (!canSetRoles) {
|
||||
return (
|
||||
<HelpTooltip>
|
||||
<HelpTooltipTrigger size="small" />
|
||||
<HelpTooltipIconTrigger size="small" />
|
||||
<HelpTooltipContent>
|
||||
<HelpTooltipTitle>Externally controlled</HelpTooltipTitle>
|
||||
<HelpTooltipText>
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import {
|
||||
HelpTooltip,
|
||||
HelpTooltipContent,
|
||||
HelpTooltipIconTrigger,
|
||||
HelpTooltipLink,
|
||||
HelpTooltipLinksGroup,
|
||||
HelpTooltipText,
|
||||
HelpTooltipTitle,
|
||||
HelpTooltipTrigger,
|
||||
} from "components/HelpTooltip/HelpTooltip";
|
||||
import type { FC } from "react";
|
||||
import { docs } from "utils/docs";
|
||||
@@ -45,7 +45,7 @@ export const TableColumnHelpTooltip: FC<Props> = ({ variant }) => {
|
||||
|
||||
return (
|
||||
<HelpTooltip>
|
||||
<HelpTooltipTrigger size="small" />
|
||||
<HelpTooltipIconTrigger size="small" />
|
||||
<HelpTooltipContent>
|
||||
<HelpTooltipTitle>{variantLang.title}</HelpTooltipTitle>
|
||||
<HelpTooltipText>{variantLang.text}</HelpTooltipText>
|
||||
|
||||
@@ -29,9 +29,9 @@ import { Avatar } from "components/Avatar/Avatar";
|
||||
import {
|
||||
HelpTooltip,
|
||||
HelpTooltipContent,
|
||||
HelpTooltipIconTrigger,
|
||||
HelpTooltipText,
|
||||
HelpTooltipTitle,
|
||||
HelpTooltipTrigger,
|
||||
} from "components/HelpTooltip/HelpTooltip";
|
||||
import { Loader } from "components/Loader/Loader";
|
||||
import { Stack } from "components/Stack/Stack";
|
||||
@@ -284,7 +284,7 @@ const UsersLatencyPanel: FC<UsersLatencyPanelProps> = ({
|
||||
<PanelTitle css={{ display: "flex", alignItems: "center", gap: 8 }}>
|
||||
Latency by user
|
||||
<HelpTooltip>
|
||||
<HelpTooltipTrigger size="small" />
|
||||
<HelpTooltipIconTrigger size="small" />
|
||||
<HelpTooltipContent>
|
||||
<HelpTooltipTitle>How is latency calculated?</HelpTooltipTitle>
|
||||
<HelpTooltipText>
|
||||
@@ -352,7 +352,7 @@ const UsersActivityPanel: FC<UsersActivityPanelProps> = ({
|
||||
<PanelTitle css={{ display: "flex", alignItems: "center", gap: 8 }}>
|
||||
Activity by user
|
||||
<HelpTooltip>
|
||||
<HelpTooltipTrigger size="small" />
|
||||
<HelpTooltipIconTrigger size="small" />
|
||||
<HelpTooltipContent>
|
||||
<HelpTooltipTitle>How is activity calculated?</HelpTooltipTitle>
|
||||
<HelpTooltipText>
|
||||
|
||||
@@ -13,11 +13,11 @@ import * as Yup from "yup";
|
||||
import {
|
||||
HelpTooltip,
|
||||
HelpTooltipContent,
|
||||
HelpTooltipIconTrigger,
|
||||
HelpTooltipLink,
|
||||
HelpTooltipLinksGroup,
|
||||
HelpTooltipText,
|
||||
HelpTooltipTitle,
|
||||
HelpTooltipTrigger,
|
||||
} from "../../components/HelpTooltip/HelpTooltip";
|
||||
import { docs } from "../../utils/docs";
|
||||
|
||||
@@ -121,9 +121,13 @@ export const PublishTemplateVersionDialog: FC<
|
||||
/>
|
||||
|
||||
<HelpTooltip>
|
||||
<HelpTooltipTrigger />
|
||||
<HelpTooltipIconTrigger />
|
||||
|
||||
<HelpTooltipContent>
|
||||
{/**
|
||||
* 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>
|
||||
{Language.activeVersionHelpTitle}
|
||||
</HelpTooltipTitle>
|
||||
|
||||
@@ -157,6 +157,12 @@ export const WithError = {
|
||||
},
|
||||
};
|
||||
|
||||
export const PublishDialog = {
|
||||
args: {
|
||||
isAskingPublishParameters: true,
|
||||
},
|
||||
};
|
||||
|
||||
export const Published = {
|
||||
args: {
|
||||
publishedVersion: MockTemplateVersion,
|
||||
|
||||
@@ -12,11 +12,11 @@ import { Button } from "components/Button/Button";
|
||||
import {
|
||||
HelpTooltip,
|
||||
HelpTooltipContent,
|
||||
HelpTooltipIconTrigger,
|
||||
HelpTooltipLink,
|
||||
HelpTooltipLinksGroup,
|
||||
HelpTooltipText,
|
||||
HelpTooltipTitle,
|
||||
HelpTooltipTrigger,
|
||||
} from "components/HelpTooltip/HelpTooltip";
|
||||
import { Margins } from "components/Margins/Margins";
|
||||
import {
|
||||
@@ -72,7 +72,7 @@ const Language = {
|
||||
const TemplateHelpTooltip: FC = () => {
|
||||
return (
|
||||
<HelpTooltip>
|
||||
<HelpTooltipTrigger />
|
||||
<HelpTooltipIconTrigger />
|
||||
<HelpTooltipContent>
|
||||
<HelpTooltipTitle>{Language.templateTooltipTitle}</HelpTooltipTitle>
|
||||
<HelpTooltipText>{Language.templateTooltipText}</HelpTooltipText>
|
||||
|
||||
@@ -320,9 +320,9 @@ export const TemplateInfoPopover: Story = {
|
||||
await step("activate hover trigger", async () => {
|
||||
await userEvent.hover(canvas.getByText(baseWorkspace.name));
|
||||
await waitFor(() =>
|
||||
expect(
|
||||
canvas.getByRole("presentation", { hidden: true }),
|
||||
).toHaveTextContent(MockTemplate.display_name),
|
||||
expect(screen.getByRole("tooltip")).toHaveTextContent(
|
||||
MockTemplate.display_name,
|
||||
),
|
||||
);
|
||||
});
|
||||
},
|
||||
@@ -346,9 +346,9 @@ export const TemplateInfoPopoverWithoutDisplayName: Story = {
|
||||
await step("activate hover trigger", async () => {
|
||||
await userEvent.hover(canvas.getByText(baseWorkspace.name));
|
||||
await waitFor(() =>
|
||||
expect(
|
||||
canvas.getByRole("presentation", { hidden: true }),
|
||||
).toHaveTextContent(MockTemplate.name),
|
||||
expect(screen.getByRole("tooltip")).toHaveTextContent(
|
||||
MockTemplate.name,
|
||||
),
|
||||
);
|
||||
});
|
||||
},
|
||||
|
||||
@@ -6,7 +6,6 @@ import type * as TypesGen from "api/typesGenerated";
|
||||
import { Avatar } from "components/Avatar/Avatar";
|
||||
import { AvatarData } from "components/Avatar/AvatarData";
|
||||
import { CopyButton } from "components/CopyButton/CopyButton";
|
||||
import { Popover, PopoverTrigger } from "components/deprecated/Popover/Popover";
|
||||
import {
|
||||
Topbar,
|
||||
TopbarAvatar,
|
||||
@@ -15,7 +14,11 @@ import {
|
||||
TopbarIcon,
|
||||
TopbarIconButton,
|
||||
} from "components/FullPageLayout/Topbar";
|
||||
import { HelpTooltipContent } from "components/HelpTooltip/HelpTooltip";
|
||||
import {
|
||||
HelpTooltip,
|
||||
HelpTooltipContent,
|
||||
HelpTooltipTrigger,
|
||||
} from "components/HelpTooltip/HelpTooltip";
|
||||
import { ChevronLeftIcon, CircleDollarSign, TrashIcon } from "lucide-react";
|
||||
import { useDashboard } from "modules/dashboard/useDashboard";
|
||||
import { linkToTemplate, useLinks } from "modules/navigation";
|
||||
@@ -253,21 +256,18 @@ const OwnerBreadcrumb: FC<OwnerBreadcrumbProps> = ({
|
||||
ownerAvatarUrl,
|
||||
}) => {
|
||||
return (
|
||||
<Popover mode="hover">
|
||||
<PopoverTrigger>
|
||||
<HelpTooltip>
|
||||
<HelpTooltipTrigger asChild>
|
||||
<span css={styles.breadcrumbSegment}>
|
||||
<Avatar size="sm" fallback={ownerName} src={ownerAvatarUrl} />
|
||||
<span css={styles.breadcrumbText}>{ownerName}</span>
|
||||
</span>
|
||||
</PopoverTrigger>
|
||||
</HelpTooltipTrigger>
|
||||
|
||||
<HelpTooltipContent
|
||||
anchorOrigin={{ vertical: "bottom", horizontal: "center" }}
|
||||
transformOrigin={{ vertical: "top", horizontal: "center" }}
|
||||
>
|
||||
<HelpTooltipContent align="center">
|
||||
<AvatarData title={ownerName} subtitle="Owner" src={ownerAvatarUrl} />
|
||||
</HelpTooltipContent>
|
||||
</Popover>
|
||||
</HelpTooltip>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -283,8 +283,8 @@ const OrganizationBreadcrumb: FC<OrganizationBreadcrumbProps> = ({
|
||||
orgIconUrl,
|
||||
}) => {
|
||||
return (
|
||||
<Popover mode="hover">
|
||||
<PopoverTrigger>
|
||||
<HelpTooltip>
|
||||
<HelpTooltipTrigger asChild>
|
||||
<span css={styles.breadcrumbSegment}>
|
||||
<Avatar
|
||||
size="sm"
|
||||
@@ -294,12 +294,9 @@ const OrganizationBreadcrumb: FC<OrganizationBreadcrumbProps> = ({
|
||||
/>
|
||||
<span css={styles.breadcrumbText}>{orgName}</span>
|
||||
</span>
|
||||
</PopoverTrigger>
|
||||
</HelpTooltipTrigger>
|
||||
|
||||
<HelpTooltipContent
|
||||
anchorOrigin={{ vertical: "bottom", horizontal: "center" }}
|
||||
transformOrigin={{ vertical: "top", horizontal: "center" }}
|
||||
>
|
||||
<HelpTooltipContent align="center">
|
||||
<AvatarData
|
||||
title={
|
||||
orgPageUrl ? (
|
||||
@@ -323,7 +320,7 @@ const OrganizationBreadcrumb: FC<OrganizationBreadcrumbProps> = ({
|
||||
imgFallbackText={orgName}
|
||||
/>
|
||||
</HelpTooltipContent>
|
||||
</Popover>
|
||||
</HelpTooltip>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -346,8 +343,8 @@ const WorkspaceBreadcrumb: FC<WorkspaceBreadcrumbProps> = ({
|
||||
}) => {
|
||||
return (
|
||||
<div className="flex items-center">
|
||||
<Popover mode="hover">
|
||||
<PopoverTrigger>
|
||||
<HelpTooltip>
|
||||
<HelpTooltipTrigger asChild>
|
||||
<span css={styles.breadcrumbSegment}>
|
||||
<TopbarAvatar
|
||||
src={templateIconUrl}
|
||||
@@ -358,12 +355,9 @@ const WorkspaceBreadcrumb: FC<WorkspaceBreadcrumbProps> = ({
|
||||
{workspaceName}
|
||||
</span>
|
||||
</span>
|
||||
</PopoverTrigger>
|
||||
</HelpTooltipTrigger>
|
||||
|
||||
<HelpTooltipContent
|
||||
anchorOrigin={{ vertical: "bottom", horizontal: "center" }}
|
||||
transformOrigin={{ vertical: "top", horizontal: "center" }}
|
||||
>
|
||||
<HelpTooltipContent align="center">
|
||||
<AvatarData
|
||||
title={
|
||||
<Link
|
||||
@@ -393,7 +387,7 @@ const WorkspaceBreadcrumb: FC<WorkspaceBreadcrumbProps> = ({
|
||||
imgFallbackText={templateDisplayName}
|
||||
/>
|
||||
</HelpTooltipContent>
|
||||
</Popover>
|
||||
</HelpTooltip>
|
||||
<CopyButton text={workspaceName} label="Copy workspace name" />
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import {
|
||||
HelpTooltip,
|
||||
HelpTooltipContent,
|
||||
HelpTooltipIconTrigger,
|
||||
HelpTooltipLink,
|
||||
HelpTooltipLinksGroup,
|
||||
HelpTooltipText,
|
||||
HelpTooltipTitle,
|
||||
HelpTooltipTrigger,
|
||||
} from "components/HelpTooltip/HelpTooltip";
|
||||
import type { FC } from "react";
|
||||
import { docs } from "utils/docs";
|
||||
@@ -22,7 +22,7 @@ const Language = {
|
||||
export const WorkspaceHelpTooltip: FC = () => {
|
||||
return (
|
||||
<HelpTooltip>
|
||||
<HelpTooltipTrigger />
|
||||
<HelpTooltipIconTrigger />
|
||||
<HelpTooltipContent>
|
||||
<HelpTooltipTitle>{Language.workspaceTooltipTitle}</HelpTooltipTitle>
|
||||
<HelpTooltipText>{Language.workspaceTooltipText}</HelpTooltipText>
|
||||
|
||||
Reference in New Issue
Block a user