mirror of
https://github.com/coder/coder.git
synced 2026-06-02 20:48:20 +00:00
fix: resolve <Badges /> to use <Badge /> (#21747)
Continuing the work from #21740 This pull-request updates all of our badges to use the `<Badge />` component. This is inline with our Figma design/guidelines, so going-forth and we're standardised across the application. I've added `<EnterpriseBadge />` and `<DeprecatedBadge />` to the `Badges.stories.tsx` so we can track these in future (they were missing previously). In `site/src/components/Form/Form.tsx` we were using these components within a `<h2 />` which would cause invalid semantic HTML. I chose the easy route around this and made them sit in their own `<header>` with a flex. ### Preview | Old | New | | --- | --- | | <img width="512" height="288" alt="BADGES_OLD" src="https://github.com/user-attachments/assets/196b0a53-37b2-4aee-b66e-454ac0ff1271" /> | <img width="512" height="288" alt="BADGES_OLD-1" src="https://github.com/user-attachments/assets/f0fb2871-40e2-4f0d-972c-cbf4249cf2d7" /> | | <img width="512" height="288" alt="DEPRECATED_OLD" src="https://github.com/user-attachments/assets/cce36b6c-e91a-47f6-8d20-02b9f40ea44e" /> | <img width="512" height="289" alt="DEPRECATED_NEW" src="https://github.com/user-attachments/assets/8a1f5168-d128-4733-819e-c1cb6641b83b" /> | | <img width="512" height="288" alt="ENTERPRISE_OLD" src="https://github.com/user-attachments/assets/aba677ce-23c7-4820-913b-886d049f81ef" /> | <img width="512" height="288" alt="ENTERPRISE_NEW" src="https://github.com/user-attachments/assets/eca9729d-c98a-4848-9f10-28e42e2c3cd3" /> | --------- Co-authored-by: Ben Potter <me@bpmct.net> Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -24,6 +24,8 @@ const badgeVariants = cva(
|
||||
"border border-solid border-border-destructive bg-surface-red text-highlight-red shadow",
|
||||
green:
|
||||
"border border-solid border-border-green bg-surface-green text-highlight-green shadow",
|
||||
purple:
|
||||
"border border-solid border-border-purple bg-surface-purple text-highlight-purple shadow",
|
||||
info: "border border-solid border-border-pending bg-surface-sky text-highlight-sky shadow",
|
||||
},
|
||||
size: {
|
||||
|
||||
@@ -2,8 +2,10 @@ import type { Meta, StoryObj } from "@storybook/react-vite";
|
||||
import {
|
||||
AlphaBadge,
|
||||
Badges,
|
||||
DeprecatedBadge,
|
||||
DisabledBadge,
|
||||
EnabledBadge,
|
||||
EnterpriseBadge,
|
||||
EntitledBadge,
|
||||
HealthyBadge,
|
||||
NotHealthyBadge,
|
||||
@@ -65,3 +67,13 @@ export const Alpha: Story = {
|
||||
children: <AlphaBadge />,
|
||||
},
|
||||
};
|
||||
export const Enterprise: Story = {
|
||||
args: {
|
||||
children: <EnterpriseBadge />,
|
||||
},
|
||||
};
|
||||
export const Deprecated: Story = {
|
||||
args: {
|
||||
children: <DeprecatedBadge />,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import type { Interpolation, Theme } from "@emotion/react";
|
||||
import { Badge } from "components/Badge/Badge";
|
||||
import { Stack } from "components/Stack/Stack";
|
||||
import {
|
||||
@@ -12,72 +11,50 @@ import {
|
||||
type HTMLAttributes,
|
||||
type PropsWithChildren,
|
||||
} from "react";
|
||||
import { cn } from "utils/cn";
|
||||
|
||||
const styles = {
|
||||
badge: {
|
||||
fontSize: 10,
|
||||
height: 24,
|
||||
fontWeight: 600,
|
||||
textTransform: "uppercase",
|
||||
letterSpacing: "0.085em",
|
||||
padding: "0 12px",
|
||||
borderRadius: 9999,
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
width: "fit-content",
|
||||
whiteSpace: "nowrap",
|
||||
},
|
||||
|
||||
enabledBadge: (theme) => ({
|
||||
border: `1px solid ${theme.roles.success.outline}`,
|
||||
backgroundColor: theme.roles.success.background,
|
||||
color: theme.roles.success.text,
|
||||
}),
|
||||
errorBadge: (theme) => ({
|
||||
border: `1px solid ${theme.roles.error.outline}`,
|
||||
backgroundColor: theme.roles.error.background,
|
||||
color: theme.roles.error.text,
|
||||
}),
|
||||
warnBadge: (theme) => ({
|
||||
border: `1px solid ${theme.roles.warning.outline}`,
|
||||
backgroundColor: theme.roles.warning.background,
|
||||
color: theme.roles.warning.text,
|
||||
}),
|
||||
} satisfies Record<string, Interpolation<Theme>>;
|
||||
|
||||
export const EnabledBadge: FC = () => {
|
||||
return (
|
||||
<span css={[styles.badge, styles.enabledBadge]} className="option-enabled">
|
||||
<Badge className="option-enabled" variant="green" border="solid">
|
||||
Enabled
|
||||
</span>
|
||||
</Badge>
|
||||
);
|
||||
};
|
||||
|
||||
export const EntitledBadge: FC = () => {
|
||||
return <span css={[styles.badge, styles.enabledBadge]}>Entitled</span>;
|
||||
return (
|
||||
<Badge border="solid" variant="green">
|
||||
Entitled
|
||||
</Badge>
|
||||
);
|
||||
};
|
||||
|
||||
interface HealthyBadge {
|
||||
interface HealthyBadgeProps {
|
||||
derpOnly?: boolean;
|
||||
}
|
||||
export const HealthyBadge: FC<HealthyBadge> = ({ derpOnly }) => {
|
||||
|
||||
export const HealthyBadge: FC<HealthyBadgeProps> = ({ derpOnly }) => {
|
||||
return (
|
||||
<span css={[styles.badge, styles.enabledBadge]}>
|
||||
<Badge variant="green" border="solid">
|
||||
{derpOnly ? "Healthy (DERP only)" : "Healthy"}
|
||||
</span>
|
||||
</Badge>
|
||||
);
|
||||
};
|
||||
|
||||
export const NotHealthyBadge: FC = () => {
|
||||
return <span css={[styles.badge, styles.errorBadge]}>Unhealthy</span>;
|
||||
return (
|
||||
<Badge variant="destructive" border="solid">
|
||||
Unhealthy
|
||||
</Badge>
|
||||
);
|
||||
};
|
||||
|
||||
export const NotRegisteredBadge: FC = () => {
|
||||
return (
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<span css={[styles.badge, styles.warnBadge]}>Never seen</span>
|
||||
<Badge variant="warning" border="solid">
|
||||
Never seen
|
||||
</Badge>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side="bottom" className="max-w-xs">
|
||||
Workspace Proxy has never come online and needs to be started.
|
||||
@@ -90,7 +67,9 @@ export const NotReachableBadge: FC = () => {
|
||||
return (
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<span css={[styles.badge, styles.warnBadge]}>Not reachable</span>
|
||||
<Badge variant="warning" border="solid">
|
||||
Not reachable
|
||||
</Badge>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side="bottom" className="max-w-xs">
|
||||
Workspace Proxy not responding to http(s) requests.
|
||||
@@ -100,42 +79,21 @@ export const NotReachableBadge: FC = () => {
|
||||
};
|
||||
|
||||
export const DisabledBadge: FC = forwardRef<
|
||||
HTMLSpanElement,
|
||||
HTMLAttributes<HTMLSpanElement>
|
||||
HTMLDivElement,
|
||||
HTMLAttributes<HTMLDivElement>
|
||||
>((props, ref) => {
|
||||
return (
|
||||
<span
|
||||
{...props}
|
||||
ref={ref}
|
||||
css={[
|
||||
styles.badge,
|
||||
(theme) => ({
|
||||
border: `1px solid ${theme.experimental.l1.outline}`,
|
||||
backgroundColor: theme.experimental.l1.background,
|
||||
color: theme.experimental.l1.text,
|
||||
}),
|
||||
]}
|
||||
className="option-disabled"
|
||||
>
|
||||
<Badge ref={ref} {...props} className="option-disabled">
|
||||
Disabled
|
||||
</span>
|
||||
</Badge>
|
||||
);
|
||||
});
|
||||
|
||||
export const EnterpriseBadge: FC = () => {
|
||||
return (
|
||||
<span
|
||||
css={[
|
||||
styles.badge,
|
||||
(theme) => ({
|
||||
backgroundColor: theme.branding.enterprise.background,
|
||||
border: `1px solid ${theme.branding.enterprise.border}`,
|
||||
color: theme.branding.enterprise.text,
|
||||
}),
|
||||
]}
|
||||
>
|
||||
<Badge variant="info" border="solid">
|
||||
Enterprise
|
||||
</span>
|
||||
</Badge>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -147,13 +105,7 @@ export const PremiumBadge: FC<PremiumBadgeProps> = ({
|
||||
children = "Premium",
|
||||
}) => {
|
||||
return (
|
||||
<Badge
|
||||
size="sm"
|
||||
className={cn(
|
||||
"bg-surface-purple border border-solid border-border-purple uppercase",
|
||||
"tracking-[0.085em] text-white rounded-full px-3 font-semibold",
|
||||
)}
|
||||
>
|
||||
<Badge variant="purple" border="solid">
|
||||
{children}
|
||||
</Badge>
|
||||
);
|
||||
@@ -161,52 +113,25 @@ export const PremiumBadge: FC<PremiumBadgeProps> = ({
|
||||
|
||||
export const PreviewBadge: FC = () => {
|
||||
return (
|
||||
<span
|
||||
css={[
|
||||
styles.badge,
|
||||
(theme) => ({
|
||||
border: `1px solid ${theme.roles.preview.outline}`,
|
||||
backgroundColor: theme.roles.preview.background,
|
||||
color: theme.roles.preview.text,
|
||||
}),
|
||||
]}
|
||||
>
|
||||
<Badge variant="purple" border="solid">
|
||||
Preview
|
||||
</span>
|
||||
</Badge>
|
||||
);
|
||||
};
|
||||
|
||||
export const AlphaBadge: FC = () => {
|
||||
return (
|
||||
<span
|
||||
css={[
|
||||
styles.badge,
|
||||
(theme) => ({
|
||||
border: `1px solid ${theme.roles.preview.outline}`,
|
||||
backgroundColor: theme.roles.preview.background,
|
||||
color: theme.roles.preview.text,
|
||||
}),
|
||||
]}
|
||||
>
|
||||
<Badge variant="purple" border="solid">
|
||||
Alpha
|
||||
</span>
|
||||
</Badge>
|
||||
);
|
||||
};
|
||||
|
||||
export const DeprecatedBadge: FC = () => {
|
||||
return (
|
||||
<span
|
||||
css={[
|
||||
styles.badge,
|
||||
(theme) => ({
|
||||
border: `1px solid ${theme.roles.danger.outline}`,
|
||||
backgroundColor: theme.roles.danger.background,
|
||||
color: theme.roles.danger.text,
|
||||
}),
|
||||
]}
|
||||
>
|
||||
<Badge variant="warning" border="solid">
|
||||
Deprecated
|
||||
</span>
|
||||
</Badge>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -108,11 +108,13 @@ export const FormSection = forwardRef<HTMLDivElement, FormSectionProps>(
|
||||
]}
|
||||
className={classes.sectionInfo}
|
||||
>
|
||||
<h2 css={styles.formSectionInfoTitle} className={classes.infoTitle}>
|
||||
{title}
|
||||
<header className="flex items-center gap-4">
|
||||
<h2 css={styles.formSectionInfoTitle} className={classes.infoTitle}>
|
||||
{title}
|
||||
</h2>
|
||||
{alpha && <AlphaBadge />}
|
||||
{deprecated && <DeprecatedBadge />}
|
||||
</h2>
|
||||
</header>
|
||||
<div css={styles.formSectionInfoDescription}>{description}</div>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user