import { playgroundParameters } from "@charcuterie/storybook-config/story-parameters" import type { Meta, StoryObj } from "@storybook/react" import type { ReactNode } from "react" import { controlSizeArgType, intentArgType, } from "../argTypes.storyHelpers.ts" import { Button } from "../Button/Button.tsx" import { ContainerBoard, StoryCell, StoryGrid, StoryRow, StorySection, } from "../board.storyHelpers.tsx" import { ArrowRightIcon, ExternalIcon, } from "../icons.storyHelpers.tsx" import type { IntentAppearance } from "../intentStyles.ts" import { RouterLinkProvider } from "../RouterLink/RouterLinkProvider.tsx" import type { RouterLinkProps } from "../RouterLink/routerLink.ts" import { ButtonLink } from "./ButtonLink.tsx" /** * A stand-in for react-router's `` — the marker attribute is * how the board (and `ButtonLink.test.tsx`) shows which destinations * the injected router was handed. */ const SoftRouterLink = ({ href, ...linkProps }: RouterLinkProps): ReactNode => ( ) const INTENTS = [ "neutral", "accent", "success", "warning", "danger", "info", ] as const const APPEARANCES: IntentAppearance[] = [ "solid", "soft", "outline", "ghost", ] const meta = { title: "Components/Actions/ButtonLink", component: ButtonLink, parameters: { layout: "padded" }, argTypes: { intent: intentArgType, size: controlSizeArgType, }, // The component's own defaults, restated. Storybook has not // seeded `args` from a docgen `defaultValue` since v7, so without // this the props table prints `"solid"` in the Default column // while the radio beside it has nothing selected. args: { appearance: "solid", href: "/channels/settings", intent: "accent", isDisabled: false, isExternal: false, isFullWidth: false, size: "md", }, } satisfies Meta export default meta type Story = StoryObj export const Playground: Story = { parameters: playgroundParameters, args: { children: "Configure" }, } /** * The claim this component exists to make, side by side: a * `ButtonLink` and a `Button` with identical visual props are the * same pixels, and one of them is an ``. * * `ButtonLink.test.tsx` compares their computed styles rather than * trusting this board — but the board is where a human sees it. */ export const BesideAButton: Story = { args: { children: "Configure" }, render: (buttonLinkProps) => ( ), } export const AllVariants: Story = { args: { children: "Configure" }, render: (buttonLinkProps) => ( {APPEARANCES.flatMap((appearance) => INTENTS.map((intent) => ( {intent} )), )} ), } export const AllSizes: Story = { args: { children: "Configure" }, render: (buttonLinkProps) => ( Small Medium Large ), } /** * Hover, active, and focus are real here, not simulated: the * pseudo-states addon forces them, which is the only way to review a * hover colour in a static board. * * There is no `loading` cell, and that is the design: a navigation has * no pending state this component owns. */ export const AllStates: Story = { args: { children: "Configure" }, parameters: { pseudo: { active: ["#active"], focusVisible: ["#focus-target"], hover: ["#hover"], }, }, render: (buttonLinkProps) => ( } /> ), } /** * Plex Channels' actual control. It reads as the primary action on the * card and it goes to another page, so it is an `` — today it is a * `