# RadioGroup

Eine RadioGroup ermöglicht die Auswahl genau einer Option aus einer Gruppe.

```tsx
import {
  Label,
  Radio,
  RadioGroup,
} from "@mittwald/flow-react-components";

<RadioGroup defaultValue="more">
  <Label>Täglicher Kaffeekonsum</Label>
  <Radio value="more">Mehr als 6 Tassen</Radio>
  <Radio value="5-6">5-6 Tassen</Radio>
  <Radio value="3-4">3-4 Tassen</Radio>
  <Radio value="1-2">1-2 Tassen</Radio>
  <Radio value="none">Trinke keinen Kaffee</Radio>
</RadioGroup>
```

---

# Best Practices

- Beschrifte die RadioGroup mit einem [Label](/04-components/content/label).
  Übernimmt ein nahes Element wie eine [Heading](/04-components/content/heading)
  diese Rolle, setze stattdessen `aria-label` bzw. `aria-labelledby`.
- Nutze bei vielen Optionen ein [Select](/04-components/form-controls/select),
  um die Oberfläche aufgeräumt zu halten. Als Faustregel ab etwa sieben
  Optionen.
- Wähle zu Beginn eine sinnvolle Option vor. Das vermeidet unnötige
  Fehlermeldungen.
- Ordne die Optionen nach Wichtigkeit. Die wichtigste steht oben; eine Option
  mit weitreichenden Folgen gehört nach unten und ist nie vorausgewählt.
- Formuliere die Optionen klar unterscheidbar. So erkennt der User die passende
  Wahl auf einen Blick.
- Folge in einem Formular dem [Form-Pattern](/03-patterns/01-patterns/forms).
  Dort sind Aufbau, Validierung und Fehlerbehandlung geregelt.

---

# States

In einer RadioGroup sollte immer eine sinnvolle Option vorausgewählt
(**Selected**) sein.

## Disabled

Um Optionen in der RadioGroup zu deaktivieren, lässt sich der Disabled-State für
die gesamte RadioGroup oder gezielt für einzelne Radios bzw. RadioButtons
setzen.

```tsx
import {
  RadioButton,
  RadioGroup,
} from "@mittwald/flow-react-components";

<RadioGroup
  defaultValue="selected"
  l={[1, 1]}
  aria-label="States"
>
  <RadioButton value="default">Domain buchen</RadioButton>
  <RadioButton value="selected">Domain buchen</RadioButton>
  <RadioButton isDisabled value="default">
    Domain buchen
  </RadioButton>
  <RadioButton isDisabled value="selected">
    Domain buchen
  </RadioButton>
</RadioGroup>
```

## Error

Ein Error-State wird angezeigt, wenn etwas schiefgelaufen ist (siehe
[Fehlermeldungen](/02-foundations/03-content-guidelines/03-fehlermeldungen)). Da
meist eine Option vorselektiert ist, wird er nur selten benötigt.

```tsx
import {
  FieldError,
  Label,
  RadioButton,
  RadioGroup,
} from "@mittwald/flow-react-components";

<RadioGroup isInvalid>
  <Label>Datenbank-Typ</Label>
  <RadioButton value="mysql">MySQL</RadioButton>
  <RadioButton value="redis">Redis</RadioButton>
  <FieldError>Bitte wähle eine Option aus</FieldError>
</RadioGroup>
```

---

# RadioButtons

Um Inhalte stärker hervorzuheben und die Klickfläche zu vergrößern, lassen sich
statt `<Radio />` auch `<RadioButton />` nutzen.

```tsx
import {
  Label,
  RadioButton,
  RadioGroup,
} from "@mittwald/flow-react-components";

<RadioGroup defaultValue="mysql">
  <Label>Datenbank-Typ</Label>
  <RadioButton value="mysql">MySQL</RadioButton>
  <RadioButton value="redis">Redis</RadioButton>
</RadioGroup>
```

## Weiterer Inhalt

Anstelle einer einfachen Bezeichnung kann der `<RadioButton />` auch mit
zusätzlichem Inhalt gefüllt werden. Verwende dazu innerhalb des
`<RadioButton />` [Text](/04-components/content/text) für eine Überschrift und
`<Content />` für Fließtext.

