stories/atoms/components/textField/TextField.stories.mdx
import { useRef, useState } from 'react'
import { ArgsTable, Meta, Story, Canvas } from '@storybook/addon-docs'
import { ThemeProvider } from 'ui/atoms/theme/ThemeProvider'
import { ThemeSwitcher } from 'ui/atoms/theme/ThemeSwitcher'
import { TextField } from 'ui/atoms/textField/TextField'
import { Icon } from 'ui/atoms/icon/Icon'
import '../component.scss'
<Meta
title="Atoms/TextField"
component={TextField}
parameters={{
actions: {
argTypesRegex: '^on.*'
},
docs: {
source: {
type: 'code'
}
}
}}
decorators={[
Story => (
<ThemeProvider>
<div className="story-theme-switcher">
<ThemeSwitcher />
</div>
<div className="component-story-main">
<Story />
</div>
</ThemeProvider>
)
]}
/>
# TextField component
> Allows the user to enter some text into a field.
[![stability-unstable](https://img.shields.io/badge/stability-unstable-yellow.svg)](https://github.com/emersion/stability-badges#unstable)
## Description
`TextField` is a minimalist text input component that integrates easily with OKP4 design system.
## Overview
<Story
name="Usage"
argTypes={{
onChange: {
control: false
},
ref: {
control: false
},
rightIcon: {
control: false
},
value: {
control: { type: 'select' },
options: ['string', ['array', 'of', 'string']]
}
}}
args={{
placeholder: "I'm a placeholder!",
defaultValue: "I'm set with a default value",
value: undefined
}}
>
{args => (
<div style={{ display: 'flex', justifyContent: 'center', maxWidth: '100%' }}>
<TextField {...args} />
</div>
)}
</Story>
---
### Properties
<ArgsTable story="Usage" />
## Sizes
Size options are `x-small`, `small`, `medium` (default), `large`, `x-large` and will be automatically adjusted responsively to the screen size.
If the [multiline](/docs/atoms-textfield--multiline) property is `true` and [disableAreaResize](/docs/atoms-textfield--disable-area-resize) is `false`, then the size is automatically `auto`.
<Canvas>
<Story name="Sizes">
<div
style={{
display: 'flex',
flexDirection: 'column',
gap: '10px',
alignItems: 'center'
}}
>
<TextField size="x-small" placeholder="x-small" />
<TextField size="small" placeholder="small" />
<TextField size="medium" placeholder="medium" />
<TextField size="large" placeholder="large" />
<TextField size="x-large" placeholder="x-large" />
<TextField size="large" placeholder="large but multiline with resizing" multiline />
<TextField
size="large"
placeholder="large and multiline without resizing"
multiline
disableAreaResize
/>
</div>
</Story>
</Canvas>
## Disabling the text field
The `TextField` component can be disabled by setting the `disabled` property to `true`.
<Canvas>
<Story name="Disabled">
<div style={{ display: 'flex', justifyContent: 'center', maxWidth: '100%' }}>
<TextField disabled defaultValue="This text field is disabled" />
</div>
</Story>
</Canvas>
## Readonly
When set to `true`, makes the element not mutable, meaning the user can not edit the control.
<Canvas>
<Story name="Readonly">
<div style={{ display: 'flex', justifyContent: 'center', maxWidth: '100%' }}>
<TextField readOnly defaultValue="This text field is readonly" />
</div>
</Story>
</Canvas>
## Helper text
The `TextField` component comes with a property called `helperText` which provides additional context to the input field.
This property is optional and is displayed with an `info` semantic color (except if `hasError` has been set to `true`)
<Canvas>
<Story name="Helper text">
<div style={{ display: 'flex', justifyContent: 'center', maxWidth: '100%' }}>
<TextField helperText="Phone number must begin with +33" placeholder="Phone number" />
</div>
</Story>
</Canvas>
## Error state
If `hasError` property is set to `true`, the `TextField` component will be displayed with an `error` semantic color.
In addition, the `helperText` property can be set to display an error message below the `TextField`.
<Canvas>
<Story name="Error state">
<div style={{ display: 'flex', justifyContent: 'center', maxWidth: '100%' }}>
<TextField
defaultValue="My phone is broken"
hasError
helperText="Please enter a valid phone number"
/>
</div>
</Story>
</Canvas>
## Full width
If set to `true`, the `TextField` component will take 100% of its container's width.
<Canvas>
<Story name="Full width">
<div style={{ display: 'flex', justifyContent: 'center', maxWidth: '100%' }}>
<TextField defaultValue="I'm set with full width" fullWidth />
</div>
</Story>
</Canvas>
## Controlled TextField
When the component is controlled, the parent passes a value to the `TextField` which displays it.
Parent has its own state in order to control the value which is passed, and it also specify an `onChange` handler property which will be called when user updates the value.
In the example below, everytime the input value changes, current state is updated and a log message displays the content of the value.
<Canvas>
<Story name="Controlled">
{() => {
const [value, setValue] = useState('I am controlled!')
const handleChange = event => {
setValue(event.target.value)
console.log(`My new value is : ${event.target.value}`)
}
return (
<div style={{ display: 'flex', justifyContent: 'center', maxWidth: '100%' }}>
<TextField value={value} onChange={handleChange} />
</div>
)
}}
</Story>
</Canvas>
## Uncontrolled TextField
The `TextField` component comes with an optional property called `defaultValue`.
It can be used when the component in uncontrolled, which implies that no value is passed to `value` or `onChange`.
Data is handled by the DOM itself, this is the source of truth.
If you want to access the value, you can use a ref to get value from the DOM, but it is recommended to interact with values by choosing a controlled component which is more appropriated.
<Canvas>
<Story name="Uncontrolled">
{() => {
const textFieldRef = useRef()
return (
<div style={{ display: 'flex', justifyContent: 'center', maxWidth: '100%' }}>
<TextField defaultValue="I am uncontrolled!" ref={textFieldRef} />
</div>
)
}}
</Story>
</Canvas>
## Right icon
This property allows the user to add a custom `icon` at the right of the input area.
<Canvas>
<Story name="Right icon">
{() => {
const [value, setValue] = useState('Clear me!')
const handleClick = event => {
setValue('')
}
const handleChange = event => {
setValue(event.target.value)
}
return (
<div style={{ display: 'flex', justifyContent: 'center', maxWidth: '100%' }}>
<TextField
value={value}
onChange={handleChange}
rightIcon={
<div onClick={handleClick} style={{ cursor: 'pointer' }}>
<Icon name="cross" size={18} />
</div>
}
/>
</div>
)
}}
</Story>
</Canvas>
## Border
The `withBorder` property allows you to add a themed border to the textField and thus to bring more visual impact within the interface.
<Canvas>
<Story name="Border">
<div style={{ display: 'flex', justifyContent: 'center', maxWidth: '100%' }}>
<TextField defaultValue="I'm with a themed border." withBorder />
</div>
</Story>
</Canvas>
## Multiline
The `multiline` property allows you to write text on several lines.
The simple text field becomes a text area and the [`size`](/docs/atoms-textfield--sizes) is automatically **auto**.
When `fullWidth` is **true** the user can only resize the height of the textField.
<Canvas>
<Story name="Multiline">
<div
style={{
display: 'flex',
flexDirection: 'column',
gap: '10px',
alignItems: 'center'
}}
>
<TextField
placeholder="I'm a multiline text field, so my size is automatically set to auto. You can resize my width and heigh."
multiline
numberOfLines={5}
/>
<TextField
placeholder="I'm a multiline text field in full width. My size is automatically set to auto and you can only resize my heigh."
multiline
numberOfLines={5}
fullWidth
/>
</div>
</Story>
</Canvas>
## Number of lines
If the text field is multiline, the property `numberOfLines` defines the initial height of the text field.
By default, the text area is initialized with two lines.
<Canvas>
<Story name="Number of lines">
<div
style={{
display: 'flex',
flexDirection: 'column',
gap: '10px',
alignItems: 'center'
}}
>
<TextField
placeholder="I'm a multiline text field with 5 lines."
multiline
numberOfLines={5}
/>
<TextField
placeholder="I'm a multiline text field with 7 lines."
multiline
numberOfLines={7}
/>
<TextField
placeholder="I'm a multiline text field with 10 lines."
multiline
numberOfLines={10}
/>
</div>
</Story>
</Canvas>
## Disable resizing
If the text field is multiline, the property `disableAreaResize` allows to disable the resizing of the text area.
When `disableAreaResize` is `false`, then users can define a [`size`](/docs/atoms-textfield--sizes).
<Canvas>
<Story name="Disable area resize">
<div
style={{
display: 'flex',
flexDirection: 'column',
gap: '10px',
alignItems: 'center'
}}
>
<TextField placeholder="You can not resize me." multiline disableAreaResize />
<TextField placeholder="You can resize me." multiline disableAreaResize={false} />
</div>
</Story>
</Canvas>