# Form Components

## Packages

Core form primitives live in `@octopusdeploy/design-system-components`.
Octopus-specific form fields (`Select`, `Slug`, `TimeSpanSelector`, `NumberPicker`) live in
`@octopusdeploy/design-system-octopus-components`. All exports are flat — no namespace prefixes.

## Text input

Use `TextField` from `@octopusdeploy/design-system-components` for free-text input.

```tsx
import { TextField } from "@octopusdeploy/design-system-components";

<TextField
  label="Name"
  value={name}
  onChange={setName}
  error={nameError}
/>
```

Required props: `label`, `value`, `onChange`. Never pass `className` or `style`.

`@octopusdeploy/design-system-octopus-components` also exports a `Text` component that wraps
`TextField` with an Octopus-specific label strategy. Prefer `TextField` unless you specifically
need that label strategy behaviour.

## Number input

Use `NumberField` from `@octopusdeploy/design-system-components`.

❌ Don't use `Number` or `NumberPicker` from octopus-components — both are deprecated.

## Select / dropdown

Use `Select` from `@octopusdeploy/design-system-octopus-components`.

❌ Don't use `DeprecatedSelect` — it is a class-based component and has no place in new code.

## Switch — instant changes only

`Switch` is exclusively for changes that take effect immediately without a submit action. It is
never a form field that participates in form submission.

✅ Correct: toggling a feature on/off inline, enabling/disabling a setting that saves immediately
❌ Wrong: using `Switch` inside a form where the user clicks a Save button

```tsx
import { Switch } from "@octopusdeploy/design-system-components";

// With label — accessibleName is optional
<Switch label="Enable feature" value={enabled} onChange={setEnabled} />

// Without visible label — accessibleName is required
<Switch accessibleName="Enable notifications" value={enabled} onChange={setEnabled} />
```

## Checkbox and RadioButton

Use `Checkbox`, `CheckboxNew`, and `RadioButton` from `@octopusdeploy/design-system-components`.
Check JSDoc for any props marked `@deprecated` before using.

## Form composition

Forms should use design system form components exclusively. Do not build custom input wrappers
around native `<input>`, `<select>`, or `<textarea>` elements.

Field descriptions, error states, and labels are built into each field component. Pass `error` and
`description` as props rather than rendering supplementary text outside the component.

## Layout

**Default to a single-column layout.** The majority of forms should stack fields vertically,
top-to-bottom, left-aligned. Labels sit above inputs.

**Multi-column layouts: use sparingly and max 2 columns.** A second column is only appropriate
when fields are closely related and naturally grouped side-by-side (e.g. first name and last name).
Do not use more than 2 columns. Multi-column layouts must stack to single-column on smaller
viewports.

❌ Don't use 2 columns just to reduce vertical space — this increases cognitive load and makes
the form harder to complete in order.
✅ Do use 2 columns when fields are semantically paired and the relationship is obvious.

Set a sensible min/max width on the form container so fields don't stretch to fill very wide
viewports — excessively wide inputs are harder to read and scan.

**Field sizing** should reflect the expected input length. A postcode field should be narrow; a
URL field should be wide. Avoid large size jumps between adjacent fields.

## Labels

Labels should be:
- Maximum three words, written in sentence case
- No colon at the end
- Never replaced by placeholders — placeholders disappear on input and are inaccessible

Use the `required` prop to mark fields where the component supports it — do not add custom text.
Mark only optional fields when most are required, or only required fields when most are optional.

## Hiding and disabling fields

**Prefer disabled over hidden** for fields the user cannot access due to permissions. Hiding a
field entirely gives no signal that the capability exists. Use `disabled` to show it exists but
is unavailable, with a tooltip explaining why where possible.

Use `readOnly` (not `disabled`) when a field already contains data that cannot be edited in the
current context.

**Progressive disclosure** is appropriate when a field is only relevant based on another
selection. Hide the dependent field by default and reveal it only when the triggering condition
is met — for example, showing a retry interval field only when automatic retries are enabled.

## Submit button behaviour

Enable the submit button by default when a user is filling out a form for the first time.
Disable it only:
- While the form is submitting (to prevent double-submission)
- When the user is editing an existing form and has made no changes yet
- When permissions prevent the action (provide a tooltip explaining why)

❌ Don't disable submit buttons to enforce validation — show validation errors instead.

## Validation

Validate on submission (server-side) as the primary method. Display errors inline using the
`error` prop on each field. For errors not tied to a specific field, or when there are more than
3 errors, show a summary at the top of the form.

Do not render validation messages as separate elements outside the field component's `error` prop.

## Slug and time span fields

`Slug` and `TimeSpanSelector` are available in `@octopusdeploy/design-system-octopus-components`
for these specific input types. Use them rather than building custom implementations.
