@cnamts/synapse 0.0.15-alpha → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (133) hide show
  1. package/dist/components/Accordion/Accordion.d.ts +39 -0
  2. package/dist/components/Accordion/config.d.ts +9 -0
  3. package/dist/components/ChipList/ChipList.d.ts +1 -1
  4. package/dist/components/CookiesSelection/CookiesSelection.d.ts +26 -26
  5. package/dist/components/CopyBtn/CopyBtn.d.ts +2 -0
  6. package/dist/components/Customs/SyInputSelect/SyInputSelect.d.ts +12 -0
  7. package/dist/components/Customs/SySelect/SySelect.d.ts +43 -16
  8. package/dist/components/Customs/SyTextField/SyTextField.d.ts +1391 -1
  9. package/dist/components/DatePicker/DatePicker.d.ts +2810 -16
  10. package/dist/components/DatePicker/DateTextInput.d.ts +1401 -4
  11. package/dist/components/DiacriticPicker/DiacriticPicker.d.ts +27 -0
  12. package/dist/components/DiacriticPicker/config.d.ts +14 -0
  13. package/dist/components/DiacriticPicker/locales.d.ts +6 -0
  14. package/dist/components/DownloadBtn/DownloadBtn.d.ts +1 -1
  15. package/dist/components/FooterBar/FooterBar.d.ts +1 -1
  16. package/dist/components/LangBtn/LangBtn.d.ts +4 -4
  17. package/dist/components/NirField/NirField.d.ts +2796 -4
  18. package/dist/components/NotificationBar/NotificationBar.d.ts +1 -1
  19. package/dist/components/PasswordField/PasswordField.d.ts +1 -1
  20. package/dist/components/PeriodField/PeriodField.d.ts +5636 -48
  21. package/dist/components/PhoneField/PhoneField.d.ts +1 -0
  22. package/dist/components/PhoneField/tests/types.d.ts +18 -0
  23. package/dist/components/SyAlert/SyAlert.d.ts +72 -1
  24. package/dist/components/SyTextArea/SyTextArea.d.ts +900 -0
  25. package/dist/components/SyTextArea/locales.d.ts +3 -0
  26. package/dist/components/SyTextArea/trimStartOnUpdate.d.ts +1 -0
  27. package/dist/components/SyTextArea/useTextActions.d.ts +13 -0
  28. package/dist/components/SyTextArea/wrapText.d.ts +1 -0
  29. package/dist/components/TableToolbar/TableToolbar.d.ts +10 -4
  30. package/dist/components/TableToolbar/config.d.ts +3 -2
  31. package/dist/components/UploadWorkflow/UploadWorkflow.d.ts +26 -26
  32. package/dist/components/index.d.ts +4 -0
  33. package/dist/composables/date/useDateFormat.d.ts +2 -2
  34. package/dist/composables/date/useDateFormatDayjs.d.ts +23 -0
  35. package/dist/composables/date/useDateInitializationDayjs.d.ts +18 -0
  36. package/dist/composables/date/useHolidayDay.d.ts +36 -0
  37. package/dist/design-system-v3.js +5106 -4208
  38. package/dist/design-system-v3.umd.cjs +4 -1
  39. package/dist/designTokens/tokens/pa/paLightTheme.d.ts +1 -32
  40. package/dist/style.css +1 -1
  41. package/dist/utils/rules/index.d.ts +1 -0
  42. package/dist/utils/rules/isHolidayDay/index.d.ts +11 -0
  43. package/dist/utils/rules/isHolidayDay/locales.d.ts +2 -0
  44. package/package.json +3 -2
  45. package/src/assets/settings.scss +12 -0
  46. package/src/components/Accordion/Accordion.mdx +69 -0
  47. package/src/components/Accordion/Accordion.stories.ts +262 -0
  48. package/src/components/Accordion/Accordion.vue +319 -0
  49. package/src/components/Accordion/config.ts +9 -0
  50. package/src/components/Accordion/tests/__snapshots__/accordion.spec.ts.snap +155 -0
  51. package/src/components/Accordion/tests/accordion.spec.ts +492 -0
  52. package/src/components/CopyBtn/CopyBtn.stories.ts +189 -0
  53. package/src/components/CopyBtn/CopyBtn.vue +29 -1
  54. package/src/components/CopyBtn/tests/CopyBtn.spec.ts +102 -0
  55. package/src/components/Customs/SyInputSelect/SyInputSelect.stories.ts +155 -1
  56. package/src/components/Customs/SyInputSelect/SyInputSelect.vue +97 -14
  57. package/src/components/Customs/SyInputSelect/tests/SyInputSelect.spec.ts +386 -106
  58. package/src/components/Customs/SySelect/SySelect.stories.ts +121 -2
  59. package/src/components/Customs/SySelect/SySelect.vue +33 -8
  60. package/src/components/Customs/SySelect/tests/SySelect.spec.ts +290 -1
  61. package/src/components/Customs/SyTextField/Accessibilite.stories.ts +7 -0
  62. package/src/components/Customs/SyTextField/SyTextField.stories.ts +13 -0
  63. package/src/components/Customs/SyTextField/SyTextField.vue +87 -20
  64. package/src/components/DatePicker/ComplexDatePicker/ComplexDatePicker.vue +795 -0
  65. package/src/components/DatePicker/DatePicker.stories.ts +432 -1
  66. package/src/components/DatePicker/DatePicker.vue +82 -27
  67. package/src/components/DatePicker/DatePickerValidation.stories.ts +9 -1
  68. package/src/components/DatePicker/DateTextInput.vue +101 -138
  69. package/src/components/DatePicker/docExamples/DatePickerBidirectionalValidation.vue +282 -0
  70. package/src/components/DatePicker/examples/DatePickerHolidayRule.vue +130 -0
  71. package/src/components/DatePicker/tests/DatePicker.spec.ts +33 -32
  72. package/src/components/DatePicker/tests/DateTextInput.spec.ts +81 -33
  73. package/src/components/DiacriticPicker/DiacriticPicker.mdx +104 -0
  74. package/src/components/DiacriticPicker/DiacriticPicker.stories.ts +447 -0
  75. package/src/components/DiacriticPicker/DiacriticPicker.vue +262 -0
  76. package/src/components/DiacriticPicker/config.ts +15 -0
  77. package/src/components/DiacriticPicker/locales.ts +6 -0
  78. package/src/components/DiacriticPicker/tests/DiatriticPicker.spec.ts +132 -0
  79. package/src/components/DialogBox/DialogBox.vue +1 -3
  80. package/src/components/NirField/NirField.stories.ts +172 -0
  81. package/src/components/NirField/NirField.vue +15 -7
  82. package/src/components/NotificationBar/Accessibilite.stories.ts +1 -1
  83. package/src/components/NotificationBar/NotificationBar.stories.ts +14 -0
  84. package/src/components/NotificationBar/NotificationBar.vue +26 -3
  85. package/src/components/NotificationBar/{options.ts → config.ts} +0 -1
  86. package/src/components/PaginatedTable/PaginatedTable.vue +0 -11
  87. package/src/components/PasswordField/PasswordField.stories.ts +4 -3
  88. package/src/components/PasswordField/PasswordField.vue +26 -18
  89. package/src/components/PasswordField/tests/PasswordField.spec.ts +1 -10
  90. package/src/components/PhoneField/PhoneField.stories.ts +143 -0
  91. package/src/components/PhoneField/PhoneField.vue +88 -30
  92. package/src/components/PhoneField/tests/PhoneField.additional.spec.ts +266 -0
  93. package/src/components/PhoneField/tests/PhoneField.spec.ts +248 -28
  94. package/src/components/PhoneField/tests/types.d.ts +19 -0
  95. package/src/components/SyAlert/Accessibilite.stories.ts +4 -0
  96. package/src/components/SyAlert/SyAlert.mdx +3 -7
  97. package/src/components/SyAlert/SyAlert.stories.ts +19 -12
  98. package/src/components/SyAlert/SyAlert.vue +88 -51
  99. package/src/components/SyAlert/tests/SyAlert.spec.ts +20 -2
  100. package/src/components/SyAlert/tests/__snapshots__/SyAlert.spec.ts.snap +83 -75
  101. package/src/components/SyTextArea/SyTextArea.mdx +17 -0
  102. package/src/components/SyTextArea/SyTextArea.stories.ts +322 -0
  103. package/src/components/SyTextArea/SyTextArea.vue +113 -0
  104. package/src/components/SyTextArea/locales.ts +3 -0
  105. package/src/components/SyTextArea/tests/SyTextArea.spec.ts +194 -0
  106. package/src/components/SyTextArea/trimStartOnUpdate.ts +12 -0
  107. package/src/components/SyTextArea/useTextActions.ts +52 -0
  108. package/src/components/SyTextArea/wrapText.ts +42 -0
  109. package/src/components/TableToolbar/TableToolbar.mdx +86 -1
  110. package/src/components/TableToolbar/TableToolbar.stories.ts +422 -74
  111. package/src/components/TableToolbar/TableToolbar.vue +25 -8
  112. package/src/components/TableToolbar/config.ts +3 -2
  113. package/src/components/TableToolbar/tests/__snapshots__/TableToolbar.spec.ts.snap +35 -12
  114. package/src/components/index.ts +4 -0
  115. package/src/composables/date/useDateFormat.ts +17 -1
  116. package/src/composables/date/useDateFormatDayjs.ts +84 -0
  117. package/src/composables/date/useDateInitializationDayjs.ts +133 -0
  118. package/src/composables/date/useHolidayDay.ts +98 -0
  119. package/src/composables/rules/useFieldValidation.ts +16 -3
  120. package/src/composables/validation/useValidation.ts +2 -1
  121. package/src/designTokens/tokens/pa/paLightTheme.ts +10 -41
  122. package/src/stories/Accessibilite/Avancement/Avancement.mdx +12 -0
  123. package/src/stories/Accessibilite/Avancement/Avancement.stories.ts +134 -0
  124. package/src/stories/Accessibilite/Introduction.mdx +5 -2
  125. package/src/stories/DesignTokens/colors.stories.ts +100 -41
  126. package/src/utils/rules/index.ts +1 -0
  127. package/src/utils/rules/isHolidayDay/IsHolidayDay.mdx +52 -0
  128. package/src/utils/rules/isHolidayDay/IsHolidayDay.stories.ts +129 -0
  129. package/src/utils/rules/isHolidayDay/index.ts +36 -0
  130. package/src/utils/rules/isHolidayDay/locales.ts +5 -0
  131. package/src/utils/rules/isHolidayDay/tests/isHolidayDay.spec.ts +35 -0
  132. /package/dist/components/NotificationBar/{options.d.ts → config.d.ts} +0 -0
  133. /package/src/components/DatePicker/{DatePickerValidationExamples.vue → docExamples/DatePickerValidationExamples.vue} +0 -0
