# Forms

Forms sind strukturierte Oberflächen zur Eingabe, Bearbeitung und Übermittlung
von Daten. Sie bestehen aus Form Controls wie TextFields, NumberFields,
Checkboxen oder Radios, die gemeinsam eine in sich geschlossene Aufgabe
abbilden. Jede Eingabe steht dabei in einem klaren Kontext und wird durch Label
oder FieldDescription verständlich gemacht. Eine gute Form verhält sich
vorhersehbar, führt Schritt für Schritt durch den Prozess und gibt jederzeit
eindeutiges Feedback. Ziel ist es, die kognitive Last gering zu halten und
ertrauen in die Dateneingabe zu schaffen.

---

# Aufbau einer Form

```tsx
import {
  ActionGroup,
  ColumnLayout,
  Heading,
  Label,
  Section,
  Segment,
  SegmentedControl,
  Text,
  TextField,
} from "@mittwald/flow-react-components";
import {
  Form,
  SubmitButton,
  typedField,
} from "@mittwald/flow-react-components/react-hook-form";
import { useForm, useWatch } from "react-hook-form";

export default () => {
  const form = useForm<{
    firstName: string;
    lastName: string;
    street: string;
    houseNumber: string;
    zip: string;
    city: string;
    phone?: string;
    paymentMethod: "invoice" | "debit";
    accountHolder: string;
    iban: string;
  }>({
    defaultValues: {
      firstName: "",
      lastName: "",
      street: "",
      houseNumber: "",
      phone: "",
      paymentMethod: "invoice",
      accountHolder: "",
      iban: "",
    },
  });

  const Field = typedField(form);

  const watchedPaymentMethod = useWatch({
    control: form.control,
    name: "paymentMethod",
  });

  return (
    <Form
      form={form}
      onSubmit={() => console.log("submitted")}
    >
      <Section>
        <Heading>Zahlungsinformationen</Heading>
        <Heading level={3}>Zahlungsart</Heading>
        <Field name="paymentMethod">
          <SegmentedControl aria-label="Zahlungsart">
            <Segment value="invoice">Rechnung</Segment>
            <Segment value="debit">Lastschrift</Segment>
          </SegmentedControl>
        </Field>

        {watchedPaymentMethod === "invoice" && (
          <Text>
            Bitte bezahle deine Rechnungen innerhalb von 14
            Tagen.
          </Text>
        )}

        {watchedPaymentMethod === "debit" && (
          <>
            <ColumnLayout m={[1, 1]}>
              <Field
                name="accountHolder"
                rules={{
                  required:
                    "Bitte gib einen Kontoinhaber ein",
                }}
              >
                <TextField>
                  <Label>Kontoinhaber</Label>
                </TextField>
              </Field>

              <Field
                name="iban"
                rules={{
                  required: "Bitte gib eine IBAN ein",
                }}
              >
                <TextField>
                  <Label>IBAN</Label>
                </TextField>
              </Field>
            </ColumnLayout>
          </>
        )}

        <Heading level={3}>Rechnungsadresse</Heading>
        <ColumnLayout l={[1, 1]}>
          <Field
            name="firstName"
            rules={{
              required: "Bitte gib einen Vornamen ein",
            }}
          >
            <TextField>
              <Label>Vorname</Label>
            </TextField>
          </Field>
          <Field
            name="lastName"
            rules={{
              required: "Bitte gib einen Nachnamen ein",
            }}
          >
            <TextField>
              <Label>Nachname</Label>
            </TextField>
          </Field>
          <ColumnLayout m={[2, 1]} s={[2, 1]}>
            <Field
              name="street"
              rules={{
                required: "Bitte gib eine Straße ein",
              }}
            >
              <TextField>
                <Label>Straße</Label>
              </TextField>
            </Field>
            <Field
              name="houseNumber"
              rules={{
                required: "Bitte gib eine Hausnr. ein",
              }}
            >
              <TextField>
                <Label>Hausnummer</Label>
              </TextField>
            </Field>
          </ColumnLayout>
          <ColumnLayout m={[1, 2]} s={[1, 2]}>
            <Field
              name="zip"
              rules={{
                required: "Bitte gib eine Postleitzahl ein",
              }}
            >
              <TextField>
                <Label>Postleitzahl</Label>
              </TextField>
            </Field>
            <Field
              name="city"
              rules={{
                required: "Bitte gib einen Ort ein",
              }}
            >
              <TextField>
                <Label>Ort</Label>
              </TextField>
            </Field>
          </ColumnLayout>
          <Field name="phone">
            <TextField>
              <Label>Telefonnummer</Label>
            </TextField>
          </Field>
        </ColumnLayout>
        <ActionGroup>
          <SubmitButton color="accent">
            Speichern
          </SubmitButton>
        </ActionGroup>
      </Section>
    </Form>
  );
}
```

