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 | |
label | string | '' | Texte du label associé au select. | |
name | string | Nom du champ. | ||
description | string | Si true , l'infobulle s'affiche au survol. | ||
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 | getRandomId('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('')
</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
: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 }})
</div>
</template>
⚙️ Code source du composant
vue
<script lang="ts" setup>
import { computed } from 'vue'
import { getRandomId } from '../../utils/random-utils'
import type { DsfrSelectProps } from './DsfrSelect.types'
export type { DsfrSelectProps }
defineOptions({
inheritAttrs: false,
})
const props = withDefaults(defineProps<DsfrSelectProps>(), {
selectId: () => getRandomId('select'),
modelValue: undefined,
options: () => [],
label: '',
name: undefined,
description: undefined,
successMessage: '',
errorMessage: '',
defaultUnselectedText: 'Sélectionner une option',
})
defineEmits<{ (e: 'update:modelValue', payload: string | number): void }>()
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="description"
class="fr-hint-text"
>{{ 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
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="!!(typeof option === 'object' && option!.disabled)"
:aria-disabled="!!(typeof option === 'object' && option!.disabled)"
>
{{ typeof option === 'object' ? option!.text : option }}
</option>
</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
modelValue?: DsfrSelectOption
label?: string
options?: (DsfrSelectOption | { value: DsfrSelectOption, text: string, disabled?: boolean })[]
successMessage?: string
errorMessage?: string
defaultUnselectedText?: string
}