🌟 Introduction 
Ce composant permet de gérer un ensemble de cases à cocher DSFR. Il est composé d'un libellé (legend), d'options individuelles représentées par le composant DsfrCheckbox, et d'un message d'information, d'erreur ou de validation global.
📐 Structure 
Le composant DsfrCheckboxSet est composé des éléments suivants :
- Un élément <fieldset>contenant l'ensemble des cases à cocher
- Une légende (legend) définie par la proplegendet personnalisable avec le slotlegend
- Un groupe de cases à cocher individuelles rendues par le composant DsfrCheckbox
- Un message d'information, d'erreur ou de validation, affiché en dessous du groupe de cases à cocher
🛠️ Props 
| Nom | Type | Description | Obligatoire | 
|---|---|---|---|
| options | (DsfrCheckboxProps & InputHTMLAttributes)[] | Tableau d'options définissant les cases à cocher individuelles | ✅ | 
| modelValue | string[] | Valeur courante du composant, un tableau de valeurs (propriété valuede chaque option de la propoptions) des cases cochées | ✅ | 
| disabled | boolean | Indique si l'ensemble des cases à cocher est désactivé | |
| errorMessage | string | Message d'erreur global à afficher | |
| inline | boolean | Affiche les cases à cocher en ligne (par défaut : false) | |
| legend | string | Texte de la légende | |
| required | boolean | Indique si l'ensemble des cases à cocher est obligatoire | |
| small | boolean | Affiche les cases à cocher en taille réduite | |
| titleId | string | Identifiant unique du champ (générée automatiquement si non fournie) | |
| validMessage | string | Message de validation global à afficher | 
Attention
Avant la v7, le tableau modelValue était un tableau de string avec les valeurs des propriétés de l’attribut name de chaque case à cocher.
Ce n’était ni une API idéale, ni le comportement attendu en Vue natif ou en HTML/JS natif.
📡 Événements 
DsfrCheckboxSet émet l'événement suivant :
| Nom | Description | 
|---|---|
| update:modelValue | Est émis lorsque la sélection des cases à cocher change | 
🧩 Slots 
DsfrCheckboxSet fournit les slots suivants pour la personnalisation :
- legend: Permet de personnaliser le contenu de la légende.
- required-tip: Permet d'ajouter plus qu’un astérisque pour indiquer que le champ est obligatoire ou d’autres détails sur cette case à cocher.
🪆 Relation avec DsfrCheckbox 
DsfrChecboxSet utilise en interne DsfrCheckbox, et permet de récupérer dans modelValue sous forme de tableau les valeurs de la prop value de chaque case à cocher qui est cochée.
Cf. les exemples ci-dessous
📝 Exemples 
<script lang="ts" setup>
import type { DsfrCheckboxProps } from '../DsfrCheckbox.types'
import { ref } from 'vue'
import DsfrCheckboxSet from '../DsfrCheckboxSet.vue'
const modelValue1 = ref([])
const modelValue2 = ref([])
const modelValue3 = ref([])
const modelValue4 = ref([])
const modelValue5 = ref([])
const modelValue6 = ref([])
const options1: Omit<DsfrCheckboxProps, 'modelValue'>[] = [
  {
    label: 'Première valeur',
    value: 'une chaîne en premier',
    id: 'name1-1',
    name: 'name1-1',
    hint: 'La première valeur est une chaîne de caractères',
  },
  {
    label: 'Deuxième valeur',
    value: 42,
    id: 'name1-2',
    name: 'name1-2',
    hint: 'La valeur est ici un chiffre',
  },
  {
    label: 'Troisième valeur',
    value: { foo: 'foo', bar: 42 },
    id: 'name1-3',
    name: 'name1-3',
    hint: 'Et ici la valeur est un littéral objet',
  },
]
const options2 = structuredClone(options1).map(option => Object.fromEntries(
  Object.entries(option).map(([key, value]) => [key, ['id', 'name'].includes(key) ? (value as string).replace('name1', 'name2') : value]),
)) as Omit<DsfrCheckboxProps, 'modelValue'>[]
const options3 = structuredClone(options1).map(option => Object.fromEntries(
  Object.entries(option).map(([key, value]) => [key, ['id', 'name'].includes(key) ? (value as string).replace('name1', 'name3') : value]),
)) as Omit<DsfrCheckboxProps, 'modelValue'>[]
const options4 = structuredClone(options1).map(option => Object.fromEntries(
  Object.entries(option).filter(([key]) => key !== 'hint').map(([key, value]) => [key, ['id', 'name'].includes(key) ? (value as string).replace('name1', 'name4') : value]),
)) as Omit<DsfrCheckboxProps, 'modelValue'>[]
const options5 = structuredClone(options1).map(option => Object.fromEntries(
  Object.entries(option).filter(([key]) => key !== 'hint').map(([key, value]) => [key, ['id', 'name'].includes(key) ? (value as string).replace('name1', 'name5') : value]),
)) as Omit<DsfrCheckboxProps, 'modelValue'>[]
const options6 = structuredClone(options1).map(option => Object.fromEntries(
  Object.entries(option).filter(([key]) => key !== 'hint').map(([key, value]) => [key, ['id', 'name'].includes(key) ? (value as string).replace('name1', 'name6') : value]),
)) as Omit<DsfrCheckboxProps, 'modelValue'>[]
const errorMessage = 'Message d’erreur'
const validMessage = 'Message de validation'
</script>
<template>
  <div class="fr-container fr-my-2v">
    <div class="fr-my-2v">
      <DsfrCheckboxSet
        v-model="modelValue1"
        legend="Groupe de cases à cocher simple"
        :options="options1"
      />
      <p>
        modelValue1: {{ modelValue1 }}
      </p>
    </div>
    <div class="fr-my-2v">
      <DsfrCheckboxSet
        v-model="modelValue2"
        legend="Groupe de cases à cocher avec erreur"
        :options="options2"
        :error-message="errorMessage"
      />
      <p>
        modelValue2: {{ modelValue2 }}
      </p>
    </div>
    <div class="fr-my-2v">
      <DsfrCheckboxSet
        v-model="modelValue3"
        legend="Groupe de cases à cocher avec message de validation"
        :options="options3"
        :valid-message="validMessage"
      />
      <p>
        modelValue3: {{ modelValue3 }}
      </p>
    </div>
    <div class="fr-my-2v">
      <DsfrCheckboxSet
        v-model="modelValue4"
        legend="Groupe de cases à cocher en ligne"
        :options="options4"
        inline
      />
      <p>
        modelValue4: {{ modelValue4 }}
      </p>
    </div>
    <div class="fr-my-2v">
      <DsfrCheckboxSet
        v-model="modelValue5"
        legend="Groupe de cases à cocher en ligne avec erreur"
        :options="options5"
        inline
        error-message="Message d’erreur"
      />
      <p>
        modelValue5: {{ modelValue5 }}
      </p>
    </div>
    <div class="fr-my-2v">
      <DsfrCheckboxSet
        v-model="modelValue6"
        legend="Groupe de cases à cocher en ligne avec erreur"
        :options="options6"
        inline
        valid-message="Message de validation"
      />
      <p>
        modelValue6: {{ modelValue6 }}
      </p>
    </div>
  </div>
