From 5827997d094930ec9e13da4069b6e376904a79cc Mon Sep 17 00:00:00 2001 From: myelinated-wackerow <263208946+myelinated-wackerow@users.noreply.github.com> Date: Mon, 4 May 2026 18:40:53 -0700 Subject: [PATCH 1/9] feat(storybook): add Dialog story Vanilla shadcn Dialog with stories for default usage, header+footer composition, width override via className, and long-form scrollable content. Title hierarchy `UI / Primitives / Dialog` per #18121. Refs: #18121 Co-Authored-By: Claude Opus 4.7 Co-Authored-By: wackerow <54227730+wackerow@users.noreply.github.com> --- .../ui/__stories__/Dialog.stories.tsx | 131 ++++++++++++++++++ 1 file changed, 131 insertions(+) create mode 100644 src/components/ui/__stories__/Dialog.stories.tsx diff --git a/src/components/ui/__stories__/Dialog.stories.tsx b/src/components/ui/__stories__/Dialog.stories.tsx new file mode 100644 index 00000000000..46fb6887633 --- /dev/null +++ b/src/components/ui/__stories__/Dialog.stories.tsx @@ -0,0 +1,131 @@ +import { fn } from "storybook/test" +import type { Meta, StoryObj } from "@storybook/nextjs" + +import { Button } from "../buttons/Button" +import { + Dialog, + DialogClose, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, + DialogTrigger, +} from "../dialog" +import { Flex } from "../flex" + +const meta = { + title: "UI / Primitives / Dialog", + component: Dialog, + parameters: { + docs: { + description: { + component: + "Vanilla shadcn `Dialog`. For most app use, prefer `Modal` (`UI / Modal`), which wraps this primitive with `size`, `variant`, and `actionButton` props. The overlay is always rendered as part of `DialogContent`. Width is constrained to `max-w-lg` by default; override via `className` on `DialogContent`.", + }, + }, + }, +} satisfies Meta + +export default meta + +type Story = StoryObj + +export const Default: Story = { + args: { defaultOpen: true }, + render: (args) => ( + + + + + + + Dialog title + + A short description of what this dialog does. + + + + + ), +} + +export const WithFooter: Story = { + args: { defaultOpen: true }, + render: (args) => ( + + + + + + + Confirm action + + This action cannot be undone. Are you sure you want to proceed? + + + + + + + + + + + + + ), +} + +export const Widths: Story = { + args: { defaultOpen: true }, + parameters: { + docs: { + description: { + story: + "`DialogContent` defaults to `max-w-lg`. Override via `className` for narrower or wider dialogs.", + }, + }, + }, + render: (args) => ( + + + + + + + Wide dialog (max-w-2xl) + + For wider content, pass a Tailwind width class to `DialogContent`. + For canonical app sizing, use `Modal` instead. + + + + + ), +} + +export const LongContent: Story = { + args: { defaultOpen: true }, + render: (args) => ( + + + + + + + Terms of use + + Long-form content scrolls within the dialog when it overflows the + viewport. Ethereum is a decentralized, open-source blockchain + featuring smart-contract functionality. Ether is the native + cryptocurrency of the platform. Among cryptocurrencies, ether is + second only to bitcoin in market capitalization. + + + + + ), +} From 73c4992d2daaa875f4e8ac404167991f0ff1de0c Mon Sep 17 00:00:00 2001 From: myelinated-wackerow <263208946+myelinated-wackerow@users.noreply.github.com> Date: Mon, 4 May 2026 18:41:35 -0700 Subject: [PATCH 2/9] feat(storybook): expand Modal story coverage Add stories for `size: md|lg|xl`, `variant: simulator|unstyled`, and the no-`actionButton` variant. Retitle to `UI / Modal` per #18121. Refs: #18121 Co-Authored-By: Claude Opus 4.7 Co-Authored-By: wackerow <54227730+wackerow@users.noreply.github.com> --- .../ui/__stories__/Modal.stories.tsx | 63 ++++++++++++++++--- 1 file changed, 56 insertions(+), 7 deletions(-) diff --git a/src/components/ui/__stories__/Modal.stories.tsx b/src/components/ui/__stories__/Modal.stories.tsx index 7648ff21f1f..dd554d1d6c9 100644 --- a/src/components/ui/__stories__/Modal.stories.tsx +++ b/src/components/ui/__stories__/Modal.stories.tsx @@ -1,16 +1,16 @@ import { fn } from "storybook/test" -import { Meta, StoryObj } from "@storybook/nextjs" +import type { Meta, StoryObj } from "@storybook/nextjs" import ModalComponent from "../dialog-modal" const meta = { - title: "Molecules/Overlay Content/Modal", + title: "UI / Modal", component: ModalComponent, args: { defaultOpen: true, - title: "Modal Title", + title: "Modal title", children: - "This is the base component to be used in the modal window. Please change the text to preview final content for ethereum.org", + "Base content for the modal. Replace this with the final copy and components for your screen.", actionButton: { label: "Save", onClick: fn(), @@ -22,10 +22,59 @@ export default meta type Story = StoryObj -export const Modal: Story = {} +export const Default: Story = {} -export const Xl: Story = { +export const SizeMd: Story = { + args: { size: "md" }, +} + +export const SizeLg: Story = { + args: { size: "lg" }, +} + +export const SizeXl: Story = { + args: { size: "xl" }, +} + +export const VariantSimulator: Story = { + args: { variant: "simulator" }, + parameters: { + docs: { + description: { + story: + "`simulator` variant moves the close button inline with the header content; used for embedded simulator-style modals.", + }, + }, + }, +} + +export const VariantUnstyled: Story = { args: { - size: "xl", + variant: "unstyled", + actionButton: undefined, + children: + "Unstyled content area. Use when the modal needs to render fully custom layout without the default padding, rounding, or background.", + }, + parameters: { + docs: { + description: { + story: + "`unstyled` variant strips the default content padding, rounding, and background so the consumer can render custom chrome.", + }, + }, + }, +} + +export const WithoutActionButton: Story = { + args: { + actionButton: undefined, + }, + parameters: { + docs: { + description: { + story: + "Without `actionButton`, the footer is omitted and the modal becomes informational. Closes via the X button or escape.", + }, + }, }, } From 73eef012f53b537dfcf863ade341418cc98840d1 Mon Sep 17 00:00:00 2001 From: myelinated-wackerow <263208946+myelinated-wackerow@users.noreply.github.com> Date: Mon, 4 May 2026 18:42:18 -0700 Subject: [PATCH 3/9] feat(storybook): add Sheet story Cover `side: top|bottom|left|right`, `hideOverlay`, and a header+body+footer composition. Title hierarchy `UI / Primitives / Sheet` per #18121. Refs: #18121 Co-Authored-By: Claude Opus 4.7 Co-Authored-By: wackerow <54227730+wackerow@users.noreply.github.com> --- .../ui/__stories__/Sheet.stories.tsx | 175 ++++++++++++++++++ 1 file changed, 175 insertions(+) create mode 100644 src/components/ui/__stories__/Sheet.stories.tsx diff --git a/src/components/ui/__stories__/Sheet.stories.tsx b/src/components/ui/__stories__/Sheet.stories.tsx new file mode 100644 index 00000000000..92c847a65a1 --- /dev/null +++ b/src/components/ui/__stories__/Sheet.stories.tsx @@ -0,0 +1,175 @@ +import type { Meta, StoryObj } from "@storybook/nextjs" + +import { Button } from "../buttons/Button" +import { + Sheet, + SheetClose, + SheetContent, + SheetDescription, + SheetFooter, + SheetHeader, + SheetTitle, + SheetTrigger, +} from "../sheet" + +const meta = { + title: "UI / Primitives / Sheet", + component: Sheet, + parameters: { + docs: { + description: { + component: + "Side-panel overlay built on Radix Dialog. Use `side` to choose entry edge, and `hideOverlay` when the sheet should appear without dimming the page (e.g., persistent filter panels). For lazy-mount + stay-mounted behavior, see `PersistentPanel`.", + }, + }, + }, +} satisfies Meta + +export default meta + +type Story = StoryObj + +const SAMPLE_BODY = ( +

