Files
HMDesign/src/components/card.stories.tsx
T
shkim4 4c29894e56 refactor(storybook): simplify component and preview examples
Drop page / section descriptions, state notes and repeated captions from
the representative Stories of 44 components, the Patterns, Center
Graphic and OrgChart pages. Value readouts, spec lists and captions hide
only on the representative page; hidden verification Stories keep them.
Tabs Default API and the Filter Bar draft / applied counts move to the
hidden DefaultApi and DraftApplied Stories; Center Overview is hidden
with its direct URL kept. Story IDs are unchanged.

Align the component, pattern and OrgChart docs with the pages, point the
quality reference check at the DraftApplied Story, and make the P8
styles check expect no section header on the preview-only Expanded page.
2026-10-23 00:28:05 +09:00

135 lines
5.1 KiB
TypeScript

import type { ReactNode } from "react"
import type { Meta, StoryObj } from "@storybook/react-vite"
import { Button } from "@hm-design/ui"
import { Card, CardAction, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, type CardSurface } from "@hm-design/ui"
import { Input } from "@hm-design/ui"
import { StateBoard } from "@/components/story-board"
import { StoryPage, StoryPageHeader, StorySection } from "@/components/story-page"
// Story structure follows Button / Input / Select: one representative "Card" Story shows the ui_viz cases on one page,
// the same specimens also exist as separate Stories tagged `verification` (hidden from the sidebar, see .storybook/main.ts).
// ui_viz (content-container.md Type B Content Card) defines only the Default state, two surfaces, a Title with an optional
// Meta / Action, and a body. Hover / Selected / Clickable, Footer and media are not defined, so they are not shown here
// except in the Compatibility Story.
function Body({ children }: { children?: ReactNode }) {
return <p className="text-sm">{children ?? "본문은 카드 안쪽 여백 24px 안에 놓입니다. 내용물의 모양은 각 컴포넌트가 정합니다."}</p>
}
function Specimen({ surface = "surface", action, title = "프로젝트 정보", children }: { surface?: CardSurface; action?: boolean; title?: string; children?: ReactNode }) {
return (
<Card surface={surface}>
<CardHeader>
<CardTitle>{title}</CardTitle>
{action ? (
<CardAction>
<Button variant="primary" appearance="solid" size="sm">
자세히 보기
</Button>
</CardAction>
) : null}
</CardHeader>
<CardContent>{children ?? <Body />}</CardContent>
</Card>
)
}
function FormSpecimen() {
return (
<Card>
<CardHeader>
<CardTitle>입력 폼</CardTitle>
</CardHeader>
<CardContent>
<div className="grid gap-2">
<label htmlFor="card-form-name" className="text-sm font-medium text-muted-foreground">
프로젝트명
</label>
<Input id="card-form-name" size="medium" placeholder="프로젝트명을 입력하세요" />
</div>
</CardContent>
</Card>
)
}
const meta = {
title: "Components/Data & Display/Card",
component: Card,
parameters: { layout: "padded" },
argTypes: {
surface: { control: "inline-radio", options: ["surface", "content"], description: "ui_viz surface 축: White(기본) / Beige." },
},
} satisfies Meta<typeof Card>
export default meta
type Story = StoryObj<typeof meta>
// Representative Story: the common Story page (Overview → Variants → Usage) with every ui_viz case. Named after the component, as in ui_viz.
// It is the only Card entry in the default sidebar, so Storybook shows Card as a single 1-depth item.
export const All: Story = {
name: "Card",
parameters: { layout: "fullscreen", controls: { disable: true } },
render: () => (
<StoryPage>
<StoryPageHeader title="Card" />
<StorySection title="Overview">
<StateBoard min={360} states={[{ id: "surface", label: "White", children: <Specimen /> }]} />
</StorySection>
<StorySection title="Variants">
<StateBoard
min={360}
states={[
{ id: "content", label: "Content (Beige)", children: <Specimen surface="content" /> },
{ id: "action", label: "Title + Action", children: <Specimen action /> },
]}
/>
</StorySection>
<StorySection title="Usage">
<StateBoard min={360} states={[{ id: "form", label: "본문 조합 · Input", children: <FormSpecimen /> }]} />
</StorySection>
</StoryPage>
),
}
// Verification Stories: the same specimens as separate Stories, hidden from the sidebar (tag `verification`).
export const Surfaces: Story = {
tags: ["verification"],
render: () => (
<div className="grid max-w-md gap-6">
<Specimen />
<Specimen surface="content" />
</div>
),
}
export const Action: Story = { tags: ["verification"], render: () => <div className="max-w-md"><Specimen action /></div> }
// HM only: not part of the ui_viz Card spec. Footer, Description and size="sm" are shadcn defaults that ui_viz does not define.
export const Compatibility: Story = {
tags: ["verification"],
render: () => (
<div className="grid max-w-md gap-6">
<Card>
<CardHeader>
<CardTitle>shadcn 호환</CardTitle>
<CardDescription>ui_viz에 없는 Description 입니다. shadcn 기본 스타일을 씁니다.</CardDescription>
</CardHeader>
<CardContent>
<Body>Footer도 ui_viz 규격이 없어 shadcn 기본 표면을 씁니다.</Body>
</CardContent>
<CardFooter>
<Button variant="cancel" appearance="solid" size="sm">
닫기
</Button>
</CardFooter>
</Card>
<Card size="sm">
<CardHeader>
<CardTitle>size="sm"</CardTitle>
</CardHeader>
<CardContent>
<Body>shadcn 작은 크기(여백 12px)입니다. ui_viz에 없습니다.</Body>
</CardContent>
</Card>
</div>
),
}