@@ -0,0 +1,130 @@
1
+ <script lang="ts" setup>
2
+ import { ref, computed } from 'vue'
3
+ import { useFieldValidation } from '@/composables/rules/useFieldValidation'
4
+ import { useHolidayDay } from '@/composables/date/useHolidayDay'
5
+
6
+ const selectedDate = ref('')
7
+ const { generateRules } = useFieldValidation()
8
+ const { isHolidayDay, getJoursFeries } = useHolidayDay()
9
+
10
+ // Année courante pour afficher les jours fériés
11
+ const currentYear = new Date().getFullYear()
12
+
13
+ // Création de la règle qui vérifie qu'une date n'est pas un jour férié
14
+ const holidayRules = generateRules([
15
+ {
16
+ type: 'isHolidayDay',
17
+ options: {
18
+ fieldName: 'La date',
19
+ message: 'Vous ne pouvez pas sélectionner un jour férié.',
20
+ },
21
+ },
22
+ ])
23
+
24
+ // Vérification si la date sélectionnée est un jour férié (pour l'affichage)
25
+ const isDateHoliday = computed(() => {
26
+ if (!selectedDate.value) return false
27
+ return isHolidayDay(selectedDate.value)
28
+ })
29
+
30
+ // Liste des jours fériés de l'année courante
31
+ const currentYearHolidays = computed(() => {
32
+ const holidays = getJoursFeries(currentYear)
33
+ return Array.from(holidays).sort()
34
+ })
35
+ </script>
36
+
37
+ <template>
38
+ <div class="date-picker-holiday-example">
39
+ <h2>DatePicker avec règle de validation pour jours fériés</h2>
40
+ <p>
41
+ Cet exemple montre comment utiliser la règle <code>isHolidayDay</code> pour empêcher
42
+ la sélection de jours fériés dans un DatePicker.
43
+ </p>
44
+
45
+ <div class="date-picker-container">
46
+ <DatePicker
47
+ v-model="selectedDate"
48
+ label="Date (pas de jour férié)"
49
+ :rules="holidayRules"
50
+ error-messages
51
+ placeholder="Sélectionnez une date non fériée"
52
+ />
53
+ </div>
54
+
55
+ <div class="info-container mt-4">
56
+ <h3>Informations</h3>
57
+ <p>Date sélectionnée : <strong>{{ selectedDate || 'Aucune' }}</strong></p>
58
+
59
+ <div
60
+ v-if="selectedDate"
61
+ class="mt-2"
62
+ >
63
+ <p
64
+ v-if="isDateHoliday"
65
+ class="error-text"
66
+ >
67
+ ⚠️ Le {{ selectedDate }} est un jour férié.
68
+ </p>
69
+ <p
70
+ v-else
71
+ class="success-text"
72
+ >
73
+ ✅ Le {{ selectedDate }} n'est pas un jour férié.
74
+ </p>
75
+ </div>
76
+
77
+ <div
78
+ v-if="currentYearHolidays.length"
79
+ class="holiday-list mt-4"
80
+ >
81
+ <h4>Jours fériés {{ currentYear }} :</h4>
82
+ <ul>
83
+ <li
84
+ v-for="(date, index) in currentYearHolidays"
85
+ :key="index"
86
+ >
87
+ {{ date }}
88
+ </li>
89
+ </ul>
90
+ </div>
91
+ </div>
92
+ </div>
93
+ </template>
94
+
95
+ <style scoped>
96
+ .date-picker-holiday-example {
97
+ max-width: 800px;
98
+ margin: 0 auto;
99
+ padding: 20px;
100
+ }
101
+
102
+ .date-picker-container {
103
+ max-width: 400px;
104
+ }
105
+
106
+ .info-container {
107
+ background-color: #f5f5f5;
108
+ padding: 15px;
109
+ border-radius: 4px;
110
+ }
111
+
112
+ .error-text {
113
+ color: #d32f2f;
114
+ font-weight: bold;
115
+ }
116
+
117
+ .success-text {
118
+ color: #2e7d32;
119
+ font-weight: bold;
120
+ }
121
+
122
+ .holiday-list {
123
+ max-height: 200px;
124
+ overflow-y: auto;
125
+ background-color: #fff;
126
+ padding: 10px;
127
+ border-radius: 4px;
128
+ border: 1px solid #e0e0e0;
129
+ }
130
+ </style>
@@ -60,12 +60,6 @@ describe('DatePicker.vue', () => {
60
60
  expect(wrapper.emitted('update:modelValue')[0]).toEqual(['01/01/2023'])
61
61
  })