+ Sheets are good for secondary tasks that should not pull the user away from + the main page context, like filters, settings, or a navigation drawer. +

+) + +export const Default: Story = { + args: { defaultOpen: true }, + render: (args) => ( + + + + + + + Sheet title + A short description of the sheet. + +
{SAMPLE_BODY}
+
+
+ ), +} + +export const SideRight: Story = { + args: { defaultOpen: true }, + render: (args) => ( + + + + + + + Right sheet + +
{SAMPLE_BODY}
+
+
+ ), +} + +export const SideLeft: Story = { + args: { defaultOpen: true }, + render: (args) => ( + + + + + + + Left sheet + +
{SAMPLE_BODY}
+
+
+ ), +} + +export const SideTop: Story = { + args: { defaultOpen: true }, + render: (args) => ( + + + + + + + Top sheet + +
{SAMPLE_BODY}
+
+
+ ), +} + +export const SideBottom: Story = { + args: { defaultOpen: true }, + render: (args) => ( + + + + + + + Bottom sheet + +
{SAMPLE_BODY}
+
+
+ ), +} + +export const WithoutOverlay: Story = { + args: { defaultOpen: true }, + parameters: { + docs: { + description: { + story: + "`hideOverlay` skips the dimmed backdrop so the sheet sits beside live page content.", + }, + }, + }, + render: (args) => ( + + + + + + + No overlay + + The page behind stays interactive. + + +
{SAMPLE_BODY}
+
+
+ ), +} + +export const WithHeaderAndFooter: Story = { + args: { defaultOpen: true }, + render: (args) => ( + + + + + + + Edit profile + + Make changes to your profile and save when you are done. + + +
{SAMPLE_BODY}
+ + Cancel + + +
+
+ ), +} From 996f69a0d1bb75ff4a7873018f6c58b449b9a610 Mon Sep 17 00:00:00 2001 From: myelinated-wackerow <263208946+myelinated-wackerow@users.noreply.github.com> Date: Mon, 4 May 2026 18:42:57 -0700 Subject: [PATCH 4/9] feat(storybook): add Popover story Cover the three `align` options (`start|center|end`) side-by-side, plus default, rich-content, and explicit-close compositions. Title hierarchy `UI / Primitives / Popover` per #18121. Refs: #18121 Co-Authored-By: Claude Opus 4.7 Co-Authored-By: wackerow <54227730+wackerow@users.noreply.github.com> --- .../ui/__stories__/Popover.stories.tsx | 129 ++++++++++++++++++ 1 file changed, 129 insertions(+) create mode 100644 src/components/ui/__stories__/Popover.stories.tsx diff --git a/src/components/ui/__stories__/Popover.stories.tsx b/src/components/ui/__stories__/Popover.stories.tsx new file mode 100644 index 00000000000..02dcb927bbd --- /dev/null +++ b/src/components/ui/__stories__/Popover.stories.tsx @@ -0,0 +1,129 @@ +import type { Meta, StoryObj } from "@storybook/nextjs" + +import { Button } from "../buttons/Button" +import { HStack } from "../flex" +import { + Popover, + PopoverClose, + PopoverContent, + PopoverTrigger, +} from "../popover" + +const meta = { + title: "UI / Primitives / Popover", + component: Popover, + parameters: { + docs: { + description: { + component: + "Anchored floating panel built on Radix Popover. The arrow is always rendered as part of `PopoverContent`. Use `align` to shift the panel relative to its trigger.", + }, + }, + }, +} satisfies Meta + +export default meta + +type Story = StoryObj + +export const Default: Story = { + args: { defaultOpen: true }, + render: (args) => ( + + + + + +

