import { useMemo, useState } from "react" import type { Meta, StoryObj } from "@storybook/react-vite" import { Card, CardContent, PageHeader, PageHeaderContent, PageHeaderTitle, PageHeaderDescription, Badge, Button, Combobox, DataTable, DataTableColumnHeader, DataTableViewOptions, DatePicker, DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger, FilterBar, FilterBarActions, FilterBarSearch, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, dataTableSelectColumn, toast, useDataTable, type DataTableColumnDef, } from "@hm-design/ui" import "../list-screen.css" import { MoreHorizontalIcon } from "lucide-react" import { StoryPage, StoryPageHeader, StorySection } from "@/components/story-page" import { formatDate, sameDay, sampleUsers, statusLabel, type SampleUser } from "./sample-users" // Data Table is a pattern: the HM Table primitive driven by TanStack Table v9 (useDataTable). Columns and data stay referentially stable // (module scope / useMemo). Row actions use the HM Dropdown Menu and report through Sonner `toast` (Story composition only). const statusBadge = (status: SampleUser["status"]) => ( {statusLabel[status]} ) const columnLabels = { name: "이름", department: "부서", status: "상태", createdAt: "등록일" } const columns: DataTableColumnDef[] = [ dataTableSelectColumn(), { accessorKey: "name", header: ({ column }) => }, { accessorKey: "department", header: "부서" }, { accessorKey: "status", header: "상태", cell: ({ row }) => statusBadge(row.original.status), filterFn: "equalsString", enableSorting: false, }, { accessorKey: "createdAt", header: ({ column }) => , cell: ({ row }) => formatDate(row.original.createdAt), filterFn: (row, columnId, value: Date) => sameDay(row.getValue(columnId), value), }, { id: "actions", enableHiding: false, cell: ({ row }) => ( toast(`${row.original.name} 보기`)}>보기 toast(`${row.original.name} 수정`)}>수정 toast.error(`${row.original.name} 삭제 요청`)}> 삭제 ), }, ] function Basic({ data = sampleUsers, loading }: { data?: SampleUser[]; loading?: boolean }) { const table = useDataTable({ columns, data, getRowId: (row) => row.id }) return (
) } const statusOptions = [ { value: "active", label: "활성" }, { value: "pending", label: "대기" }, { value: "inactive", label: "비활성" }, ] const departmentOptions = [...new Set(sampleUsers.map((u) => u.department))].map((d) => ({ value: d, label: d })) // Filter Bar → filter state → Data Table: search is the TanStack global filter, status / date are column filters. function WithFilterBar({ extended = true }: { extended?: boolean }) { const data = useMemo(() => sampleUsers, []) const table = useDataTable({ columns, data, getRowId: (row) => row.id }) const [search, setSearch] = useState("") const [status, setStatus] = useState("all") const [date, setDate] = useState() const [department, setDepartment] = useState() const apply = () => { table.setGlobalFilter(search || undefined) table.getColumn("status")?.setFilterValue(status === "all" ? undefined : status) table.getColumn("department")?.setFilterValue(department) table.getColumn("createdAt")?.setFilterValue(date) } const reset = () => { setSearch(""); setStatus("all"); setDate(undefined); setDepartment(undefined) table.resetGlobalFilter(true); table.resetColumnFilters(true) } return
사용자 목록조건을 입력한 뒤 조회합니다.
{ event.preventDefault(); apply() }}> {extended && <> }
} const meta = { title: "Patterns/Data/Data Table", parameters: { layout: "padded", controls: { disable: true } } } satisfies Meta export default meta type Story = StoryObj // Representative Story: selection (mixed header), sorting, column visibility, row actions and pagination with page size. export const All: Story = { name: "Data Table", parameters: { layout: "fullscreen" }, render: () => ( ), } // Combination Story: Filter Bar (search, status, department, date, reset) filtering the Data Table rows. export const WithFilterBarStory: Story = { tags: ["verification"], name: "With Filter Bar", render: () => } export const Loading: Story = { tags: ["verification"], render: () => } export const Empty: Story = { tags: ["verification"], render: () => }