# React Components/Form Field

## Props


| Name | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| `id` | `` | No |  | The field id. |
| `invalid` | `` | No |  | Whether the component is in error state. |


## Subcomponents


### FormFieldError




### FormFieldHelper




### FormFieldLabel




### FormFieldLabelSubLabel



## Examples


### Accessibility Label

```tsx
{
  globals: {
    imports: `import { FormField, FormFieldHelper, FormFieldLabel, Input } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <FormField>
      <FormFieldLabel>
        Login:
      </FormFieldLabel>

      <Input name="input" />

      <FormFieldHelper>
        Username or email address
      </FormFieldHelper>
    </FormField>
}
```

### Anatomy Tech

```tsx
{
  tags: ['!dev'],
  render: ({}) => <FormField invalid>
      <FormFieldLabel>
        Description:
        <FormFieldLabelSubLabel>
          - mandatory
        </FormFieldLabelSubLabel>
      </FormFieldLabel>

      <Textarea name="description" />

      <FormFieldHelper>
        <Text preset={TEXT_PRESET.caption}>
          Helper text
        </Text>
      </FormFieldHelper>

      <FormFieldError>
        Error message
      </FormFieldError>
    </FormField>
}
```

### Default

```tsx
{
  globals: {
    imports: `import { FormField, Textarea } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <FormField>
      <Textarea name="textarea" />
    </FormField>
}
```

### Demo

```tsx
{
  render: (args: DemoArg) => <FormField invalid={args.invalid}>
      <FormFieldLabel>
        {args.label}
        <FormFieldLabelSubLabel>
          {args.sublabel}
        </FormFieldLabelSubLabel>
      </FormFieldLabel>

      <Textarea name="demo" />

      <FormFieldHelper>
        {args.helperText}
      </FormFieldHelper>

      <FormFieldError>
        {args.errorText}
      </FormFieldError>
    </FormField>,
  argTypes: orderControls({
    errorText: {
      table: {
        category: CONTROL_CATEGORY.slot,
        type: {
          summary: 'string'
        }
      },
      control: 'text'
    },
    helperText: {
      table: {
        category: CONTROL_CATEGORY.slot,
        type: {
          summary: 'string'
        }
      },
      control: 'text'
    },
    invalid: {
      table: {
        category: CONTROL_CATEGORY.general
      },
      control: 'boolean'
    },
    label: {
      table: {
        category: CONTROL_CATEGORY.slot,
        type: {
          summary: 'string'
        }
      },
      control: 'text'
    },
    sublabel: {
      table: {
        category: CONTROL_CATEGORY.slot,
        type: {
          summary: 'string'
        }
      },
      control: 'text'
    }
  })
}
```

### Error

```tsx
{
  globals: {
    imports: `import { FormField, FormFieldError, Textarea } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <FormField invalid>
      <Textarea name="textarea" />

      <FormFieldError>
        Error message
      </FormFieldError>
    </FormField>
}
```

### Guide Form Critical

```tsx
{
  globals: {
    imports: `import { BUTTON_COLOR, BUTTON_VARIANT, Button, FormField, FormFieldHelper, FormFieldLabel, FormFieldLabelSubLabel, Input, TEXT_PRESET, Text } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => {
    const [localError, setLocalError] = useState<Record<string, string | undefined>>({});
    function onBlur(e: FormEvent): void {
      const target = e.target as HTMLInputElement;
      setLocalError(error => ({
        ...error,
        [target.name]: target.validity.valid ? undefined : target.validationMessage
      }));
    }
    return <form style={{
      display: 'flex',
      flexFlow: 'column',
      rowGap: '8px'
    }}>
        <FormField>
          <FormFieldLabel>
            Please type DELETE to confirm
            <FormFieldLabelSubLabel>
              - mandatory
            </FormFieldLabelSubLabel>
          </FormFieldLabel>

          <Input invalid={!!localError.confirmation} name="confirmation" onBlur={onBlur} pattern="^DELETE$" placeholder="DELETE" required />

          <FormFieldHelper>
            <Text preset={TEXT_PRESET.caption}>
              This action is irreversible.
            </Text>
          </FormFieldHelper>
        </FormField>

        <div style={{
        display: "flex",
        justifyContent: "flex-end",
        gap: "16px"
      }}>
          <Button variant={BUTTON_VARIANT.outline}>
            Cancel
          </Button>

          <Button color={BUTTON_COLOR.critical}>
            Delete account
          </Button>
        </div>
      </form>;
  }
}
```

### Guide Form Error

```tsx
{
  globals: {
    imports: `import { BUTTON_COLOR, BUTTON_VARIANT, Button, FormField, FormFieldError, FormFieldHelper, FormFieldLabel, FormFieldLabelSubLabel, Input, Message, MessageBody, TEXT_PRESET, Text } from '@ovhcloud/ods-react';
import { type FormEvent, useState } from 'react';`
  },
  tags: ['!dev'],
  render: ({}) => {
    const [localError, setLocalError] = useState<Record<string, string | undefined>>({});
    const [globalError, setGlobalError] = useState('');
    const [isSubmitting, setIsSubmitting] = useState(false);
    function handleSubmit(e: FormEvent): void {
      e.preventDefault();
      setGlobalError('');
      setIsSubmitting(true);

      // Fake API call
      setTimeout(() => {
        setGlobalError('Something went wrong');
        setIsSubmitting(false);
      }, 1000);
    }
    function onBlur(e: FormEvent): void {
      const target = e.target as HTMLInputElement;
      setLocalError(error => ({
        ...error,
        [target.name]: target.validity.valid ? undefined : target.validationMessage
      }));
    }
    function onInvalid(e: FormEvent): void {
      e.preventDefault();
      const target = e.target as HTMLInputElement;
      setLocalError(error => ({
        ...error,
        [target.name]: target.validationMessage
      }));
    }
    return <form onSubmit={handleSubmit} style={{
      display: 'flex',
      flexFlow: 'column',
      rowGap: '8px'
    }}>
        <FormField invalid={!!localError.firstname}>
          <FormFieldLabel>
            First name
            <FormFieldLabelSubLabel>
              - mandatory
            </FormFieldLabelSubLabel>
          </FormFieldLabel>

          <Input invalid={!!localError.firstname} name="firstname" onBlur={onBlur} onInvalid={onInvalid} placeholder="Type your first name" required />

          <FormFieldError>
            {localError.firstname}
          </FormFieldError>
        </FormField>

        <FormField invalid={!!localError.email}>
          <FormFieldLabel>
            Email
            <FormFieldLabelSubLabel>
              - mandatory
            </FormFieldLabelSubLabel>
          </FormFieldLabel>

          <Input invalid={!!localError.email} name="email" onBlur={onBlur} onInvalid={onInvalid} pattern="^((?!\.)[\w\-_.]*[^.])(@\w+)(\.\w+(\.\w+)?[^.\W])$" placeholder="Type your email" required />

          <FormFieldError>
            {localError.email}
          </FormFieldError>
        </FormField>

        {!!globalError && <Message color={MESSAGE_COLOR.critical} onRemove={() => setGlobalError('')}>
            <MessageBody>
              {globalError}
            </MessageBody>
          </Message>}

        <div style={{
        display: "flex",
        justifyContent: "flex-end",
        gap: "16px"
      }}>
          <Button disabled={isSubmitting} variant={BUTTON_VARIANT.outline}>
            Cancel
          </Button>

          <Button color={BUTTON_COLOR.critical} loading={isSubmitting} type="submit">
            Delete account
          </Button>
        </div>
      </form>;
  }
}
```

### Guide Form Grouped Field

```tsx
{
  globals: {
    imports: `import { Button, FormField, FormFieldLabel, FormFieldLabelSubLabel, Input, TEXT_PRESET, Text } from '@ovhcloud/ods-react';
import { type FormEvent, useState } from 'react';`
  },
  tags: ['!dev'],
  render: ({}) => {
    const [localError, setLocalError] = useState<Record<string, string | undefined>>({});
    function onBlur(e: FormEvent): void {
      const target = e.target as HTMLInputElement;
      setLocalError(error => ({
        ...error,
        [target.name]: target.validity.valid ? undefined : target.validationMessage
      }));
    }
    return <form style={{
      display: 'flex',
      flexFlow: 'column',
      rowGap: '8px'
    }}>
        <Text preset={TEXT_PRESET.heading3}>
          Personal Information
        </Text>

        <FormField>
          <FormFieldLabel>
            First name
            <FormFieldLabelSubLabel>
              - mandatory
            </FormFieldLabelSubLabel>
          </FormFieldLabel>

          <Input invalid={!!localError.firstname} name="firstname" onBlur={onBlur} placeholder="Type your first name" required />
        </FormField>

        <FormField>
          <FormFieldLabel>
            Last name
            <FormFieldLabelSubLabel>
              - mandatory
            </FormFieldLabelSubLabel>
          </FormFieldLabel>

          <Input invalid={!!localError.lastname} name="lastname" onBlur={onBlur} placeholder="Type your last name" required />
        </FormField>

        <Text preset={TEXT_PRESET.heading3}>
          Company Information
        </Text>

        <FormField>
          <FormFieldLabel>
            Company name
          </FormFieldLabel>

          <Input name="company" placeholder="OVHcloud" />
        </FormField>

        <FormField>
          <FormFieldLabel>
            VAT number
          </FormFieldLabel>

          <Input name="vat" placeholder="vat" />
        </FormField>

        <div style={{
        display: "flex",
        justifyContent: "center"
      }}>
          <Button>
            Save
          </Button>
        </div>
      </form>;
  }
}
```

### Guide Form Mandatory

```tsx
{
  globals: {
    imports: `import { FormField, FormFieldHelper, FormFieldLabel, FormFieldLabelSubLabel, Input, PhoneNumber, PhoneNumberControl, TEXT_PRESET, Text, Textarea } from '@ovhcloud/ods-react';
import { type FormEvent, useState } from 'react';`
  },
  tags: ['!dev'],
  render: ({}) => {
    const [localError, setLocalError] = useState<Record<string, string | undefined>>({});
    function onBlur(e: FormEvent): void {
      const target = e.target as HTMLInputElement;
      setLocalError(error => ({
        ...error,
        [target.name]: target.validity.valid ? undefined : target.validationMessage
      }));
    }
    return <form style={{
      display: 'flex',
      flexFlow: 'column',
      rowGap: '8px'
    }}>
        <FormField>
          <FormFieldLabel>
            First name
            <FormFieldLabelSubLabel>
              - mandatory
            </FormFieldLabelSubLabel>
          </FormFieldLabel>

          <Input invalid={!!localError.firstname} name="firstname" onBlur={onBlur} placeholder="Type your first name" required />
        </FormField>

        <FormField>
          <FormFieldLabel>
            Last name
            <FormFieldLabelSubLabel>
              - mandatory
            </FormFieldLabelSubLabel>
          </FormFieldLabel>

          <Input invalid={!!localError.lastname} name="lastname" onBlur={onBlur} placeholder="Type your last name" required />
        </FormField>

        <FormField>
          <FormFieldLabel>
            Company
          </FormFieldLabel>

          <Input name="company" placeholder="Company" />
        </FormField>

        <FormField>
          <FormFieldLabel>
            Phone
          </FormFieldLabel>

          <PhoneNumber name="phonenumber">
            <PhoneNumberControl />
          </PhoneNumber>

          <FormFieldHelper>
            <Text preset={TEXT_PRESET.caption}>
              Include country code (e.g. +33 6 00 00 00 00)
            </Text>
          </FormFieldHelper>
        </FormField>

        <FormField>
          <FormFieldLabel>
            Email
            <FormFieldLabelSubLabel>
              - mandatory
            </FormFieldLabelSubLabel>
          </FormFieldLabel>

          <Input invalid={!!localError.email} name="email" onBlur={onBlur} pattern="^((?!\.)[\w\-_.]*[^.])(@\w+)(\.\w+(\.\w+)?[^.\W])$" placeholder="Your email" required type="email" />

          <FormFieldHelper>
            <Text preset={TEXT_PRESET.caption}>
              Format: name@example.com
            </Text>
          </FormFieldHelper>
        </FormField>
      </form>;
  }
}
```

### Guide Form Simple

```tsx
{
  globals: {
    imports: `import { BUTTON_VARIANT, Button, FormField, FormFieldLabel, PhoneNumber, PhoneNumberControl, PhoneNumberCountryList } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <form style={{
    display: 'flex',
    flexFlow: 'column',
    rowGap: '8px'
  }}>
      <FormField>
        <FormFieldLabel>
          Phone number
        </FormFieldLabel>

        <PhoneNumber name="phonenumber">
          <PhoneNumberCountryList />
          <PhoneNumberControl />
        </PhoneNumber>
      </FormField>

      <div style={{
      display: "flex",
      justifyContent: "flex-end",
      gap: "16px"
    }}>
        <Button variant={BUTTON_VARIANT.outline}>
          Back
        </Button>

        <Button>
          Update profile
        </Button>
      </div>
    </form>
}
```

### Helper

```tsx
{
  globals: {
    imports: `import { TEXT_PRESET, FormField, FormFieldHelper, Text, Textarea } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <FormField>
      <Textarea name="textarea" />

      <FormFieldHelper>
        <Text preset={TEXT_PRESET.caption}>
          Helper text
        </Text>
      </FormFieldHelper>
    </FormField>
}
```

### Label

```tsx
{
  globals: {
    imports: `import { FormField, FormFieldLabel, Textarea } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <FormField>
      <FormFieldLabel>
        Description:
      </FormFieldLabel>

      <Textarea name="textarea" />
    </FormField>
}
```

### Label Sub Label

```tsx
{
  globals: {
    imports: `import { FormField, FormFieldLabel, FormFieldLabelSubLabel, Textarea } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <FormField>
      <FormFieldLabel>
        Description
        <FormFieldLabelSubLabel>- mandatory</FormFieldLabelSubLabel>
      </FormFieldLabel>

      <Textarea name="textarea" />
    </FormField>
}
```

### Overview

```tsx
{
  parameters: {
    layout: 'centered'
  },
  tags: ['!dev'],
  render: ({}) => {
    const MAX_COUNT = 200;
    const [count, setCount] = useState(0);
    function onInput(e: FormEvent): void {
      setCount((e.target as HTMLTextAreaElement).value.length);
    }
    return <FormField invalid={count > MAX_COUNT}>
        <FormFieldLabel>
          Description:
        </FormFieldLabel>

        <Textarea name="description" onInput={onInput} />

        <FormFieldHelper style={{
        display: 'flex',
        justifyContent: 'space-between'
      }}>
          <Text preset={TEXT_PRESET.caption}>
            Helper text
          </Text>

          <Text preset={TEXT_PRESET.caption}>
            {count}/{MAX_COUNT}
          </Text>
        </FormFieldHelper>

        <FormFieldError>
          Error message
        </FormFieldError>
      </FormField>;
  }
}
```

### Theme Generator

```tsx
{
  parameters: {
    layout: 'fullscreen'
  },
  tags: ['!dev'],
  render: ({}) => <div style={{
    display: 'flex',
    flexDirection: 'column',
    gap: '12px'
  }}>
      <FormField>
        <FormFieldLabel>Label</FormFieldLabel>
        <Input name="input" />
      </FormField>

      <FormField>
        <Input name="input" />
        <FormFieldHelper>Helper text</FormFieldHelper>
      </FormField>

      <FormField invalid>
        <Input name="input" />
        <FormFieldError>Error message</FormFieldError>
      </FormField>
    </div>
}
```