+ Popovers anchor to their trigger and surface secondary information or + short forms. +

+
+
+ ), +} + +export const Alignments: Story = { + parameters: { + docs: { + description: { + story: + "All three `align` options shown side-by-side. `start` left-aligns to the trigger, `end` right-aligns, `center` is the default.", + }, + }, + }, + render: () => ( + + + + + + +

Aligned to the start edge of the trigger.

+
+
+ + + + + + +

Centered under the trigger (default).

+
+
+ + + + + + +

Aligned to the end edge of the trigger.

+
+
+
+ ), +} + +export const WithRichContent: Story = { + args: { defaultOpen: true }, + render: (args) => ( + + + + + +
+

Network details

+

+ Layer 2 networks scale Ethereum by handling transactions off the + main chain while inheriting its security guarantees. +

+ +
+
+
+ ), +} + +export const WithCloseAction: Story = { + args: { defaultOpen: true }, + render: (args) => ( + + + + + +
+

+ Use `PopoverClose` to dismiss the panel from inside the content. +

+ + + +
+
+
+ ), +} From 9d13c6c30c23f42b7f1ed3fc1e990b3d026d4c2e Mon Sep 17 00:00:00 2001 From: myelinated-wackerow <263208946+myelinated-wackerow@users.noreply.github.com> Date: Mon, 4 May 2026 18:43:57 -0700 Subject: [PATCH 5/9] feat(storybook): add DropdownMenu story Cover simple menu, submenus, checkbox items, radio items, and separator/label/shortcut composition. Title hierarchy `UI / Primitives / DropdownMenu` per #18121. Refs: #18121 Co-Authored-By: Claude Opus 4.7 Co-Authored-By: wackerow <54227730+wackerow@users.noreply.github.com> --- .../ui/__stories__/DropdownMenu.stories.tsx | 176 ++++++++++++++++++ 1 file changed, 176 insertions(+) create mode 100644 src/components/ui/__stories__/DropdownMenu.stories.tsx diff --git a/src/components/ui/__stories__/DropdownMenu.stories.tsx b/src/components/ui/__stories__/DropdownMenu.stories.tsx new file mode 100644 index 00000000000..81591789108 --- /dev/null +++ b/src/components/ui/__stories__/DropdownMenu.stories.tsx @@ -0,0 +1,176 @@ +import { useState } from "react" +import type { Meta, StoryObj } from "@storybook/nextjs" + +import { Button } from "../buttons/Button" +import { + DropdownMenu, + DropdownMenuCheckboxItem, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuLabel, + DropdownMenuRadioGroup, + DropdownMenuRadioItem, + DropdownMenuSeparator, + DropdownMenuShortcut, + DropdownMenuSub, + DropdownMenuSubContent, + DropdownMenuSubTrigger, + DropdownMenuTrigger, +} from "../dropdown-menu" + +const meta = { + title: "UI / Primitives / DropdownMenu", + component: DropdownMenu, + parameters: { + docs: { + description: { + component: + "Anchored menu built on Radix DropdownMenu. Supports plain items, checkbox items, radio groups, submenus, labels, separators, and shortcut hints.", + }, + }, + }, +} satisfies Meta + +export default meta + +type Story = StoryObj + +export const Default: Story = { + args: { defaultOpen: true }, + render: (args) => ( + + + + + + Profile + Settings + Sign out + + + ), +} + +export const WithSubmenus: Story = { + args: { defaultOpen: true }, + render: (args) => ( + + + + + + New file + + Share + + Copy link + Email + Embed + + + Delete + + + ), +} + +const CheckboxMenu = () => { + const [showToolbar, setShowToolbar] = useState(true) + const [showSidebar, setShowSidebar] = useState(false) + const [showStatus, setShowStatus] = useState(true) + + return ( + + + + + + Appearance + + Toolbar + + + Sidebar + + + Status bar + + + + ) +} + +export const WithCheckboxItems: Story = { + render: () => , +} + +const RadioMenu = () => { + const [theme, setTheme] = useState("system") + + return ( + + + + + + Theme + + Light + Dark + System + + + + ) +} + +export const WithRadioItems: Story = { + render: () => , +} + +export const WithSeparators: Story = { + args: { defaultOpen: true }, + parameters: { + docs: { + description: { + story: + "Combines labels, items, separators, and shortcut hints to model a typical app menu.", + }, + }, + }, + render: (args) => ( + + + + + + File + + New Ctrl+N + + + Open Ctrl+O + + + Edit + + Cut Ctrl+X + + + Copy Ctrl+C + + + Sign out + + + ), +} From bf06297990924a27047c50b85dd8a6c3e9a9b184 Mon Sep 17 00:00:00 2001 From: myelinated-wackerow <263208946+myelinated-wackerow@users.noreply.github.com> Date: Mon, 4 May 2026 18:44:44 -0700 Subject: [PATCH 6/9] feat(storybook): add Select story Cover placeholder, default-value, grouped items with labels and separators, disabled state (with and without value), error state via className override, and disabled-item composition. Title hierarchy `UI / Primitives / Select` per #18121. Note: `Select` has no built-in `hasError` variant; the error story applies `border-error` directly. A future cleanup PR could add the variant to match `Input`/`Textarea`. Refs: #18121 Co-Authored-By: Claude Opus 4.7 Co-Authored-By: wackerow <54227730+wackerow@users.noreply.github.com> --- .../ui/__stories__/Select.stories.tsx | 173 ++++++++++++++++++ 1 file changed, 173 insertions(+) create mode 100644 src/components/ui/__stories__/Select.stories.tsx diff --git a/src/components/ui/__stories__/Select.stories.tsx b/src/components/ui/__stories__/Select.stories.tsx new file mode 100644 index 00000000000..87de6bbc439 --- /dev/null +++ b/src/components/ui/__stories__/Select.stories.tsx @@ -0,0 +1,173 @@ +import type { Meta, StoryObj } from "@storybook/nextjs" + +import { VStack } from "../flex" +import { + Select, + SelectContent, + SelectGroup, + SelectItem, + SelectLabel, + SelectSeparator, + SelectTrigger, + SelectValue, +} from "../select" + +const meta = { + title: "UI / Primitives / Select", + component: Select, + parameters: { + docs: { + description: { + component: + "Single-select dropdown built on Radix Select. Use `SelectGroup` + `SelectLabel` to group items, `SelectSeparator` between groups. Note: `Select` does not currently expose a `hasError` variant — error styling is applied via `className` on `SelectTrigger`.", + }, + }, + }, +} satisfies Meta + +export default meta + +type Story = StoryObj + +const FRUITS = ["Apple", "Banana", "Cherry", "Date", "Elderberry"] as const + +export const WithPlaceholder: Story = { + render: () => ( +
+ +
+ ), +} + +export const WithDefaultValue: Story = { + render: () => ( +
+ +
+ ), +} + +export const WithGroups: Story = { + render: () => ( +
+ +
+ ), +} + +export const Disabled: Story = { + render: () => ( + + + + + ), +} + +export const ErrorState: Story = { + parameters: { + docs: { + description: { + story: + "`Select` lacks a built-in `hasError` variant. Apply `border-error` (and matching focus token) on `SelectTrigger` to mirror the error styling used by `Input` and `Textarea`.", + }, + }, + }, + render: () => ( +
+ +
+ ), +} + +export const WithDisabledItem: Story = { + render: () => ( +
+ +
+ ), +} From 4f8df0cec2176c62cf9ca6396a9cea30dfa7ede0 Mon Sep 17 00:00:00 2001 From: myelinated-wackerow <263208946+myelinated-wackerow@users.noreply.github.com> Date: Mon, 4 May 2026 18:45:58 -0700 Subject: [PATCH 7/9] feat(storybook): add PersistentPanel story Cover default usage, all four sides, and a render-count probe demonstrating that children persist across open/close cycles. Title hierarchy `UI / PersistentPanel` per #18121. Stories use a controlled wrapper since `PersistentPanel` is controlled-only (no `defaultOpen`). Component description contrasts with `Sheet`'s mount-on-open behavior so designers can see the difference at a glance. Refs: #18121 Co-Authored-By: Claude Opus 4.7 Co-Authored-By: wackerow <54227730+wackerow@users.noreply.github.com> --- .../__stories__/PersistentPanel.stories.tsx | 156 ++++++++++++++++++ 1 file changed, 156 insertions(+) create mode 100644 src/components/ui/__stories__/PersistentPanel.stories.tsx diff --git a/src/components/ui/__stories__/PersistentPanel.stories.tsx b/src/components/ui/__stories__/PersistentPanel.stories.tsx new file mode 100644 index 00000000000..df69167b8c4 --- /dev/null +++ b/src/components/ui/__stories__/PersistentPanel.stories.tsx @@ -0,0 +1,156 @@ +import { useRef, useState } from "react" +import type { Meta, StoryObj } from "@storybook/nextjs" + +import { Button } from "../buttons/Button" +import { PersistentPanel } from "../persistent-panel" + +const meta = { + title: "UI / PersistentPanel", + component: PersistentPanel, + args: { + open: false, + children: null, + }, + parameters: { + docs: { + description: { + component: + "Custom side-panel overlay tuned for expensive content. Unlike `Sheet` (which mounts/unmounts on every open), `PersistentPanel` lazy-mounts on first open and then stays mounted -- toggles only flip CSS visibility. Use for heavy filter forms, virtualized lists, or any content where re-mount cost matters. For lighter cases, prefer `Sheet`.", + }, + }, + }, +} satisfies Meta + +export default meta + +type Story = StoryObj + +type DemoProps = { + side?: "left" | "right" | "top" | "bottom" + initialOpen?: boolean + ariaLabel: string + body?: React.ReactNode +} + +const PanelDemo = ({ + side = "left", + initialOpen = true, + ariaLabel, + body, +}: DemoProps) => { + const [open, setOpen] = useState(initialOpen) + const triggerRef = useRef(null) + + return ( + <> + + +
+

