style(storybook): refine form component stories
This commit is contained in:
1 parent
e47eb040f2
commit
f6b92d781a
23 files changed
+471
-182
No files matched your search
@@ -1,7 +1,7 @@
|
||||
import { useId, type ComponentProps, type ReactNode } from "react"
|
||||
import type { Meta, StoryObj } from "@storybook/react-vite"
|
||||
import { Textarea } from "@hm-design/ui"
|
||||
import { StateBoard } from "@/components/story-board"
|
||||
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
|
||||
@@ -9,8 +9,8 @@ import { StoryPage, StoryPageHeader, StorySection } from "@/components/story-pag
|
||||
// ui_viz docs/components/textarea.md §6 Reference Viewer: Default, Hover, Focus, Error, Error + Focus, Readonly, Disabled and Multiline.
|
||||
//
|
||||
// Label, Required star and Helper/Error message are not part of the Textarea component. Specimen composes them with plain
|
||||
// elements and shadcn Token utilities, the way a consumer would: Label 14/20/500 and Helper 12/18/400 in the secondary
|
||||
// text color, Error message 11/18 in the destructive color, field gap 8.
|
||||
// elements and shadcn Token utilities, the way a consumer would: Label 14/20/500 in the default text color (Disabled keeps
|
||||
// the secondary color) and Helper 12/18/400 in the secondary text color, Error message 11/18 in the destructive color, field gap 8.
|
||||
|
||||
function Specimen({
|
||||
label = "Description",
|
||||
@@ -25,7 +25,7 @@ function Specimen({
|
||||
const messageId = `${fieldId}-message`
|
||||
return (
|
||||
<div className="grid gap-2">
|
||||
<label htmlFor={fieldId} className="justify-self-start text-sm font-medium text-muted-foreground">
|
||||
<label htmlFor={fieldId} className={`justify-self-start text-sm font-medium ${textarea.disabled ? "text-muted-foreground" : "text-foreground"}`}>
|
||||
{label}
|
||||
{required ? (
|
||||
<span aria-hidden="true" className="text-destructive">
|
||||
@@ -49,6 +49,23 @@ 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 }) {
|
||||
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>
|
||||
)
|
||||
}
|
||||
|
||||
const narrow = (children: ReactNode) => <div className="max-w-sm">{children}</div>
|
||||
const placeholder = "Enter a description"
|
||||
const multiline = "First line\nSecond line keeps its line break.\nThird line."
|
||||
@@ -86,12 +103,13 @@ export const All: Story = {
|
||||
render: () => (
|
||||
<StoryPage>
|
||||
<StoryPageHeader title="Textarea" />
|
||||
<StorySection title="Overview">
|
||||
<StateBoard states={[{ id: "default", label: "Default", children: <Specimen placeholder={placeholder} /> }]} />
|
||||
</StorySection>
|
||||
{/* 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">
|
||||
<StateBoard
|
||||
<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" /> },
|
||||
@@ -99,21 +117,15 @@ export const All: Story = {
|
||||
/>
|
||||
</StorySection>
|
||||
<StorySection title="Validation">
|
||||
<StateBoard
|
||||
{/* 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="설명을 입력하세요." /> },
|
||||
]}
|
||||
/>
|
||||
</StorySection>
|
||||
<StorySection title="Usage">
|
||||
<StateBoard
|
||||
states={[
|
||||
{ id: "multiline", label: "Multiline · Wrap", children: <Specimen defaultValue={multiline} /> },
|
||||
{ id: "resize", label: "Resize · none", children: <Specimen resize="none" defaultValue="This field cannot be resized." /> },
|
||||
]}
|
||||
/>
|
||||
</StorySection>
|
||||
</StoryPage>
|
||||
),
|
||||
play: async ({ canvasElement }) => {
|
||||
@@ -151,6 +163,17 @@ export const Required: Story = {
|
||||
tags: ["verification"],
|
||||
render: () => narrow(<Specimen required />),
|
||||
}
|
||||
// Former Usage section of the representative page, kept as separate Stories.
|
||||
export const Multiline: Story = {
|
||||
tags: ["verification"],
|
||||
name: "Multiline · Wrap",
|
||||
render: () => narrow(<Specimen defaultValue={multiline} />),
|
||||
}
|
||||
export const ResizeNone: Story = {
|
||||
tags: ["verification"],
|
||||
name: "Resize · none",
|
||||
render: () => narrow(<Specimen resize="none" defaultValue="This field cannot be resized." />),
|
||||
}
|
||||
export const Readonly: Story = {
|
||||
tags: ["verification"],
|
||||
render: () => narrow(<Specimen readOnly defaultValue="Confirmed description" />),
|
||||
|
||||
Reference in new issue
Block a user