Files
HMDesign/src/components/accordion.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

144 lines
6.3 KiB
TypeScript

import type { ReactNode } from "react"
import type { Meta, StoryObj } from "@storybook/react-vite"
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from "@hm-design/ui"
import { StateBoard } from "@/components/story-board"
import { StoryPage, StoryPageHeader, StorySection } from "@/components/story-page"
// The representative "Accordion" Story puts each Radix mode on one page with real, clickable items. The same sections also exist
// as Stories tagged `verification`, which the sidebar hides by default (see .storybook/main.ts). Modes use the Radix API
// (type, collapsible, defaultValue, disabled); there are no HM variant props.
const longText = "긴 텍스트는 줄바꿈으로 전체를 표시합니다. 공백 없는 긴 문자열도 너비를 밀어내지 않고 줄바꿈합니다. ".repeat(6)
const narrow = (children: ReactNode) => <div className="w-full max-w-md">{children}</div>
function SingleSpecimen() {
return narrow(
<Accordion type="single" defaultValue="a">
<AccordionItem value="a">
<AccordionTrigger>배송은 얼마나 걸리나요?</AccordionTrigger>
<AccordionContent>주문 후 2~3일 안에 도착합니다.</AccordionContent>
</AccordionItem>
<AccordionItem value="b">
<AccordionTrigger>반품은 어떻게 하나요?</AccordionTrigger>
<AccordionContent>수령 후 7일 이내에 마이페이지에서 신청합니다.</AccordionContent>
</AccordionItem>
<AccordionItem value="c">
<AccordionTrigger>결제 수단은 무엇이 있나요?</AccordionTrigger>
<AccordionContent>카드와 계좌이체를 지원합니다.</AccordionContent>
</AccordionItem>
</Accordion>
)
}
function CollapsibleSingleSpecimen() {
return narrow(
<Accordion type="single" collapsible>
<AccordionItem value="a">
<AccordionTrigger>열린 항목을 다시 눌러 닫을 수 있습니다</AccordionTrigger>
<AccordionContent>collapsible 속성이 있어 모두 닫힌 상태가 가능합니다.</AccordionContent>
</AccordionItem>
<AccordionItem value="b">
<AccordionTrigger>다른 항목</AccordionTrigger>
<AccordionContent>한 번에 하나만 열립니다.</AccordionContent>
</AccordionItem>
</Accordion>
)
}
function MultipleSpecimen() {
return narrow(
<Accordion type="multiple" defaultValue={["a", "b"]}>
<AccordionItem value="a">
<AccordionTrigger>기본 정보</AccordionTrigger>
<AccordionContent>여러 항목을 동시에 열 수 있습니다.</AccordionContent>
</AccordionItem>
<AccordionItem value="b">
<AccordionTrigger>상세 정보</AccordionTrigger>
<AccordionContent>처음부터 열린 항목입니다.</AccordionContent>
</AccordionItem>
<AccordionItem value="c">
<AccordionTrigger>기타</AccordionTrigger>
<AccordionContent>접힌 항목입니다.</AccordionContent>
</AccordionItem>
</Accordion>
)
}
function DisabledSpecimen() {
return narrow(
<Accordion type="single" collapsible>
<AccordionItem value="a">
<AccordionTrigger>사용 가능한 항목</AccordionTrigger>
<AccordionContent>키보드 이동은 비활성 항목을 건너뜁니다.</AccordionContent>
</AccordionItem>
<AccordionItem value="b" disabled>
<AccordionTrigger>비활성 항목</AccordionTrigger>
<AccordionContent>열 수 없습니다.</AccordionContent>
</AccordionItem>
<AccordionItem value="c">
<AccordionTrigger>다음 항목</AccordionTrigger>
<AccordionContent>비활성 항목 다음입니다.</AccordionContent>
</AccordionItem>
</Accordion>
)
}
function LongSpecimen() {
return narrow(
<Accordion type="single" defaultValue="a">
<AccordionItem value="a">
<AccordionTrigger>{`여러 줄로 이어지는 긴 Trigger 문구입니다. ${longText.slice(0, 60)}`}</AccordionTrigger>
<AccordionContent>
<p>{longText}</p>
<p className="mt-2">AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA</p>
</AccordionContent>
</AccordionItem>
<AccordionItem value="b">
<AccordionTrigger>두 번째 항목</AccordionTrigger>
<AccordionContent>짧은 내용</AccordionContent>
</AccordionItem>
</Accordion>
)
}
const sections = [
{ id: "single", label: "Single", node: <SingleSpecimen /> },
{ id: "collapsible-single", label: "Collapsible Single", node: <CollapsibleSingleSpecimen /> },
{ id: "multiple", label: "Multiple", node: <MultipleSpecimen /> },
{ id: "disabled", label: "Disabled Item", node: <DisabledSpecimen /> },
{ id: "long", label: "Long Content", node: <LongSpecimen /> },
]
// No `component` here: Accordion props are a union (single | multiple), so Stories without args would not type-check.
const meta = { title: "Components/Disclosure/Accordion", parameters: { layout: "padded" } } satisfies Meta
export default meta
type Story = StoryObj<typeof meta>
const board = (...ids: string[]) => (
<StateBoard min={360} states={sections.filter((s) => ids.includes(s.id)).map((s) => ({ id: s.id, label: s.label, children: s.node }))} />
)
// Representative Story: the common Story page; the sections group the same specimens.
export const All: Story = {
name: "Accordion",
parameters: { layout: "fullscreen", controls: { disable: true } },
render: () => (
<StoryPage>
<StoryPageHeader title="Accordion" />
<StorySection title="Overview">{board("single")}</StorySection>
<StorySection title="Variants">
{board("collapsible-single", "multiple")}
</StorySection>
<StorySection title="States">{board("disabled")}</StorySection>
<StorySection title="Usage">{board("long")}</StorySection>
</StoryPage>
),
}
// Verification Stories: the same specimens, hidden from the sidebar (tag `verification`).
export const Single: Story = { tags: ["verification"], render: () => <SingleSpecimen /> }
export const Multiple: Story = { tags: ["verification"], render: () => <MultipleSpecimen /> }
export const Disabled: Story = { tags: ["verification"], render: () => <DisabledSpecimen /> }
export const LongContent: Story = { tags: ["verification"], name: "Long Content", render: () => <LongSpecimen /> }