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

149 lines
5.2 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 { STORY_KICKER, 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
kicker={STORY_KICKER.components}
title="Menubar"
description="데스크톱 앱 형태의 상단 메뉴 막대입니다. 메뉴가 열린 상태에서 좌우 방향키로 이웃 메뉴로 이동합니다."
/>
<StorySection title="Overview" description="파일(단축키 · Disabled · 하위 메뉴 · 위험 항목), 편집, 보기(Label · 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()
},
}