# Input Text

Input text is used in forms that accept short or long answers from users.

## Design & usage guidelines

### Controlled

Use this to allow users to provide short answers.

```tsx
import React, { useState } from "react";
import type { InputTextRebuiltProps } from "@jobber/components/InputText";
import { InputText } from "@jobber/components/InputText";

export const ControlledExample = ({
  onChange,
  ...props
}: Partial<InputTextRebuiltProps>) => {
  const [age, setAge] = useState("Veintisiete");

  return (
    <InputText
      version={2}
      name="age"
      placeholder="Age in words"
      {...props}
      value={age}
      onChange={newValue => {
        setAge(newValue);
        onChange?.(newValue);
      }}
    />
  );
};
```

**Show code**

### Multiline

Use this to allow users to provide long answers. The default number of rows is
three. Note that `loading={true}` is unimplemented for multiline input text.

For web, you can set a minimum and maximum number of rows. See:
[Web/rows example](/storybook/web/?path=/story/components-forms-and-inputs-inputtext--multiline).

```tsx
import React from "react";
import type { InputTextLegacyProps } from "@jobber/components/InputText";
import { InputText } from "@jobber/components/InputText";

export function InputTextMultilineExample(
  props: Partial<Omit<InputTextLegacyProps, "multiline">>,
) {
  return (
    <InputText multiline={true} placeholder="Describe your age" {...props} />
  );
}
```

### Prefix/suffix

Use a prefix or suffix when additional visual cues about an input's function may
be helpful.