</template>⚙️ Code source du composant 
<script lang="ts">
import type { DsfrCheckboxSetProps } from './DsfrCheckbox.types'
import { computed } from 'vue'
import { useRandomId } from '../../utils/random-utils'
import DsfrCheckbox from './DsfrCheckbox.vue'
export type { DsfrCheckboxSetProps }
</script>
<script lang="ts" setup>
const props = withDefaults(defineProps<DsfrCheckboxSetProps>(), {
  titleId: () => useRandomId('checkbox', 'set'),
  errorMessage: '',
  validMessage: '',
  legend: '',
  options: () => [],
  modelValue: () => [],
})
defineSlots<{
  default?: () => any
  legend?: () => any
  'required-tip'?: () => any
}>()
const message = computed(() => {
  return props.errorMessage || props.validMessage
})
const additionalMessageClass = computed(() => {
  return props.errorMessage ? 'fr-error-text' : 'fr-valid-text'
})
const ariaLabelledby = computed(() => message.value ? `${props.titleId} messages-${props.titleId}` : props.titleId)
const modelValue = defineModel()
</script>
<template>
  <div class="fr-form-group">
    <fieldset
      class="fr-fieldset"
      :class="{
        'fr-fieldset--error': errorMessage,
        'fr-fieldset--valid': !errorMessage && validMessage,
      }"
      :disabled="disabled"
      :aria-labelledby="ariaLabelledby"
      :aria-invalid="ariaInvalid"
      :role="(errorMessage || validMessage) ? 'group' : undefined"
    >
      <legend
        v-if="legend || $slots.legend"
        :id="titleId"
        class="fr-fieldset__legend fr-text--regular"
      >
        <!-- @slot Slot pour personnaliser tout le contenu de la balise <legend> cf. [DsfrInput](/?path=/story/composants-champ-de-saisie-champ-simple-dsfrinput--champ-avec-label-personnalise). Une **rte le même nom pour une légende simple** (texte sans mise en forme) -->
        <slot name="legend">
          {{ legend }}
          <!-- @slot Slot pour indiquer que le champ est obligatoire. Par défaut, met une astérisque si `required` est à true (dans un `<span class="required">`) -->
          <slot name="required-tip">
            <span
              v-if="required"
              class="required"
            > *</span>
          </slot>
        </slot>
      </legend>
      <slot>
        <DsfrCheckbox
          v-for="option in options"
          :id="option.id"
          :key="option.id || option.name"
          v-model="modelValue"
          :value="option.value"
          :name="option.name"
          :label="option.label"
          :disabled="option.disabled"
          :aria-disabled="option.disabled"
          :small="small"
          :inline="inline"
          :hint="option.hint"
        />
      </slot>
      <div
        v-if="message"
        :id="`messages-${titleId}`"
        class="fr-messages-group"
        role="alert"
      >
        <p
          class="fr-message--info  flex  items-center"
          :class="additionalMessageClass"
        >
          <span>{{ message }}</span>
        </p>
      </div>
    </fieldset>
  </div>
</template>import type { InputHTMLAttributes } from 'vue'
export type DsfrCheckboxProps = {
  id?: string
  name: string
  required?: boolean
  value: unknown
  checked?: boolean
  modelValue: Array<unknown>
  small?: boolean
  inline?: boolean
  readonly?: boolean
  readonlyOpacity?: number
  label?: string
  errorMessage?: string
  validMessage?: string
  hint?: string
}
type PropsWithoutModelValue = Omit<DsfrCheckboxProps, 'modelValue'>
export type DsfrCheckboxSetProps = {
  titleId?: string
  disabled?: boolean
  inline?: boolean
  required?: boolean
  small?: boolean
  errorMessage?: string
  validMessage?: string
  legend?: string
  options?: (PropsWithoutModelValue & InputHTMLAttributes)[]
  modelValue?: Array<unknown>
  ariaInvalid?: boolean
}