62
62
 
63
- it('toggles the date picker visibility on focus', async () => {
64
- const input = wrapper.find('input')
65
- await input.trigger('focus')
66
- expect(wrapper.vm.isDatePickerVisible).toBe(true)
67
- })
68
-
69
63
  it('renders the date picker with proper structure', async () => {
70
64
  // Ouvrir le DatePicker pour que les éléments soient dans le DOM
71
65
  wrapper.vm.isDatePickerVisible = true
@@ -151,17 +145,15 @@ describe('DatePicker.vue', () => {
151
145
  plugins: [vuetify],
152
146
  },
153
147
  props: {
154
- required: true, // Le champ est requis
148
+ required: true,
155
149
  },
156
150
  })
157
151
 
158
- // Simule une date valide
159
152
  wrapper.vm.selectedDates = [new Date('2023-01-01')]
160
153
  await nextTick()
161
154
 
162
155
  const result = wrapper.vm.validateOnSubmit()
163
156
 
164
- // Vérifie que validateOnSubmit retourne true et qu'il n'y a pas d'erreurs
165
157
  expect(result).toBe(true)
166
158
  expect(wrapper.vm.errorMessages).toEqual([])
167
159
  })
@@ -172,34 +164,32 @@ describe('DatePicker.vue', () => {
172
164
  plugins: [vuetify],
173
165
  },
174
166
  props: {
175
- required: true, // Le champ est requis
167
+ required: true,
176
168
  },
177
169
  })
