style(storybook): unify form element story layouts
- Button: Sizes | Variants side by side, States as Brand / Standard Solid / Standard Outline matrices (3 / 2 / 1 columns), 8px button gap; the Interact row stays only in the verification "Brand States" Story - Input / Textarea / Select: state name in the field Label slot, repeated field Label and group titles removed, aria-label keeps field meaning with the visible state name; Select uses the Input field grid - Combobox / Date Picker: Sizes / States use the same state-name Label and Input grid; Usage unchanged - Toggle: top Variants / States in 2 columns, 8px toggle gap, 단독 toggle keeps its own width - Component code, props, tokens and verification Stories unchanged; component docs updated to match
This commit is contained in:
1 parent
e8c16f5292
commit
a7b56ff23e
14 files changed
+351
-187
No files matched your search
@@ -1,7 +1,6 @@
|
||||
import { useId, type ComponentProps, type ReactNode } from "react"
|
||||
import type { Meta, StoryObj } from "@storybook/react-vite"
|
||||
import { Textarea } from "@hm-design/ui"
|
||||
import { StateBoard, type BoardState } from "@/components/story-board"
|
||||
import { StoryPage, StoryPageHeader, StorySection } from "@/components/story-page"
|
||||
|
||||
// Story structure follows Input: one representative "Textarea" Story shows every state on one page; the same specimens
|
||||
@@ -49,19 +48,20 @@ function Specimen({
|
||||
)
|
||||
}
|
||||
|
||||
// Representative-page layout (Storybook presentation only). StateBoard's own auto-fill grid cannot give equal columns that
|
||||
// fill a cell, so each specimen sits in its own single-column StateBoard (min larger than any column) inside this grid.
|
||||
// Full class names are listed so Tailwind can see them.
|
||||
const specimenColumns = {
|
||||
4: "min-[1000px]:grid-cols-4",
|
||||
} as const
|
||||
|
||||
function SpecimenGrid({ states, columns }: { states: BoardState[]; columns: keyof typeof specimenColumns }) {
|
||||
// Representative-page layout (Storybook presentation only). 2026-11-12, same rule as Input: the state name sits in the field
|
||||
// Label slot (no repeated "Description" under a separate group title); the textarea keeps a meaningful accessible name through
|
||||
// aria-label, which also contains the visible state name (Label in Name). PC (iframe width 1000px and up): 4 equal columns;
|
||||
// below that the columns shrink to fit (220px minimum, down to 1). The grid takes the common section indent (.sb-page-indent).
|
||||
function FieldGrid({ fields }: { fields: { id: string; label: string; props: ComponentProps<typeof Specimen> }[] }) {
|
||||
return (
|
||||
<div className={`grid items-start gap-6 [grid-template-columns:repeat(auto-fill,minmax(min(100%,220px),1fr))] ${specimenColumns[columns]}`}>
|
||||
{states.map((state) => (
|
||||
<StateBoard key={state.id} min={10000} states={[state]} />
|
||||
))}
|
||||
<div className="sb-page-indent">
|
||||
<div className="grid items-start gap-6 [grid-template-columns:repeat(auto-fill,minmax(min(100%,220px),1fr))] min-[1000px]:grid-cols-4">
|
||||
{fields.map(({ id, label, props }) => (
|
||||
<div key={id} className="min-w-0" data-overview-state={id}>
|
||||
<Specimen label={label} aria-label={`Description (${label})`} {...props} />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -106,23 +106,21 @@ export const All: Story = {
|
||||
{/* PC (iframe width 1000px and up): States in 4 equal columns, Validation's 2 specimens on the same column width.
|
||||
Below that the columns shrink to fit (220px minimum, down to 1). Multiline and Resize live in their own hidden Stories. */}
|
||||
<StorySection title="States">
|
||||
<SpecimenGrid
|
||||
columns={4}
|
||||
states={[
|
||||
{ id: "default", label: "Default", children: <Specimen placeholder={placeholder} /> },
|
||||
{ id: "focus", label: "Focus", children: <Specimen placeholder={placeholder} /> },
|
||||
{ id: "readonly", label: "Readonly", children: <Specimen readOnly defaultValue="Confirmed description" /> },
|
||||
{ id: "disabled", label: "Disabled", children: <Specimen disabled defaultValue="Unavailable description" /> },
|
||||
<FieldGrid
|
||||
fields={[
|
||||
{ id: "default", label: "Default", props: { placeholder } },
|
||||
{ id: "focus", label: "Focus", props: { placeholder } },
|
||||
{ id: "readonly", label: "Readonly", props: { readOnly: true, defaultValue: "Confirmed description" } },
|
||||
{ id: "disabled", label: "Disabled", props: { disabled: true, defaultValue: "Unavailable description" } },
|
||||
]}
|
||||
/>
|
||||
</StorySection>
|
||||
<StorySection title="Validation">
|
||||
{/* Same 4-column track as States, so the two specimens keep the States width instead of stretching across the page. */}
|
||||
<SpecimenGrid
|
||||
columns={4}
|
||||
states={[
|
||||
{ id: "error", label: "Error", children: <Specimen error defaultValue="Invalid value" /> },
|
||||
{ id: "error-message", label: "Error Message", children: <Specimen error message="설명을 입력하세요." /> },
|
||||
{/* Same 4-column track as States, so the two fields keep the States width instead of stretching across the page. */}
|
||||
<FieldGrid
|
||||
fields={[
|
||||
{ id: "error", label: "Error", props: { error: true, defaultValue: "Invalid value" } },
|
||||
{ id: "error-message", label: "Error Message", props: { error: true, message: "설명을 입력하세요." } },
|
||||
]}
|
||||
/>
|
||||
</StorySection>
|
||||
|
||||
Reference in new issue
Block a user