Skip to content

Commit c5b7a2c

Browse files
authored
Merge pull request #1112 from dnum-mi/develop
Develop
2 parents 3de9d8c + 81aa680 commit c5b7a2c

File tree

14 files changed

+255
-32
lines changed

14 files changed

+255
-32
lines changed

.vitepress/config.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,10 @@ const composants = [
254254
text: 'DsfrMultiselect',
255255
link: '/composants/DsfrMultiselect.md',
256256
},
257+
{
258+
text: 'DsfrNavigation',
259+
link: '/composants/DsfrNavigation.md',
260+
},
257261
{
258262
text: 'DsfrNotice',
259263
link: '/composants/DsfrNotice.md',

docs/guide/pour-commencer.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -61,11 +61,11 @@ npm install @gouvfr/dsfr @gouvminint/vue-dsfr
6161

6262
:::
6363

64-
### Utiliser la bibliothèque en tant que plugin
64+
### ~~Utiliser la bibliothèque en tant que plugin~~
6565

66-
::: warning Méthode déconseillée
66+
::: danger Méthode déconseillée
6767

68-
Cette méthode est déconseillée. Elle est présente dans la documentation pour des raisons historiques.
68+
Cette méthode est **déconseillée**. Elle est présente dans la documentation pour des raisons historiques.
6969
Cette méthode est facile à mettre en place et permet de tester rapidement les composants dans une application, cependant, comme elle enregistre tous les composants, ils sont tous présents dans le bundle final, ce qui est loin d’être optimal.
7070

7171
On peut avoir le meilleur des deux mondes (**import automatique** des composants et un **bundle final optimisé** qui ne contient que les composants réellement utilisés) avec l’auto-import et le component resolver livrés avec les versions [5.5+ de VueDsfr](https://github.com/dnum-mi/vue-dsfr/releases/tag/v5.5.0).
@@ -75,7 +75,7 @@ Voir plus loin la [section correspondante](#avoir-un-bundle-optimise-et-une-dx-o
7575

7676
:::
7777

78-
#### Ajouter le plugin
78+
#### ~~Ajouter le plugin~~
7979

8080
Ajouter la bibliothèque en tant que plugin a deux conséquences :
8181

src/components/DsfrCheckbox/DsfrCheckboxSet.spec.ts

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,4 +155,63 @@ describe('DsfrCheckboxSet', () => {
155155
// Then
156156
expect(legendEl.nextElementSibling).toBe(null)
157157
})
158+
159+
describe('legend', () => {
160+
// Given
161+
const firstLabelText = 'Premier label'
162+
const secondLabelText = 'Deuxième label'
163+
const modelValue = []
164+
const options = [
165+
{
166+
id: '1',
167+
label: firstLabelText,
168+
value: 'un',
169+
modelValue,
170+
name: '1',
171+
},
172+
{
173+
id: '2',
174+
label: secondLabelText,
175+
value: 'deux',
176+
modelValue,
177+
name: '2',
178+
},
179+
]
180+
const legendText = 'Légende de l’ensemble des champs'
181+
it('should render legend cause slot is used', async () => {
182+
// When
183+
const { container } = render(DsfrCheckboxSet, {
184+
props: {
185+
legend: legendText,
186+
errorMessage: 'Message d’erreur',
187+
options,
188+
},
189+
})
190+
expect(container.querySelectorAll('legend')).toHaveLength(1)
191+
})
192+
it('should render legend cause props legend is not empty', async () => {
193+
// When
194+
const { container } = render(DsfrCheckboxSet, {
195+
props: {
196+
legend: legendText,
197+
options,
198+
},
199+
slots: {
200+
legend: () => legendText,
201+
},
202+
})
203+
expect(container.querySelectorAll('legend')).toHaveLength(1)
204+
})
205+
it('should not render legend cause no legend is provided', async () => {
206+
// When
207+
const { container } = render(DsfrCheckboxSet, {
208+
props: {
209+
options,
210+
},
211+
})
212+
213+
// Then
214+
expect(container.querySelectorAll('legend')).toHaveLength(0)
215+
})
216+
})
158217
})

src/components/DsfrCheckbox/DsfrCheckboxSet.vue

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,12 @@ const props = withDefaults(defineProps<DsfrCheckboxSetProps>(), {
1919
modelValue: () => [],
2020
})
2121
22+
defineSlots<{
23+
default?: () => any
24+
legend?: () => any
25+
'required-tip'?: () => any
26+
}>()
27+
2228
const message = computed(() => {
2329
return props.errorMessage || props.validMessage
2430
})
@@ -45,6 +51,7 @@ const modelValue = defineModel()
4551
:role="(errorMessage || validMessage) ? 'group' : undefined"
4652
>
4753
<legend
54+
v-if="legend || $slots.legend"
4855
:id="titleId"
4956
class="fr-fieldset__legend fr-text--regular"
5057
>

src/components/DsfrFieldset/DsfrFieldset.vue

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,16 @@ withDefaults(defineProps<DsfrFieldsetProps>(), {
1212
})
1313
1414
defineSlots<{
15-
default: () => any
16-
legend: () => any
17-
hint: () => any
15+
default?: () => any
16+
legend?: () => any
17+
hint?: () => any
1818
}>()
1919
</script>
2020

2121
<template>
2222
<fieldset class="fr-fieldset">
2323
<legend
24-
v-if="legend || $slots.legend?.()"
24+
v-if="legend || $slots.legend"
2525
:id="legendId"
2626
class="fr-fieldset__legend"
2727
:class="legendClass"
@@ -31,7 +31,7 @@ defineSlots<{
3131
<slot name="legend" />
3232
</legend>
3333
<div
34-
v-if="hint || $slots.hint?.()"
34+
v-if="hint || $slots.hint"
3535
class="fr-fieldset__element"
3636
>
3737
<span

src/components/DsfrFooter/DsfrFooter.vue

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,22 +54,22 @@ const props = withDefaults(defineProps<DsfrFooterProps>(), {
5454
{
5555
label: 'info.gouv.fr',
5656
href: 'https://info.gouv.fr',
57-
title: 'Informations gouvernementales, nouvelle fenêtre',
57+
title: 'info.gouv.fr, Informations gouvernementales, ouvre une nouvelle fenêtre',
5858
},
5959
{
6060
label: 'service-public.fr',
6161
href: 'https://service-public.fr',
62-
title: 'Informations et démarches administratives, nouvelle fenêtre',
62+
title: 'service-public.fr, Informations et démarches administratives, ouvre une nouvelle fenêtre',
6363
},
6464
{
6565
label: 'legifrance.gouv.fr',
6666
href: 'https://legifrance.gouv.fr',
67-
title: 'Service public de diffusion du droit, nouvelle fenêtre',
67+
title: 'legifrance.gouv.fr, Service public de diffusion du droit, ouvre une nouvelle fenêtre',
6868
},
6969
{
7070
label: 'data.gouv.fr',
7171
href: 'https://data.gouv.fr',
72-
title: 'Plateforme des données publiques, nouvelle fenêtre',
72+
title: 'data.gouv.fr, Plateforme des données publiques, ouvre une nouvelle fenêtre',
7373
},
7474
],
7575
operatorLinkText: 'Revenir à l’accueil',

src/components/DsfrInput/DsfrInputGroup.vue

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
<script lang="ts" setup>
2+
import { computed } from 'vue'
3+
24
import { useRandomId } from '../../utils/random-utils'
35
46
import DsfrInput from './DsfrInput.vue'
@@ -10,7 +12,7 @@ defineOptions({
1012
inheritAttrs: false,
1113
})
1214
13-
withDefaults(defineProps<DsfrInputGroupProps>(), {
15+
const props = withDefaults(defineProps<DsfrInputGroupProps>(), {
1416
descriptionId: () => useRandomId('input', 'group'),
1517
hint: '',
1618
label: '',
@@ -23,6 +25,28 @@ withDefaults(defineProps<DsfrInputGroupProps>(), {
2325
})
2426
2527
defineEmits<{ (e: 'update:modelValue', payload: string): void }>()
28+
29+
function getDescriptionIdFromArray (messages: string[], baseId: string): string {
30+
return Array.from(Array.from({ length: messages.length })).map((_, idx) => `${baseId}-${idx + 1}`).join(' ')
31+
}
32+
const descId = computed(() => {
33+
if (!props.errorMessage && !props.validMessage) {
34+
return undefined
35+
}
36+
if (Array.isArray(props.errorMessage)) {
37+
return getDescriptionIdFromArray(props.errorMessage, props.descriptionId)
38+
}
39+
if (typeof props.errorMessage === 'string') {
40+
return props.errorMessage
41+
}
42+
if (typeof props.validMessage === 'string') {
43+
return props.validMessage
44+
}
45+
if (Array.isArray(props.validMessage)) {
46+
return getDescriptionIdFromArray(props.validMessage, props.descriptionId)
47+
}
48+
return undefined
49+
})
2650
</script>
2751

2852
<template>
@@ -41,7 +65,7 @@ defineEmits<{ (e: 'update:modelValue', payload: string): void }>()
4165
<slot
4266
:is-valid="!!validMessage"
4367
:is-invalid="!!errorMessage"
44-
:description-id="((errorMessage || validMessage) && descriptionId) || undefined"
68+
:description-id="descId"
4569
/>
4670
<DsfrInput
4771
v-if="!$slots.default"
@@ -50,7 +74,7 @@ defineEmits<{ (e: 'update:modelValue', payload: string): void }>()
5074
:is-invalid="!!errorMessage"
5175
:label="label"
5276
:hint="hint"
53-
:description-id="((errorMessage || validMessage) && descriptionId) || undefined"
77+
:description-id="descId"
5478
:label-visible="labelVisible"
5579
:model-value="modelValue"
5680
:placeholder="placeholder"
@@ -65,8 +89,8 @@ defineEmits<{ (e: 'update:modelValue', payload: string): void }>()
6589
v-if="Array.isArray(errorMessage)"
6690
>
6791
<p
68-
v-for="message in errorMessage"
69-
:id="descriptionId"
92+
v-for="(message, idx) in errorMessage"
93+
:id="`${descriptionId}-${idx + 1}`"
7094
:key="message"
7195
:data-testid="descriptionId"
7296
class="fr-error-text"
@@ -85,11 +109,11 @@ defineEmits<{ (e: 'update:modelValue', payload: string): void }>()
85109
</p>
86110

87111
<template
88-
v-if="Array.isArray(validMessage)"
112+
v-else-if="Array.isArray(validMessage)"
89113
>
90114
<p
91-
v-for="message in validMessage"
92-
:id="descriptionId"
115+
v-for="(message, idx) in validMessage"
116+
:id="`${descriptionId}-${idx + 1}`"
93117
:key="message"
94118
:data-testid="descriptionId"
95119
class="fr-valid-text"

src/components/DsfrMultiselect/DsfrMultiselect.types.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,11 @@ export type DsfrMultiSelectProps<T> = {
2626
}
2727

2828
export type DsfrMultiSelectSlots<T> = {
29-
label: () => VNode
30-
'required-tip': () => VNode
31-
hint: () => VNode
32-
'button-label': () => VNode
33-
legend: () => VNode
34-
'checkbox-label': (props: { option: T }) => VNode
35-
'no-results': () => VNode
29+
label?: () => VNode
30+
'required-tip'?: () => VNode
31+
hint?: () => VNode
32+
'button-label'?: () => VNode
33+
legend?: () => VNode
34+
'checkbox-label'?: (props: { option: T }) => VNode
35+
'no-results'?: () => VNode
3636
}

src/components/DsfrMultiselect/DsfrMultiselect.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -321,7 +321,7 @@ const finalLabelClass = computed(() => [
321321
</slot>
322322

323323
<span
324-
v-if="props.hint || $slots.hint?.()"
324+
v-if="props.hint || $slots.hint"
325325
class="fr-hint-text"
326326
>
327327
<slot name="hint">{{ props.hint }}</slot>
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# Navigation principale - `DsfrNavigation`
2+
3+
## 🌟 Introduction
4+
5+
Le composant `DsfrNavigation`, est le système central de navigation au sein d’un site. Elle permet d’orienter aisément l’usager à travers l'application voire même jusqu'aux confins de la galaxie !
6+
7+
## 🛠️Props
8+
9+
| Nom | Type | Défaut | Obligatoire | Description |
10+
|-----------------|---------------|-------------------------|---------------|-------------------------------------------------------------------------------------------------------------|
11+
| `id` | `string` | `() => useRandomId(...)`| | Identifiant unique pour la nav. Si non spécifié, un ID aléatoire est généré. |
12+
| `label` | `string` | `Menu principal` | | Nom associé à la navigation. Utile pour l'accessibilité. |
13+
| `navItems` | `array` | `() => []` || Tableau contenant les liens ou sous menus accessibles depuis la navigation. |
14+
15+
## 📡Événements
16+
17+
| Nom | Description |
18+
|---------------------|-------------------------------------------------------------------------------------------------|
19+
| `click` | Événement émis au clic qui déclence l'ouverture ou la fermeture d'un menu. |
20+
| `keydown` | Événement émis en appuyant sur la touche "Echap" qui déclence lla fermeture d'un menu ouvert. |
21+
22+
---
23+
24+
## 🧩 Slots
25+
26+
| Nom | Description |
27+
|------------------|------------------------------------------------------------------------------------------------------------|
28+
| `default` | Slot par défaut pour le contenu de la navigation, il se trouve dans la balise `<ul class="fr-nav__list">`. |
29+
30+
---
31+
32+
## 📝 Exemples
33+
34+
Exemple simple d'utilisation de `DsfrNavigation` :
35+
36+
::: code-group
37+
<Story data-title="Démo" minH="500px">
38+
<DsfrNavigationDemo />
39+
</Story>
40+
41+
<<< docs-demo/DsfrNavigationDemo.vue [Code de la démo]
42+
:::
43+
44+
## ⚙️ Code source du composant
45+
46+
::: code-group
47+
48+
<<< DsfrNavigation.vue
49+
<<< DsfrNavigation.types.ts
50+
51+
:::
52+
53+
<script setup>
54+
import DsfrNavigationDemo from './docs-demo/DsfrNavigationDemo.vue'
55+
</script>
56+
57+
Avec `DsfrNavigation`, toute destination est à portée de clic, Bon voyage !

0 commit comments

Comments
 (0)