style(storybook): refine form component stories

This commit is contained in:
shkim4 committed 2026-11-09 15:33:23 +09:00
1 parent e47eb040f2
commit f6b92d781a
23 files changed
+471 -182

No files matched your search

+72 -17
View File
@@ -2,7 +2,7 @@ import { useState } from "react"
import { DownloadIcon } from "lucide-react"
import type { Meta, StoryObj } from "@storybook/react-vite"
import { Button, type ButtonAppearance } from "@hm-design/ui"
import { StateBoard } from "@/components/story-board"
import { StateBoard, type BoardState } from "@/components/story-board"
import { StoryPage, StoryPageHeader, StorySection } from "@/components/story-page"
// Story structure follows ui_viz (implementations/web/storybook/Button.stories.jsx):
@@ -159,12 +159,13 @@ const standardStates = [
// Without `title` the table sits under a StateBoard label (representative page) and drops the Role column:
// every button already shows its role name, so the row header would repeat it.
function StandardStateTable({ appearance, title }: { appearance: "solid" | "outline"; title?: string }) {
// `compact` (representative page only) tightens the cell padding so a 5x4 / 4x4 matrix stays short; buttons are untouched.
function StandardStateTable({ appearance, title, compact = false }: { appearance: "solid" | "outline"; title?: string; compact?: boolean }) {
return (
<section className="min-w-0">
{title ? <h3 className={heading}>{title}</h3> : null}
<div className={`${title ? "mt-3 " : ""}min-w-0 max-w-full overflow-x-auto`}>
<table className="w-full border-collapse [&_td]:p-3 [&_th]:p-3 [&_th]:text-left">
<table className={`w-full border-collapse [&_th]:text-left ${compact ? "[&_td]:px-2 [&_td]:py-1 [&_th]:px-2 [&_th]:py-1" : "[&_td]:p-3 [&_th]:p-3"}`}>
<thead>
<tr>
{title ? <th scope="col">Role</th> : null}
@@ -230,31 +231,85 @@ function DestructiveBoard() {
)
}
// Representative Story: the common Story page (Overview → Sizes → Variants → States) with the ui_viz Button "All" boards.
// Compact PC layout of the representative page (Storybook presentation only; Button itself is unchanged).
// Page and section spacing are tightened for this Story only through `compactPage`; the shared story-page.css is not edited.
// The important modifier is required because the shared rules use the same specificity.
const compactPage = [
"py-4!",
"[&_.sb-page-header]:pb-3!",
"[&_.sb-page-title]:mt-1!",
"[&_.sb-page-title]:mb-2!",
"[&_.sb-page-section]:mt-5!",
"[&_.sb-page-section-header]:pb-2!",
"[&_.sb-page-section-header]:mb-3!",
].join(" ")
// 8px gap so the three Brand sizes stay on one line in the narrowest (Brand) column, same as the Variants rows.
const compactRow = "flex flex-wrap items-center gap-2"
// Equal two columns over the whole content width. StateBoard's own auto-fill grid left an empty third column
// (2 items in a grid that fits 3), so the pairs are laid out here and each StateBoard holds one specimen.
const twoColumns = "grid gap-6 min-[900px]:grid-cols-[repeat(2,minmax(0,1fr))]"
// StateBoard lays its states out in an auto-fill grid. A board that holds one specimen inside a wide column would put it
// in the first of several inner columns (about half width at 1920px), so `min` is set larger than any column to force one.
const SingleColumnBoard = ({ states }: { states: BoardState[] }) => <StateBoard min={10000} states={states} />
const columnsAuto = (min: number) => `[grid-template-columns:repeat(auto-fit,minmax(min(100%,${min}px),1fr))]`
// 8px gap keeps all three Variants groups on one line at the 1280px PC width (Brand 3 + Solid 5 + Outline 4 buttons in a 3-column grid); narrower widths wrap.
function CompactRoleRow({ appearance }: { appearance: (typeof appearances)[number][0] }) {
return (
<div className="flex flex-wrap items-center gap-2">
{rolesFor(appearance).map(([variant, label]) => (
<Button key={variant} variant={variant} appearance={appearance}>
{label}
</Button>
))}
</div>
)
}
function CompactSizeRow({ appearance }: { appearance: "brand" | "solid" }) {
return (
<div className={compactRow}>
{sizes.map(([size, label]) => (
<Button key={size} appearance={appearance} size={size}>
{label}
</Button>
))}
</div>
)
}
// Representative Story: the common Story page (Sizes → Variants → States) with the ui_viz Button "All" boards.
// The former Overview button was the default Brand Medium button already shown under Sizes, so Overview is merged into Sizes.
// Named after the component, as in ui_viz. It is the only Button entry in the default sidebar, so Storybook shows Button as a single 1-depth item.
export const All: Story = {
name: "Button",
parameters: { layout: "fullscreen" },
render: () => (
<StoryPage>
<StoryPage className={compactPage}>
<StoryPageHeader title="Button" />
<StorySection title="Overview">
<Button>Button</Button>
</StorySection>
<StorySection title="Sizes">
<SizesBoard />
{/* Same columns as Variants below (Brand / Standard Solid / Standard Outline): Brand and Standard Solid sizes line up with
their Variants columns and the third column stays empty. */}
<div className={`grid gap-6 ${columnsAuto(260)} min-[960px]:[grid-template-columns:minmax(0,234fr)_minmax(0,353fr)_minmax(0,271fr)]`}>
<SingleColumnBoard states={[{ id: "sizes-brand", label: "Brand", children: <CompactSizeRow appearance="brand" /> }]} />
<SingleColumnBoard states={[{ id: "sizes-solid", label: "Standard Solid", children: <CompactSizeRow appearance="solid" /> }]} />
</div>
</StorySection>
<StorySection title="Variants">
<StateBoard states={appearances.map(([appearance, label]) => ({ id: `roles-${appearance}`, label, wide: true, children: <RoleRow appearance={appearance} /> }))} />
<div className={`grid gap-6 ${columnsAuto(260)} min-[960px]:[grid-template-columns:minmax(0,234fr)_minmax(0,353fr)_minmax(0,271fr)]`}>
{appearances.map(([appearance, label]) => (
<SingleColumnBoard key={appearance} states={[{ id: `roles-${appearance}`, label, children: <CompactRoleRow appearance={appearance} /> }]} />
))}
</div>
</StorySection>
<StorySection title="States">
<StateBoard
states={[
{ id: "brand-states", label: "Brand", wide: true, children: <BrandStates showNote={false} /> },
{ id: "standard-solid-states", label: "Standard Solid", wide: true, children: <StandardStateTable appearance="solid" /> },
{ id: "standard-outline-states", label: "Standard Outline", wide: true, children: <StandardStateTable appearance="outline" /> },
]}
/>
<div className="grid gap-6">
<SingleColumnBoard states={[{ id: "brand-states", label: "Brand", wide: true, children: <BrandStates showNote={false} /> }]} />
<div className={twoColumns}>
<SingleColumnBoard states={[{ id: "standard-solid-states", label: "Standard Solid", children: <StandardStateTable appearance="solid" compact /> }]} />
<SingleColumnBoard states={[{ id: "standard-outline-states", label: "Standard Outline", children: <StandardStateTable appearance="outline" compact /> }]} />
</div>
</div>
</StorySection>
</StoryPage>
),