stories/atoms/components/textField/TextField.stories.mdx

Summary

Maintainability
Test Coverage
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>