178
170
 
179
- // Simule l'absence de date sélectionnée
180
171
  wrapper.vm.selectedDates = null
181
172
  await nextTick()
182
173
 
183
174
  const result = wrapper.vm.validateOnSubmit()
184
175
 
185
- // Vérifie que validateOnSubmit retourne false et qu'il y a des erreurs
186
176
  expect(result).toBe(false)
187
177
  expect(wrapper.vm.errorMessages).toContain('La date est requise.')
188
178
  })
189
179
 
190
180
  it('parses a valid date string into a Date instance', () => {
191
- const modelValue = '15/01/2023' // Chaîne valide
181
+ const modelValue = '15/01/2023'
192
182
  const result = wrapper.vm.initializeSelectedDates(modelValue, 'DD/MM/YYYY')
193
183
 
194
- expect(result).toBeInstanceOf(Date) // Doit retourner une instance de Date
195
- expect(result.toISOString().split('T')[0]).toBe('2023-01-15') // Correspond à la date attendue
184
+ expect(result).toBeInstanceOf(Date)
185
+ expect(result.toISOString().split('T')[0]).toBe('2023-01-15')
196
186
  })
197
187
 
198
188
  it('returns null if modelValue is null or undefined', () => {
199
189
  const modelValue = null
200
190
  const result = wrapper.vm.initializeSelectedDates(modelValue, 'DD/MM/YYYY')
201
191
 
202
- expect(result).toBeNull() // Doit retourner null
192
+ expect(result).toBeNull()
203
193
  })
204
194
 
