Liste déroulante - DsfrSelect 
🌟 Introduction 
Le DsfrSelect est un composant Vue permettant à un utilisateur de choisir un élément dans une liste donnée.
La liste déroulante fournit une liste d’option parmi lesquelles l’utilisateur peut choisir. Seule la partie visible du composant est stylisé : la liste d’options déroulées conserve le style du navigateur.
🏅 La documentation sur liste déroulante sur le DSFR
La story sur le select sur le storybook de VueDsfr🛠️ Props 
| Nom | Type | Défaut | Obligatoire | Description | 
|---|---|---|---|---|
| modelValue | string | number | Valeur associée à l'option sélectionnée. | ||
| required | boolean | Indique si le select est obligatoire. | ||
| disabled | boolean | Indique si le select est désactivé. | ||
| options | (string | undefined | { value: string | undefined, text: string disabled?: boolean})[] | [] | Options à sélectionner | |
| optionGroups | ({ label: string, options: (DsfrSelectOption | { value: DsfrSelectOption, text: string, disabled?: boolean })[], disabled?: boolean })[] | [] | Groupes d'options | |
| label | string | '' | Texte du label associé au select. | |
| name | string | Nom du champ. | ||
| descriptiondeprecated | string | Deprecated, utiliser hint plutôt. | ||
| hint | string | Texte d'indice pour guider. | ||
| successMessage | string | '' | Message de validation à afficher en dessous du select. | |
| errorMessage | string | '' | Message d'erreur à afficher en dessous du select. | |
| defaultUnselectedText | string | 'Sélectionner une option' | Si true, l'infobulle s'affiche au survol. | |
| selectId | string | useRandomId('select') | Identifiant unique pour le select. Utilisé pour l'accessibilité. | 
📡Évenements 
DsfrSelect émet l'événement suivant :
| Nom | type | Description | 
|---|---|---|
| update:modelValue | string | number | Est émis lorsque la valeur du select change. | 
🧩 Slots 
- Aucun slot n'est disponible dans ce ce composant.
📝 Exemples 
vue
<script lang="ts" setup>
import { ref } from 'vue'
import DsfrSelect from '../DsfrSelect.vue'
const selectedOption1 = ref(0)
const selectedOption2 = ref(null)
const selectedOption3 = ref('')
const selectedOption4 = ref('')
</script>
<template>
  <div
    class="flex flex-col"
  >
    <DsfrSelect
      v-model.number="selectedOption1"
      label="Label du select"
      :options="[1, 2, 3, 4, 5]"
    />
    Select 1 : {{ selectedOption1 }} ({{ typeof selectedOption1 }})
    <DsfrSelect
      v-model="selectedOption2"
      label="Label du select 1"
      description="Description du select"
      :options="['Value 1', 'Value 2', 'Value 3', 'Value 4', 'Value 5']"
    />
    Select 2 : {{ selectedOption2 }} ({{ typeof selectedOption2 }})
    <DsfrSelect
      v-model="selectedOption3"
      label="Label du select 3"
      border-bottom
      required
      disabled
      :options="[
        { value: 'Value 1', text: 'Text 1' },
        { value: 'Value 2', text: 'Text 2' },
        { value: 'Value 3', text: 'Text 3' },
        { value: 'Value 4', text: 'Text 4' },
        { value: 'Value 5', text: 'Text 5' },
      ]"
    />
    Select 3 : {{ selectedOption3 }} ({{ typeof selectedOption3 }})
    <DsfrSelect
      v-model="selectedOption4"
      label="Select 4 avec groupes d'options (optgroup) dont 1 est désactivé"
      border-bottom
      required
      :option-groups="[
        {
          label: 'groupe 1',
          options: [
            { value: 'Value 1', text: 'Text 1' },
            { value: 'Value 2', text: 'Text 2' },
          ],
        },
        {
          label: 'groupe 2',
          disabled: true,
          options: [
            { value: 'Value 3', text: 'Text 3' },
            { value: 'Value 4', text: 'Text 4' },
            { value: 'Value 5', text: 'Text 5' },
          ],
        },
      ]"
    />
    Select 4 : {{ selectedOption4 }} ({{ typeof selectedOption4 }})
  </div>