```tsx
import {
  Content,
  RadioButton,
  RadioGroup,
  Text,
} from "@mittwald/flow-react-components";

<RadioGroup
  defaultValue="bookDomain"
  aria-label="Domain"
  l={[1, 1]}
>
  <RadioButton value="bookDomain">
    <Text>Domain buchen</Text>
    <Content>
      Du hast eine Wunsch-Domain? Kein Problem, wir helfen
      dir, die passende Domain für dich zu finden.
      <br />
      <strong>
        <small>8,28€ jährlich</small>
      </strong>
    </Content>
  </RadioButton>
  <RadioButton value="moveDomain">
    <Text>Domain umziehen</Text>
    <Content>
      Du hast schon eine Domain und möchtest sie von deinem
      jetzigen Anbieter zu mittwald umziehen.
      <br />
      <strong>
        <small>8,28€ jährlich</small>
      </strong>
    </Content>
  </RadioButton>
  <RadioButton value="virtualHost">
    <Text>Virtual Host einrichten</Text>
    <Content>
      Die Domain bleibt bei deinem bisherigen Anbieter, du
      kannst sie aber für deine Website in unserem mStudio
      verwenden.
      <br />
      <strong>
        <small>kostenlos</small>
      </strong>
    </Content>
  </RadioButton>
  <RadioButton value="subdomain">
    <Text>Subdomain anlegen</Text>
    <Content>
      Eine Subdomain von einer bereits vorhandenen Domain
      erstellen, um sie für dein Projekt zu verwenden.
      <br />
      <strong>
        <small>kostenlos</small>
      </strong>
    </Content>
  </RadioButton>
</RadioGroup>
```

## Benutzerdefinierte Spalten

Die RadioGroup verwendet das
[ColumnLayout](/04-components/structure/column-layout), um eine Anpassung der
Spalten zu ermöglichen. Bei der Verwendung von RadioButtons entspricht der
Default dem des ColumnLayouts, während Radios im Default untereinander angezeigt
werden.

```tsx
import {
  Label,
  RadioButton,
  RadioGroup,
} from "@mittwald/flow-react-components";

<RadioGroup
  defaultValue="one"
  s={[1, 1]}
  m={[1, 1, 1]}
  l={[1, 1, 1, 1]}
>
  <Label>Benutzerdefinierte Spalten</Label>
  <RadioButton value="one">Spalte 1</RadioButton>
  <RadioButton value="two">Spalte 2</RadioButton>
  <RadioButton value="three">Spalte 3</RadioButton>
</RadioGroup>
```

---

# FieldDescription

Für eine kurze Hilfestellung zu den Optionen kann unterhalb der `<Radio />` oder
`<RadioButton />` eine `<FieldDescription />` eingebaut werden.

```tsx
import {
  FieldDescription,
  Label,
  Radio,
  RadioGroup,
} from "@mittwald/flow-react-components";

<RadioGroup defaultValue="more">
  <Label>Wie viele Pflanzen besitzt du?</Label>
  <Radio value="more">Mehr als 9 Pflanzen</Radio>
  <Radio value="6-8">6-8 Pflanzen</Radio>
  <Radio value="3-5">3-5 9 Pflanzen</Radio>
  <Radio value="1-2">1-2 Pflanzen</Radio>
  <Radio value="none">Keine</Radio>
  <FieldDescription>
    Mehrere identische Pflanzen in einem Topf gelten als
    eine Pflanze.
  </FieldDescription>
</RadioGroup>
```

---

# Kombiniere mit …

## ContextualHelp

Benutze die [ContextualHelp](/04-components/overlays/contextual-help) Component,
wenn du weitere Informationen bereitstellen möchtest, und diese zu lang für die
FieldDescription sind.

