From 35f1f7705a3358abdabbbe82495b2bcd3128e368 Mon Sep 17 00:00:00 2001 From: Dimitris Giannaris Date: Tue, 19 Nov 2024 11:36:30 +0200 Subject: [PATCH] Introduce TextAreaField to symbiosis (#21) * fix: numberfield with icon styles * feat(NumberField): add icon, onChange, and onBlur props to Storybook controls Enhances the Storybook documentation for the NumberField component by adding controls for the icon prop and descriptions for onChange and onBlur event handlers. This improvement provides better visibility and understanding of the component's capabilities for developers using Storybook. * feat(ui): add TextAreaField component Introduces a new TextAreaField component to the UI package, providing a customizable textarea input with various features such as error handling, hints, icons, and accessibility support. This component enhances form functionality by offering a multi-line text input option with consistent styling and behavior across the application. * feat(storybook): add TextAreaField component stories Introduces a new set of stories for the TextAreaField component in Storybook. This addition enhances the documentation and testing capabilities for the TextAreaField, providing various examples of its usage and configurations. The stories showcase different states and properties of the component, including default, error, hint, icon, disabled, and controlled scenarios, which will help developers understand and utilize the component more effectively. --- .../src/stories/NumberField.stories.tsx | 29 ++ .../src/stories/TextAreaField.stories.tsx | 263 ++++++++++++++++++ .../ui/src/components/NumberField/index.tsx | 2 +- .../ui/src/components/TextAreaField/index.tsx | 100 +++++++ .../ui/src/components/TextAreaField/styles.ts | 64 +++++ .../ui/src/components/TextAreaField/types.ts | 19 ++ packages/ui/src/index.ts | 4 + 7 files changed, 480 insertions(+), 1 deletion(-) create mode 100644 examples/storybook/src/stories/TextAreaField.stories.tsx create mode 100644 packages/ui/src/components/TextAreaField/index.tsx create mode 100644 packages/ui/src/components/TextAreaField/styles.ts create mode 100644 packages/ui/src/components/TextAreaField/types.ts diff --git a/examples/storybook/src/stories/NumberField.stories.tsx b/examples/storybook/src/stories/NumberField.stories.tsx index 3a510cf..8055f5d 100644 --- a/examples/storybook/src/stories/NumberField.stories.tsx +++ b/examples/storybook/src/stories/NumberField.stories.tsx @@ -131,6 +131,35 @@ const meta: Meta = { }, }, }, + icon: { + control: { + type: "text", + }, + description: "Icon name for the input field", + table: { + type: { + summary: "string", + }, + }, + }, + onChange: { + control: false, + description: "Event handler called when the value changes", + table: { + type: { + summary: "(value: string) => void", + }, + }, + }, + onBlur: { + control: false, + description: "Event handler called when the field loses focus", + table: { + type: { + summary: "(value: string) => void", + }, + }, + }, }, }; diff --git a/examples/storybook/src/stories/TextAreaField.stories.tsx b/examples/storybook/src/stories/TextAreaField.stories.tsx new file mode 100644 index 0000000..a51add5 --- /dev/null +++ b/examples/storybook/src/stories/TextAreaField.stories.tsx @@ -0,0 +1,263 @@ +import * as React from "react"; +import type { Meta, StoryObj } from "@storybook/react"; +import { TextAreaField, type TextAreaFieldProps } from "@synopsisapp/symbiosis-ui"; + +const meta: Meta = { + title: "Components/TextAreaField", + component: TextAreaField, + tags: ["autodocs"], + argTypes: { + label: { + control: { + type: "text", + }, + description: "Label for the TextAreaField", + table: { + type: { + summary: "string", + }, + }, + }, + defaultValue: { + control: { + type: "text", + }, + description: "Default value for the textarea field", + table: { + type: { + summary: "string", + }, + }, + }, + error: { + control: { + type: "text", + }, + description: "Error message for the textarea field", + table: { + type: { + summary: "string", + }, + }, + }, + required: { + control: { + type: "boolean", + }, + description: "Whether the textarea field is required", + table: { + type: { + summary: "boolean", + }, + }, + }, + value: { + control: { + type: "text", + }, + description: "Value of the TextAreaField", + table: { + type: { + summary: "string", + }, + }, + }, + hint: { + control: { + type: "text", + }, + description: "Hint text for the TextAreaField", + table: { + type: { + summary: "string", + }, + }, + }, + placeholder: { + control: { + type: "text", + }, + description: "Placeholder text for the TextAreaField", + table: { + type: { + summary: "string", + }, + }, + }, + icon: { + control: { + type: "text", + }, + description: "Icon name for the textarea field", + table: { + type: { + summary: "string", + }, + }, + }, + disabled: { + control: { + type: "boolean", + }, + description: "Whether the TextAreaField is disabled", + table: { + type: { + summary: "boolean", + }, + }, + }, + rows: { + control: { + type: "number", + }, + description: "Number of visible text lines", + table: { + type: { + summary: "number", + }, + }, + }, + onChange: { + control: false, + description: "Event handler called when the value changes", + table: { + type: { + summary: "(value: string) => void", + }, + }, + }, + onBlur: { + control: false, + description: "Event handler called when the field loses focus", + table: { + type: { + summary: "(value: string) => void", + }, + }, + }, + name: { + control: { + type: "text", + }, + description: "Name of the textarea field", + table: { + type: { + summary: "string", + }, + }, + }, + id: { + control: { + type: "text", + }, + description: "ID of the textarea field", + table: { + type: { + summary: "string", + }, + }, + }, + }, +}; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + render: (args) => , + args: { + label: "Default TextAreaField", + placeholder: "Enter text here...", + rows: 4, + }, +}; + +export const WithError: Story = { + render: (args) => , + args: { + label: "TextAreaField with Error", + error: "This field is required", + required: true, + rows: 4, + }, +}; + +export const WithHint: Story = { + render: (args) => , + args: { + label: "TextAreaField with Hint", + hint: "This is a helpful hint message", + rows: 4, + }, +}; + +export const WithIcon: Story = { + render: (args) => , + args: { + label: "TextAreaField with Icon", + icon: "symbiosis-minus", + placeholder: "Type your message...", + rows: 4, + }, +}; + +export const Disabled: Story = { + render: (args) => , + args: { + label: "Disabled TextAreaField", + disabled: true, + placeholder: "This field is disabled", + rows: 4, + }, +}; + +export const DifferentRows: Story = { + render: (args) => ( + <> + + + + + ), + args: { + placeholder: "Enter text here...", + }, +}; + +export const Controlled: Story = { + render: (args) => { + const [value, setValue] = React.useState(""); + + return ; + }, + args: { + label: "Controlled TextAreaField", + placeholder: "Type something...", + rows: 4, + }, + parameters: { + docs: { + source: { + code: ` +const ControlledTextAreaField = () => { + const [value, setValue] = React.useState(""); + + return ; +} +`, + language: "tsx", + type: "code", + }, + }, + }, +}; + +export const CustomStyled: Story = { + render: (args) => , + args: { + label: "Custom Styled TextAreaField", + placeholder: "Enter text here...", + hint: "This is a custom styled hint", + rows: 4, + }, +}; diff --git a/packages/ui/src/components/NumberField/index.tsx b/packages/ui/src/components/NumberField/index.tsx index 5723726..2cbb2f3 100644 --- a/packages/ui/src/components/NumberField/index.tsx +++ b/packages/ui/src/components/NumberField/index.tsx @@ -110,7 +110,7 @@ export const NumberField = ({ tone="monochrome-dark" size="small-100" isDisabled={disabled} - className="focus:before:border-none" + className={cn("focus:before:border-none", icon ? "left-6" : "left-0")} /> {icon && ( { + const formId = id ?? label ?? ""; + const ref = React.useRef(null); + + const hasError = Boolean(error); + + return ( +
+ {label && ( + + )} +
+
+ {icon && ( + + )} +