Skip to content

Commit 2c49fd9

Browse files
feat: add copy button for workspace name in breadcrumb (#17822)
Co-authored-by: BrunoQuaresma <bruno_nonato_quaresma@hotmail.com>
1 parent bbceebd commit 2c49fd9

File tree

6 files changed

+95
-160
lines changed

6 files changed

+95
-160
lines changed

site/src/components/CodeExample/CodeExample.tsx

+4-30
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import type { Interpolation, Theme } from "@emotion/react";
2-
import { visuallyHidden } from "@mui/utils";
3-
import { type FC, type KeyboardEvent, type MouseEvent, useRef } from "react";
2+
import type { FC } from "react";
43
import { MONOSPACE_FONT_FAMILY } from "theme/constants";
54
import { CopyButton } from "../CopyButton/CopyButton";
65

@@ -21,33 +20,8 @@ export const CodeExample: FC<CodeExampleProps> = ({
2120
// the secure option, not remember to opt in
2221
secret = true,
2322
}) => {
24-
const buttonRef = useRef<HTMLButtonElement>(null);
25-
const triggerButton = (event: KeyboardEvent | MouseEvent) => {
26-
const clickTriggeredOutsideButton =
27-
event.target instanceof HTMLElement &&
28-
!buttonRef.current?.contains(event.target);
29-
30-
if (clickTriggeredOutsideButton) {
31-
buttonRef.current?.click();
32-
}
33-
};
34-
3523
return (
36-
<div
37-
css={styles.container}
38-
className={className}
39-
onClick={triggerButton}
40-
onKeyDown={(event) => {
41-
if (event.key === "Enter") {
42-
triggerButton(event);
43-
}
44-
}}
45-
onKeyUp={(event) => {
46-
if (event.key === " ") {
47-
triggerButton(event);
48-
}
49-
}}
50-
>
24+
<div css={styles.container} className={className}>
5125
<code css={[styles.code, secret && styles.secret]}>
5226
{secret ? (
5327
<>
@@ -60,7 +34,7 @@ export const CodeExample: FC<CodeExampleProps> = ({
6034
* readily available in the HTML itself
6135
*/}
6236
<span aria-hidden>{obfuscateText(code)}</span>
63-
<span css={{ ...visuallyHidden }}>
37+
<span className="sr-only">
6438
Encrypted text. Please access via the copy button.
6539
</span>
6640
</>
@@ -69,7 +43,7 @@ export const CodeExample: FC<CodeExampleProps> = ({
6943
)}
7044
</code>
7145

72-
<CopyButton ref={buttonRef} text={code} />
46+
<CopyButton text={code} label="Copy code" />
7347
</div>
7448
);
7549
};
+35-68
Original file line numberDiff line numberDiff line change
@@ -1,77 +1,44 @@
1-
import { type Interpolation, type Theme, css } from "@emotion/react";
2-
import IconButton from "@mui/material/Button";
3-
import Tooltip from "@mui/material/Tooltip";
1+
import { Button, type ButtonProps } from "components/Button/Button";
2+
import {
3+
Tooltip,
4+
TooltipContent,
5+
TooltipProvider,
6+
TooltipTrigger,
7+
} from "components/Tooltip/Tooltip";
48
import { useClipboard } from "hooks/useClipboard";
5-
import { CheckIcon } from "lucide-react";
6-
import { type ReactNode, forwardRef } from "react";
7-
import { FileCopyIcon } from "../Icons/FileCopyIcon";
9+
import { CheckIcon, CopyIcon } from "lucide-react";
10+
import type { FC } from "react";
811

9-
interface CopyButtonProps {
10-
children?: ReactNode;
12+
type CopyButtonProps = ButtonProps & {
1113
text: string;
12-
ctaCopy?: string;
13-
wrapperStyles?: Interpolation<Theme>;
14-
buttonStyles?: Interpolation<Theme>;
15-
tooltipTitle?: string;
16-
}
17-
18-
const Language = {
19-
tooltipTitle: "Copy to clipboard",
20-
ariaLabel: "Copy to clipboard",
14+
label: string;
2115
};
2216

23-
/**
24-
* Copy button used inside the CodeBlock component internally
25-
*/
26-
export const CopyButton = forwardRef<HTMLButtonElement, CopyButtonProps>(
27-
(props, ref) => {
28-
const {
29-
text,
30-
ctaCopy,
31-
wrapperStyles,
32-
buttonStyles,
33-
tooltipTitle = Language.tooltipTitle,
34-
} = props;
35-
const { showCopiedSuccess, copyToClipboard } = useClipboard({
36-
textToCopy: text,
37-
});
17+
export const CopyButton: FC<CopyButtonProps> = ({
18+
text,
19+
label,
20+
...buttonProps
21+
}) => {
22+
const { showCopiedSuccess, copyToClipboard } = useClipboard({
23+
textToCopy: text,
24+
});
3825

39-
return (
40-
<Tooltip title={tooltipTitle} placement="top">
41-
<div css={[{ display: "flex" }, wrapperStyles]}>
42-
<IconButton
43-
ref={ref}
44-
css={[styles.button, buttonStyles]}
45-
size="small"
46-
aria-label={Language.ariaLabel}
47-
variant="text"
26+
return (
27+
<TooltipProvider>
28+
<Tooltip>
29+
<TooltipTrigger asChild>
30+
<Button
31+
size="icon"
32+
variant="subtle"
4833
onClick={copyToClipboard}
34+
{...buttonProps}
4935
>
50-
{showCopiedSuccess ? (
51-
<CheckIcon css={styles.copyIcon} />
52-
) : (
53-
<FileCopyIcon css={styles.copyIcon} />
54-
)}
55-
{ctaCopy && <div css={{ marginLeft: 8 }}>{ctaCopy}</div>}
56-
</IconButton>
57-
</div>
36+
{showCopiedSuccess ? <CheckIcon /> : <CopyIcon />}
37+
<span className="sr-only">{label}</span>
38+
</Button>
39+
</TooltipTrigger>
40+
<TooltipContent>{label}</TooltipContent>
5841
</Tooltip>
59-
);
60-
},
61-
);
62-
63-
const styles = {
64-
button: (theme) => css`
65-
border-radius: 8px;
66-
padding: 8px;
67-
min-width: 32px;
68-
69-
&:hover {
70-
background: ${theme.palette.background.paper};
71-
}
72-
`,
73-
copyIcon: css`
74-
width: 20px;
75-
height: 20px;
76-
`,
77-
} satisfies Record<string, Interpolation<Theme>>;
42+
</TooltipProvider>
43+
);
44+
};

site/src/components/GitDeviceAuth/GitDeviceAuth.tsx

+5-1
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,11 @@ export const GitDeviceAuth: FC<GitDeviceAuthProps> = ({
134134
Copy your one-time code:&nbsp;
135135
<div css={styles.copyCode}>
136136
<span css={styles.code}>{externalAuthDevice.user_code}</span>
137-
&nbsp; <CopyButton text={externalAuthDevice.user_code} />
137+
&nbsp;{" "}
138+
<CopyButton
139+
text={externalAuthDevice.user_code}
140+
label="Copy user code"
141+
/>
138142
</div>
139143
<br />
140144
Then open the link below and paste it:

site/src/components/Icons/FileCopyIcon.tsx

-10
This file was deleted.

site/src/modules/dashboard/Navbar/UserDropdown/UserDropdownContent.tsx

+2-10
Original file line numberDiff line numberDiff line change
@@ -151,15 +151,7 @@ export const UserDropdownContent: FC<UserDropdownContentProps> = ({
151151
</Tooltip>
152152
<CopyButton
153153
text={buildInfo.deployment_id}
154-
buttonStyles={css`
155-
width: 16px;
156-
height: 16px;
157-
158-
svg {
159-
width: 16px;
160-
height: 16px;
161-
}
162-
`}
154+
label="Copy deployment ID"
163155
/>
164156
</div>
165157
)}
@@ -181,7 +173,7 @@ const GithubStar: FC<SvgIconProps> = (props) => (
181173
fill="currentColor"
182174
{...props}
183175
>
184-
<path d="M8 .25a.75.75 0 0 1 .673.418l1.882 3.815 4.21.612a.75.75 0 0 1 .416 1.279l-3.046 2.97.719 4.192a.751.751 0 0 1-1.088.791L8 12.347l-3.766 1.98a.75.75 0 0 1-1.088-.79l.72-4.194L.818 6.374a.75.75 0 0 1 .416-1.28l4.21-.611L7.327.668A.75.75 0 0 1 8 .25Z"></path>
176+
<path d="M8 .25a.75.75 0 0 1 .673.418l1.882 3.815 4.21.612a.75.75 0 0 1 .416 1.279l-3.046 2.97.719 4.192a.751.751 0 0 1-1.088.791L8 12.347l-3.766 1.98a.75.75 0 0 1-1.088-.79l.72-4.194L.818 6.374a.75.75 0 0 1 .416-1.28l4.21-.611L7.327.668A.75.75 0 0 1 8 .25Z" />
185177
</svg>
186178
);
187179

site/src/pages/WorkspacePage/WorkspaceTopbar.tsx

+49-41
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { workspaceQuota } from "api/queries/workspaceQuota";
55
import type * as TypesGen from "api/typesGenerated";
66
import { Avatar } from "components/Avatar/Avatar";
77
import { AvatarData } from "components/Avatar/AvatarData";
8+
import { CopyButton } from "components/CopyButton/CopyButton";
89
import {
910
Topbar,
1011
TopbarAvatar,
@@ -346,50 +347,57 @@ const WorkspaceBreadcrumb: FC<WorkspaceBreadcrumbProps> = ({
346347
templateDisplayName,
347348
}) => {
348349
return (
349-
<Popover mode="hover">
350-
<PopoverTrigger>
351-
<span css={styles.breadcrumbSegment}>
352-
<TopbarAvatar src={templateIconUrl} fallback={templateDisplayName} />
353-
<span css={[styles.breadcrumbText, { fontWeight: 500 }]}>
354-
{workspaceName}
355-
</span>
356-
</span>
357-
</PopoverTrigger>
358-
359-
<HelpTooltipContent
360-
anchorOrigin={{ vertical: "bottom", horizontal: "center" }}
361-
transformOrigin={{ vertical: "top", horizontal: "center" }}
362-
>
363-
<AvatarData
364-
title={
365-
<Link
366-
component={RouterLink}
367-
to={rootTemplateUrl}
368-
css={{ color: "inherit" }}
369-
>
370-
{templateDisplayName}
371-
</Link>
372-
}
373-
subtitle={
374-
<Link
375-
component={RouterLink}
376-
to={`${rootTemplateUrl}/versions/${encodeURIComponent(templateVersionName)}`}
377-
css={{ color: "inherit" }}
378-
>
379-
Version: {latestBuildVersionName}
380-
</Link>
381-
}
382-
avatar={
383-
<Avatar
384-
variant="icon"
350+
<div className="flex items-center">
351+
<Popover mode="hover">
352+
<PopoverTrigger>
353+
<span css={styles.breadcrumbSegment}>
354+
<TopbarAvatar
385355
src={templateIconUrl}
386356
fallback={templateDisplayName}
387357
/>
388-
}
389-
imgFallbackText={templateDisplayName}
390-
/>
391-
</HelpTooltipContent>
392-
</Popover>
358+
359+
<span css={[styles.breadcrumbText, { fontWeight: 500 }]}>
360+
{workspaceName}
361+
</span>
362+
</span>
363+
</PopoverTrigger>
364+
365+
<HelpTooltipContent
366+
anchorOrigin={{ vertical: "bottom", horizontal: "center" }}
367+
transformOrigin={{ vertical: "top", horizontal: "center" }}
368+
>
369+
<AvatarData
370+
title={
371+
<Link
372+
component={RouterLink}
373+
to={rootTemplateUrl}
374+
css={{ color: "inherit" }}
375+
>
376+
{templateDisplayName}
377+
</Link>
378+
}
379+
subtitle={
380+
<Link
381+
component={RouterLink}
382+
to={`${rootTemplateUrl}/versions/${encodeURIComponent(templateVersionName)}`}
383+
css={{ color: "inherit" }}
384+
>
385+
Version: {latestBuildVersionName}
386+
</Link>
387+
}
388+
avatar={
389+
<Avatar
390+
variant="icon"
391+
src={templateIconUrl}
392+
fallback={templateDisplayName}
393+
/>
394+
}
395+
imgFallbackText={templateDisplayName}
396+
/>
397+
</HelpTooltipContent>
398+
</Popover>
399+
<CopyButton text={workspaceName} label="Copy workspace name" />
400+
</div>
393401
);
394402
};
395403

0 commit comments

Comments
 (0)