Skip to content

Commit

Permalink
feat(textarea): improve textarea props and doc
Browse files Browse the repository at this point in the history
  • Loading branch information
andresz1 committed Jul 24, 2023
1 parent 3ea40f7 commit af8cce2
Show file tree
Hide file tree
Showing 3 changed files with 88 additions and 30 deletions.
39 changes: 32 additions & 7 deletions packages/components/textarea/src/Textarea.doc.mdx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Meta, Canvas } from '@storybook/addon-docs'
import { ArgTypes } from '@storybook/blocks'
import { ArgTypes as ExtendedArgTypes } from '@docs/helpers/ArgTypes'

import { Textarea } from '.'
import { Textarea, TextareaGroup } from '.'

import * as stories from './Textarea.stories'

Expand All @@ -25,7 +25,26 @@ import { Textarea } from '@spark-ui/textarea'

## Props

<ArgTypes of={Textarea} />
<ExtendedArgTypes
of={Textarea}
description="The textarea component allows you to easily create multi-line text inputs."
subcomponents={{
TextareaGroup: {
of: TextareaGroup,
description:
'Use this wrapper whenever you need a more complex textarea. For example if you need to have leading/trailing icons.',
},
'TextareaGroup.LeadingIcon': {
of: TextareaGroup.LeadingIcon,
description: 'Prepend a decorative icon inside the textarea (to the left).',
},
'TextareaGroup.TrailingIcon': {
of: TextareaGroup.TrailingIcon,
description:
'Append a decorative icon inside the textarea (to the right). This element will be replaced by a status indicator whenever the textarea is in error/alert/success state.',
},
}}
/>

## Usage

Expand All @@ -49,17 +68,23 @@ Use `disabled` prop to indicate the textarea is disabled.

<Canvas of={stories.Disabled} />

## InputGroup
## TextareaGroup

Use `InputGroup` component to group your textarea with elements like icons.
Use `TextareaGroup` component to group your textarea with elements like icons.

### Elements

<Canvas of={stories.GroupElements} />
<Canvas of={stories.GroupIcons} />

### State

Use `state` prop to assign a specific state to the group, choosing from: `error`, `alert` and `success`. By doing so, the outline styles will be updated, and a state indicator will be displayed accordingly.

<Canvas of={stories.GroupState} />

### Disabled

Use `isDisabled` prop of the `InputGroup` to indicate that the input group is disabled. Use this prop instead of the textarea's disabled prop when it is within a group.
Use `disabled` prop of the `TextareaGroup` to indicate that the textarea group is disabled. Use this prop instead of the textarea's disabled prop when it is within a group.

<Canvas of={stories.GroupDisabled} />

