Files
web/packages/design-system/lib/components/TextArea/TextArea.stories.tsx
Rasmus Langvad b966cf1d53 Merged in fix/3693-group-inputs-storybook (pull request #3402)
fix(SW-3693): Refactor Input stories to use FormInput component and showcase all controls

* Refactor Input stories to use FormInput component and showcase all controls

* Updated stories and added autocomplete prop to PasswordInput

* Merge branch 'master' into fix/3693-group-inputs-storybook

* Use FormTextArea in stories for TextArea to show description and error texts

* Merged master into fix/3693-group-inputs-storybook

* Merge branch 'master' into fix/3693-group-inputs-storybook

* Removed redundant font name and fixed broken icons in stories

* Merge branch 'fix/3693-group-inputs-storybook' of bitbucket.org:scandic-swap/web into fix/3693-group-inputs-storybook

* Merged master into fix/3693-group-inputs-storybook

* Merge branch 'master' into fix/3693-group-inputs-storybook


Approved-by: Bianca Widstam
2026-01-21 16:20:04 +00:00

372 lines
9.9 KiB
TypeScript

import { zodResolver } from "@hookform/resolvers/zod"
import type { Meta, StoryObj } from "@storybook/nextjs-vite"
import { useEffect } from "react"
import { FormProvider, useForm } from "react-hook-form"
import { z } from "zod"
import { FormTextArea } from "../Form/FormTextArea"
import type { MaterialIconProps } from "../Icons/MaterialIcon"
import { Typography } from "../Typography"
interface FormTextAreaStoryProps {
label?: string
placeholder?: string
description?: string
descriptionIcon?: MaterialIconProps["icon"]
disabled?: boolean
readOnly?: boolean
required?: boolean
showLabel?: boolean
showDescription?: boolean
showDescriptionIcon?: boolean
filled?: boolean
defaultValue?: string
errorMessage?: string
name?: string
}
function FormTextAreaComponent({
label,
placeholder,
description,
descriptionIcon = "info",
disabled = false,
readOnly = false,
required = false,
showLabel = true,
showDescription = true,
showDescriptionIcon = true,
filled = false,
defaultValue = "",
errorMessage,
name = "textarea",
}: FormTextAreaStoryProps) {
const schema = z.object({
[name]: errorMessage
? z.string().min(1, errorMessage)
: required
? z.string().min(1, "This field is required")
: z.string().optional(),
})
const methods = useForm<{ [key: string]: string }>({
resolver: zodResolver(schema),
defaultValues: {
[name]: filled ? defaultValue : "",
},
mode: "onChange",
})
useEffect(() => {
if (errorMessage) {
// Set error manually if errorMessage is provided
methods.setError(name, {
type: "manual",
message: errorMessage,
})
// Trigger validation to show the error immediately
methods.trigger(name)
} else {
// Clear errors if errorMessage is removed
methods.clearErrors(name)
}
}, [errorMessage, methods, name])
return (
<FormProvider {...methods}>
<div style={{ maxWidth: "500px", padding: "1rem" }}>
<FormTextArea
name={name}
label={showLabel ? label : undefined}
placeholder={placeholder}
description={showDescription ? description : undefined}
descriptionIcon={showDescriptionIcon ? descriptionIcon : undefined}
disabled={disabled}
readOnly={readOnly}
registerOptions={required ? { required: true } : undefined}
/>
</div>
</FormProvider>
)
}
const meta: Meta<typeof FormTextAreaComponent> = {
title: "Core Components/Input/TextArea",
component: FormTextAreaComponent,
argTypes: {
label: {
control: "text",
name: "Label",
description: "Label text",
table: {
type: { summary: "string" },
order: 1,
},
},
placeholder: {
control: "text",
name: "Placeholder",
description: "Placeholder text",
table: {
type: { summary: "string" },
defaultValue: { summary: "undefined" },
order: 2,
},
},
showDescription: {
control: "boolean",
name: "Show Description",
description: "Show description",
table: {
type: { summary: "boolean" },
defaultValue: { summary: "true" },
order: 3,
},
},
description: {
control: "text",
name: "Description",
description: "Helper text (hidden when error is shown)",
table: {
type: { summary: "string" },
defaultValue: { summary: "undefined" },
order: 4,
},
},
descriptionIcon: {
control: "select",
name: "Description Icon",
options: ["info", "warning", "error"],
description: "Description icon",
table: {
type: { summary: "string" },
defaultValue: { summary: "'info'" },
order: 5,
},
},
required: {
control: "boolean",
name: "Required",
description: "Required field",
table: {
type: { summary: "boolean" },
defaultValue: { summary: "false" },
order: 6,
},
},
disabled: {
control: "boolean",
name: "Disabled",
description: "Disabled state",
table: {
type: { summary: "boolean" },
defaultValue: { summary: "false" },
order: 7,
},
},
readOnly: {
control: "boolean",
name: "Read Only",
description: "Read-only state",
table: {
type: { summary: "boolean" },
defaultValue: { summary: "false" },
order: 8,
},
},
showLabel: {
control: "boolean",
name: "Show Label",
description: "Show label",
table: {
type: { summary: "boolean" },
defaultValue: { summary: "true" },
order: 9,
},
},
errorMessage: {
control: "text",
name: "Error Message",
description: "Error message",
table: {
type: { summary: "string" },
defaultValue: { summary: "undefined" },
order: 10,
},
},
},
}
export default meta
type Story = StoryObj<typeof FormTextAreaComponent>
export const Default: Story = {
args: {
label: "Label",
placeholder: "Placeholder",
required: false,
disabled: false,
readOnly: false,
showLabel: true,
showDescription: true,
errorMessage: undefined,
},
}
// ============================================================================
// TextArea Variations Showcase
// ============================================================================
const showcaseSchema = z.object({
default: z.string().optional(),
placeholder: z.string().optional(),
withDescription: z.string().optional(),
withDescriptionPlaceholder: z.string().optional(),
filled: z.string().optional(),
required: z.string().min(1, "This field is required"),
disabled: z.string().optional(),
disabledFilled: z.string().optional(),
error: z.string().min(10, "Must be at least 10 characters"),
errorFilled: z.string().min(10, "Invalid content"),
descriptionInfo: z.string().optional(),
descriptionWarning: z.string().optional(),
})
type ShowcaseFormData = z.infer<typeof showcaseSchema>
function TextAreaShowcase() {
const methods = useForm<ShowcaseFormData>({
resolver: zodResolver(showcaseSchema),
defaultValues: {
filled:
"This is a sample text that fills the textarea with some content.",
disabledFilled: "This textarea is disabled and cannot be edited.",
error: "Short",
errorFilled: "Invalid content",
},
mode: "onChange",
})
// Trigger validation for error examples on mount
useEffect(() => {
methods.trigger(["error", "errorFilled"])
}, [methods])
return (
<FormProvider {...methods}>
<div
style={{
display: "flex",
flexDirection: "column",
gap: "3rem",
maxWidth: "800px",
padding: "2rem",
}}
>
{/* Basic States */}
<section>
<Typography variant="Title/sm">
<h2>Basic States</h2>
</Typography>
<div
style={{
display: "grid",
gridTemplateColumns: "repeat(auto-fit, minmax(300px, 1fr))",
gap: "1.5rem",
marginTop: "1rem",
}}
>
<FormTextArea name="default" label="Default" />
<FormTextArea
name="placeholder"
label="Placeholder"
placeholder="Enter your message here..."
/>
<FormTextArea
name="withDescription"
label="With Description"
description="This is a default textarea with a description"
/>
<FormTextArea
name="withDescriptionPlaceholder"
label="With Description"
placeholder="Enter your message here..."
description="This is a default textarea with a description"
/>
<FormTextArea name="filled" label="Filled" />
<FormTextArea
name="required"
label="Required"
registerOptions={{ required: true }}
hideError
/>
<FormTextArea name="disabled" label="Disabled" disabled />
<FormTextArea
name="disabledFilled"
label="Disabled Filled"
disabled
/>
</div>
</section>
{/* Supporting Text Variations */}
<section>
<Typography variant="Title/sm">
<h2>Supporting Text</h2>
</Typography>
<div
style={{
display: "grid",
gridTemplateColumns: "repeat(auto-fit, minmax(300px, 1fr))",
gap: "1.5rem",
marginTop: "1rem",
}}
>
<FormTextArea
name="descriptionInfo"
label="Info Description"
description="This is an informational message"
descriptionIcon="info"
/>
<FormTextArea
name="descriptionWarning"
label="Warning Description"
description="This is a warning message"
descriptionIcon="warning"
/>
</div>
</section>
{/* Validation States */}
<section>
<Typography variant="Title/sm">
<h2>Validation States</h2>
</Typography>
<div
style={{
display: "grid",
gridTemplateColumns: "repeat(auto-fit, minmax(300px, 1fr))",
gap: "1.5rem",
marginTop: "1rem",
}}
>
<FormTextArea
name="error"
label="Error State"
registerOptions={{ minLength: 10 }}
/>
</div>
</section>
</div>
</FormProvider>
)
}
export const AllVariations: StoryObj<typeof TextAreaShowcase> = {
render: () => <TextAreaShowcase />,
parameters: {
layout: "fullscreen",
},
}