Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 8a3a79f

Browse files
refactor: match StatusIndicator component with the new designs (#16458)
Reference: https://www.figma.com/design/WfqIgsTFXN2BscBSSyXWF8/Coder-kit?node-id=489-4278&m=dev
1 parent 15d5563 commit 8a3a79f

File tree

10 files changed

+177
-76
lines changed

10 files changed

+177
-76
lines changed
Original file line numberDiff line numberDiff line change
@@ -1,63 +1,55 @@
11
import type { Meta, StoryObj } from "@storybook/react";
2-
import { StatusIndicator } from "./StatusIndicator";
2+
import { StatusIndicator, StatusIndicatorDot } from "./StatusIndicator";
33

44
const meta: Meta<typeof StatusIndicator> = {
55
title: "components/StatusIndicator",
66
component: StatusIndicator,
7-
args: {},
7+
args: {
8+
children: (
9+
<>
10+
<StatusIndicatorDot />
11+
Status
12+
</>
13+
),
14+
},
815
};
916

1017
export default meta;
1118
type Story = StoryObj<typeof StatusIndicator>;
1219

1320
export const Success: Story = {
1421
args: {
15-
color: "success",
16-
},
17-
};
18-
19-
export const SuccessOutline: Story = {
20-
args: {
21-
color: "success",
22-
variant: "outlined",
23-
},
24-
};
25-
26-
export const Warning: Story = {
27-
args: {
28-
color: "warning",
22+
variant: "success",
2923
},
3024
};
3125

32-
export const WarningOutline: Story = {
26+
export const Failed: Story = {
3327
args: {
34-
color: "warning",
35-
variant: "outlined",
28+
variant: "failed",
3629
},
3730
};
3831

39-
export const Danger: Story = {
32+
export const Inactive: Story = {
4033
args: {
41-
color: "danger",
34+
variant: "inactive",
4235
},
4336
};
4437

45-
export const DangerOutline: Story = {
38+
export const Warning: Story = {
4639
args: {
47-
color: "danger",
48-
variant: "outlined",
40+
variant: "warning",
4941
},
5042
};
5143

52-
export const Inactive: Story = {
44+
export const Pending: Story = {
5345
args: {
54-
color: "inactive",
46+
variant: "pending",
5547
},
5648
};
5749

58-
export const InactiveOutline: Story = {
50+
export const Small: Story = {
5951
args: {
60-
color: "inactive",
61-
variant: "outlined",
52+
variant: "success",
53+
size: "sm",
6254
},
6355
};
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,97 @@
1-
import { useTheme } from "@emotion/react";
2-
import type { FC } from "react";
3-
import type { ThemeRole } from "theme/roles";
1+
import { type VariantProps, cva } from "class-variance-authority";
2+
import { type FC, createContext, useContext } from "react";
3+
import { cn } from "utils/cn";
44

5-
interface StatusIndicatorProps {
6-
color: ThemeRole;
7-
variant?: "solid" | "outlined";
8-
}
5+
const statusIndicatorVariants = cva(
6+
"font-medium inline-flex items-center gap-2",
7+
{
8+
variants: {
9+
variant: {
10+
success: "text-content-success",
11+
failed: "text-content-destructive",
12+
inactive: "text-highlight-grey",
13+
warning: "text-content-warning",
14+
pending: "text-highlight-sky",
15+
},
16+
size: {
17+
sm: "text-xs",
18+
md: "text-sm",
19+
},
20+
},
21+
defaultVariants: {
22+
variant: "success",
23+
size: "md",
24+
},
25+
},
26+
);
27+
28+
type StatusIndicatorContextValue = VariantProps<typeof statusIndicatorVariants>;
29+
30+
const StatusIndicatorContext = createContext<StatusIndicatorContextValue>({});
31+
32+
export interface StatusIndicatorProps
33+
extends React.HTMLAttributes<HTMLDivElement>,
34+
StatusIndicatorContextValue {}
935

1036
export const StatusIndicator: FC<StatusIndicatorProps> = ({
11-
color,
12-
variant = "solid",
37+
size,
38+
variant,
39+
className,
40+
...props
1341
}) => {
14-
const theme = useTheme();
42+
return (
43+
<StatusIndicatorContext.Provider value={{ size, variant }}>
44+
<div
45+
className={cn(statusIndicatorVariants({ variant, size }), className)}
46+
{...props}
47+
/>
48+
</StatusIndicatorContext.Provider>
49+
);
50+
};
51+
52+
const dotVariants = cva("rounded-full inline-block border-4 border-solid", {
53+
variants: {
54+
variant: {
55+
success: "bg-content-success border-surface-green",
56+
failed: "bg-content-destructive border-surface-destructive",
57+
inactive: "bg-highlight-grey border-surface-grey",
58+
warning: "bg-content-warning border-surface-orange",
59+
pending: "bg-highlight-sky border-surface-sky",
60+
},
61+
size: {
62+
sm: "size-3 border-4",
63+
md: "size-4 border-4",
64+
},
65+
},
66+
defaultVariants: {
67+
variant: "success",
68+
size: "md",
69+
},
70+
});
71+
72+
export interface StatusIndicatorDotProps
73+
extends React.HTMLAttributes<HTMLDivElement>,
74+
VariantProps<typeof dotVariants> {}
75+
76+
export const StatusIndicatorDot: FC<StatusIndicatorDotProps> = ({
77+
className,
78+
// We allow the size and variant to be overridden directly by the component.
79+
// This allows StatusIndicatorDot to be used alone.
80+
size,
81+
variant,
82+
...props
83+
}) => {
84+
const { size: ctxSize, variant: ctxVariant } = useContext(
85+
StatusIndicatorContext,
86+
);
1587

1688
return (
1789
<div
18-
css={[
19-
{
20-
height: 8,
21-
width: 8,
22-
borderRadius: 4,
23-
},
24-
variant === "solid" && {
25-
backgroundColor: theme.roles[color].fill.solid,
26-
},
27-
variant === "outlined" && {
28-
border: `1px solid ${theme.roles[color].outline}`,
29-
},
30-
]}
90+
className={cn(
91+
dotVariants({ variant: variant ?? ctxVariant, size: size ?? ctxSize }),
92+
className,
93+
)}
94+
{...props}
3195
/>
3296
);
3397
};

site/src/index.css

+15-3
Original file line numberDiff line numberDiff line change
@@ -15,22 +15,28 @@
1515
--content-invert: 0 0% 98%;
1616
--content-disabled: 240 5% 65%;
1717
--content-success: 142 72% 29%;
18-
--content-danger: 0 84% 60%;
1918
--content-warning: 27 96% 61%;
19+
--content-destructive: 0 84% 60%;
2020
--surface-primary: 0 0% 98%;
2121
--surface-secondary: 240 5% 96%;
2222
--surface-tertiary: 240 6% 90%;
2323
--surface-quaternary: 240 5% 84%;
2424
--surface-invert-primary: 240 4% 16%;
2525
--surface-invert-secondary: 240 5% 26%;
2626
--surface-destructive: 0 93% 94%;
27+
--surface-green: 141 79% 85%;
28+
--surface-grey: 240 5% 96%;
29+
--surface-orange: 34 100% 92%;
30+
--surface-sky: 201 94% 86%;
2731
--border-default: 240 6% 90%;
2832
--border-success: 142 76% 36%;
2933
--border-destructive: 0 84% 60%;
3034
--overlay-default: 240 5% 84% / 80%;
3135
--radius: 0.5rem;
3236
--highlight-purple: 262 83% 58%;
3337
--highlight-green: 143 64% 24%;
38+
--highlight-grey: 240 5% 65%;
39+
--highlight-sky: 201 90% 27%;
3440
--border: 240 5.9% 90%;
3541
--input: 240 5.9% 90%;
3642
--ring: 240 10% 3.9%;
@@ -45,21 +51,27 @@
4551
--content-invert: 240 10% 4%;
4652
--content-disabled: 240 5% 26%;
4753
--content-success: 142 76% 36%;
48-
--content-danger: 0 91% 71%;
49-
--content-warning: 27 96% 61%;
54+
--content-warning: 31 97% 72%;
55+
--content-destructive: 0 91% 71%;
5056
--surface-primary: 240 10% 4%;
5157
--surface-secondary: 240 6% 10%;
5258
--surface-tertiary: 240 4% 16%;
5359
--surface-quaternary: 240 5% 26%;
5460
--surface-invert-primary: 240 6% 90%;
5561
--surface-invert-secondary: 240 5% 65%;
5662
--surface-destructive: 0 75% 15%;
63+
--surface-green: 145 80% 10%;
64+
--surface-grey: 240 6% 10%;
65+
--surface-orange: 13 81% 15%;
66+
--surface-sky: 204 80% 16%;
5767
--border-default: 240 4% 16%;
5868
--border-success: 142 76% 36%;
5969
--border-destructive: 0 91% 71%;
6070
--overlay-default: 240 10% 4% / 80%;
6171
--highlight-purple: 252 95% 85%;
6272
--highlight-green: 141 79% 85%;
73+
--highlight-grey: 240 4% 46%;
74+
--highlight-sky: 198 93% 60%;
6375
--border: 240 3.7% 15.9%;
6476
--input: 240 3.7% 15.9%;
6577
--ring: 240 4.9% 83.9%;

site/src/modules/provisioners/ProvisionerGroup.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import {
1616
} from "components/HelpTooltip/HelpTooltip";
1717
import { Pill } from "components/Pill/Pill";
1818
import { Stack } from "components/Stack/Stack";
19-
import { StatusIndicator } from "components/StatusIndicator/StatusIndicator";
19+
import { StatusIndicatorDot } from "components/StatusIndicator/StatusIndicator";
2020
import {
2121
Popover,
2222
PopoverContent,
@@ -127,7 +127,7 @@ export const ProvisionerGroup: FC<ProvisionerGroupProps> = ({
127127
}}
128128
>
129129
<div css={{ display: "flex", alignItems: "center", gap: 16 }}>
130-
<StatusIndicator color={hasWarning ? "warning" : "success"} />
130+
<StatusIndicatorDot variant={hasWarning ? "warning" : "success"} />
131131
<div
132132
css={{
133133
display: "flex",

site/src/pages/CreateTokenPage/CreateTokenForm.tsx

+1
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ export const CreateTokenForm: FC<CreateTokenFormProps> = ({
8080
</FormFields>
8181
</FormSection>
8282
<FormSection
83+
data-chromatic="ignore"
8384
title="Expiration"
8485
description={
8586
form.values.lifetime

site/src/pages/CreateTokenPage/CreateTokenPage.stories.tsx

-6
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,6 @@ const meta: Meta<typeof CreateTokenPage> = {
1212
},
1313
],
1414
},
15-
decorators: [
16-
(Story) => {
17-
Date.now = () => new Date("01/01/2014").getTime();
18-
return <Story />;
19-
},
20-
],
2115
};
2216

2317
export default meta;

site/src/pages/UsersPage/UsersFilter.tsx

+7-4
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,10 @@ import {
77
type UseFilterMenuOptions,
88
useFilterMenu,
99
} from "components/Filter/menu";
10-
import { StatusIndicator } from "components/StatusIndicator/StatusIndicator";
10+
import {
11+
StatusIndicator,
12+
StatusIndicatorDot,
13+
} from "components/StatusIndicator/StatusIndicator";
1114
import type { FC } from "react";
1215
import { docs } from "utils/docs";
1316

@@ -24,17 +27,17 @@ export const useStatusFilterMenu = ({
2427
{
2528
value: "active",
2629
label: "Active",
27-
startIcon: <StatusIndicator color="success" />,
30+
startIcon: <StatusIndicatorDot variant="success" />,
2831
},
2932
{
3033
value: "dormant",
3134
label: "Dormant",
32-
startIcon: <StatusIndicator color="warning" />,
35+
startIcon: <StatusIndicatorDot variant="warning" />,
3336
},
3437
{
3538
value: "suspended",
3639
label: "Suspended",
37-
startIcon: <StatusIndicator color="inactive" />,
40+
startIcon: <StatusIndicatorDot variant="inactive" />,
3841
},
3942
];
4043
return useFilterMenu({

site/src/pages/WorkspacesPage/LastUsed.tsx

+6-6
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { useTheme } from "@emotion/react";
22
import { Stack } from "components/Stack/Stack";
3-
import { StatusIndicator } from "components/StatusIndicator/StatusIndicator";
3+
import { StatusIndicatorDot } from "components/StatusIndicator/StatusIndicator";
44
import dayjs from "dayjs";
55
import relativeTime from "dayjs/plugin/relativeTime";
66
import { useTime } from "hooks/useTime";
@@ -18,19 +18,19 @@ export const LastUsed: FC<LastUsedProps> = ({ lastUsedAt }) => {
1818
const t = dayjs(lastUsedAt);
1919
const now = dayjs();
2020
let message = t.fromNow();
21-
let circle = <StatusIndicator color="info" variant="outlined" />;
21+
let circle = <StatusIndicatorDot variant="inactive" />;
2222

2323
if (t.isAfter(now.subtract(1, "hour"))) {
24-
circle = <StatusIndicator color="success" />;
24+
circle = <StatusIndicatorDot variant="success" />;
2525
// Since the agent reports on a 10m interval,
2626
// the last_used_at can be inaccurate when recent.
2727
message = "Now";
2828
} else if (t.isAfter(now.subtract(3, "day"))) {
29-
circle = <StatusIndicator color="info" />;
29+
circle = <StatusIndicatorDot variant="pending" />;
3030
} else if (t.isAfter(now.subtract(1, "month"))) {
31-
circle = <StatusIndicator color="warning" />;
31+
circle = <StatusIndicatorDot variant="warning" />;
3232
} else if (t.isAfter(now.subtract(100, "year"))) {
33-
circle = <StatusIndicator color="error" />;
33+
circle = <StatusIndicatorDot variant="failed" />;
3434
} else {
3535
message = "Never";
3636
}

0 commit comments

Comments
 (0)