8000 feat: add early access badges for dynamic parameters by jaaydenh · Pull Request #18114 · coder/coder · GitHub
[go: up one dir, main page]

Skip to content

feat: add early access badges for dynamic parameters #18114

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
May 30, 2025
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
feat: add early access badges for dynamic parameters
  • Loading branch information
jaaydenh committed May 29, 2025
commit 86c49ce0908e32ff0593b88df17806774b475783
Original file line number Diff line number Diff line change
Expand Up @@ -12,27 +12,30 @@ const meta: Meta<typeof FeatureStageBadge> = {
export default meta;
type Story = StoryObj<typeof FeatureStageBadge>;

export const MediumBeta: Story = {
export const SmallBeta: Story = {
args: {
size: "md",
size: "sm",
contentType: "beta",
},
};

export const SmallBeta: Story = {
export const MediumBeta: Story = {
args: {
size: "sm",
size: "md",
contentType: "beta",
},
};

export const LargeBeta: Story = {
export const SmallEarlyAccess: Story = {
args: {
size: "lg",
size: "sm",
contentType: "early_access",
},
};

export const MediumExperimental: Story = {
export const MediumEarlyAccess: Story = {
args: {
size: "md",
contentType: "experimental",
contentType: "early_access",
},
};
147 changes: 46 additions & 101 deletions site/src/components/FeatureStageBadge/FeatureStageBadge.tsx
Original file line number Diff line number Diff line change
@@ -1,142 +1,87 @@
import type { Interpolation, Theme } from "@emotion/react";
import Link from "@mui/material/Link";
import { visuallyHidden } from "@mui/utils";
import { HelpTooltipContent } from "components/HelpTooltip/HelpTooltip";
import { Popover, PopoverTrigger } from "components/deprecated/Popover/Popover";
import { Link } from "components/Link/Link";
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "components/Tooltip/Tooltip";
import type { FC, HTMLAttributes, ReactNode } from "react";
import { cn } from "utils/cn";
import { docs } from "utils/docs";

/**
* All types of feature that we are currently supporting. Defined as record to
* ensure that we can't accidentally make typos when writing the badge text.
*/
export const featureStageBadgeTypes = {
early_access: "early access",
beta: "beta",
experimental: "experimental",
} as const satisfies Record<string, ReactNode>;

type FeatureStageBadgeProps = Readonly<
Omit<HTMLAttributes<HTMLSpanElement>, "children"> & {
contentType: keyof typeof featureStageBadgeTypes;
labelText?: string;
size?: "sm" | "md" | "lg";
showTooltip?: boolean;
size?: "sm" | "md";
}
>;

const badgeColorClasses = {
early_access:
"border-solid border-border-warning bg-surface-orange text-content-warning hover:bg-transparent",
beta: "border-solid border-highlight-sky bg-surface-sky text-highlight-sky hover:bg-transparent",
} as const;

const badgeSizeClasses = {
sm: "text-xs font-medium px-2 py-1",
md: "text-base px-2 py-1",
} as const;

export const FeatureStageBadge: FC<FeatureStageBadgeProps> = ({
contentType,
labelText = "",
size = "md",
showTooltip = true, // This is a temporary until the deprecated popover is removed
className,
...delegatedProps
}) => {
const colorClasses = badgeColorClasses[contentType];
const sizeClasses = badgeSizeClasses[size];

return (
<Popover mode="hover">
<PopoverTrigger>
{({ isOpen }) => (
<span
css={[
styles.badge,
size === "sm" && styles.badgeSmallText,
size === "lg" && styles.badgeLargeText,
isOpen && styles.badgeHover,
]}
<TooltipProvider delayDuration={100}>
<Tooltip>
<TooltipTrigger asChild>
<button
className={cn(
"block max-w-fit cursor-default flex-shrink-0 leading-none whitespace-nowrap border rounded-md transition-colors duration-200 ease-in-out bg-transparent",
sizeClasses,
colorClasses,
className,
)}
{...delegatedProps}
>
<span style={visuallyHidden}> (This is a</span>
<span className="sr-only"> (This is a</span>
<span className="first-letter:uppercase">
{labelText && `${labelText} `}
{featureStageBadgeTypes[contentType]}
</span>
<span style={visuallyHidden}> feature)</span>
</span>
)}
</PopoverTrigger>

{showTooltip && (
<HelpTooltipContent
anchorOrigin={{ vertical: "bottom", horizontal: "center" }}
transformOrigin={{ vertical: "top", horizontal: "center" }}
>
<p css={styles.tooltipDescription}>
<span className="sr-only"> feature)</span>
</button>
</TooltipTrigger>
<TooltipContent align="start" className="max-w-xs text-sm">
<p className="m-0">
This feature has not yet 8000 reached general availability (GA).
</p>

<Link
href={docs("/install/releases/feature-stages")}
target="_blank"
rel="noreferrer"
css={styles.tooltipLink}
className="font-semibold"
>
Learn about feature stages
<span style={visuallyHidden}> (link opens in new tab)</span>
<span className="sr-only"> (link opens in new tab)</span>
</Link>
</HelpTooltipContent>
)}
</Popover>
</TooltipContent>
</Tooltip>
</TooltipProvider>
);
};

const styles = {
badge: (theme) => ({
// Base type is based on a span so that the element can be placed inside
// more types of HTML elements without creating invalid markdown, but we
// still want the default display behavior to be div-like
display: "block",
maxWidth: "fit-content",

// Base style assumes that medium badges will be the default
fontSize: "0.75rem",

cursor: "default",
flexShrink: 0,
padding: "4px 8px",
lineHeight: 1,
whiteSpace: "nowrap",
border: `1px solid ${theme.branding.featureStage.border}`,
color: theme.branding.featureStage.text,
backgroundColor: theme.branding.featureStage.background,
borderRadius: "6px",
transition:
"color 0.2s ease-in-out, border-color 0.2s ease-in-out, background-color 0.2s ease-in-out",
}),

badgeHover: (theme) => ({
color: theme.branding.featureStage.hover.text,
borderColor: theme.branding.featureStage.hover.border,
backgroundColor: theme.branding.featureStage.hover.background,
}),

badgeLargeText: {
fontSize: "1rem",
},

badgeSmallText: {
// Have to beef up font weight so that the letters still maintain the
// same relative thickness as all our other main UI text
fontWeight: 500,
fontSize: "0.625rem",
},

tooltipTitle: (theme) => ({
color: theme.palette.text.primary,
fontWeight: 600,
fontFamily: "inherit",
fontSize: 18,
margin: 0,
lineHeight: 1,
paddingBottom: "8px",
}),

tooltipDescription: {
margin: 0,
lineHeight: 1.4,
paddingBottom: "8px",
},

tooltipLink: {
fontWeight: 600,
lineHeight: 1.2,
},
} as const satisfies Record<string, Interpolation<Theme>>;
Original file line number Diff line number Diff line change
Expand Up @@ -368,6 +368,11 @@ export const CreateWorkspacePageViewExperimental: FC<
</div>
<span className="flex flex-row items-center gap-2">
<h1 className="text-3xl font- 8000 semibold m-0">New workspace</h1>
<FeatureStageBadge
contentType={"early_access"}
size="sm"
labelText="Dynamic parameters"
/>
<TooltipProvider delayDuration={100}>
<Tooltip>
<TooltipTrigger asChild>
Expand Down Expand Up @@ -555,7 +560,7 @@ export const CreateWorkspacePageViewExperimental: FC<
<div className="flex flex-col gap-2">
<div className="flex gap-2 items-center">
<Label className="text-sm">Preset</Label>
<FeatureStageBadge contentType={"beta"} size="md" />
<FeatureStageBadge contentType={"beta"} size="sm" />
</div>
<div className="flex flex-col gap-4">
<div className="max-w-lg">
Expand Down
2 changes: 1 addition & 1 deletion site/src/pages/UserSettingsPage/Section.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ export const Section: FC<SectionProps> = ({
{featureStage && (
<FeatureStageBadge
contentType={featureStage}
size="lg"
size="md"
css={{ marginBottom: "5px" }}
/>
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import type {
import { ErrorAlert } from "components/Alert/ErrorAlert";
import { Button } from "components/Button/Button";
import { EmptyState } from "components/EmptyState/EmptyState";
import { FeatureStageBadge } from "components/FeatureStageBadge/FeatureStageBadge";
import { Link } from "components/Link/Link";
import { Loader } from "components/Loader/Loader";
import {
Expand Down Expand Up @@ -205,6 +206,11 @@ const WorkspaceParametersPageExperimental: FC = () => {
<header className="flex flex-col items-start gap-2">
<span className="flex flex-row items-center gap-2">
<h1 className="text-3xl m-0">Workspace parameters</h1>
<FeatureStageBadge
contentType={"early_access"}
size="sm"
labelText="Dynamic parameters"
/>
<TooltipProvider delayDuration={100}>
<Tooltip>
<TooltipTrigger asChild>
Expand Down
Loading
0