205
195
  it('handles an invalid date string gracefully', () => {
@@ -210,24 +200,23 @@ describe('DatePicker.vue', () => {
210
200
  })
211
201
 
212
202
  it('sets selectedDates to null when input is empty', () => {
213
- wrapper.vm.updateSelectedDates('') // Simule un input vide
203
+ wrapper.vm.updateSelectedDates('')
214
204
 
215
- expect(wrapper.vm.selectedDates).toBeNull() // Vérifie que selectedDates est null
205
+ expect(wrapper.vm.selectedDates).toBeNull()
216
206
  })
217
207
 
218
208
  it('parses a valid date string and updates selectedDates', () => {
219
209
  const validInput = '15/01/2023'
220
- wrapper.vm.updateSelectedDates(validInput) // Simule un input valide
210
+ wrapper.vm.updateSelectedDates(validInput)
221
211
 
222
- expect(wrapper.vm.selectedDates).toBeInstanceOf(Date) // Vérifie que selectedDates est une instance de Date
223
- expect(wrapper.vm.selectedDates.toISOString().split('T')[0]).toBe('2023-01-15') // Vérifie la date exacte
212
+ expect(wrapper.vm.selectedDates).toBeInstanceOf(Date)
213
+ expect(wrapper.vm.selectedDates.toISOString().split('T')[0]).toBe('2023-01-15')
224
214
  })
225
215
 
226
216
  it('does not update selectedDates for invalid date string', () => {
227
217
  const invalidInput = 'invalid-date'
228
- wrapper.vm.updateSelectedDates(invalidInput) // Simule un input invalide
229
-
230
- expect(wrapper.vm.selectedDates).toBeNull() // Vérifie que selectedDates reste null
218
+ wrapper.vm.updateSelectedDates(invalidInput)
219
+ expect(wrapper.vm.selectedDates).toBeNull()
231
220
  })
232
221
 
233
222
  it('toggles date picker visibility correctly', async () => {
@@ -235,15 +224,12 @@ describe('DatePicker.vue', () => {
235
224
  global: { plugins: [vuetify] },
236
225
  })
237
226
 
238
- // État initial : le date picker est caché
239
227
  expect(wrapper.vm.isDatePickerVisible).toBe(false)
240
228
 
241
- // Ouvrir le date picker
242
229
  wrapper.vm.openDatePicker()
243
230
  await nextTick()
244
231
  expect(wrapper.vm.isDatePickerVisible).toBe(true)
245
232
 
246
- // Fermer le date picker
247
233
  wrapper.vm.isDatePickerVisible = false
248
234
  await nextTick()
249
235
  expect(wrapper.vm.isDatePickerVisible).toBe(false)
@@ -257,7 +243,6 @@ describe('DatePicker.vue', () => {
257
243
  },
258
244
  })
259
245
 
260
- // Valider sans date (devrait échouer)
261
246
  const result = await wrapper.vm.validateOnSubmit()
262
247
  expect(result).toBe(false)
263
248
  expect(wrapper.vm.errorMessages.length).toBeGreaterThan(0)
@@ -299,11 +284,9 @@ describe('DatePicker.vue', () => {
299
284
  },
300
285
  })
301
286
 
302
- // Valider sans date pour générer une erreur
303
287
  await wrapper.vm.validateOnSubmit()
304
288
  await nextTick()
305
289
 
306
- // Vérifier que l'erreur est ajoutée à errorMessages
307
290
  expect(wrapper.vm.errorMessages.length).toBeGreaterThan(0)
308
291
  const errorMessage = wrapper.find('.v-messages__message')
309
292
  expect(errorMessage.exists()).toBe(true)
@@ -318,12 +301,30 @@ describe('DatePicker.vue', () => {
318
301
  },
319
302
  })
320
303
 
321
- // Vérifier que le mode birth date est appliqué
322
304
  expect(wrapper.props('isBirthDate')).toBe(true)
323
305
 
324
- // Ouvrir le picker et vérifier qu'il est visible
325
306
  wrapper.vm.openDatePicker()
326
307
  await nextTick()
327
308
  expect(wrapper.vm.isDatePickerVisible).toBe(true)
328
309
  })
310
+
311
+ it('takes into account disabled and readonly limitations', async () => {
312
+ const wrapper = mount(DatePicker, {
313
+ global: { plugins: [vuetify] },
314
+ props: {
315
+ disabled: true,
316
+ },
317
+ })
318
+
319
+ wrapper.vm.openDatePicker()
320
+ await nextTick()
321
+
322
+ expect(wrapper.vm.isDatePickerVisible).toBe(false)
323
+
324
+ await wrapper.setProps({ disabled: false, readonly: true })
325
+ wrapper.vm.openDatePicker()
326
+ await nextTick()
327
+
328
+ expect(wrapper.vm.isDatePickerVisible).toBe(false)
329
+ })
329
330
  })
@@ -38,7 +38,7 @@ describe('DateTextInput.vue', () => {
38
38
  await input.trigger('blur')
39
39
  await wrapper.vm.$nextTick()
40
40
  const textField = wrapper.findComponent(SyTextField)
41
- expect(textField.props('errorMessages')).toContain('Format de date invalide')
41
+ expect(textField.props('errorMessages')).toContain('Format de date invalide (DD/MM/YYYY)')
42
42
  })
43
43
 
