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

Lines changed: 4 additions & 30 deletions
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
};
Lines changed: 35 additions & 68 deletions
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

Lines changed: 5 additions & 1 deletion
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

Lines changed: 0 additions & 10 deletions
This file was deleted.

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

Lines changed: 2 additions & 10 deletions
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

Lines changed: 49 additions & 41 deletions
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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy