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

113 lines
4.3 KiB
TypeScript

import { useState, type ReactNode } from "react"
import type { Meta, StoryObj } from "@storybook/react-vite"
import { CalendarIcon, SettingsIcon, UserIcon } from "lucide-react"
import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandSeparator, CommandShortcut } from "@hm-design/ui"
import { StateBoard } from "@/components/story-board"
import { StoryPage, StoryPageHeader, StorySection } from "@/components/story-page"
// The representative "Command" Story shows real, typeable lists; the same specimens also exist as Stories tagged `verification`
// (hidden from the sidebar). Filtering is cmdk's. The bordered box is Story composition (Command draws no border of its own).
// A selection log shows that Enter / click reach `onSelect`.
const box = (node: ReactNode) => <div className="w-full max-w-sm rounded-md border">{node}</div>
// `showValue={false}` hides the value readout on the representative page; the verification Stories keep it.
function Basic({ showValue = true }: { showValue?: boolean }) {
const [last, setLast] = useState("-")
return (
<div className="grid gap-2">
{box(
<Command>
<CommandInput placeholder="검색..." />
<CommandList>
<CommandEmpty>결과가 없습니다.</CommandEmpty>
<CommandItem onSelect={setLast}>사과</CommandItem>
<CommandItem onSelect={setLast}>바나나</CommandItem>
<CommandItem onSelect={setLast} disabled>
체리 (품절)
</CommandItem>
<CommandItem onSelect={setLast}>포도</CommandItem>
</CommandList>
</Command>
)}
{showValue ? <span className="text-sm text-muted-foreground" data-testid="last">{`선택: ${last}`}</span> : null}
</div>
)
}
function Grouped() {
return box(
<Command>
<CommandInput placeholder="명령 검색..." />
<CommandList>
<CommandEmpty>결과가 없습니다.</CommandEmpty>
<CommandGroup heading="제안">
<CommandItem>
<CalendarIcon />
일정 보기
</CommandItem>
<CommandItem>
<UserIcon />
프로필
<CommandShortcut>⌘P</CommandShortcut>
</CommandItem>
</CommandGroup>
<CommandSeparator />
<CommandGroup heading="설정">
<CommandItem>
<SettingsIcon />
환경설정
<CommandShortcut>⌘,</CommandShortcut>
</CommandItem>
<CommandItem disabled>결제 (사용 불가)</CommandItem>
</CommandGroup>
</CommandList>
</Command>
)
}
function Long() {
return box(
<Command>
<CommandInput placeholder="항목 검색..." />
<CommandList>
<CommandEmpty>결과가 없습니다.</CommandEmpty>
{Array.from({ length: 30 }, (_, i) => (
<CommandItem key={i}>{`항목 ${i + 1} — 긴 항목 이름도 줄바꿈 없이 한 줄로 이어질 수 있습니다`}</CommandItem>
))}
</CommandList>
</Command>
)
}
const sections = [
{ id: "basic", label: "Search", node: <Basic showValue={false} /> },
{ id: "grouped", label: "Groups", node: <Grouped /> },
{ id: "long", label: "Long list", node: <Long /> },
]
const meta = { title: "Components/Search & Date/Command", component: Command, parameters: { layout: "padded" } } satisfies Meta<typeof Command>
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: "Command",
parameters: { layout: "fullscreen", controls: { disable: true } },
render: () => (
<StoryPage>
<StoryPageHeader title="Command" />
<StorySection title="Overview">{board("basic")}</StorySection>
<StorySection title="Variants">{board("grouped")}</StorySection>
<StorySection title="Usage">{board("long")}</StorySection>
</StoryPage>
),
}
export const Search: Story = { tags: ["verification"], render: () => <Basic /> }
export const GroupedList: Story = { tags: ["verification"], name: "Grouped", render: () => <Grouped /> }