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) => (