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

131 lines
4.6 KiB
TypeScript

import type { Meta, StoryObj } from "@storybook/react-vite"
// Usage: preserved native label fixtures do not define a new-screen exception. Use public Label
// for supported fields (docs/components/label.md §5); copy only actions requested by the screen.
import { Button, Input, Sheet, SheetBody, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetTitle, SheetTrigger } from "@hm-design/ui"
import { StoryPage, StoryPageHeader, StorySection } from "@/components/story-page"
// Sheet is a modal Radix overlay (Portal), so the representative Story is a row of triggers that open the real Sheet for each
// side and content case. The same cases start open in the verification Stories. Radix owns focus trap, Escape and focus return.
type Side = "top" | "right" | "bottom" | "left"
const longText = "긴 내용은 Body 영역만 세로로 scroll 하고 Header와 Footer는 그대로 보입니다. "
function Frame({
trigger,
side,
defaultOpen,
title,
description,
children,
}: {
trigger: string
side?: Side
defaultOpen?: boolean
title: string
description?: string
children: React.ReactNode
}) {
return (
<Sheet defaultOpen={defaultOpen}>
<SheetTrigger asChild>
<Button variant="primary" appearance="outline">
{trigger}
</Button>
</SheetTrigger>
<SheetContent side={side}>
<SheetHeader>
<SheetTitle>{title}</SheetTitle>
{description ? <SheetDescription>{description}</SheetDescription> : null}
</SheetHeader>
<SheetBody>{children}</SheetBody>
<SheetFooter>
<SheetClose asChild>
<Button variant="cancel" appearance="solid">
취소
</Button>
</SheetClose>
<SheetClose asChild>
<Button variant="primary" appearance="solid">
저장
</Button>
</SheetClose>
</SheetFooter>
</SheetContent>
</Sheet>
)
}
function SideCase({ side, defaultOpen }: { side: Side; defaultOpen?: boolean }) {
return (
<Frame trigger={side} side={side} defaultOpen={defaultOpen} title="설정" description="화면 가장자리에서 열리는 보조 패널입니다.">
<p>{`side="${side}"`}</p>
</Frame>
)
}
function FormCase({ defaultOpen }: { defaultOpen?: boolean }) {
return (
<Frame trigger="폼" defaultOpen={defaultOpen} title="프로젝트 정보">
<div className="grid gap-2">
<label htmlFor="sheet-form-name" className="text-sm font-medium text-muted-foreground">
프로젝트명
</label>
<Input id="sheet-form-name" size="medium" placeholder="프로젝트명을 입력하세요" />
</div>
</Frame>
)
}
function LongCase({ defaultOpen }: { defaultOpen?: boolean }) {
return (
<Frame trigger="긴 내용" defaultOpen={defaultOpen} title="긴 내용" description="Body만 scroll 합니다.">
{Array.from({ length: 30 }, (_, i) => (
<p key={i} className="mb-3">
{longText}
</p>
))}
</Frame>
)
}
const meta = { title: "Components/Overlay/Sheet", component: Sheet, parameters: { layout: "centered" } } satisfies Meta<typeof Sheet>
export default meta
type Story = StoryObj<typeof meta>
export const All: Story = {
name: "Sheet",
parameters: { layout: "fullscreen", controls: { disable: true } },
render: () => (
<StoryPage>
<StoryPageHeader
title="Sheet"
/>
<StorySection title="Overview">
<SideCase side="right" />
</StorySection>
<StorySection title="Variants">
<div className="flex flex-wrap gap-3">
<SideCase side="left" />
<SideCase side="top" />
<SideCase side="bottom" />
</div>
</StorySection>
<StorySection title="Usage">
<div className="flex flex-wrap gap-3">
<FormCase />
<LongCase />
</div>
</StorySection>
</StoryPage>
),
}
// Verification Stories: the same cases starting open, hidden from the sidebar (tag `verification`).
export const Right: Story = { tags: ["verification"], render: () => <SideCase side="right" defaultOpen /> }
export const Left: Story = { tags: ["verification"], render: () => <SideCase side="left" defaultOpen /> }
export const Top: Story = { tags: ["verification"], render: () => <SideCase side="top" defaultOpen /> }
export const Bottom: Story = { tags: ["verification"], render: () => <SideCase side="bottom" defaultOpen /> }
export const Form: Story = { tags: ["verification"], render: () => <FormCase defaultOpen /> }
export const LongContent: Story = { tags: ["verification"], name: "Long Content", render: () => <LongCase defaultOpen /> }