Skip to content

Groupe de champs de saisie - DsfrInputGroup

🌟 Introduction

Bienvenue dans la documentation du composant DsfrInputGroup, conçu pour envelopper vos champs de saisie avec une élégance à la française. Que vous composiez un message d'amour ou remplissiez un formulaire administratif, ce composant est là pour apporter de l'ordre et de la clarté à vos interfaces utilisateur.

Ce composant est très utile si vous souhaitez afficher un message d’erreur ou de succès pour un ou plusieurs champs de saisie DsfrInput.

🛠️ Props

NomTypeDéfautObligatoireDescription
descriptionIdFunction() => getRandomId(...)ID unique pour la description du groupe, généré automatiquement si non spécifié.
hintstring''Texte d'indice pour guider l'utilisateur dans le groupe de champs.
labelstring''Le libellé associé au groupe de champs.
labelClassstring''Classe CSS personnalisée pour le style du libellé.
modelValuestring''La valeur liée au modèle du composant DsfrInput.
wrapperClassstring''Classe CSS pour le style du conteneur du groupe.
placeholderstringundefinedTexte de l'espace réservé pour l'input.
errorMessagestring | string[]undefinedMessage(s) d'erreur à afficher si une erreur est présente.
validMessagestring | string[]undefinedMessage(s) de validation à afficher si l'input est valide.

Attributs implicitement déclarés

Important

Toutes les props passées à <DsfrInputGroup> dans une template et qui ne sont pas définies dans les props seront passées à la balise <DsfrInput>. Si ces props ne sont pas définies non plus en tant que props dans DsfrInput, elles seront passés à la balise native <input>, comme par exemple readonly ou disabled.

Cf. la note important sur DsfrInput

📡 Événements

NomDescription
update:modelValueÉvénement émis lors de la mise à jour de la valeur de l'input.

🧩 Slots

NomDescription
before-inputSlot pour insérer du contenu avant le champ de saisie principal.
defaultSlot par défaut pour le contenu principal du groupe de champ. Utilisé pour insérer des éléments personnalisés.

📝 Exemples

Voici comment vous pourriez utiliser DsfrInputGroup :

vue
<script lang="ts" setup>
import { ref } from 'vue'

import DsfrInput from '../DsfrInput.vue'
import DsfrInputGroup from '../DsfrInputGroup.vue'

const type = 'text'
const label = 'Label champ de saisie'
const placeholder = 'Yo'
const modelValue = ref('')
const modelValue2 = ref('')
const validMessage1 = 'Message de validation'
const errorMessage1 = 'Message d’erreur'
const errorMessage2 = 'Message d’erreur 2'
const hint = 'Texte d’indice du champ'
const id = ''
const readonly = ''
</script>

<template>
  <div class="fr-container fr-my-2w">
    <h2>1. Avec un message de succès</h2>

    <DsfrInputGroup
      :id="id"
      :valid-message="validMessage1"
      :placeholder="placeholder"
      :readonly="readonly !== ''"
      :model-value="modelValue"
      :label="label"
      :type="type"
      :hint="hint"
      label-visible
    />

    <h2>2. Avec un message d’erreur</h2>

    <DsfrInputGroup
      :id="id"
      :error-message="errorMessage1"
      :placeholder="placeholder"
      :readonly="readonly !== ''"
      :model-value="modelValue"
      :label="label"
      :type="type"
      :hint="hint"
      label-visible
    />

    <h2>3. Avec plusieurs messages</h2>

    <DsfrInputGroup
      :id="id"
      :error-message="[errorMessage1, errorMessage2]"
      :valid-message="[validMessage1]"
      :placeholder="placeholder"
      :readonly="readonly !== ''"
      :model-value="modelValue"
      :label="label"
      :type="type"
      :hint="hint"
      label-visible
    />

    <h2>4. Avec plusieurs champs de saisie</h2>

    <DsfrInputGroup
      valid-message="Tout va bien pour ces deux champs"
    >
      <p>
        <DsfrInput
          :id="id"
          :placeholder="placeholder"
          :readonly="readonly !== ''"
          :model-value="modelValue"
          :label="label"
          :type="type"
          :hint="hint"
          label-visible
        />
      </p>

      <p>
        <DsfrInput
          :id="id"
          :placeholder="placeholder"
          :readonly="readonly !== ''"
          :model-value="modelValue2"
          :label="label"
          :type="type"
          :hint="hint"
          label-visible
        />
      </p>
    </DsfrInputGroup>
  </div>