```tsx
import {
  Button,
  ContextualHelp,
  ContextualHelpTrigger,
  Label,
  Radio,
  RadioGroup,
  Text,
} from "@mittwald/flow-react-components";

<RadioGroup defaultValue="more">
  <Label>
    Täglicher Kaffeekonsum
    <ContextualHelpTrigger subject="Täglicher Kaffeekonsum">
      <Button />
      <ContextualHelp>
        <Text>
          Hier gibt es weitere Informationen, die zu lang
          für die FieldDescription sind.
        </Text>
      </ContextualHelp>
    </ContextualHelpTrigger>
  </Label>
  <Radio value="more">Mehr als 6 Tassen</Radio>
  <Radio value="5-6">5-6 Tassen</Radio>
  <Radio value="3-4">3-4 Tassen</Radio>
  <Radio value="1-2">1-2 Tassen</Radio>
  <Radio value="none">Trinke keinen Kaffee</Radio>
</RadioGroup>
```

## React Hook Form

Weitere Details zur Formularlogik und -validierung findest du in der Component
[Form (React Hook Form)](/04-components/react-hook-form/form).

```tsx
import {
  Label,
  Radio,
  RadioGroup,
  Section,
} from "@mittwald/flow-react-components";
import { useForm } from "react-hook-form";
import {
  Form,
  SubmitButton,
  typedField,
} from "@mittwald/flow-react-components/react-hook-form";
import { sleep } from "@/content/04-components/actions/action/examples/lib";

export default () => {
  const form = useForm<{ coffee: string }>({
    defaultValues: { coffee: "more" },
  });
  const Field = typedField(form);

  return (
    <Section>
      <Form form={form} onSubmit={sleep}>
        <Field
          name="coffee"
          rules={{
            required: "Bitte gib deinen Kaffeekonsum an",
          }}
        >
          <RadioGroup>
            <Label>Täglicher Kaffeekonsum</Label>
            <Radio value="more">Mehr als 6 Tassen</Radio>
            <Radio value="5-6">5-6 Tassen</Radio>
            <Radio value="3-4">3-4 Tassen</Radio>
            <Radio value="1-2">1-2 Tassen</Radio>
            <Radio value="none">Trinke keinen Kaffee</Radio>
          </RadioGroup>
        </Field>
        <SubmitButton>Speichern</SubmitButton>
      </Form>
    </Section>
  );
}
```

---