44
44
  it('accepts valid date format', async () => {
@@ -116,20 +116,20 @@ describe('DateTextInput.vue', () => {
116
116
  },
117
117
  })
118
118
 
119
- // Force la validation en déclenchant un événement de perte de focus
119
+ // Force validation by triggering a blur event
120
120
  const input = wrapper.find('input')
121
121
  await input.trigger('focus')
122
122
  await input.trigger('blur')
123
123
  await wrapper.vm.$nextTick()
124
- await wrapper.vm.$nextTick() // Double nextTick pour s'assurer que les mises à jour sont terminées
125
- // On peut également forcer la validation manuellement
124
+ await wrapper.vm.$nextTick() // Double nextTick to ensure updates are completed
125
+ // We can also force validation manually
126
126
  await wrapper.vm.validateOnSubmit()
127
127
  await wrapper.vm.$nextTick()
128
128
  const textField = wrapper.findComponent(SyTextField)
129
129
  const warningMessages = textField.props('warningMessages') || []
130
- // Vérifier que le message d'avertissement est bien présent
130
+ // Check that the warning message is present
131
131
  expect(warningMessages.length).toBeGreaterThan(0)
132
- // Le message réel commence par "Attention :"
132
+ // The actual message starts with "Attention:"
133
133
  expect(warningMessages[0]).toContain('Attention :')
134
134
  })
135
135
 
@@ -139,7 +139,7 @@ describe('DateTextInput.vue', () => {
139
139
  await input.trigger('blur')
140
140
  await wrapper.vm.$nextTick()
141
141
  const textField = wrapper.findComponent(SyTextField)
142
- expect(textField.props('errorMessages')).toContain('Format de date invalide')
142
+ expect(textField.props('errorMessages')).toContain('Format de date invalide (DD/MM/YYYY)')
143
143
  })
144
144
 