{ariaLabel}

+ {body ?? ( +

+ Panel content stays mounted between opens. Open/close several + times — the children do not unmount. +

+ )} + +
+
+ + ) +} + +export const Default: Story = { + render: () => , +} + +export const SideLeft: Story = { + render: () => , +} + +export const SideRight: Story = { + render: () => , +} + +export const SideTop: Story = { + render: () => , +} + +export const SideBottom: Story = { + render: () => , +} + +const LazyMountProbe = () => { + const [renderCount, setRenderCount] = useState(0) + + // Counts every render of the panel children + return ( +
+

+ Render count: {renderCount} +

+ +
+ ) +} + +export const LazyMountAndPersistence: Story = { + parameters: { + docs: { + description: { + story: + "Open the panel, bump the counter, close, then reopen — the counter retains its value because children are not unmounted. Compare this against `Sheet`, where each open is a fresh mount.", + }, + }, + }, + render: () => ( + } + /> + ), +} + +export const StartsClosed: Story = { + parameters: { + docs: { + description: { + story: + "Demonstrates lazy mount: the panel children do not render until the first open. Inspect the DOM before clicking — there is no panel element until then.", + }, + }, + }, + render: () => ( + + ), +} From 3cd89174e132e795e21f55626f6e16ffba681da6 Mon Sep 17 00:00:00 2001 From: myelinated-wackerow <263208946+myelinated-wackerow@users.noreply.github.com> Date: Tue, 5 May 2026 15:26:33 -0700 Subject: [PATCH 8/9] feat(storybook): add Command story Cover Default, WithGroups, WithSeparators, WithShortcuts, WithDisabledItem, Empty (preset filter triggering CommandEmpty), InputWithKbdShortcut (kbd hint instead of search icon), InputWithCustomIcon, and AsDialog (`CommandDialog` modal palette). Title hierarchy `UI / Primitives / Command` per #18121. Refs: #18121 Co-Authored-By: Claude Opus 4.7 Co-Authored-By: wackerow <54227730+wackerow@users.noreply.github.com> --- .../ui/__stories__/Command.stories.tsx | 252 ++++++++++++++++++ 1 file changed, 252 insertions(+) create mode 100644 src/components/ui/__stories__/Command.stories.tsx diff --git a/src/components/ui/__stories__/Command.stories.tsx b/src/components/ui/__stories__/Command.stories.tsx new file mode 100644 index 00000000000..ef2c47ff558 --- /dev/null +++ b/src/components/ui/__stories__/Command.stories.tsx @@ -0,0 +1,252 @@ +import { Calendar, Settings, Smile, User, Wallet } from "lucide-react" +import type { Meta, StoryObj } from "@storybook/nextjs" + +import { Button } from "../buttons/Button" +import { + Command, + CommandDialog, + CommandEmpty, + CommandGroup, + CommandInput, + CommandItem, + CommandList, + CommandSeparator, + CommandShortcut, +} from "../command" + +const meta = { + title: "UI / Primitives / Command", + component: Command, + parameters: { + docs: { + description: { + component: + "Command palette built on `cmdk`. Compose `Command` > `CommandInput` + `CommandList` containing `CommandGroup`s of `CommandItem`s. `CommandSeparator` divides groups, `CommandShortcut` annotates an item with a keyboard hint, `CommandEmpty` renders when filtering yields no matches. Wrap in `CommandDialog` for a modal palette (Cmd-K style).", + }, + }, + }, +} satisfies Meta + +export default meta + +type Story = StoryObj + +export const Default: Story = { + render: () => ( + + + + No results found. + + Calendar + Settings + Profile + + + + ), +} + +export const WithGroups: Story = { + parameters: { + docs: { + description: { + story: + "Multiple `CommandGroup`s with headings let users scan by category.", + }, + }, + }, + render: () => ( + + + + No results found. + + + + Calendar + + + + Search emoji + + + + + + Profile + + + + Preferences + + + + + ), +} + +export const WithSeparators: Story = { + render: () => ( + + + + + Calendar + Search emoji + + + + Profile + Preferences + + + + ), +} + +export const WithShortcuts: Story = { + parameters: { + docs: { + description: { + story: + "`CommandShortcut` renders a right-aligned hint inside an item. Useful for surfacing keyboard accelerators.", + }, + }, + }, + render: () => ( + + + + + + New file + Ctrl+N + + + Open + Ctrl+O + + + Save + Ctrl+S + + + + + ), +} + +export const WithDisabledItem: Story = { + render: () => ( + + + + + Connect wallet + Send transaction (no wallet) + View history + + + + ), +} + +export const Empty: Story = { + parameters: { + docs: { + description: { + story: + "`CommandEmpty` renders when the active filter excludes every item. Pre-set the input value to show the empty state without typing.", + }, + }, + }, + render: () => ( + + + + No results found for that query. + + Calendar + Settings + + + + ), +} + +export const InputWithKbdShortcut: Story = { + parameters: { + docs: { + description: { + story: + "`CommandInput` accepts `kbdShortcut` to render a right-aligned hint instead of the search icon.", + }, + }, + }, + render: () => ( + + + + + Calendar + Settings + + + + ), +} + +export const InputWithCustomIcon: Story = { + render: () => ( + + + + + Connect MetaMask + Connect Rainbow + + + + ), +} + +const DialogDemo = () => ( + + + + No results found. + + + + Calendar + + + + Search emoji + + + + Profile + + + + +) + +export const AsDialog: Story = { + parameters: { + docs: { + description: { + story: + "`CommandDialog` wraps `Command` in `Dialog` + `DialogContent` for a modal palette.", + }, + }, + }, + render: () => ( + <> + + + + ), +} From c973027b3d136e18910ab8a3fc4e3b4cc2b446be Mon Sep 17 00:00:00 2001 From: myelinated-wackerow <263208946+myelinated-wackerow@users.noreply.github.com> Date: Wed, 13 May 2026 05:34:11 -0700 Subject: [PATCH 9/9] chore(storybook): disable Chromatic snapshots on new overlay stories Per team decision to keep the existing Chromatic snapshot budget controlled while these primitives get visual reference in Storybook. Snapshots can be re-enabled per-story once we assess which views need regression coverage. Co-Authored-By: Claude Opus 4.7 Co-Authored-By: wackerow <54227730+wackerow@users.noreply.github.com> --- src/components/ui/__stories__/Command.stories.tsx | 1 + src/components/ui/__stories__/Dialog.stories.tsx | 1 + src/components/ui/__stories__/DropdownMenu.stories.tsx | 1 + src/components/ui/__stories__/Modal.stories.tsx | 3 +++ src/components/ui/__stories__/PersistentPanel.stories.tsx | 1 + src/components/ui/__stories__/Popover.stories.tsx | 1 + src/components/ui/__stories__/Select.stories.tsx | 1 + src/components/ui/__stories__/Sheet.stories.tsx | 1 + 8 files changed, 10 insertions(+) diff --git a/src/components/ui/__stories__/Command.stories.tsx b/src/components/ui/__stories__/Command.stories.tsx index ef2c47ff558..312e420578b 100644 --- a/src/components/ui/__stories__/Command.stories.tsx +++ b/src/components/ui/__stories__/Command.stories.tsx @@ -18,6 +18,7 @@ const meta = { title: "UI / Primitives / Command", component: Command, parameters: { + chromatic: { disableSnapshot: true }, docs: { description: { component: diff --git a/src/components/ui/__stories__/Dialog.stories.tsx b/src/components/ui/__stories__/Dialog.stories.tsx index 46fb6887633..77f46d89679 100644 --- a/src/components/ui/__stories__/Dialog.stories.tsx +++ b/src/components/ui/__stories__/Dialog.stories.tsx @@ -18,6 +18,7 @@ const meta = { title: "UI / Primitives / Dialog", component: Dialog, parameters: { + chromatic: { disableSnapshot: true }, docs: { description: { component: diff --git a/src/components/ui/__stories__/DropdownMenu.stories.tsx b/src/components/ui/__stories__/DropdownMenu.stories.tsx index 81591789108..9493ab48284 100644 --- a/src/components/ui/__stories__/DropdownMenu.stories.tsx +++ b/src/components/ui/__stories__/DropdownMenu.stories.tsx @@ -22,6 +22,7 @@ const meta = { title: "UI / Primitives / DropdownMenu", component: DropdownMenu, parameters: { + chromatic: { disableSnapshot: true }, docs: { description: { component: diff --git a/src/components/ui/__stories__/Modal.stories.tsx b/src/components/ui/__stories__/Modal.stories.tsx index dd554d1d6c9..ad72e02ce37 100644 --- a/src/components/ui/__stories__/Modal.stories.tsx +++ b/src/components/ui/__stories__/Modal.stories.tsx @@ -6,6 +6,9 @@ import ModalComponent from "../dialog-modal" const meta = { title: "UI / Modal", component: ModalComponent, + parameters: { + chromatic: { disableSnapshot: true }, + }, args: { defaultOpen: true, title: "Modal title", diff --git a/src/components/ui/__stories__/PersistentPanel.stories.tsx b/src/components/ui/__stories__/PersistentPanel.stories.tsx index df69167b8c4..7be32d98205 100644 --- a/src/components/ui/__stories__/PersistentPanel.stories.tsx +++ b/src/components/ui/__stories__/PersistentPanel.stories.tsx @@ -12,6 +12,7 @@ const meta = { children: null, }, parameters: { + chromatic: { disableSnapshot: true }, docs: { description: { component: diff --git a/src/components/ui/__stories__/Popover.stories.tsx b/src/components/ui/__stories__/Popover.stories.tsx index 02dcb927bbd..d63e9c82ed2 100644 --- a/src/components/ui/__stories__/Popover.stories.tsx +++ b/src/components/ui/__stories__/Popover.stories.tsx @@ -13,6 +13,7 @@ const meta = { title: "UI / Primitives / Popover", component: Popover, parameters: { + chromatic: { disableSnapshot: true }, docs: { description: { component: diff --git a/src/components/ui/__stories__/Select.stories.tsx b/src/components/ui/__stories__/Select.stories.tsx index 87de6bbc439..5a2338a70af 100644 --- a/src/components/ui/__stories__/Select.stories.tsx +++ b/src/components/ui/__stories__/Select.stories.tsx @@ -16,6 +16,7 @@ const meta = { title: "UI / Primitives / Select", component: Select, parameters: { + chromatic: { disableSnapshot: true }, docs: { description: { component: diff --git a/src/components/ui/__stories__/Sheet.stories.tsx b/src/components/ui/__stories__/Sheet.stories.tsx index 92c847a65a1..b5c379df1e7 100644 --- a/src/components/ui/__stories__/Sheet.stories.tsx +++ b/src/components/ui/__stories__/Sheet.stories.tsx @@ -16,6 +16,7 @@ const meta = { title: "UI / Primitives / Sheet", component: Sheet, parameters: { + chromatic: { disableSnapshot: true }, docs: { description: { component: