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.
131 lines
4.6 KiB
TypeScript
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 /> }
|