Files
HMDesign/src/components/menubar.stories.tsx
T
shkim4 da0e647b49 docs(storybook): simplify sidebar Story pages around the UI
Drop the category kicker from the 83 sidebar Stories (kicker is now optional;
hidden verification Stories keep it) and shorten or remove repeated, obvious
or internal page/section descriptions. Required specs, limits, virtual-data
and undecided-value notices stay. Story IDs, exports, fixtures, shared state
notes and package code are unchanged. Record: docs/reports/storybook-ui-simplification-2026-10-22.md
2026-10-22 16:13:58 +09:00

148 lines
5.0 KiB
TypeScript

import { useState } from "react"
import type { Meta, StoryObj } from "@storybook/react-vite"
import {
Menubar,
MenubarCheckboxItem,
MenubarContent,
MenubarGroup,
MenubarItem,
MenubarLabel,
MenubarMenu,
MenubarRadioGroup,
MenubarRadioItem,
MenubarSeparator,
MenubarShortcut,
MenubarSub,
MenubarSubContent,
MenubarSubTrigger,
MenubarTrigger,
} from "@hm-design/ui"
import { StoryPage, StoryPageHeader, StorySection } from "@/components/story-page"
// Menubar is a desktop menu bar (Left / Right between top menus). The representative Story is the real bar and opens the real
// menus; Radix owns the keyboard model and focus. Hidden verification Stories isolate trigger focus and open + focus.
function Specimen() {
const [grid, setGrid] = useState(true)
const [ruler, setRuler] = useState(false)
const [zoom, setZoom] = useState("100")
return (
<Menubar>
<MenubarMenu>
<MenubarTrigger>파일</MenubarTrigger>
<MenubarContent>
<MenubarItem>
새로 만들기 <MenubarShortcut>⌘N</MenubarShortcut>
</MenubarItem>
<MenubarItem>
열기 <MenubarShortcut>⌘O</MenubarShortcut>
</MenubarItem>
<MenubarItem disabled>저장 (사용 불가)</MenubarItem>
<MenubarSeparator />
<MenubarSub>
<MenubarSubTrigger>내보내기</MenubarSubTrigger>
<MenubarSubContent>
<MenubarItem>PDF</MenubarItem>
<MenubarItem>이미지</MenubarItem>
</MenubarSubContent>
</MenubarSub>
<MenubarSeparator />
<MenubarItem variant="destructive">종료</MenubarItem>
</MenubarContent>
</MenubarMenu>
<MenubarMenu>
<MenubarTrigger>편집</MenubarTrigger>
<MenubarContent>
<MenubarItem>
실행 취소 <MenubarShortcut>⌘Z</MenubarShortcut>
</MenubarItem>
<MenubarItem>
다시 실행 <MenubarShortcut>⇧⌘Z</MenubarShortcut>
</MenubarItem>
</MenubarContent>
</MenubarMenu>
<MenubarMenu>
<MenubarTrigger>보기</MenubarTrigger>
<MenubarContent>
<MenubarGroup>
<MenubarLabel>표시</MenubarLabel>
<MenubarCheckboxItem checked={grid} onCheckedChange={setGrid}>
격자
</MenubarCheckboxItem>
<MenubarCheckboxItem checked={ruler} onCheckedChange={setRuler}>
눈금자
</MenubarCheckboxItem>
</MenubarGroup>
<MenubarSeparator />
<MenubarLabel>확대</MenubarLabel>
<MenubarRadioGroup value={zoom} onValueChange={setZoom}>
<MenubarRadioItem value="75">75%</MenubarRadioItem>
<MenubarRadioItem value="100">100%</MenubarRadioItem>
<MenubarRadioItem value="125">125%</MenubarRadioItem>
</MenubarRadioGroup>
</MenubarContent>
</MenubarMenu>
</Menubar>
)
}
const meta = { title: "Components/Navigation/Menubar", component: Menubar, parameters: { layout: "padded" } } satisfies Meta<typeof Menubar>
export default meta
type Story = StoryObj<typeof meta>
// Representative Story: File (item, shortcut, disabled, separator, sub menu, destructive), Edit, View (label, checkbox, radio).
export const All: Story = {
name: "Menubar",
parameters: { layout: "fullscreen", controls: { disable: true } },
render: () => (
<StoryPage>
<StoryPageHeader
title="Menubar"
description="열린 상태에서 좌우 방향키로 이웃 메뉴 이동"
/>
<StorySection title="Overview" description="단축키 · Disabled · 하위 메뉴 · 위험 항목 · Checkbox · Radio">
<div style={{ minHeight: 384 }}>
<Specimen />
</div>
</StorySection>
</StoryPage>
),
}
function FocusSpecimen({ open = false }: { open?: boolean }) {
return (
<div style={{ minHeight: 160 }}>
<Menubar defaultValue={open ? "file" : undefined}>
<MenubarMenu value="file">
<MenubarTrigger>파일</MenubarTrigger>
<MenubarContent>
<MenubarItem>새로 만들기</MenubarItem>
<MenubarItem disabled>저장 (사용 불가)</MenubarItem>
</MenubarContent>
</MenubarMenu>
<MenubarMenu value="disabled">
<MenubarTrigger disabled>사용 불가</MenubarTrigger>
</MenubarMenu>
</Menubar>
</div>
)
}
export const TriggerFocus: Story = {
tags: ["verification"],
render: () => <FocusSpecimen />,
play: async ({ canvasElement }) => {
canvasElement.querySelector<HTMLElement>('[data-slot="menubar-trigger"]')?.focus()
},
}
export const OpenFocus: Story = {
tags: ["verification"],
render: () => <FocusSpecimen open />,
play: async ({ canvasElement }) => {
// Verification only: actual trigger focus while its real menu is open.
// Production keyboard opening still moves focus into the menu (Radix default).
canvasElement.querySelector<HTMLElement>('[data-slot="menubar-trigger"]')?.focus()
},
}