</template>

⚙️ Code source du composant

vue
<script lang="ts" setup>
import { getRandomId } from '../../utils/random-utils'

import DsfrInput from './DsfrInput.vue'
import type { DsfrInputGroupProps } from './DsfrInput.types'

export type { DsfrInputGroupProps }

defineOptions({
  inheritAttrs: false,
})

withDefaults(defineProps<DsfrInputGroupProps>(), {
  descriptionId: () => getRandomId('basic', 'input'),
  hint: '',
  label: '',
  labelClass: '',
  modelValue: '',
  wrapperClass: '',
  placeholder: undefined,
  errorMessage: undefined,
  validMessage: undefined,
})

defineEmits<{ (e: 'update:modelValue', payload: string): void }>()
</script>

<template>
  <div
    class="fr-input-group"
    :class="[
      {
        'fr-input-group--error': errorMessage,
        'fr-input-group--valid': (validMessage && !errorMessage),
      },
      wrapperClass,
    ]"
  >
    <slot name="before-input" />
    <!-- @slot Slot par défaut pour le contenu du groupe de champ -->
    <slot />
    <DsfrInput
      v-if="!$slots.default"
      v-bind="$attrs"
      :is-valid="!!validMessage"
      :is-invalid="!!errorMessage"
      :label="label"
      :hint="hint"
      :description-id="((errorMessage || validMessage) && descriptionId) || undefined"
      :label-visible="labelVisible"
      :model-value="modelValue"
      :placeholder="placeholder"
      @update:model-value="$emit('update:modelValue', $event)"
    />
    <div
      class="fr-messages-group"
      role="alert"
      aria-live="polite"
    >
      <template
        v-if="Array.isArray(errorMessage)"
      >
        <p
          v-for="message in errorMessage"
          :id="descriptionId"
          :key="message"
          :data-testid="descriptionId"
          class="fr-error-text"
        >
          {{ message }}
        </p>
      </template>
      <p
        v-else-if="errorMessage"
        :id="descriptionId"
        :key="errorMessage"
        :data-testid="descriptionId"
        class="fr-error-text"
      >
        {{ errorMessage }}
      </p>

      <template
        v-if="Array.isArray(validMessage)"
      >
        <p
          v-for="message in validMessage"
          :id="descriptionId"
          :key="message"
          :data-testid="descriptionId"
          class="fr-valid-text"
        >
          {{ message }}
        </p>
      </template>
      <p
        v-else-if="validMessage"
        :id="descriptionId"
        :key="validMessage"
        :data-testid="descriptionId"
        class="fr-valid-text"
      >
        {{ validMessage }}
      </p>
    </div>
  </div>
</template>
ts
export type DsfrInputProps = {
  id?: string
  descriptionId?: string
  hint?: string
  isInvalid?: boolean
  isValid?: boolean
  isTextarea?: boolean
  isWithWrapper?: boolean
  labelVisible?: boolean
  label?: string
  labelClass?: string
  modelValue?: string | number | null
  wrapperClass?: string
}

export type DsfrInputGroupProps = {
  descriptionId?: string
  hint?: string
  labelVisible?: boolean
  label?: string
  labelClass?: string
  modelValue?: string | number | null
  placeholder?: string
  errorMessage?: string | string[]
  validMessage?: string | string[]
  wrapperClass?: string
}