Initial commit: HMDesignSystem baseline
This commit is contained in:
commit
b6a0a61103
486 files changed
+68432
No files matched your search
@@ -0,0 +1,86 @@
|
||||
import { useState, type ReactNode } from "react"
|
||||
import type { Meta, StoryObj } from "@storybook/react-vite"
|
||||
import type { DateRange } from "react-day-picker"
|
||||
import { Calendar } from "@hm-design/ui"
|
||||
import { StateBoard } from "@/components/story-board"
|
||||
import { STORY_KICKER, StoryPage, StoryPageHeader, StorySection } from "@/components/story-page"
|
||||
|
||||
// The representative "Calendar" Story shows each mode with real, clickable calendars; the same specimens also exist as Stories
|
||||
// tagged `verification` (hidden from the sidebar). `today` is pinned to 2026-10-02 so the Story looks the same on any day; this
|
||||
// uses react-day-picker's own `today` prop, not an HM state. Korean is the Calendar default locale.
|
||||
|
||||
const today = new Date(2026, 9, 2)
|
||||
const month = new Date(2026, 9, 1)
|
||||
|
||||
const box = (node: ReactNode) => <div className="rounded-md border bg-card w-fit">{node}</div>
|
||||
|
||||
function Single() {
|
||||
const [date, setDate] = useState<Date | undefined>(new Date(2026, 9, 15))
|
||||
return (
|
||||
<div className="grid gap-2">
|
||||
{box(<Calendar mode="single" selected={date} onSelect={setDate} defaultMonth={month} today={today} />)}
|
||||
<span className="text-sm text-muted-foreground" data-testid="single">{`선택: ${date ? date.toLocaleDateString("ko-KR") : "없음"}`}</span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function DisabledDates() {
|
||||
const [date, setDate] = useState<Date | undefined>()
|
||||
return box(
|
||||
<Calendar
|
||||
mode="single"
|
||||
selected={date}
|
||||
onSelect={setDate}
|
||||
defaultMonth={month}
|
||||
today={today}
|
||||
disabled={[{ before: new Date(2026, 9, 5) }, { dayOfWeek: [0] }]}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function Range() {
|
||||
const [range, setRange] = useState<DateRange | undefined>({ from: new Date(2026, 9, 8), to: new Date(2026, 9, 14) })
|
||||
return box(<Calendar mode="range" selected={range} onSelect={setRange} defaultMonth={month} today={today} />)
|
||||
}
|
||||
|
||||
function TwoMonths() {
|
||||
return box(<Calendar mode="single" numberOfMonths={2} defaultMonth={month} today={today} />)
|
||||
}
|
||||
|
||||
const sections = [
|
||||
{ id: "single", label: "Single (today = 10/2, selected = 10/15)", node: <Single /> },
|
||||
{ id: "disabled", label: "Disabled dates (before 10/5, Sundays)", node: <DisabledDates /> },
|
||||
{ id: "range", label: "Range", node: <Range /> },
|
||||
{ id: "months", label: "Two months", node: <TwoMonths /> },
|
||||
]
|
||||
|
||||
// No `component`: Calendar props are a union of selection modes, so Stories without args would not type-check.
|
||||
const meta = { title: "Components/Search & Date/Calendar", parameters: { layout: "padded" } } satisfies Meta
|
||||
export default meta
|
||||
type Story = StoryObj<typeof meta>
|
||||
|
||||
const board = (...ids: string[]) => (
|
||||
<StateBoard min={300} states={sections.filter((s) => ids.includes(s.id)).map((s) => ({ id: s.id, label: s.label, wide: s.id === "months", children: s.node }))} />
|
||||
)
|
||||
|
||||
// Representative Story: the common Story page; the sections group the same specimens.
|
||||
export const All: Story = {
|
||||
name: "Calendar",
|
||||
parameters: { layout: "fullscreen", controls: { disable: true } },
|
||||
render: () => (
|
||||
<StoryPage>
|
||||
<StoryPageHeader
|
||||
kicker={STORY_KICKER.components}
|
||||
title="Calendar"
|
||||
description="날짜를 고르는 달력입니다. 기본 locale은 한국어이며, 이 화면의 오늘은 2026-10-02로 고정되어 있습니다."
|
||||
/>
|
||||
<StorySection title="Overview" description='mode="single"'>{board("single")}</StorySection>
|
||||
<StorySection title="Variants" description='mode="range"와 여러 달 표시(numberOfMonths=2)입니다.'>{board("range", "months")}</StorySection>
|
||||
<StorySection title="States" description="선택할 수 없는 날짜(disabled)입니다.">{board("disabled")}</StorySection>
|
||||
</StoryPage>
|
||||
),
|
||||
}
|
||||
|
||||
export const SingleSelection: Story = { tags: ["verification"], name: "Single", render: () => <Single /> }
|
||||
export const DisabledSelection: Story = { tags: ["verification"], name: "Disabled Dates", render: () => <DisabledDates /> }
|
||||
export const RangeSelection: Story = { tags: ["verification"], name: "Range", render: () => <Range /> }
|
||||
Reference in new issue
Block a user