Files
HMDesign/src/components/breadcrumb.stories.tsx
T

150 lines
5.5 KiB
TypeScript

import { Fragment, type ReactNode } from "react"
import type { Meta, StoryObj } from "@storybook/react-vite"
import {
Breadcrumb,
BreadcrumbEllipsis,
BreadcrumbItem,
BreadcrumbLink,
BreadcrumbList,
BreadcrumbPage,
BreadcrumbSeparator,
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "@hm-design/ui"
import { StateBoard } from "@/components/story-board"
import { STORY_KICKER, StoryPage, StoryPageHeader, StorySection } from "@/components/story-page"
// The representative "Breadcrumb" Story shows each case on one page (real links with sample hrefs); the same specimens also exist
// as Stories tagged `verification` (hidden from the sidebar). BreadcrumbEllipsis is presentational: the Dropdown Menu example is
// a consumer composition, not part of Breadcrumb.
const narrow = (children: ReactNode) => <div className="w-full max-w-xl">{children}</div>
function Basic() {
return (
<Breadcrumb>
<BreadcrumbList>
<BreadcrumbItem>
<BreadcrumbLink href="#home">홈</BreadcrumbLink>
</BreadcrumbItem>
<BreadcrumbSeparator />
<BreadcrumbItem>
<BreadcrumbLink href="#users">사용자</BreadcrumbLink>
</BreadcrumbItem>
<BreadcrumbSeparator />
<BreadcrumbItem>
<BreadcrumbPage>상세</BreadcrumbPage>
</BreadcrumbItem>
</BreadcrumbList>
</Breadcrumb>
)
}
function Ellipsis() {
return (
<Breadcrumb>
<BreadcrumbList>
<BreadcrumbItem>
<BreadcrumbLink href="#home">홈</BreadcrumbLink>
</BreadcrumbItem>
<BreadcrumbSeparator />
<BreadcrumbItem>
<DropdownMenu>
<DropdownMenuTrigger aria-label="중간 경로 보기">
<BreadcrumbEllipsis />
</DropdownMenuTrigger>
<DropdownMenuContent align="start">
<DropdownMenuItem>문서</DropdownMenuItem>
<DropdownMenuItem>설정</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</BreadcrumbItem>
<BreadcrumbSeparator />
<BreadcrumbItem>
<BreadcrumbLink href="#project">프로젝트</BreadcrumbLink>
</BreadcrumbItem>
<BreadcrumbSeparator />
<BreadcrumbItem>
<BreadcrumbPage>상세</BreadcrumbPage>
</BreadcrumbItem>
</BreadcrumbList>
</Breadcrumb>
)
}
function Long() {
const labels = ["홈", "프로젝트", "한맥 프로젝트 관리 시스템", "노선 정보", "시설물 목록", "교량", "상세 정보 및 점검 이력"]
return (
<Breadcrumb>
<BreadcrumbList>
{labels.map((label, i) => (
<Fragment key={label}>
<BreadcrumbItem>
{i === labels.length - 1 ? <BreadcrumbPage>{label}</BreadcrumbPage> : <BreadcrumbLink href={`#${i}`}>{label}</BreadcrumbLink>}
</BreadcrumbItem>
{i < labels.length - 1 ? <BreadcrumbSeparator /> : null}
</Fragment>
))}
</BreadcrumbList>
</Breadcrumb>
)
}
// asChild: the link is any element (here a plain <a> standing in for a router Link) and keeps the Breadcrumb look.
function CustomLink() {
return (
<Breadcrumb>
<BreadcrumbList>
<BreadcrumbItem>
<BreadcrumbLink asChild>
<a href="#router-home">홈 (custom link)</a>
</BreadcrumbLink>
</BreadcrumbItem>
<BreadcrumbSeparator>/</BreadcrumbSeparator>
<BreadcrumbItem>
<BreadcrumbPage>현재 페이지</BreadcrumbPage>
</BreadcrumbItem>
</BreadcrumbList>
</Breadcrumb>
)
}
const sections = [
{ id: "basic", label: "Basic (3 depth)", node: <Basic /> },
{ id: "ellipsis", label: "Ellipsis", note: "BreadcrumbEllipsis는 표시용입니다. 메뉴 연결은 소비 조합(Dropdown Menu)", node: <Ellipsis /> },
{ id: "long", label: "Long path", note: "좁은 폭에서 줄바꿈", node: <Long /> },
{ id: "custom", label: "Custom link (asChild) and separator", node: <CustomLink /> },
]
const meta = { title: "Components/Navigation/Breadcrumb", component: Breadcrumb, parameters: { layout: "padded" } } satisfies Meta<typeof Breadcrumb>
export default meta
type Story = StoryObj<typeof meta>
const board = (...ids: string[]) => (
<StateBoard min={420} states={sections.filter((s) => ids.includes(s.id)).map((s) => ({ id: s.id, label: s.label, note: s.note, children: narrow(s.node) }))} />
)
// Representative Story: the common Story page; the sections group the same specimens.
export const All: Story = {
name: "Breadcrumb",
parameters: { layout: "fullscreen", controls: { disable: true } },
render: () => (
<StoryPage>
<StoryPageHeader
kicker={STORY_KICKER.components}
title="Breadcrumb"
description="현재 페이지까지의 상위 경로를 보여주는 내비게이션입니다. 마지막 항목은 현재 페이지입니다."
/>
<StorySection title="Overview">{board("basic")}</StorySection>
<StorySection title="Variants" description="중간 경로 생략(Ellipsis)과 사용자 링크 · 구분자입니다.">{board("ellipsis", "custom")}</StorySection>
<StorySection title="Usage">{board("long")}</StorySection>
</StoryPage>
),
}
export const Basic3: Story = { tags: ["verification"], name: "Basic", render: () => <Basic /> }
export const WithEllipsis: Story = { tags: ["verification"], name: "Ellipsis", render: () => <Ellipsis /> }
export const LongPath: Story = { tags: ["verification"], name: "Long Path", render: () => narrow(<Long />) }