Some fields have common visual patterns such as "search" having a magnifying
glass icon, "Select" having a downwards arrow, or currency inputs having a
currency symbol. These signifiers reinforce the purpose of the input to increase
[Recognition over Recall](https://www.nngroup.com/articles/ten-usability-heuristics/)
and align the input with
[Consistency and Standards](https://www.nngroup.com/articles/ten-usability-heuristics/).
With clearer guidance around the purpose of inputs, the user is able to better
focus on the task at hand.

```tsx
import React from "react";
import type { InputTextLegacyProps } from "@jobber/components/InputText";
import { InputText } from "@jobber/components/InputText";
import { Content } from "@jobber/components/Content";

export function InputTextPrefixSuffixExample(
  props: Partial<Omit<InputTextLegacyProps, "multiline" | "rows">>,
) {
  return (
    <Content>
      <InputText
        defaultValue="1,000,000"
        placeholder="Invoice Total"
        prefix={{ label: "$", icon: "invoice" }}
        suffix={{ label: ".00" }}
        {...props}
      />
      <InputText
        placeholder="Search"
        prefix={{ icon: "search" }}
        suffix={{
          icon: "cross",
          ariaLabel: "clear search",
          onClick: () => alert("This could clear a search value"),
        }}
        {...props}
      />
    </Content>
  );
}
```

### Validation message

You can add your own custom validation messages on a field to assist the user in
successfully completing a form. This doesn't *replace* server-side validation,
but minimizes the need for it as the user should be set up for success by proper
guidance pre-submission before any "bad" data gets to the server.

Follow the
[product vocabulary](/content/product-vocabulary#component-view-general-phrasing)
for guidance on writing helpful error messages.

```tsx
import React from "react";
import type { InputTextLegacyProps } from "@jobber/components/InputText";
import { InputText } from "@jobber/components/InputText";

export function InputTextValidationExample(
  props: Partial<Omit<InputTextLegacyProps, "multiline" | "rows">>,
) {
  return (
    <InputText
      placeholder="What's your age"
      validations={{
        required: {
          value: true,
          message: "You have to tell us your age",
        },
        validate: val => {
          if (val.length > 0 && !isNaN(val)) {
            return "Type your age in words please.";
          }

          if (val.length >= 10) {
            return "That seems too old.";
          }

          return true;
        },
      }}
      {...props}
    />
  );
}
```

## States

### Disabled

```tsx
import React from "react";
import type { InputTextLegacyProps } from "@jobber/components/InputText";
import { InputText } from "@jobber/components/InputText";

export function InputTextDisabledExample(
  props: Partial<Omit<InputTextLegacyProps, "multiline" | "rows">>,
) {
  return (
    <InputText
      placeholder="Credit card"
      value="**** **** **** 1234"
      disabled={true}
      {...props}
    />
  );
}
```

### Invalid

For mobile, you can pass a string to the `invalid` prop to display an error.
See:
[Mobile/invalid example](/storybook/mobile/?path=/story/components-forms-and-inputs-inputtext--invalid).

```tsx
import React from "react";
import type { InputTextLegacyProps } from "@jobber/components/InputText";
import { InputText } from "@jobber/components/InputText";

export function InputTextInvalidExample(
  props: Partial<Omit<InputTextLegacyProps, "multiline" | "rows">>,
) {
  return (
    <InputText placeholder="Email" value="atlantis" invalid={true} {...props} />
  );
}
```

### External label

You can use `FormFieldLabel` to provide a label outside of the input. The
`showMiniLabel` prop on `InputText` can be used to hide the mini label that
appears when a value is provided.

```tsx
import React from "react";
import type { InputTextLegacyProps } from "@jobber/components/InputText";
import { InputText } from "@jobber/components/InputText";
import { FormFieldLabel } from "@jobber/components/FormField";

export function InputTextExternalLabelExample(
  props: Partial<Omit<InputTextLegacyProps, "multiline" | "rows">>,
) {
  return (
    <div style={{ width: "100%" }}>
      <FormFieldLabel external={true} htmlFor="ext-input">
        External label
      </FormFieldLabel>
      <InputText
        id="ext-input"
        name="name"
        clearable="always"
        showMiniLabel={false}
        {...props}
      />
    </div>
  );
}
```

### Keyboard

Determine what default keyboard appears on mobile.

```tsx
import React from "react";
import type { InputTextLegacyProps } from "@jobber/components/InputText";
import { InputText } from "@jobber/components/InputText";

export function InputTextKeyboardExample(
  props: Partial<Omit<InputTextLegacyProps, "multiline" | "rows">>,
) {
  return (
    <InputText placeholder="Describe your age" keyboard="numeric" {...props} />
  );
}
```


## Configuration

### react-hook-form

Atlantis utilizes [React Hook Form](https://react-hook-form.com/) to handle
`input` and `form` validation. This means that the
[React Hook Form validation options](https://react-hook-form.com/api#register)
are available out of the box.

This includes, but is not limited to:

* `required` - A Boolean which, if true, indicates that the input must have a
  value.
* `maxLength` - The maximum length of the value to accept for this input.
* `minLength` - The minimum length of the value to accept for this input.
* `pattern` - The regex pattern for the input.
* `validate` - You can pass a callback function as the argument to validate, or
  you can pass an object of callback functions to validate all of them.

### Using onValidation (Web)

If you need to capture the error message and render it outside of the component.
Read the [InputValidation](../InputValidation/InputValidation.md) documentation.

### Version 2 Shim (Experimental)

If you need to use the version 2 shim, you can pass `version={2}` to the
`InputText` and provide the required props. Due to issues with typescript
[see](https://github.com/microsoft/TypeScript/issues/32447) the aria attributes
(and other props separated by `-`) are not typed correctly. This means that the
shim will not enforce the version to be set to `2` when using these props. As a
result version 1 usages will have these props appear in the intellisense but
they will not do anything.


## Props

### Web

| Prop | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| `align` | `"center" | "right"` | No | — | Determines the alignment of the text inside the input. |
| `autocomplete` | `boolean | AutocompleteTypes` | No | — | Determines if browser form autocomplete is enabled. Note that "one-time-code" is experimental and should not be used ... |
| `autofocus` | `boolean` | No | — | Determines if the input should be auto-focused, using the HTML attribute |
| `clearable` | `Clearable` | No | — | Add a clear action on the input that clears the value.  You should always use `while-editing` if you want the input t... |
| `defaultValue` | `string | Date` | No | — | Initial value of the input. Only use this when you need to pre-populate the field with a data that is not controlled ... |
| `description` | `ReactNode` | No | — | Further description of the input, can be used for a hint. |
| `disabled` | `boolean` | No | — | Disable the input |
| `id` | `string` | No | — | A unique identifier for the input. |
| `inline` | `boolean` | No | — | Adjusts the form field to go inline with a content. This also silences the given `validations` prop. You'd have to us... |
| `inputRef` | `RefObject<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>` | No | — |  |
| `invalid` | `boolean` | No | — | Highlights the field red to indicate an error. |
| `keyboard` | `"numeric" | "decimal"` | No | — | Determines what kind of keyboard appears on mobile web. |
| `loading` | `boolean` | No | — | Show a spinner to indicate loading |
| `maxLength` | `number` | No | — | Maximum character length for an input. This also changes the width to roughly the same size as the max length. This i... |
| `multiline` | `boolean` | No | — | Use this when you're expecting a long answer. |
| `name` | `string` | No | — | Name of the input. |
| `onBlur` | `(event?: FocusEvent<Element, Element>) => void` | No | — | Blur callback. |
| `onChange` | `(newValue: string | number | boolean | Date, event?: ChangeEvent<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement, Element>) => void` | No | — | onChange handler that provides the new value (or event) @param newValue @param event |
| `onEnter` | `(event: KeyboardEvent<Element>) => void` | No | — | A callback to handle "Enter" keypress. This will only run if Enter is the only key. Will not run if Shift or Control ... |
| `onFocus` | `(event?: FocusEvent<Element, Element>) => void` | No | — | Focus callback. |
| `onValidation` | `(message: string) => void` | No | — | Callback to get the the status and message when validating a field @param message |
| `placeholder` | `string` | No | — | Text that appears inside the input when empty and floats above the value as a mini label once the user enters a value... |
| `prefix` | `Affix` | No | — | Adds a prefix label and icon to the field |
| `readonly` | `boolean` | No | — | Prevents users from editing the value. |
| `ref` | `Ref<InputTextRef>` | No | — | Allows getting a ref to the component instance. Once the component unmounts, React will set `ref.current` to `null` (... |
| `rows` | `number | RowRange` | No | — | Specifies the visible height of a long answer form field. Can be in the form of a single number to set a static heigh... |
| `showMiniLabel` | `boolean` | No | `true` | Controls the visibility of the mini label that appears inside the input when a value is entered. By default, the plac... |
| `size` | `"small" | "large"` | No | — | Adjusts the interface to either have small or large spacing. |
| `suffix` | `XOR<Affix, Suffix>` | No | — | Adds a suffix label and icon with an optional action to the field |
| `toolbar` | `ReactNode` | No | — | Toolbar to render content below the input. |
| `toolbarVisibility` | `"while-editing" | "always"` | No | — | Determines the visibility of the toolbar. |
| `validations` | `RegisterOptions` | No | — | Show an error message above the field. This also highlights the the field red if an error message shows up. |
| `value` | `string | number | Date` | No | — | Set the component to the given value. |
| `version` | `1` | No | — | Experimental: Determine which version of the FormField to use. Right now this isn't used but it will be used in the f... |
