Skip to content

Commit 300ad87

Browse files
refactor: replace and remove deprecated Avatar component (#15930)
Close #14997
1 parent 137dc6e commit 300ad87

File tree

72 files changed

+461
-915
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

72 files changed

+461
-915
lines changed

site/e2e/tests/deployment/workspaceProxies.spec.ts

Lines changed: 14 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,12 @@ test("default proxy is online", async ({ page }) => {
2525
`table.MuiTable-root tr[data-testid="primary"]`,
2626
);
2727

28-
const workspaceProxyName = workspaceProxyPrimary.locator("td.name span");
29-
const workspaceProxyURL = workspaceProxyPrimary.locator("td.url");
30-
const workspaceProxyStatus = workspaceProxyPrimary.locator("td.status span");
28+
const summary = workspaceProxyPrimary.locator(".summary");
29+
const status = workspaceProxyPrimary.locator(".status");
3130

32-
await expect(workspaceProxyName).toHaveText("Default");
33-
await expect(workspaceProxyURL).toHaveText(`http://localhost:${coderPort}`);
34-
await expect(workspaceProxyStatus).toHaveText("Healthy");
31+
await expect(summary).toContainText("Default");
32+
await expect(summary).toContainText(`http://localhost:${coderPort}`);
33+
await expect(status).toContainText("Healthy");
3534
});
3635