Formularfelder sollten in einer vorhersehbaren und intuitiven Reihenfolge
angeordnet sein. Für eine intuitive Bedienung:

- Ordne die Felder nach ihrer Wichtigkeit.
- Platziere zusammengehörige Felder nah beieinander.
- Platziere nach Möglichkeit Eingabefelder mit Tastatureingabe nah beieinander,
  damit Mausnutzer nicht zwischen Klicken und Tippen wechseln müssen.

Zur Strukturierung von Formularen können Form Controls mithilfe von
[Sections](/04-components/structure/section/overview) gruppiert und mit
[ColumnLayouts](/04-components/structure/column-layout/overview) in Spalten
aufgeteilt werden. Forms werden im mStudio in der Regel in einem
[Modal](/04-components/overlays/modal/overview) platziert. Weitere Informationen
findest du im Pattern
[Anlegen und Bearbeiten](/03-patterns/01-patterns/anlegeprozess).

## Progressively disclosed Form Controls

In manchen Fällen können Form Controls schrittweise eingeblendet werden. Sie
sind dabei visuell unter einem übergeordneten Control verschachtelt. Ein
gängiges Muster ist die Nutzung des "aktivierten" Zustands einer
[Checkbox](/04-components/form-controls/checkbox/overview) oder
[RadioGroup](/04-components/form-controls/radio-group/overview). Dieser Zustand
entscheidet, ob ein zugehöriges Form Control angezeigt wird.

---

# Aufbau eines Form Controls

Forms können verschiedene Elemente enthalten. Standardmäßig sind alle Angaben
Pflichtfelder. Eine Ausnahme bilden Felder mit dem Zusatz „(optional)“ am Label
oder der Heading.

## Label

Ein gutes [Label](/04-components/content/label/overview) vermittelt alle
notwendigen Informationen klar und prägnant (max. 2 Wörter). Ist ein Form
Control optional, füge den Zusatz „(optional)“ hinzu. Können die
Eingabeanforderungen aus dem Kontext erschlossen werden, kann auf ein sichtbares
Label verzichtet werden. In diesem Fall muss das Form Control über
`aria-labelledby` verknüpft oder mit `aria-label` beschrieben werden.

## Fields / Controls

Das Design System bietet eine große Auswahl an wiederverwendbaren Form Controls.
Dazu gehören verschiedene **Fields** wie zum Beispiel
[TextField](/04-components/form-controls/text-field/overview),
[NumberField](/04-components/form-controls/number-field/overview) oder
[PasswordCreationField](/04-components/form-controls/password-creation-field/overview)
sowie **Controls** wie
[Checkboxen](/04-components/form-controls/checkbox/overview),
[Selects](/04-components/form-controls/select/overview) oder
[RadioGroups](/04-components/form-controls/radio-group/overview). Alle
Components sind im Bereich Components unter dem Menüpunkt Form Controls zu
finden.

## FieldDescription

