# CheckboxGroup

> Varias casillas que son un solo dato: el valor sale como array y el error es del grupo.

```tsx
import { CheckboxGroup, CheckboxGroupItem } from "sebs7n-ui/checkbox-group"
```

## Ejemplos

### Un dato que son varias casillas

Los tres checkboxes son un solo campo: un `name`, un array como valor y un error que es del grupo, no de una casilla. El `FieldLabel` nombra al grupo entero; cada `CheckboxGroupItem` trae su propia etiqueta.

```tsx
import { Button } from "sebs7n-ui/button"
import { CheckboxGroup, CheckboxGroupItem } from "sebs7n-ui/checkbox-group"
import { Field, FieldDescription, FieldError, FieldLabel } from "sebs7n-ui/field"
import { Form } from "sebs7n-ui/form"
import { useState } from "react"

function ServiciosDelPaquete() {
  const [enviado, setEnviado] = useState<string[] | null>(null)

  return (
    <Form
      className="w-full max-w-sm"
      onFormSubmit={(valores) => setEnviado(valores.servicios as string[])}
    >
      <Field
        name="servicios"
        validate={(valor) => ((valor as string[]).length > 0 ? null : "Elegí al menos un servicio")}
      >
        <FieldLabel required>Servicios incluidos</FieldLabel>
        <CheckboxGroup>
          <CheckboxGroupItem value="soporte">Soporte</CheckboxGroupItem>
          <CheckboxGroupItem value="capacitacion">Capacitación</CheckboxGroupItem>
          <CheckboxGroupItem value="migracion">Migración de los datos anteriores</CheckboxGroupItem>
        </CheckboxGroup>
        <FieldDescription>Se cotizan por separado.</FieldDescription>
        <FieldError />
      </Field>
      <Button type="submit">Cotizar</Button>
      {enviado && <p className="text-mono-callout text-label-secondary">servicios: {JSON.stringify(enviado)}</p>}
    </Form>
  )
}
```

### Seleccionar todo

El motivo principal por el que existe el componente. El padre lleva `parent` y el grupo, `allValues`: con algunos tildados queda indeterminado, y de un click pasa a todos o a ninguno. No aporta valor propio al array ni viaja en el submit.

```tsx
import { CheckboxGroup, CheckboxGroupItem } from "sebs7n-ui/checkbox-group"
import { Field, FieldDescription, FieldLabel } from "sebs7n-ui/field"
import { useState } from "react"

function PermisosDelOperador() {
  const permisos = ["reservas", "pagos", "clientes", "reportes"]
  const [valor, setValor] = useState<string[]>(["reservas", "clientes"])

  return (
    <Field className="w-full max-w-sm" name="permisos">
      <FieldLabel>Permisos del operador</FieldLabel>
      <CheckboxGroup allValues={permisos} onValueChange={setValor} value={valor}>
        <CheckboxGroupItem parent>Acceso total</CheckboxGroupItem>
        <div className="flex flex-col gap-3 border-l border-separator pl-4">
          <CheckboxGroupItem value="reservas">Crear y editar reservas</CheckboxGroupItem>
          <CheckboxGroupItem value="pagos">Registrar pagos</CheckboxGroupItem>
          <CheckboxGroupItem value="clientes">Ver la ficha del cliente</CheckboxGroupItem>
          <CheckboxGroupItem value="reportes">Descargar reportes</CheckboxGroupItem>
        </div>
      </CheckboxGroup>
      <FieldDescription>
        {valor.length} de {permisos.length} habilitados.
      </FieldDescription>
    </Field>
  )
}
```

### Opciones con ayuda y opciones apagadas

La ayuda de un ítem describe solo a ese ítem: el lector la anuncia al entrar en esa casilla y no en las otras. `disabled` apaga la casilla y su etiqueta juntas.

```tsx
import { CheckboxGroup, CheckboxGroupItem } from "sebs7n-ui/checkbox-group"
import { Field, FieldLabel } from "sebs7n-ui/field"

function AvisosDeLaCuenta() {
  return (
    <Field className="w-full max-w-sm" name="avisos">
      <FieldLabel>Avisos por email</FieldLabel>
      <CheckboxGroup defaultValue={["reserva"]}>
        <CheckboxGroupItem description="Al confirmar y al cancelar." value="reserva">
          Movimientos de una reserva
        </CheckboxGroupItem>
        <CheckboxGroupItem description="Un resumen los lunes a la mañana." value="resumen">
          Resumen semanal
        </CheckboxGroupItem>
        <CheckboxGroupItem description="Necesita un plan con facturación electrónica." disabled value="facturas">
          Facturas emitidas
        </CheckboxGroupItem>
      </CheckboxGroup>
    </Field>
  )
}
```