3736
test("custom proxy is online", async ({ page }) => {
@@ -57,19 +56,16 @@ test("custom proxy is online", async ({ page }) => {
5756
waitUntil: "domcontentloaded",
5857
});
5958

60-
const workspaceProxy = page.locator("table.MuiTable-root tr", {
59+
const proxyRow = page.locator("table.MuiTable-root tr", {
6160
hasText: proxyName,
6261
});
6362

64-
const workspaceProxyName = workspaceProxy.locator("td.name span");
65-
const workspaceProxyURL = workspaceProxy.locator("td.url");
66-
const workspaceProxyStatus = workspaceProxy.locator("td.status span");
63+
const summary = proxyRow.locator(".summary");
64+
const status = proxyRow.locator(".status");
6765

68-
await expect(workspaceProxyName).toHaveText(proxyName);
69-
await expect(workspaceProxyURL).toHaveText(
70-
`http://127.0.0.1:${workspaceProxyPort}`,
71-
);
72-
await expect(workspaceProxyStatus).toHaveText("Healthy");
66+
await expect(summary).toContainText(proxyName);
67+
await expect(summary).toContainText(`http://127.0.0.1:${workspaceProxyPort}`);
68+
await expect(status).toContainText("Healthy");
7369

7470
// Tear down the proxy
7571
await stopWorkspaceProxy(proxyServer);
@@ -89,13 +85,13 @@ const waitUntilWorkspaceProxyIsHealthy = async (
8985
while (retries < maxRetries) {
9086
await page.reload();
9187

92-
const workspaceProxy = page.locator("table.MuiTable-root tr", {
88+
const proxyRow = page.locator("table.MuiTable-root tr", {
9389
hasText: proxyName,
9490
});
95-
const workspaceProxyStatus = workspaceProxy.locator("td.status span");
91+
const status = proxyRow.locator(".status");
9692

9793
try {
98-
await expect(workspaceProxyStatus).toHaveText("Healthy", {
94+
await expect(status).toContainText("Healthy", {
9995
timeout: 1_000,
10096
});
10197
return; // healthy!
Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import type { Meta, StoryObj } from "@storybook/react";
2-
import { Avatar, AvatarFallback, AvatarImage } from "./Avatar";
2+
import { Avatar } from "./Avatar";
33

44
const meta: Meta<typeof Avatar> = {
55
title: "components/Avatar",
66
component: Avatar,
77
args: {
8-
children: <AvatarImage src="https://github.com/kylecarbs.png" />,
8+
src: "https://github.com/kylecarbs.png",
99
},
1010
};
1111

@@ -16,7 +16,7 @@ export const ImageLgSize: Story = {
1616
args: { size: "lg" },
1717
};
1818

19-
export const ImageDefaultSize: Story = {};
19+
export const ImageMdSize: Story = {};
2020

2121
export const ImageSmSize: Story = {
2222
args: { size: "sm" },
@@ -26,48 +26,51 @@ export const IconLgSize: Story = {
2626
args: {
2727
size: "lg",
2828
variant: "icon",
29-
children: (
30-
<AvatarImage src="https://em-content.zobj.net/source/apple/391/billed-cap_1f9e2.png" />
31-
),
29+
src: "https://em-content.zobj.net/source/apple/391/billed-cap_1f9e2.png",
3230
},
3331
};
3432

35-
export const IconDefaultSize: Story = {
33+
export const IconMdSize: Story = {
3634
args: {
3735
variant: "icon",
38-
children: (
39-
<AvatarImage src="https://em-content.zobj.net/source/apple/391/billed-cap_1f9e2.png" />
40-
),
36+
src: "https://em-content.zobj.net/source/apple/391/billed-cap_1f9e2.png",
4137
},
4238
};
4339

4440
export const IconSmSize: Story = {
4541
args: {
4642
variant: "icon",
4743
size: "sm",
48-
children: (
49-
<AvatarImage src="https://em-content.zobj.net/source/apple/391/billed-cap_1f9e2.png" />
50-
),
44+
src: "https://em-content.zobj.net/source/apple/391/billed-cap_1f9e2.png",
45+
},
46+
};
47+
48+
export const NonSquaredIcon: Story = {
49+
args: {
50+
variant: "icon",
51+
src: "/icon/docker.png",
5152
},
5253
};
5354

5455
export const FallbackLgSize: Story = {
5556
args: {
57+
src: "",
5658
size: "lg",
57-
58-
children: <AvatarFallback>AR</AvatarFallback>,
59+
fallback: "Adriana Rodrigues",
5960
},
6061
};
6162

62-
export const FallbackDefaultSize: Story = {
63+
export const FallbackMdSize: Story = {
6364
args: {
64-
children: <AvatarFallback>AR</AvatarFallback>,
65+
src: "",
66+
fallback: "Adriana Rodrigues",
6567
},
6668
};
6769

6870
export const FallbackSmSize: Story = {
6971
args: {
72+
src: "",
7073
size: "sm",
71-
children: <AvatarFallback>AR</AvatarFallback>,
74+
fallback: "Adriana Rodrigues",
7275
},
7376
};

site/src/components/Avatar/Avatar.tsx

Lines changed: 48 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,94 +1,92 @@
1-
import * as AvatarPrimitive from "@radix-ui/react-avatar";
2-
import { type VariantProps, cva } from "class-variance-authority";
31
/**
42
* Copied from shadc/ui on 12/16/2024
53
* @see {@link https://ui.shadcn.com/docs/components/avatar}
64
*
75
* This component was updated to support the variants and match the styles from
86
* the Figma design:
97
* @see {@link https://www.figma.com/design/WfqIgsTFXN2BscBSSyXWF8/Coder-kit?node-id=711-383&t=xqxOSUk48GvDsjGK-0}
8+
*
9+
* It was also simplified to make usage easier and reduce boilerplate.
10+
* @see {@link https://github.com/coder/coder/pull/15930#issuecomment-2552292440}
1011
*/
12+
13+
import { useTheme } from "@emotion/react";
14+
import * as AvatarPrimitive from "@radix-ui/react-avatar";
15+
import { type VariantProps, cva } from "class-variance-authority";
1116
import * as React from "react";
17+
import { getExternalImageStylesFromUrl } from "theme/externalImages";
1218
import { cn } from "utils/cn";
1319

1420
const avatarVariants = cva(
1521
"relative flex shrink-0 overflow-hidden rounded border border-solid bg-surface-secondary text-content-secondary",
1622
{
1723
variants: {
1824
size: {
19-
lg: "h-10 w-10 rounded-[6px] text-sm font-medium",
20-
default: "h-6 w-6 text-2xs",
21-
sm: "h-[18px] w-[18px] text-[8px]",
25+
lg: "h-[--avatar-lg] w-[--avatar-lg] rounded-[6px] text-sm font-medium",
26+
md: "h-[--avatar-default] w-[--avatar-default] text-2xs",
27+
sm: "h-[--avatar-sm] w-[--avatar-sm] text-[8px]",
2228
},
2329
variant: {
24-
default: "",
25-
icon: "",
30+
default: null,
31+
icon: null,
2632
},
2733
},
2834
defaultVariants: {
29-
size: "default",
35+
size: "md",
3036
},
3137
compoundVariants: [
3238
{
3339
size: "lg",
3440
variant: "icon",
35-
className: "p-[9px]",
41+
className: "p-2",
3642
},
3743
{
38-
size: "default",
44+
size: "md",
3945
variant: "icon",
40-
className: "p-[3px]",
46+
className: "p-1",
4147
},
4248
{
4349
size: "sm",
4450
variant: "icon",
45-
className: "p-[2px]",
51+
className: "p-[3px]",
4652
},
4753
],
4854
},
4955
);
5056

51-
export interface AvatarProps
52-
extends React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Root>,
53-
VariantProps<typeof avatarVariants> {}
57+
export type AvatarProps = AvatarPrimitive.AvatarProps &
58+
VariantProps<typeof avatarVariants> & {
59+
src?: string;
60+
61+
fallback?: string;
62+
};
5463

5564
const Avatar = React.forwardRef<
5665
React.ElementRef<typeof AvatarPrimitive.Root>,
5766
AvatarProps
58-
>(({ className, size, variant, ...props }, ref) => (
59-
<AvatarPrimitive.Root
60-
ref={ref}
61-
className={cn(avatarVariants({ size, variant, className }))}
62-
{...props}
63-
/>
64-
));
65-
Avatar.displayName = AvatarPrimitive.Root.displayName;
66-
67-
const AvatarImage = React.forwardRef<
68-
React.ElementRef<typeof AvatarPrimitive.Image>,
69-
React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Image>
70-
>(({ className, ...props }, ref) => (
71-
<AvatarPrimitive.Image
72-
ref={ref}
73-
className={cn("aspect-square h-full w-full", className)}
74-
{...props}
75-
/>
76-
));
77-
AvatarImage.displayName = AvatarPrimitive.Image.displayName;
67+
>(({ className, size, variant, src, fallback, children, ...props }, ref) => {
68+
const theme = useTheme();
7869

79-
const AvatarFallback = React.forwardRef<
80-
React.ElementRef<typeof AvatarPrimitive.Fallback>,
81-
React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Fallback>
82-
>(({ className, ...props }, ref) => (
83-
<AvatarPrimitive.Fallback
84-
ref={ref}
85-
className={cn(
86-
"flex h-full w-full items-center justify-center rounded-full",
87-
className,
88-
)}
89-
{...props}
90-
/>
91-
));
92-
AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName;
70+
return (
71+
<AvatarPrimitive.Root
72+
ref={ref}
73+
className={cn(avatarVariants({ size, variant, className }))}
74+
{...props}
75+
>
76+
<AvatarPrimitive.Image
77+
src={src}
78+
className="aspect-square h-full w-full object-contain"
79+
css={getExternalImageStylesFromUrl(theme.externalImages, src)}
80+
/>
81+
{fallback && (
82+
<AvatarPrimitive.Fallback className="flex h-full w-full items-center justify-center rounded-full">
83+
{fallback.charAt(0).toUpperCase()}
84+
</AvatarPrimitive.Fallback>
85+
)}
86+
{children}
87+
</AvatarPrimitive.Root>
88+
);
89+
});
90+
Avatar.displayName = AvatarPrimitive.Root.displayName;
9391

94-
export { Avatar, AvatarImage, AvatarFallback };
92+
export { Avatar };

site/src/components/AvatarCard/AvatarCard.stories.tsx renamed to site/src/components/Avatar/AvatarCard.stories.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ export const WithImage: Story = {
1313
args: {
1414
header: "Coder",
1515
imgUrl: "https://avatars.githubusercontent.com/u/95932066?s=200&v=4",
16-
altText: "Coder",
1716
subtitle: "56 members",
1817
},
1918
};

site/src/components/AvatarCard/AvatarCard.tsx renamed to site/src/components/Avatar/AvatarCard.tsx

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,17 @@
11
import { type CSSObject, useTheme } from "@emotion/react";
2-
import { Avatar } from "components/deprecated/Avatar/Avatar";
2+
import { Avatar } from "components/Avatar/Avatar";
33
import type { FC, ReactNode } from "react";
44

55
type AvatarCardProps = {
66
header: string;
77
imgUrl: string;
8-
altText: string;
9-
background?: boolean;
10-
118
subtitle?: ReactNode;
129
maxWidth?: number | "none";
1310
};
1411

1512
export const AvatarCard: FC<AvatarCardProps> = ({
1613
header,
1714
imgUrl,
18-
altText,
19-
background,
2015
subtitle,
2116
maxWidth = "none",
2217
}) => {
@@ -72,9 +67,7 @@ export const AvatarCard: FC<AvatarCardProps> = ({
7267
)}
7368
</div>
7469

75-
<Avatar background={background} src={imgUrl} alt={altText} size="md">
76-
{header}
77-
</Avatar>
70+
<Avatar size="lg" src={imgUrl} fallback={header} />
7871
</div>
7972
);
8073
};

site/src/components/AvatarData/AvatarData.tsx renamed to site/src/components/Avatar/AvatarData.tsx

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { useTheme } from "@emotion/react";
2+
import { Avatar } from "components/Avatar/Avatar";
23
import { Stack } from "components/Stack/Stack";
3-
import { Avatar } from "components/deprecated/Avatar/Avatar";
44
import type { FC, ReactNode } from "react";
55

66
export interface AvatarDataProps {
@@ -29,17 +29,17 @@ export const AvatarData: FC<AvatarDataProps> = ({
2929
const theme = useTheme();
3030
if (!avatar) {
3131
avatar = (
32-
<Avatar background src={src}>
33-
{(typeof title === "string" ? title : imgFallbackText) || "-"}
34-
</Avatar>
32+
<Avatar
33+
src={src}
34+
fallback={(typeof title === "string" ? title : imgFallbackText) || "-"}
35+
/>
3536
);
3637
}
3738

3839
return (
3940
<Stack
40-
spacing={1.5}
41+
spacing={1}
4142
direction="row"
42-
alignItems="center"
4343
css={{
4444
minHeight: 40, // Make it predictable for the skeleton
4545
width: "100%",

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