Expand Down
77 changes: 55 additions & 22 deletions packages/components/textarea/src/Textarea.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,30 +14,32 @@ const meta: Meta<typeof Textarea> = {

export default meta

export const Default: StoryFn = _args => <Textarea rows={2} aria-label="Message" />
export const Default: StoryFn = _args => (
<Textarea rows={2} placeholder="Describe what you want to sell" aria-label="Description" />
)

export const Uncontrolled: StoryFn = _args => (
<Textarea defaultValue="IPhone 12" aria-label="Message" />
<Textarea rows={2} defaultValue="IPhone 12 in good condition" aria-label="Description" />
)

export const Controlled: StoryFn = () => {
const [value, setValue] = useState('IPhone 13')
const [value, setValue] = useState('IPhone 12 in good condition')

const handleChange = (event: ChangeEvent<HTMLTextAreaElement>) => {
setValue(event.target.value)
}

return <Textarea value={value} onChange={handleChange} aria-label="Message" />
return <Textarea rows={2} value={value} onChange={handleChange} aria-label="Description" />
}

export const Disabled: StoryFn = _args => (
<Textarea defaultValue="IPhone" aria-label="Message" disabled />
<Textarea rows={2} defaultValue="IPhone 12 in good condition" aria-label="Description" disabled />
)

export const GroupElements: StoryFn = _args => (
export const GroupIcons: StoryFn = _args => (
<TextareaGroup>
<TextareaGroup.LeadingIcon>
<Check />
<PenOutline />
</TextareaGroup.LeadingIcon>

<Textarea rows={2} aria-label="Message" />
Expand All @@ -48,13 +50,43 @@ export const GroupElements: StoryFn = _args => (
</TextareaGroup>
)

export const GroupState: StoryFn = _args => {
return (
<div className="flex flex-col gap-xl">
<TextareaGroup state="error">
<Textarea rows={2} aria-label="Description" />
</TextareaGroup>

<TextareaGroup state="alert">
<TextareaGroup.LeadingIcon>
<PenOutline />
</TextareaGroup.LeadingIcon>

<Textarea rows={2} aria-label="Description" />
</TextareaGroup>

<TextareaGroup state="success">
<TextareaGroup.LeadingIcon>
<PenOutline />
</TextareaGroup.LeadingIcon>

<Textarea rows={2} aria-label="Description" />

<TextareaGroup.TrailingIcon>
<Check />
</TextareaGroup.TrailingIcon>
</TextareaGroup>
</div>
)
}

export const GroupDisabled: StoryFn = _args => (
<TextareaGroup disabled>
<TextareaGroup.LeadingIcon>
<PenOutline />
</TextareaGroup.LeadingIcon>

<Textarea rows={2} aria-label="Message" />
<Textarea rows={2} defaultValue="IPhone 12 in good condition" aria-label="Message" />

<TextareaGroup.TrailingIcon>
<Check />
Expand Down Expand Up @@ -96,54 +128,55 @@ export const FieldRequired: StoryFn = _args => {

export const FieldHelperMessage: StoryFn = _args => {
return (
<FormField name="title">
<FormField.Label>Title</FormField.Label>
<FormField name="description">
<FormField.Label>Description</FormField.Label>

<Textarea rows={2} />

<FormField.HelperMessage>
An effective title significantly increases your chances of making a sale
An effective description significantly increases your chances of making a sale
</FormField.HelperMessage>
</FormField>
)
}

export const FieldCharactersCount: StoryFn = _args => {
const MAX_LENGTH = 90
export const FieldCharactersCount: StoryFn = () => {
const maxLength = 90
const [value, setValue] = useState('')

const handleChange = (event: ChangeEvent<HTMLTextAreaElement>) => {
setValue(event.target.value)
}

return (
<FormField name="textarea-with-a-characters-count">
<FormField.Label>Textarea with a characters count</FormField.Label>
<FormField name="description">
<FormField.Label>Description</FormField.Label>

<Textarea value={value} onChange={handleChange} maxLength={MAX_LENGTH} />
<Textarea rows={2} value={value} onChange={handleChange} maxLength={maxLength} />

<div className="flex justify-between gap-md">
<div className="grow">
<FormField.HelperMessage>
Type the text but take into account the max length
An effective description significantly increases your chances of making a sale
</FormField.HelperMessage>
<FormField.ErrorMessage>This is an error</FormField.ErrorMessage>

<FormField.ErrorMessage>The description is invalid</FormField.ErrorMessage>
</div>

<FormField.CharactersCount value={value} maxLength={MAX_LENGTH} />
<FormField.CharactersCount value={value} maxLength={maxLength} />
</div>
</FormField>
)
}

export const FieldInvalid: StoryFn = _args => {
return (
<FormField name="title" state="error">
<FormField.Label>Title</FormField.Label>
<FormField name="description" state="error">
<FormField.Label>Description</FormField.Label>

<Textarea rows={2} />

<FormField.ErrorMessage>The title is invalid</FormField.ErrorMessage>
<FormField.ErrorMessage>The description is invalid</FormField.ErrorMessage>
</FormField>
)
}
2 changes: 1 addition & 1 deletion packages/components/textarea/src/TextareaGroup.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { InputGroup, InputGroupProps } from '@spark-ui/input'

export type TextareaGroupProps = InputGroupProps
export type TextareaGroupProps = Omit<InputGroupProps, 'onClear'>

export const TextareaGroup = (props: InputGroupProps) => {
return <InputGroup {...props} />
Expand Down

0 comments on commit af8cce2

Please sign in to comment.