Skip to content

Accordéon - DsfrAccordion

🌟 Introduction

Les accordéons permettent aux utilisateurs d'afficher et de masquer des sections de contenu présentés dans une page.

🏅 La documentation sur l’accordéon sur le DSFR

La story sur l’accordéon sur le storybook de VueDsfr

📐 Structure

Un accordéon est constitué des éléments suivants :

  • un en-tête (prop title, de type string), correspondant au titre de la section - obligatoire.
  • une icône, indique quand le panneau est fermé, quand le panneau est ouvert.
  • un séparateur
  • une zone de contenu, masquée par défaut pouvant contenir tout type d'élément, le slot par défaut est fait pour ça

Autres props :

  • id : identifiant du contenu de l’accordéon, qui est utilisé aussi pour l’attribut aria-controls du bouton qui permet de plier et déplier l’accordéon
  • expandedId : identifiant de l’accordéon actuellement déplié (pour savoir si l’accordéon doit être déplié)

🛠️ Props

NomTypeDéfautObligatoire
titlestringgetRandomId('accordion')
titleTagTitleTag'h3'
idstringrandom string

📡 Évenements

Pas d'événements spécifiques émis directement par ce composant.

🧩 Slots

  • title : Slot pour le contenu personnalisé du titre de l’accordéon. Si non utilisé, le texte fourni via la prop title sera utilisé.
  • default : Slot pour le contenu principal de l'accordéon, affiché dans la section repliable.

Utilisation

Ce composant peut être utilisé uniquement avec DsfrAccordionsGroup.

⚙️ Code source du composant

vue
<script lang="ts" setup>
import { inject, onMounted, ref, toRef, watch } from 'vue'

import { getRandomId } from '../../utils/random-utils'
import { useCollapsable } from '../../composables'
import type { DsfrAccordionProps } from './DsfrAccordion.types'
import { registerTabKey } from './injection-key'

export type { DsfrAccordionProps }

const props = withDefaults(
  defineProps<DsfrAccordionProps>(),
  {
    id: () => getRandomId('accordion'),
    title: 'Sans intitulé',
    titleTag: 'h3',
  },
)

const {
  collapse,
  collapsing,
  cssExpanded,
  doExpand,
  onTransitionEnd,
} = useCollapsable()

const isStandaloneActive = ref()

const useAccordion = inject(registerTabKey)!
const { isActive, expand } = useAccordion?.(toRef(() => props.title)) ?? { isActive: isStandaloneActive, expand: () => isStandaloneActive.value = !isStandaloneActive.value }

onMounted(() => {
  // Accordion can be expanded by default
  // We need to trigger the expand animation on mounted
  if (isActive.value) {
    doExpand(true)
  }
})

watch(isActive, (newValue, oldValue) => {
  /*
  * @see https://github.com/GouvernementFR/dsfr/blob/main/src/core/script/collapse/collapse.js
  */
  if (newValue !== oldValue) {
    doExpand(newValue)
  }
})
</script>

<template>
  <section class="fr-accordion">
    <component
      :is="titleTag"
      class="fr-accordion__title"
    >
      <button
        class="fr-accordion__btn"
        :aria-expanded="isActive"
        :aria-controls="id"
        type="button"
        @click="expand()"
      >
        <!-- @slot Slot pour le contenu personnalisé du titre de l’accordéon. Une **props du même nom est utilisable pour du texte simple** sans mise en forme. -->
        <slot name="title">
          {{ title }}
        </slot>
      </button>
    </component>
    <div
      :id="id"
      ref="collapse"
      class="fr-collapse"
      :class="{
        'fr-collapse--expanded': cssExpanded, // Need to use a separate data to add/remove the class after a RAF
        'fr-collapsing': collapsing,
      }"
      @transitionend="onTransitionEnd(isActive)"
    >
      <!-- @slot Slot par défaut pour le contenu de l’accordéon: sera dans `<div class="fr-collapse">` -->
      <slot />
    </div>
  </section>
</template>
ts
import type { TitleTag } from '@/common-types'

export type DsfrAccordionProps = {
  id?: string
  selected?: boolean
  title?: string
  titleTag?: TitleTag
}