import { playgroundParameters } from "@charcuterie/storybook-config/story-parameters" import type { Meta, StoryObj } from "@storybook/react" import { Badge } from "../Badge/Badge.tsx" import { ContainerBoard, StoryCell, StoryGrid, StorySection, } from "../board.storyHelpers.tsx" import { MediaTile } from "./MediaTile.tsx" /** * An inline SVG rather than a fixture file or a placeholder service: * it decodes synchronously, needs no network, and makes the * `loaded` state deterministic in CI — which matters because the * *cached* path is one of the three states this component exists to * get right. */ const toPosterSrc = (label: string) => `data:image/svg+xml;utf8,${encodeURIComponent( `${label}`, )}` /** 404s, which is what makes the error state real rather than mocked. */ const MISSING_SRC = "/charcuterie-missing-poster.png" const meta = { title: "Components/Data/MediaTile", component: MediaTile, parameters: { layout: "padded" }, args: { aspectRatio: "poster" }, } satisfies Meta export default meta type Story = StoryObj export const Playground: Story = { parameters: playgroundParameters, args: { alt: "Blade Runner (1982) poster", src: toPosterSrc("Blade Runner"), subtitle: "1982 · 4K remaster", title: "Blade Runner", }, } export const AllVariants: Story = { args: { alt: "Poster", title: "Blade Runner" }, render: () => ( ), } /** * The three states are the component, and no hand-rolled version in * the fleet models more than one of them. */ export const AllStates: Story = { args: { alt: "Poster", title: "Blade Runner" }, render: () => ( 4K } href="#blade-runner" src={toPosterSrc("Blade Runner")} subtitle="1982 · 4K remaster" title="Blade Runner" /> ), } /** * The caption's type steps up past `--cq-sm`, so a tile in a dense * 8-across grid and the same tile in a 3-across one are legible at * both — the intermediate widths where the fleet's poster grids look * wrong today. */ export const Responsive: Story = { args: { alt: "Poster", title: "Blade Runner" }, render: () => ( ), } export const Grid: Story = { args: { alt: "Poster", title: "Blade Runner" }, render: () => ( // Two elements, not one: the container and the grid that queries // it **must** be different, because a container query matches // descendants only. Collapsed into one `
` this compiles, // generates real CSS, and silently stays two columns — which on a // desktop is four 700px posters.
{[ "Blade Runner", "Dune", "Arrival", "Solaris", "Stalker", "Annihilation", ].map((title, index) => ( ))}
), } /** * The linked tile's contract: the accessible name is exactly the * title — not the title plus the subtitle plus the alt text — and * Tab reaches it. `getByRole("link", { name: "Blade Runner" })` is * what an agent will actually write. */ export const Interactive: Story = { args: { alt: "Blade Runner (1982) poster", href: "#blade-runner", src: toPosterSrc("Blade Runner"), subtitle: "1982 · 4K remaster", title: "Blade Runner", }, } /** * Same chrome as the link — pointer, hover fade, focus ring — * as a button. Wrapping the tile in a bare `