# Properties

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `id` | `string` | - | The element's unique identifier. See [MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/id). |
| `translate` | `"yes" \| "no"` | - | - |
| `className` | `ClassNameOrFunction<RadioGroupRenderProps>` | `'react-aria-RadioGroup'` | The CSS [className](https://developer.mozilla.org/en-US/docs/Web/API/Element/className) for the element. A function may be provided to compute the class based on component state. |
| `style` | `StyleOrFunction<TooltipRenderProps>` | - | The inline [style](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/style) for the element. A function may be provided to compute the style based on component state. |
| `render` | `DOMRenderFunction<"div", TooltipRenderProps>` | - | Overrides the default DOM element with a custom render function. This allows rendering existing components with built-in styles and behaviors such as router links, animation libraries, and pre-styled components. Requirements: - You must render the expected element type (e.g. if `<button>` is expected, you cannot render an `<a>`). - Only a single root DOM element can be rendered (no fragments). - You must pass through props and ref to the underlying DOM element, merging with your own prop as appropriate. |
| `dir` | `string` | - | - |
| `lang` | `string` | - | - |
| `hidden` | `boolean` | - | - |
| `inert` | `boolean` | - | - |
| `validationBehavior` | `"native" \| "aria"` | `'native'` | Whether to use native HTML form validation to prevent form submission when the value is missing or invalid, or mark the field as required or invalid via ARIA. |
| `isDisabled` | `boolean` | - | Whether the input is disabled. |
| `isReadOnly` | `boolean` | - | Whether the input can be selected but not changed by the user. |
| `isRequired` | `boolean` | - | Whether user input is required on the input before form submission. |
| `isInvalid` | `boolean` | - | Whether the input value is invalid. |
| `validate` | `((value: TimeValue) => true \| ValidationError)` | - | A function that returns an error message if a given value is invalid. Validation errors are displayed to the user when the form is submitted if `validationBehavior="native"`. For realtime validation, use the `isInvalid` prop instead. |
| `value` | `TimeValue` | - | The current value (controlled). |
| `defaultValue` | `TimeValue` | - | The default value (uncontrolled). |
| `name` | `string` | - | The name of the input element, used when submitting an HTML form. See [MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#htmlattrdefname). |
| `form` | `string` | - | The `<form>` element to associate the input with. The value of this attribute must be the id of a `<form>` in the same document. See [MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/input#form). |
| `slot` | `string` | - | A slot name for the component. Slots allow the component to receive props from a parent component. An explicit `null` value indicates that the local props completely override all props received from a parent. |
| `orientation` | `Orientation` | `'vertical'` | The axis the Radio Button(s) should align with. |
| `children` | `ReactNode` | - | - |
| `wrapWith` | `ReactElement<unknown, string \| JSXElementConstructor<any>>` | - | A React element the component is wrapped with. The element is cloned and receives the component as its only child — useful to render the component inside a link, a tooltip trigger or any other wrapper without changing the surrounding markup. |
| `ref` | `Ref<HTMLSpanElement>` | - | Allows getting a ref to the component instance. Once the component unmounts, React will set `ref.current` to `null` (or call the ref with `null` if you passed a callback ref). @see [React Docs](https://react.dev/learn/referencing-values-with-refs#refs-and-the-dom) |
| `key` | `Key` | - | - |
| `s` | `(number)[]` | - | Column layout for container size s. |
| `m` | `(number)[]` | - | Column layout for container size m. |
| `l` | `(number)[]` | - | Column layout for container size l. |

### Events

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `onClick` | `MouseEventHandler<HTMLDivElement>` | - | - |
| `onClickCapture` | `MouseEventHandler<HTMLDivElement>` | - | - |
| `onAuxClick` | `MouseEventHandler<HTMLDivElement>` | - | - |
| `onAuxClickCapture` | `MouseEventHandler<HTMLDivElement>` | - | - |
| `onContextMenu` | `MouseEventHandler<HTMLDivElement>` | - | - |
| `onContextMenuCapture` | `MouseEventHandler<HTMLDivElement>` | - | - |
| `onDoubleClick` | `MouseEventHandler<HTMLDivElement>` | - | - |
| `onDoubleClickCapture` | `MouseEventHandler<HTMLDivElement>` | - | - |
| `onMouseDown` | `MouseEventHandler<HTMLDivElement>` | - | - |
| `onMouseDownCapture` | `MouseEventHandler<HTMLDivElement>` | - | - |
| `onMouseEnter` | `MouseEventHandler<HTMLDivElement>` | - | - |
| `onMouseLeave` | `MouseEventHandler<HTMLDivElement>` | - | - |
| `onMouseMove` | `MouseEventHandler<HTMLDivElement>` | - | - |
| `onMouseMoveCapture` | `MouseEventHandler<HTMLDivElement>` | - | - |
| `onMouseOut` | `MouseEventHandler<HTMLDivElement>` | - | - |
| `onMouseOutCapture` | `MouseEventHandler<HTMLDivElement>` | - | - |
| `onMouseOver` | `MouseEventHandler<HTMLDivElement>` | - | - |
| `onMouseOverCapture` | `MouseEventHandler<HTMLDivElement>` | - | - |
| `onMouseUp` | `MouseEventHandler<HTMLDivElement>` | - | - |
| `onMouseUpCapture` | `MouseEventHandler<HTMLDivElement>` | - | - |
| `onTouchCancel` | `TouchEventHandler<HTMLDivElement>` | - | - |
| `onTouchCancelCapture` | `TouchEventHandler<HTMLDivElement>` | - | - |
| `onTouchEnd` | `TouchEventHandler<HTMLDivElement>` | - | - |
| `onTouchEndCapture` | `TouchEventHandler<HTMLDivElement>` | - | - |
| `onTouchMove` | `TouchEventHandler<HTMLDivElement>` | - | - |
| `onTouchMoveCapture` | `TouchEventHandler<HTMLDivElement>` | - | - |
| `onTouchStart` | `TouchEventHandler<HTMLDivElement>` | - | - |
| `onTouchStartCapture` | `TouchEventHandler<HTMLDivElement>` | - | - |
| `onPointerDown` | `PointerEventHandler<HTMLDivElement>` | - | - |
| `onPointerDownCapture` | `PointerEventHandler<HTMLDivElement>` | - | - |
| `onPointerMove` | `PointerEventHandler<HTMLDivElement>` | - | - |
| `onPointerMoveCapture` | `PointerEventHandler<HTMLDivElement>` | - | - |
| `onPointerUp` | `PointerEventHandler<HTMLDivElement>` | - | - |
| `onPointerUpCapture` | `PointerEventHandler<HTMLDivElement>` | - | - |
| `onPointerCancel` | `PointerEventHandler<HTMLDivElement>` | - | - |
| `onPointerCancelCapture` | `PointerEventHandler<HTMLDivElement>` | - | - |
| `onPointerEnter` | `PointerEventHandler<HTMLDivElement>` | - | - |
| `onPointerLeave` | `PointerEventHandler<HTMLDivElement>` | - | - |
| `onPointerOver` | `PointerEventHandler<HTMLDivElement>` | - | - |
| `onPointerOverCapture` | `PointerEventHandler<HTMLDivElement>` | - | - |
| `onPointerOut` | `PointerEventHandler<HTMLDivElement>` | - | - |
| `onPointerOutCapture` | `PointerEventHandler<HTMLDivElement>` | - | - |
| `onGotPointerCapture` | `PointerEventHandler<HTMLDivElement>` | - | - |
| `onGotPointerCaptureCapture` | `PointerEventHandler<HTMLDivElement>` | - | - |
| `onLostPointerCapture` | `PointerEventHandler<HTMLDivElement>` | - | - |
| `onLostPointerCaptureCapture` | `PointerEventHandler<HTMLDivElement>` | - | - |
| `onScroll` | `UIEventHandler<HTMLDivElement>` | - | - |
| `onScrollCapture` | `UIEventHandler<HTMLDivElement>` | - | - |
| `onWheel` | `WheelEventHandler<HTMLDivElement>` | - | - |
| `onWheelCapture` | `WheelEventHandler<HTMLDivElement>` | - | - |
| `onAnimationStart` | `AnimationEventHandler<HTMLDivElement>` | - | - |
| `onAnimationStartCapture` | `AnimationEventHandler<HTMLDivElement>` | - | - |
| `onAnimationEnd` | `AnimationEventHandler<HTMLDivElement>` | - | - |
| `onAnimationEndCapture` | `AnimationEventHandler<HTMLDivElement>` | - | - |
| `onAnimationIteration` | `AnimationEventHandler<HTMLDivElement>` | - | - |
| `onAnimationIterationCapture` | `AnimationEventHandler<HTMLDivElement>` | - | - |
| `onTransitionCancel` | `TransitionEventHandler<HTMLDivElement>` | - | - |
| `onTransitionCancelCapture` | `TransitionEventHandler<HTMLDivElement>` | - | - |
| `onTransitionEnd` | `TransitionEventHandler<HTMLDivElement>` | - | - |
| `onTransitionEndCapture` | `TransitionEventHandler<HTMLDivElement>` | - | - |
| `onTransitionRun` | `TransitionEventHandler<HTMLDivElement>` | - | - |
| `onTransitionRunCapture` | `TransitionEventHandler<HTMLDivElement>` | - | - |
| `onTransitionStart` | `TransitionEventHandler<HTMLDivElement>` | - | - |
| `onTransitionStartCapture` | `TransitionEventHandler<HTMLDivElement>` | - | - |
| `onFocus` | `((e: FocusEvent<Element, Element>) => void)` | - | Handler that is called when the element receives focus. |
| `onBlur` | `((e: FocusEvent<Element, Element>) => void)` | - | Handler that is called when the element loses focus. |
| `onFocusChange` | `((isFocused: boolean) => void)` | - | Handler that is called when the element's focus status changes. |
| `onChange` | `((value: TimeValue) => void)` | - | Handler that is called when the value changes. |

### Accessibility

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `aria-label` | `string` | - | Defines a string value that labels the current element. |
| `aria-labelledby` | `string` | - | Identifies the element (or elements) that labels the current element. |
| `aria-describedby` | `string` | - | Identifies the element (or elements) that describes the object. |
| `aria-details` | `string` | - | Identifies the element (or elements) that provide a detailed, extended description for the object. |
| `aria-errormessage` | `string` | - | Identifies the element that provides an error message for the object. |