</template>⚙️ Code source du composant 
vue
<script lang="ts" setup>
import type { DsfrSelectProps } from './DsfrSelect.types'
import { computed } from 'vue'
import { useRandomId } from '../../utils/random-utils'
export type { DsfrSelectProps }
defineOptions({
  inheritAttrs: false,
})
const props = withDefaults(defineProps<DsfrSelectProps>(), {
  selectId: () => useRandomId('select'),
  modelValue: undefined,
  options: () => [],
  optionGroups: () => [],
  label: '',
  name: undefined,
  description: undefined,
  hint: undefined,
  successMessage: '',
  errorMessage: '',
  defaultUnselectedText: 'Sélectionner une option',
})
defineEmits<{ (e: 'update:modelValue', payload: string | number): void }>()
if (props.description) {
  console.warn(
    '[DsfrSelect] : La prop `description` est dépréciée. Veuillez utiliser `hint` à la place.',
  )
}
const message = computed(() => {
  return props.errorMessage || props.successMessage
})
const messageType = computed(() => {
  return props.errorMessage ? 'error' : 'valid'
})
</script>
<template>
  <div
    class="fr-select-group"
    :class="{ [`fr-select-group--${messageType}`]: message }"
  >
    <label
      class="fr-label"
      :for="selectId"
    >
      <!-- @slot Slot pour personnaliser tout le contenu de la balise <label> cf. [DsfrInput](/?path=/story/composants-champ-de-saisie-champ-simple-dsfrinput--champ-avec-label-personnalise). Une **props porte le même nom pour un label simple** (texte sans mise en forme) -->
      <slot name="label">
        {{ label }}
        <!-- @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>
      <span
        v-if="hint ?? description"
        class="fr-hint-text"
      >{{ hint ?? description }}</span>
    </label>
    <select
      :id="selectId"
      :class="{ [`fr-select--${messageType}`]: message }"
      class="fr-select"
      :name="name || selectId"
      :disabled="disabled"
      :aria-disabled="disabled"
      :required="required"
      v-bind="$attrs"
      @change="$emit('update:modelValue', ($event.target as HTMLInputElement)?.value)"
    >
      <option
        :selected="!options.some(option => (typeof option !== 'object' || option === null) ? option === modelValue : option.value === modelValue)"
        disabled
        value=""
        hidden=""
      >
        {{ defaultUnselectedText }}
      </option>
      <option
        v-for="(option, index) in options"
        :key="index"
        :selected="modelValue === option || (typeof option === 'object' && option!.value === modelValue)"
        :value="typeof option === 'object' ? option!.value : option"
        :disabled="!!(disabled || typeof option === 'object' && option!.disabled)"
        :aria-disabled="!!(disabled || typeof option === 'object' && option!.disabled)"
      >
        {{ typeof option === 'object' ? option!.text : option }}
      </option>
      <optgroup
        v-for="(optionGroup, index) in optionGroups"
        :key="index"
        :label="optionGroup.label"
        :disabled="optionGroup.disabled"
        :aria-disabled="!!optionGroup.disabled"
      >
        <option
          v-for="(option, idx) in optionGroup.options"
          :key="idx"
          :selected="modelValue === option || (typeof option === 'object' && option!.value === modelValue)"
          :value="typeof option === 'object' ? option!.value : option"
          :disabled="!!(disabled || typeof option === 'object' && option!.disabled || optionGroup.disabled)"
          :aria-disabled="!!(disabled || typeof option === 'object' && option!.disabled || optionGroup.disabled)"
        >
          {{ typeof option === 'object' ? option!.text : option }}
        </option>
      </optgroup>
    </select>
    <p
      v-if="message"
      :id="`select-${messageType}-desc-${messageType}`"
      :class="`fr-${messageType}-text`"
    >
      {{ message }}
    </p>
  </div>
</template>ts
export type DsfrSelectOption = string | number | null | undefined
export type DsfrSelectProps = {
  required?: boolean
  disabled?: boolean
  selectId?: string
  name?: string
  description?: string
  hint?: string
  modelValue?: DsfrSelectOption
  label?: string
  options?: (DsfrSelectOption | { value: DsfrSelectOption, text: string, disabled?: boolean })[]
  successMessage?: string
  errorMessage?: string
  defaultUnselectedText?: string
  optionGroups?: ({ label: string, options: (DsfrSelectOption | { value: DsfrSelectOption, text: string, disabled?: boolean })[], disabled?: boolean })[]
}