Die `<FieldDescription />` ist ein optionaler Hilfstext unterhalb des Form
Controls. Sie liefert zusätzliche Informationen oder Anweisungen. Verwende sie
sparsam. Sie kann Beispiele oder Hinweise zur Formatierung enthalten.

## FieldError

Ein ungültiges Field muss immer eine Fehlermeldung anzeigen. Diese erklärt,
warum der eingegebene Wert die Validierung nicht besteht. Nutze den
`<FieldError />` für diese Meldung. Beschreibe klar, warum die Eingabe ungültig
ist, und hilf dem User bei der Korrektur. Hinweise zur Formulierung findest du
in der Guideline zu
[Fehlermeldungen](/02-foundations/03-content-guidelines/03-fehlermeldungen).

## Placeholder

Placeholder sind nicht barrierefrei. Sie dürfen keine Informationen enthalten,
die für das korrekte Ausfüllen eines Fields notwendig sind. Zum einen
verschwinden Placeholder bei der Eingabe von Text. Zum anderen werden sie von
Assistenztechnologien oft nicht zuverlässig erkannt und sind schwieriger zu
verarbeiten als eine FieldDescription. Statt eines Placeholders:

- Nutze ein [Label](/04-components/content/label/overview) für essenzielle
  Informationen zu den Eingabeanforderungen.
- Nutze eine FieldDescription für Beispiele oder Formatierungshinweise.

Eine Ausnahme ist das
[SearchField](/04-components/form-controls/search-field/overview). Es besitzt
ein zusätzliches visuelles Leading Icon, das die Suche kennzeichnet.

---

# Validation

Standardmäßig erfolgt die Validierung erst beim Absenden des Forms, damit der
User es ohne Unterbrechung ausfüllen kann. Zum Absenden wird der
`<SubmitButton/>` verwendet. Nutze die Components
[Form (React Hook Form)](/04-components/form-controls/form-react-hook-form/overview)
oder
[Field (React Hook Form)](/04-components/form-controls/field-react-hook-form/overview),
um die Form Controls von Flow nahtlos in das Formular-Management von React Hook
Form zu integrieren.

```tsx
import {
  ActionGroup,
  Heading,
  Label,
  Section,
  TextArea,
} from "@mittwald/flow-react-components";
import {
  Form,
  SubmitButton,
  typedField,
} from "@mittwald/flow-react-components/react-hook-form";
import { useForm } from "react-hook-form";

export default () => {
  const form = useForm<{
    publicKey: string;
  }>({
    defaultValues: {
      publicKey: "",
    },
  });

  const Field = typedField(form);

  return (
    <Form
      form={form}
      onSubmit={() => console.log("submitted")}
    >
      <Section>
        <Heading>Validierung</Heading>
        <Field
          name="publicKey"
          rules={{
            required: "Bitte gib einen SSH-Key ein",
            validate: {
              isValid: (key) =>
                key.startsWith("rsa")
                  ? true
                  : "Der SSH-Key muss mit 'rsa' anfangen",
            },
            minLength: {
              value: 10,
              message:
                "Der SSH-Key muss mindestens 10 Zeichen lang sein",
            },
          }}
        >
          <TextArea maxLength={8000} showCharacterCount>
            <Label>SSH-Key</Label>
          </TextArea>
        </Field>

        <ActionGroup>
          <SubmitButton color="accent">
            Speichern
          </SubmitButton>
        </ActionGroup>
      </Section>
    </Form>
  );
}
```

Je nach Field stehen unterschiedliche Properties zur Verfügung, zum Beispiel:

- `isRequired` für Pflichtfelder.
- `validate` für eigene Validierungen.
- HTML Input Properties wie `type="email"`, `minLength`.

## Quick submit

Ein Formular sollte die Funktion „Quick submit“ unterstützen. Sie ermöglicht das
Absenden über `⌘ + Enter` oder `Strg + Enter` aus jedem Form Control,
insbesondere aus einem TextArea oder einem MarkdownEditor.
