import { playgroundParameters } from "@charcuterie/storybook-config/story-parameters" import type { Meta, StoryObj } from "@storybook/react" import { categoricalArgType } from "../argTypes.storyHelpers.ts" import { StoryCell, StoryGrid, StoryRow, StorySection, } from "../board.storyHelpers.tsx" import { Card } from "../Card/Card.tsx" import { Avatar } from "./Avatar.tsx" /** * **Invented, every one of them.** The component was built for a * household task board, so its real fixture is a list of the people * who live there — which must never reach a published repo and * doubly never a committed screenshot, because a PNG is opaque to * every grep and no text scrub can redact it. * * So this is a fictional studio's roster: two-word names, a mononym, * a name whose first character is outside the BMP, and a * non-human account. */ const ROSTER = [ { id: "user-1", name: "Ada Lovelace" }, { id: "user-2", name: "Wren Okonkwo" }, { id: "user-3", name: "Bo" }, { id: "user-4", name: "Ines Marchetti" }, { id: "user-5", name: "Tomas Ericsson" }, ] as const /** * A photo that needs no network and no fixture file. The initials * behind it are what the reader sees the moment it is absent, which * is the state that actually matters. */ const PORTRAIT_URL = `data:image/svg+xml;utf8,${encodeURIComponent( '', )}` /** 404s, which is what makes the fallback real rather than mocked. */ const MISSING_PORTRAIT_URL = "/charcuterie-missing-portrait.png" /** * A stand-in for an app's own glyph set. The library ships none, so * a story that wants one has to bring it — and a letter is the safe * choice in a sandbox whose font may lack the symbol. */ const AgentGlyph = () => {"AI"} const meta = { title: "Components/Data/Avatar", component: Avatar, parameters: { layout: "padded" }, argTypes: { categorical: categoricalArgType }, args: { appearance: "soft", categorical: undefined, categoricalKey: undefined, icon: undefined, imageUrl: undefined, initials: undefined, name: "Ada Lovelace", size: "md", }, } satisfies Meta export default meta type Story = StoryObj /** * Two letters on a colour nobody chose. The name is on the chip for * a screen reader and on hover for a pointer, and it is not printed * anywhere — `getByRole("img", { name: "Ada Lovelace" })` resolves. */ export const Playground: Story = { parameters: playgroundParameters, } /** * The three appearances, and the ten colours the scale holds. * * `soft` is the default because a lane of solid chips competes with * the card titles beside them. `solid` is for a chip that is the * only colour in its row; `outline` is the quiet one. */ export const AllVariants: Story = { render: (avatarProps) => ( {(["soft", "outline", "solid"] as const).map( (appearance) => ( {ROSTER.map((person) => ( ))} ), )} ), } /** * Everything the chip can be showing, including the two that are * easy to get wrong: a photo that does not load, and nobody at all. */ export const AllStates: Story = { render: (avatarProps) => ( } name="Agentic" /> {/* * There is no chip in this cell, and that is the assertion. * A grey "None" placeholder is a fact nobody needed, * repeated down every unassigned row at the same weight as * the chips that mean something. */} ), } /** * The placement it was built for: a row per task, the chip answering * "whose is this?" without spending a column on the word. * * The middle row is unassigned. It draws nothing, so the row is * shorter by exactly one chip and says nothing false. */ export const InAList: Story = { render: () => (
    {[ { assignee: ROSTER[0], title: "Fold the second scheduler into the broker", }, { assignee: undefined, title: "Fingerprint duplicate uploads", }, { assignee: ROSTER[1], title: "One composed Storybook for the fleet", }, { assignee: ROSTER[3], title: "Retire the legacy rest_command bridge", }, ].map((task) => (
  • {task.title}
  • ))}
), }