145
145
  it('formats input while typing', async () => {
@@ -169,7 +169,7 @@ describe('DateTextInput.vue', () => {
169
169
  const input = wrapper.find('input')
170
170
  await input.trigger('blur')
171
171
  await wrapper.vm.$nextTick()
172
- expect(textField.props('errorMessages')).toContain('Format de date invalide')
172
+ expect(textField.props('errorMessages')).toContain('Format de date invalide (DD/MM/YYYY)')
173
173
  })
174
174
 
175
175
  it('formats date during input', async () => {
@@ -180,13 +180,13 @@ describe('DateTextInput.vue', () => {
180
180
  })
181
181
 
182
182
  it('handles date deletion', async () => {
183
- // D'abord on met une date valide
183
+ // First set a valid date
184
184
  const input = wrapper.find('input')
185
185
  await input.setValue('01/01/2025')
186
186
  await input.trigger('blur')
187
187
  await wrapper.vm.$nextTick()
188
188
 
189
- // Puis on la supprime
189
+ // Then delete it
190
190
  await input.setValue('')
191
191
  await input.trigger('input')
192
192
  await input.trigger('blur')
@@ -266,19 +266,61 @@ describe('DateTextInput.vue', () => {
266
266
  await wrapper.vm.$nextTick()
267
267
  expect(input.element.value).toBe('01/__/____')
268
268
 
269
- // Simuler une position de curseur après le "01"
269
+ // Simulate cursor position after "01"
270
270
  input.element.setSelectionRange(2, 2)
271
271
  await input.trigger('input')
272
272
  await wrapper.vm.$nextTick()
273
273
 
274
- // La position du curseur devrait rester après le "01"
274
+ // The cursor position should remain after "01"
275
275
  expect(input.element.selectionStart).toBe(2)
276
276
  })
277
277
 
278
+ it('handles 2 digits year correctly', async () => {
279
+ const customWrapper = mount(DateTextInput, {
280
+ global: {
281
+ plugins: [vuetify],
282
+ },
283
+ props: {
284
+ modelValue: null,
285
+ format: 'DD/MM/YY',
286
+ dateFormatReturn: 'YYYY-MM-DD',
287
+ },
288
+ })
289
+
290
+ const input = customWrapper.find('input')
291
+ await input.setValue('0')
292
+ expect(input.element.value).toBe('0_/__/__')
293
+
294
+ await input.setValue('01/02/99')
295
+ expect(input.element.value).toBe('01/02/99')
296
+ expect(customWrapper.emitted('update:model-value')?.at(-1)).toEqual(['1999-02-01'])
297
+ })
298
+
299
+ it('handles ISO-8601 date format correctly', async () => {
300
+ const customWrapper = mount(DateTextInput, {
301
+ global: {
302
+ plugins: [vuetify],
303
+ },
304
+ props: {
305
+ modelValue: null,
306
+ format: 'YYYY-MM-DD',
307
+ },
308
+ })
309
+
310
+ const input = customWrapper.find('input')
311
+ await input.setValue('2025-')
312
+ expect(input.element.value).toBe('2025-__-__')
313
+ expect(customWrapper.emitted('update:model-value')).toBeFalsy()
314
+
315
+ await input.setValue('2025-01-01')
316
+ expect(input.element.value).toBe('2025-01-01')
317
+ expect(customWrapper.emitted('update:model-value')?.at(-1)).toEqual(['2025-01-01'])
318
+ })
319
+
278
320
  it('handles paste event with valid date', async () => {
279
321
  const input = wrapper.find('input')
280
322
 
281
- // Créer un événement de collage avec les données
323
+ // Create a paste event with data
282
324
  const clipboardData = {
283
325
  getData: () => '01/01/2025',
284
326
  }
@@ -297,26 +339,26 @@ describe('DateTextInput.vue', () => {
297
339
  await input.trigger('keydown', {
298
340
  key: 'Tab',
299
341
  })
300
- // Vérifier que le composant n'empêche pas la navigation par tab
342
+ // Check that the component doesn't prevent tab navigation
301
343
  expect(wrapper.emitted('update:model-value')).toBeFalsy()
302
344
 
303
- // Tester le comportement avec Ctrl+V (coller)
345
+ // Test behavior with Ctrl+V (paste)
304
346
  await input.trigger('keydown', {
305
347
  key: 'v',
306
348
  ctrlKey: true,
307
349
  })
308
- // Le comportement par défaut devrait être préservé
350
+ // Default behavior should be preserved
309
351
  expect(wrapper.emitted('update:model-value')).toBeFalsy()
310
352
  })
311
353
 
312
354
  it('validates on submit correctly', async () => {
313
355
  const input = wrapper.find('input')
314
356
 
315
- // Cas 1: Champ vide avec required=true
357
+ // Case 1: Empty field with required=true
316
358
  const emptyResult = await wrapper.vm.validateOnSubmit()
317
359
  expect(emptyResult).toBe(false)
318
360
 
319
- // Cas 2: Date valide
361
+ // Case 2: Valid date
320
362
  await input.setValue('01/01/2025')
321
363
  await input.trigger('blur')
322
364
  await wrapper.vm.$nextTick()
@@ -326,23 +368,29 @@ describe('DateTextInput.vue', () => {
326
368
  })
327
369
 
328
370
  it('handles focus and blur methods correctly', async () => {
329
- // Simuler un querySelector global
330
- const originalQuerySelector = document.querySelector
371
+ // Create a mock for the input element
331
372
  const mockInput = { focus: vi.fn(), blur: vi.fn() }
332
373
 
333
- // Remplacer document.querySelector
334
- document.querySelector = vi.fn().mockReturnValue(mockInput)
374
+ // Mock the component's querySelector method
375
+ const mockQuerySelector = vi.fn().mockReturnValue(mockInput)
376
+
377
+ // Replace the element reference and its querySelector method
378
+ wrapper.vm.inputRef = {
379
+ $el: {
380
+ querySelector: mockQuerySelector,
381
+ },
382
+ }
335
383
 
336
- // Appeler les méthodes exposées
384
+ // Call the exposed methods
337
385
  wrapper.vm.focus()
338
386
  wrapper.vm.blur()
339
387
 
340
- // Vérifier que les méthodes ont été appelées
388
+ // Check that querySelector was called with the right selector
389
+ expect(mockQuerySelector).toHaveBeenCalledWith('input:not([type="hidden"])')
390
+
391
+ // Check that the methods were called
341
392
  expect(mockInput.focus).toHaveBeenCalled()
342
393
  expect(mockInput.blur).toHaveBeenCalled()
343
-
344
- // Restaurer document.querySelector
345
- document.querySelector = originalQuerySelector
346
394
  })
347
395
 
348
396
  it('initializes with model value correctly', async () => {
@@ -383,17 +431,17 @@ describe('DateTextInput.vue', () => {
383
431
  it('handles partial date input correctly', async () => {
384
432
  const input = wrapper.find('input')
385
433
 
386
- // Saisir seulement le jour
434
+ // Enter only the day
387
435
  await input.setValue('01')
388
436
  await wrapper.vm.$nextTick()
389
437
  expect(input.element.value).toBe('01/__/____')
390
438
 
391
- // Ajouter le mois
439
+ // Add the month
392
440
  await input.setValue('01/02')
393
441
  await wrapper.vm.$nextTick()
394
442
  expect(input.element.value).toBe('01/02/____')
395
443
 
396
- // Compléter la date
444
+ // Complete the date
397
445
  await input.setValue('01/02/2025')
398
446
  await wrapper.vm.$nextTick()
399
447
  expect(input.element.value).toBe('01/02/2025')
@@ -423,16 +471,16 @@ describe('DateTextInput.vue', () => {
423
471
  await input.trigger('focus')
424
472
  await input.trigger('blur')
425
473
  await customWrapper.vm.$nextTick()
426
- await customWrapper.vm.$nextTick() // Double nextTick pour fiabilité
474
+ await customWrapper.vm.$nextTick() // Double nextTick for reliability
427
475
 
428
- // Force validation manuelle
476
+ // Force manual validation
429
477
  await customWrapper.vm.validateOnSubmit()
430
478
  await customWrapper.vm.$nextTick()
431
479
 
432
480
  const textField = customWrapper.findComponent(SyTextField)
433
481
  const successMessages = textField.props('successMessages') || []
434
482
 
435
- // Vérification assouplie
483
+ // Flexible verification
436
484
  expect(successMessages.length).toBeGreaterThan(0)
437
485
  expect(successMessages[0]).toContain('valide')
438
486
  })
@@ -0,0 +1,104 @@
1
+ import {Controls, Canvas, Meta, Source} from '@storybook/blocks';
2
+
3
+ import * as DiacriticPickerStories from './DiacriticPicker.stories';
4
+
5
+ <Meta of={DiacriticPickerStories} />
6
+
7
+ # DiacriticPicker
8
+
9
+ Le composant `DiacriticPicker` permet d'ajouter facilement des caractères diacritiques (accents, cédilles, etc.) à un champ de texte ou un textarea. Il ajoute un bouton à côté du champ qui ouvre une boîte de dialogue avec des caractères diacritiques à insérer.
10
+
11
+ <Canvas of={DiacriticPickerStories.Default} />
12
+
13
+ # API
14
+
15
+ <Controls of={DiacriticPickerStories.Default} />
16
+
17
+ ## Fonctionnalités
18
+
19
+ ### Insertion de caractères
20
+ Cliquer sur un caractère dans la boîte de dialogue l'insère à la position actuelle du curseur dans le champ de texte.
21
+
22
+ ### Raccourci clavier
23
+ Le composant offre un raccourci clavier pour changer rapidement entre les variantes d'un même caractère :
24
+ - Tapez un caractère (par exemple "e")
25
+ - Appuyez sur la touche `=` pour parcourir ses variantes diacritiques (é, è, ê, ë)
26
+
27
+ ## Personnalisation
28
+
29
+ Le composant utilise le système de personnalisation Vuetify pour les éléments suivants :
30
+ - `btn` : Style du bouton d'ouverture
31
+ - `dialog` : Style de la boîte de dialogue
32
+
33
+ # Exemple d'utilisation
34
+
35
+ <Source dark code={`
36
+ <script setup>
37
+ import { DiacriticPicker } from '@cnamts/synapse'
38
+ import { ref } from 'vue'
39
+
40
+ const nom = ref('')
41
+ </script>
42
+
43
+ <template>
44
+ <DiacriticPicker v-model="nom">
45
+ <v-text-field
46
+ id="diacritic-input"
47
+ v-model="nom"
48
+ label="Nom avec accents"
49
+ color="primary"
50
+ variant="outlined"
51
+ />
52
+ </DiacriticPicker>
53
+ </template>
54
+ `} />
55
+
56
+ ## Utilisation avec un textarea
57
+
58
+ Le composant fonctionne également avec un textarea pour les textes plus longs.
59
+
60
+ <Source dark code={`
61
+ <script setup>
62
+ import { DiacriticPicker } from '@cnamts/synapse'
63
+ import { ref } from 'vue'
64
+
65
+ const adresse = ref('')
66
+ </script>
67
+
68
+ <template>
69
+ <DiacriticPicker v-model="adresse">
70
+ <v-textarea
71
+ id="diacritic-input"
72
+ v-model="adresse"
73
+ label="Adresse"
74
+ color="primary"
75
+ variant="outlined"
76
+ auto-grow
77
+ />
78
+ </DiacriticPicker>
79
+ </template>
80
+ `} />
81
+
82
+ ## Personnalisation des caractères diacritiques
83
+
84
+ Vous pouvez personnaliser la liste des caractères diacritiques affichés dans la boîte de dialogue.
85
+
86
+ <Source dark code={`
87
+ <script setup>
88
+ import { DiacriticPicker } from '@cnamts/synapse'
89
+ import { ref } from 'vue'
90
+
91
+ const text = ref('')
92
+ const caracteres = ['é', 'è', 'ê', 'à', 'ç', 'ù']
93
+ </script>
94
+
95
+ <template>
96
+ <DiacriticPicker v-model="text" :diacritics="caracteres">
97
+ <v-text-field
98
+ v-model="text"
99
+ label="Texte avec accents personnalisés"
100
+ variant="outlined"
101
+ />
102
+ </DiacriticPicker>
103
+ </template>
104
+ `} />