# 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 { InputTextProps } from "@jobber/components/InputText";
import { InputText } from "@jobber/components/InputText";

export const InputTextBasicExample = ({
  onChange,
  ...props
}: Partial<InputTextProps>) => {
  const [value, setValue] = useState(props.value ?? "");

  return (
    <InputText
      name="age"
      placeholder="Age in words"
      {...props}
      value={value}
      onChange={newValue => {
        setValue(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, { useState } from "react";
import type { InputTextProps } from "@jobber/components/InputText";
import { InputText } from "@jobber/components/InputText";

export function InputTextMultilineExample(
  props: Partial<Omit<InputTextProps, "multiline">>,
) {
  const [value, setValue] = useState<string>(props.value ?? "");

  return (
    <InputText
      multiline={true}
      placeholder="Describe your age"
      {...props}
      value={value}
      onChange={(
        newValue: string,
        event?: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>,
      ) => {
        setValue(newValue);
        props.onChange?.(newValue, event);
      }}
    />
  );
}
```

### 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, { useState } from "react";
import type { InputTextProps } from "@jobber/components/InputText";
import { InputText } from "@jobber/components/InputText";
import { Content } from "@jobber/components/Content";

export function InputTextPrefixSuffixExample(
  props: Partial<Omit<InputTextProps, "multiline" | "rows">>,
) {
  const [invoiceTotal, setInvoiceTotal] = useState<string>(
    props.value ?? "1,000,000",
  );
  const [search, setSearch] = useState<string>(props.value ?? "");

  const handleChange = (
    newValue: string,
    event?: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>,
  ) => {
    setInvoiceTotal(newValue);
    props.onChange?.(newValue, event);
  };

  const handleSearchChange = (
    newValue: string,
    event?: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>,
  ) => {
    setSearch(newValue);
    props.onChange?.(newValue, event);
  };

  return (
    <Content>
      <InputText
        placeholder="Invoice Total"
        value={invoiceTotal}
        onChange={handleChange}
        prefix={{ label: "$", icon: "invoice" }}
        suffix={{ label: ".00" }}
        {...props}
      />
      <InputText
        placeholder="Search"
        prefix={{ icon: "search" }}
        value={search}
        onChange={handleSearchChange}
        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](../product-vocabulary/product-vocabulary.md#component-view-general-phrasing)
for guidance on writing helpful error messages.

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

export function InputTextValidationExample(
  props: Partial<Omit<InputTextProps, "multiline" | "rows">>,
) {
  const [value, setValue] = useState("");
  const [error, setError] = useState<string | undefined>();

  function handleChange(newValue: string) {
    setValue(newValue);

    if (!newValue) {
      setError("You have to tell us your age");
    } else if (!isNaN(Number(newValue))) {
      setError("Type your age in words please.");
    } else if (newValue.length >= 10) {
      setError("That seems too old.");
    } else {
      setError(undefined);
    }
  }

  return (
    <InputText
      placeholder="What's your age"
      value={value}
      onChange={handleChange}
      invalid={!!error}
      error={error}
      {...props}
    />
  );
}
```

## States

### Disabled

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

export function InputTextDisabledExample(
  props: Partial<Omit<InputTextProps, "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 { InputText } from "@jobber/components/InputText";
import type { InputTextProps } from "@jobber/components/InputText";

export function InputTextInvalidExample(
  props: Partial<Omit<InputTextProps, "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, { useState } from "react";
import type { InputTextProps } from "@jobber/components/InputText";
import { InputText } from "@jobber/components/InputText";
import { FormFieldLabel } from "@jobber/components/FormField";

export function InputTextExternalLabelExample(
  props: Partial<Omit<InputTextProps, "multiline" | "rows">>,
) {
  const [value, setValue] = useState<string>(props.value ?? "");

  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}
        value={value}
        onChange={(
          newValue: string,
          event?: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>,
        ) => {
          setValue(newValue);
          props.onChange?.(newValue, event);
        }}
      />
    </div>
  );
}
```

### Keyboard

Determine what default keyboard appears on mobile.

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

export function InputTextKeyboardExample(
  props: Partial<Omit<InputTextProps, "multiline" | "rows">>,
) {
  const [value, setValue] = useState<string>(props.value ?? "");

  return (
    <InputText
      placeholder="Describe your age"
      inputMode="numeric"
      {...props}
      value={value}
      onChange={(
        newValue: string,
        event?: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>,
      ) => {
        setValue(newValue);
        props.onChange?.(newValue, event);
      }}
    />
  );
}
```


## Props

### Web

| Prop | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| `value` | `string` | Yes | — | The current value of the input. |
| `align` | `"center" | "right"` | No | — | Determines the alignment of the text inside the input. |
| `aria-activedescendant` | `string` | No | — | ID of the currently active descendant element. Used for composite widgets like combobox or listbox. @see {@link https... |
| `aria-autocomplete` | `"both" | "inline" | "list" | "none"` | No | — | Indicates the type of autocomplete interaction. @see {@link https://www.w3.org/TR/wai-aria-1.2/#aria-autocomplete} |
| `aria-controls` | `string` | No | — | Indicates the element that controls the current element. @see {@link https://www.w3.org/TR/wai-aria-1.2/#aria-controls} |
| `aria-describedby` | `string` | No | — | Identifies the element (or elements) that describes the object. @see {@link https://www.w3.org/TR/wai-aria-1.2/#aria-... |
| `aria-details` | `string` | No | — | Identifies the element (or elements) that provide a detailed, extended description. @see {@link https://www.w3.org/TR... |
| `aria-expanded` | `Booleanish` | No | — | Indicates whether the element is expanded or collapsed. @see {@link https://www.w3.org/TR/wai-aria-1.2/#aria-expanded} |
| `aria-label` | `string` | No | — | Defines a string value that labels the current element. @see {@link https://www.w3.org/TR/wai-aria-1.2/#aria-label} |
| `aria-labelledby` | `string` | No | — | Identifies the element (or elements) that labels the current element. @see {@link https://www.w3.org/TR/wai-aria-1.2/... |
| `aria-required` | `Booleanish` | No | — | Indicates that user input is required before form submission. @see {@link https://www.w3.org/TR/wai-aria-1.2/#aria-re... |
| `autoComplete` | `string` | No | — | Autocomplete behavior for the input (React casing, string values only). Use standard HTML autocomplete values or "on"... |
| `autoFocus` | `boolean` | No | — | Whether the input should be auto-focused (React casing). |
| `clearable` | `Clearable` | No | — | Add a clear action on the input that clears the value. |
| `description` | `ReactNode` | No | — | Further description of the input, can be used for a hint. |
| `disabled` | `boolean` | No | — | Whether the input is disabled. |
| `error` | `string` | No | — | Error message to display. This also highlights the field red. |
| `id` | `string` | No | — | The unique identifier for the input element. |
| `inline` | `boolean` | No | — | Adjusts the form field to go inline with content. |
| `inputMode` | `"decimal" | "email" | "none" | "numeric" | "search" | "tel" | "text" | "url"` | No | — | Input mode hint for virtual keyboards. |
| `invalid` | `boolean` | No | — | Highlights the field red to indicate an error. |
| `loading` | `boolean` | No | — | Show a spinner to indicate loading. |
| `maxLength` | `number` | No | — | Maximum number of characters allowed in the input. |
| `multiline` | `boolean` | No | — | Use this when you're expecting a long answer. |
| `name` | `string` | No | — | The name attribute for the input element. |
| `onBlur` | `(event: FocusEvent<HTMLInputElement | HTMLTextAreaElement, Element>) => void` | No | — | Blur event handler. |
| `onChange` | `(newValue: string, event?: ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => void` | No | — | Custom onChange handler that provides the new value as the first argument. |
| `onClick` | `(event: MouseEvent<HTMLInputElement | HTMLTextAreaElement, MouseEvent>) => void` | No | — | Click event handler. |
| `onEnter` | `(event: KeyboardEvent<Element>) => void` | No | — | @deprecated Use `onKeyDown` or `onKeyUp` instead. |
| `onFocus` | `(event: FocusEvent<HTMLInputElement | HTMLTextAreaElement, Element>) => void` | No | — | Focus event handler. |
| `onKeyDown` | `(event: KeyboardEvent<HTMLInputElement | HTMLTextAreaElement>) => void` | No | — | Key down event handler. |
| `onKeyUp` | `(event: KeyboardEvent<HTMLInputElement | HTMLTextAreaElement>) => void` | No | — | Key up event handler. |
| `onMouseDown` | `(event: MouseEvent<HTMLInputElement | HTMLTextAreaElement, MouseEvent>) => void` | No | — | Mouse down event handler. |
| `onMouseUp` | `(event: MouseEvent<HTMLInputElement | HTMLTextAreaElement, MouseEvent>) => void` | No | — | Mouse up event handler. |
| `onPointerDown` | `(event: PointerEvent<HTMLInputElement | HTMLTextAreaElement>) => void` | No | — | Pointer down event handler. |
| `onPointerUp` | `(event: PointerEvent<HTMLInputElement | HTMLTextAreaElement>) => void` | No | — | Pointer up event handler. |
| `pattern` | `string` | No | — | Validation pattern (regex) for the input. |
| `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 | — | Whether the input is read-only (HTML standard casing). |
| `ref` | `Ref<HTMLInputElement | HTMLTextAreaElement>` | No | — | Allows getting a ref to the component instance. Once the component unmounts, React will set `ref.current` to `null` (... |
| `required` | `boolean` | No | — | Whether the input is required before form submission. |
| `role` | `string` | No | — | Role attribute for accessibility. |
| `rows` | `RowRange | number` | 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` | When false, the placeholder text only serves as a standard placeholder and disappears when the user types, instead of... |
| `size` | `"large" | "small"` | No | — | Adjusts the interface to either have small or large spacing. |
| `suffix` | `{ onClick: () => void; readonly ariaLabel: string; readonly icon: IconNames; readonly label?: string; } | { onClick?: never; ariaLabel?: never; readonly label?: string; readonly icon?: IconNames; }` | No | — | Adds a suffix label and icon with an optional action to the field. |
| `tabIndex` | `number` | No | — | Tab index for keyboard navigation. |
| `toolbar` | `ReactNode` | No | — | Toolbar to render content below the input. |
| `toolbarVisibility` | `"always" | "while-editing"` | No | — | Determines the visibility of the toolbar. |