## Props

### CheckboxGroup

Hereda las props de `CheckboxGroup`.

| Prop | Tipo | Por defecto | Descripción |
|---|---|---|---|
| `className` | `string` | — | Se fusiona con las clases del componente vía `cn()` (tailwind-merge): lo que pongas gana. |
| `allValues` | `string[]` | — | **Heredada de Base UI.** La lista completa de valores. Solo hace falta si hay un ítem `parent`. |
| `onValueChange` | `((value: string[], eventDetails: CheckboxGroupChangeEventDetails) => void)` | — | **Heredada de Base UI.** Recibe el array nuevo cada vez que se tilda o destilda una casilla. |
| `value` | `string[]` | — | **Heredada de Base UI.** Los valores tildados. Para dejarlo no controlado, `defaultValue`. |

### CheckboxGroupItem

Hereda las props de `Checkbox.Root`.

| Prop | Tipo | Por defecto | Descripción |
|---|---|---|---|
| `checkboxClassName` | `string` | — | Clases de la casilla. En `className` van las de la fila entera. |
| `description` | `React.ReactNode` | — | Ayuda debajo de la etiqueta. Describe a esa opción, no al grupo. |
| `disabled` | `boolean` | `false` | Apaga la casilla y su etiqueta. El `disabled` del grupo o del `Field` le gana. |
| `parent` | `boolean` | `false` | Convierte al ítem en el «seleccionar todo» del grupo. Necesita `allValues` en el grupo. |
| `value` | `string` | — | La identidad de la opción: es lo que entra y sale del array. |
| `className` | `string` | — | Se fusiona con las clases del componente vía `cn()` (tailwind-merge): lo que pongas gana. |

## Teclado

| Tecla | Qué hace |
|---|---|
| Tab | Una parada por casilla. No es un `RadioGroup`: acá no hay flechas, porque las opciones no compiten entre sí. |
| Espacio | Tilda y destilda la casilla que tiene el foco. |
| Espacio en el padre | Pasa de indeterminado a todos, y de todos a ninguno. |

## Accesibilidad

- El grupo es `role="group"` con **un solo** nombre accesible: el `FieldLabel` del campo que lo envuelve. Cada casilla conserva el suyo.
- El error del campo describe al grupo (`aria-describedby` sobre el `role="group"`), así que se anuncia al entrar en el grupo y no hace falta moverle el foco.
- El padre emite `aria-checked="mixed"` cuando hay selección parcial, y `aria-controls` con los ids de las casillas que gobierna.
- La ayuda de un ítem describe solo a ese ítem: por dentro cada opción es un `Field.Item`, que abre su propio ámbito de etiquetado.
- La etiqueta de la opción es el `<label>` nativo de su casilla: clickear el texto tilda, que es la mitad del área útil de una lista de opciones.

## Reglas de uso

- **Va adentro de un `Field`, aunque no haya formulario.** De ahí salen el nombre del grupo, el error y el estado; un grupo suelto —los filtros de una lista— se envuelve igual en un `<Field>` pelado, que es un `<div>` con contexto.
- **El `value` de cada ítem es lo que termina en el array, y no se reemplaza por `name`**: adentro de un `Field`, el `name` del campo le gana al de la casilla y las tres opciones terminarían con el mismo valor.
- **«Seleccionar todo» necesita las dos piezas**: `allValues` en el grupo —la lista completa, que es de donde sale la cuenta— y `parent` en el ítem. El padre no aporta valor propio ni viaja en el submit.
- El error de "tildá al menos uno" es del grupo: va en el `validate` del `Field`, no en cada casilla. Un `Fieldset` no tiene dónde mostrarlo.
- De 2 a 7 opciones visibles. Más que eso se busca, no se recorre: `Combobox` con `multiple`.
- Para opciones excluyentes, `RadioGroup`. Para una sola casilla —aceptar los términos—, `Checkbox` solo.

## Relacionados

[checkbox](/docs/components/checkbox.md) · [field](/docs/components/field.md) · [radio-group](/docs/components/radio-group.md)
