@cnamts/synapse 1.0.25 → 1.0.26

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 (211) hide show
  1. package/dist/{AutocompleteFilter-D7qBuCAP.js → AutocompleteFilter-BPR-a55G.js} +1 -1
  2. package/dist/{DateFilter-BitMWrMU.js → DateFilter-CknrJWs2.js} +2 -2
  3. package/dist/{NumberFilter-BTLUxw0a.js → NumberFilter-DJ-yNlzv.js} +1 -1
  4. package/dist/{PeriodFilter-B5rUIPAC.js → PeriodFilter-CiB5Oa9Z.js} +1 -1
  5. package/dist/{SelectFilter-l4QnRcuk.js → SelectFilter-EiafX97M.js} +1 -1
  6. package/dist/{TextFilter-C9hj6Qrp.js → TextFilter-BzOmpdxj.js} +1 -1
  7. package/dist/{apLightTheme-DnIM24Lv.js → apLightTheme-DS0Uy44H.js} +20 -16
  8. package/dist/components/Customs/Selects/SyAutocomplete/SyAutocomplete.d.ts +4 -2
  9. package/dist/components/Customs/Selects/SySelect/SySelect.d.ts +60 -289
  10. package/dist/components/Customs/SyCheckBoxGroup/SyCheckBoxGroup.d.ts +1 -0
  11. package/dist/components/Customs/SyCheckbox/SyCheckbox.d.ts +1 -0
  12. package/dist/components/Customs/SyRadioGroup/SyRadioGroup.d.ts +1 -0
  13. package/dist/components/Customs/SyTextField/SyTextField.d.ts +2 -4
  14. package/dist/components/DatePicker/CalendarMode/DatePicker.d.ts +50 -49
  15. package/dist/components/DatePicker/ComplexDatePicker/ComplexDatePicker.d.ts +29 -28
  16. package/dist/components/DatePicker/DateTextInput/DateTextInput.d.ts +8 -8
  17. package/dist/components/DatePicker/composables/useDatePickerState.d.ts +3 -3
  18. package/dist/components/DatePicker/composables/useDateTextField.d.ts +2 -2
  19. package/dist/components/DatePicker/composables/useInputBlurHandler.d.ts +2 -2
  20. package/dist/components/DatePicker/types.d.ts +1 -2
  21. package/dist/components/LunarCalendar/useLunarCalendarValidation.d.ts +1 -0
  22. package/dist/components/MonthPicker/MonthPicker.d.ts +1 -1
  23. package/dist/components/MonthPicker/MonthPickerText/MonthPickerInput.d.ts +1 -1
  24. package/dist/components/NirField/NirField.d.ts +8 -4
  25. package/dist/components/NirField/useNirValidation.d.ts +6 -2
  26. package/dist/components/PeriodField/PeriodField.d.ts +102 -102
  27. package/dist/components/PhoneField/PhoneField.d.ts +11 -1
  28. package/dist/components/RangeField/RangeSlider/RangeSlider.d.ts +0 -3
  29. package/dist/components/RatingPicker/EmotionPicker/EmotionPicker.d.ts +3 -1
  30. package/dist/components/RatingPicker/NumberPicker/NumberPicker.d.ts +4 -3
  31. package/dist/components/RatingPicker/RatingPicker.d.ts +18 -5
  32. package/dist/components/RatingPicker/StarsPicker/StarsPicker.d.ts +3 -1
  33. package/dist/components/RatingPicker/tests/RatingPicker.a11y.spect.d.ts +1 -0
  34. package/dist/components/RatingPicker/useRatingFocus.d.ts +18 -0
  35. package/dist/components/Tables/SyServerTable/SyServerTable.d.ts +4 -4
  36. package/dist/components/Tables/SyTable/SyTable.d.ts +4 -4
  37. package/dist/components/Tables/common/SyTablePagination.d.ts +152 -364
  38. package/dist/components/Tables/common/TableHeader.d.ts +1 -1
  39. package/dist/components/Tables/common/filters/DateFilter.d.ts +4 -4
  40. package/dist/composables/date/useDateInitializationDayjs.d.ts +3 -1
  41. package/dist/composables/unifyValidation/useCustomValidation.d.ts +3 -1
  42. package/dist/composables/unifyValidation/useValidation.d.ts +12 -6
  43. package/dist/composables/unifyValidation/useVuetifyValidation.d.ts +1 -1
  44. package/dist/composables/validation/useValidation.d.ts +1 -0
  45. package/dist/design-system-v3.js +2 -2
  46. package/dist/designTokens/tokens/amelipro/apLightTheme.d.ts +2 -0
  47. package/dist/designTokens/tokens/cnam/cnamLightTheme.d.ts +2 -0
  48. package/dist/{main-Cpx8Co6H.js → main-BsJ9ec3i.js} +9103 -9018
  49. package/dist/synapse.css +1 -1
  50. package/dist/vuetifyConfig.js +1 -1
  51. package/package.json +8 -7
  52. package/src/assets/overrides/_icons.scss +0 -13
  53. package/src/assets/overrides/_otp.scss +0 -1
  54. package/src/components/Accordion/Accordion.vue +2 -0
  55. package/src/components/CookiesSelection/CookiesInformation/CookiesInformation.vue +2 -1
  56. package/src/components/CookiesSelection/CookiesSelection.vue +2 -1
  57. package/src/components/CopyBtn/CopyBtn.vue +9 -0
  58. package/src/components/Customs/Selects/SyAutocomplete/SyAutocomplete.vue +1 -1
  59. package/src/components/Customs/Selects/SySelect/SySelect.stories.ts +413 -96
  60. package/src/components/Customs/Selects/SySelect/SySelect.vue +270 -225
  61. package/src/components/Customs/Selects/SySelect/tests/SySelect.spec.ts +245 -6
  62. package/src/components/Customs/SyCheckBoxGroup/SyCheckBoxGroup.vue +3 -3
  63. package/src/components/Customs/SyCheckbox/SyCheckbox.vue +23 -2
  64. package/src/components/Customs/SyRadioGroup/SyRadioGroup.vue +23 -5
  65. package/src/components/Customs/SyTabs/SyTabs.stories.ts +5 -5
  66. package/src/components/Customs/SyTabs/config.ts +3 -3
  67. package/src/components/Customs/SyTextField/SyTextField.vue +31 -4
  68. package/src/components/DatePicker/CalendarMode/DatePicker.stories.ts +1 -1
  69. package/src/components/DatePicker/CalendarMode/DatePicker.vue +17 -14
  70. package/src/components/DatePicker/ComplexDatePicker/ComplexDatePicker.stories.ts +1 -1
  71. package/src/components/DatePicker/ComplexDatePicker/ComplexDatePicker.vue +8 -7
  72. package/src/components/DatePicker/ComplexDatePicker/tests/ComplexDatePicker.spec.ts +1 -1
  73. package/src/components/DatePicker/DatePickerValidationExample/DatePickerValidation.stories.ts +1 -1
  74. package/src/components/DatePicker/DateTextInput/DateTextInput.vue +57 -23
  75. package/src/components/DatePicker/DateTextInput/NoCalendar.stories.ts +1 -1
  76. package/src/components/DatePicker/composables/useDatePickerState.ts +33 -14
  77. package/src/components/DatePicker/composables/useDateRangeInput.ts +2 -1
  78. package/src/components/DatePicker/composables/useDateSelection.ts +2 -1
  79. package/src/components/DatePicker/composables/useDateTextField.ts +2 -2
  80. package/src/components/DatePicker/composables/useInputBlurHandler.ts +2 -2
  81. package/src/components/DatePicker/types.ts +1 -2
  82. package/src/components/DialogBox/DialogBox.stories.ts +8 -8
  83. package/src/components/DialogBox/accessibilite/Accessibility.mdx +86 -22
  84. package/src/components/FilterSideBar/FilterSideBar.vue +2 -1
  85. package/src/components/LangBtn/LangBtn.vue +2 -1
  86. package/src/components/NotificationBar/Notification/Notification.vue +2 -2
  87. package/src/components/PaginatedTable/PaginatedTable.vue +1 -1
  88. package/src/components/PaginatedTable/Pagination.vue +1 -1
  89. package/src/components/PasswordField/PasswordField.vue +7 -3
  90. package/src/components/PhoneField/PhoneField.vue +4 -2
  91. package/src/components/RangeField/RangeSlider/RangeSlider.vue +11 -18
  92. package/src/components/RatingPicker/EmotionPicker/EmotionPicker.vue +32 -48
  93. package/src/components/RatingPicker/EmotionPicker/tests/__snapshots__/EmotionPicker.spec.ts.snap +5 -0
  94. package/src/components/RatingPicker/NumberPicker/NumberPicker.vue +48 -53
  95. package/src/components/RatingPicker/NumberPicker/tests/NumberPicker.spec.ts +2 -1
  96. package/src/components/RatingPicker/NumberPicker/tests/__snapshots__/NumberPicker.spec.ts.snap +40 -13
  97. package/src/components/RatingPicker/RatingPicker.stories.ts +65 -88
  98. package/src/components/RatingPicker/RatingPicker.vue +71 -15
  99. package/src/components/RatingPicker/StarsPicker/StarsPicker.vue +28 -37
  100. package/src/components/RatingPicker/StarsPicker/tests/StarsPicker.spec.ts +1 -1
  101. package/src/components/RatingPicker/StarsPicker/tests/__snapshots__/StarsPicker.spec.ts.snap +5 -0
  102. package/src/components/RatingPicker/accessibilite/Accessibility.mdx +137 -9
  103. package/src/components/RatingPicker/tests/RatingPicker.a11y.spect.ts +123 -0
  104. package/src/components/RatingPicker/tests/RatingPicker.spec.ts +3 -2
  105. package/src/components/RatingPicker/tests/__snapshots__/RatingPicker.spec.ts.snap +40 -11
  106. package/src/components/RatingPicker/useRatingFocus.ts +97 -0
  107. package/src/components/SyTextArea/SyTextArea.vue +32 -1
  108. package/src/components/Tables/SyServerTable/SyServerTable.vue +1 -1
  109. package/src/components/Tables/SyTable/SyTable.vue +1 -1
  110. package/src/components/Tables/common/SyTableFilter.vue +4 -4
  111. package/src/components/Tables/common/SyTablePagination.vue +1 -0
  112. package/src/components/Tables/common/TableHeader.vue +1 -1
  113. package/src/components/Tables/common/filters/DateFilter.vue +2 -2
  114. package/src/composables/date/tests/useDateFormatDayjs.spec.ts +81 -0
  115. package/src/composables/date/tests/{useDateInitialization.spec.ts → useDateInitializationDayjs.spec.ts} +39 -3
  116. package/src/composables/date/useDateInitializationDayjs.ts +4 -1
  117. package/src/composables/unifyValidation/documentationValidationProps.ts +7 -7
  118. package/src/composables/unifyValidation/tests/useCustomValidation.spec.ts +2 -1
  119. package/src/composables/unifyValidation/tests/useValidation.spec.ts +22 -0
  120. package/src/composables/unifyValidation/useCustomValidation.ts +16 -4
  121. package/src/composables/unifyValidation/useValidation.ts +46 -15
  122. package/src/composables/unifyValidation/useVuetifyValidation.ts +2 -2
  123. package/src/composables/useFormFieldErrorHandling.ts +4 -1
  124. package/src/composables/validation/tests/useValidation.spec.ts +2 -2
  125. package/src/composables/validation/useValidation.ts +15 -3
  126. package/src/composantsVuetify/VCard/VCard.mdx +59 -0
  127. package/src/composantsVuetify/VCard/v-card.stories.ts +279 -0
  128. package/src/designTokens/tokens/amelipro/apColors2026.ts +1 -1
  129. package/src/designTokens/tokens/amelipro/apLightTheme.ts +3 -0
  130. package/src/designTokens/tokens/cnam/cnamLightTheme.ts +3 -0
  131. package/src/stories/Accessibilite/Aculturation/SensibilisationAccessibilite.mdx +61 -91
  132. package/src/stories/Accessibilite/AuditDesignSystem.mdx +5 -8
  133. package/src/stories/Accessibilite/AuditEtContreAudit/Exemptions-derogations.mdx +1 -1
  134. package/src/stories/Accessibilite/AuditEtContreAudit/Introduction.mdx +11 -8
  135. package/src/stories/Accessibilite/AuditEtContreAudit/RGAA.mdx +6 -7
  136. package/src/stories/Accessibilite/Introduction.mdx +30 -30
  137. package/src/stories/Accessibilite/KitDePreAudit/Echantillonnage.mdx +168 -78
  138. package/src/stories/Accessibilite/KitDePreAudit/Introduction.mdx +13 -6
  139. package/src/stories/Accessibilite/KitDePreAudit/Outils/Introduction.mdx +66 -45
  140. package/src/stories/Accessibilite/KitDePreAudit/Outils/LecteursDEcran.mdx +23 -49
  141. package/src/stories/Accessibilite/KitDePreAudit/Outils/Tanaguru/FauxPositifs.stories.ts +6 -0
  142. package/src/stories/Accessibilite/KitDePreAudit/Outils/Tanaguru/Utilisation.mdx +7 -19
  143. package/src/stories/Accessibilite/KitDePreAudit/Preaudit.mdx +18 -20
  144. package/src/stories/Components/Components.stories.ts +52 -3
  145. package/dist/AutocompleteFilter-Df9i5mAl.cjs +0 -1
  146. package/dist/DateFilter-BJD6FMev.cjs +0 -1
  147. package/dist/NumberFilter-DGCzCXzI.cjs +0 -1
  148. package/dist/PeriodFilter-DO_ecTZW.cjs +0 -1
  149. package/dist/SelectFilter-CGwcKWLm.cjs +0 -1
  150. package/dist/TextFilter-B8nf7xoK.cjs +0 -1
  151. package/dist/apLightTheme-CEK4iY3f.cjs +0 -1
  152. package/dist/composables/date/useDateFormat.d.ts +0 -26
  153. package/dist/composables/date/useDateInitialization.d.ts +0 -18
  154. package/dist/design-system-v3.umd.cjs +0 -1
  155. package/dist/main-ByDPHpae.cjs +0 -1067
  156. package/dist/tooth-11-D3sLWv2n.cjs +0 -1
  157. package/dist/tooth-12-CXrLuH03.cjs +0 -1
  158. package/dist/tooth-13-BSfo5fpT.cjs +0 -1
  159. package/dist/tooth-14-DMzulx0h.cjs +0 -1
  160. package/dist/tooth-15-BKRFVi-9.cjs +0 -1
  161. package/dist/tooth-16-CpuxAbuM.cjs +0 -1
  162. package/dist/tooth-17-BPoahUdg.cjs +0 -1
  163. package/dist/tooth-18-DhHJz8sy.cjs +0 -1
  164. package/dist/tooth-21-Dgd5hn_X.cjs +0 -1
  165. package/dist/tooth-22-C2Tn19sB.cjs +0 -1
  166. package/dist/tooth-23-C9uaaSGb.cjs +0 -1
  167. package/dist/tooth-24-BrK9UGpf.cjs +0 -1
  168. package/dist/tooth-25-CE_EfGNp.cjs +0 -1
  169. package/dist/tooth-26-Ctv4i9Fy.cjs +0 -1
  170. package/dist/tooth-27-C5J7JkWM.cjs +0 -1
  171. package/dist/tooth-28-Z9oWqjo0.cjs +0 -1
  172. package/dist/tooth-31-BrYqmkTi.cjs +0 -1
  173. package/dist/tooth-32-BNNR0oCZ.cjs +0 -1
  174. package/dist/tooth-33-DuxvqO2J.cjs +0 -1
  175. package/dist/tooth-34-BCSCXMB6.cjs +0 -1
  176. package/dist/tooth-35-BLUXkX88.cjs +0 -1
  177. package/dist/tooth-36-IrKHYqlA.cjs +0 -1
  178. package/dist/tooth-37-BYqpdMwo.cjs +0 -1
  179. package/dist/tooth-38-B_eNXXdu.cjs +0 -1
  180. package/dist/tooth-41-Ddva4Ot8.cjs +0 -1
  181. package/dist/tooth-42-szcDqlM0.cjs +0 -1
  182. package/dist/tooth-43-B3ka6rQm.cjs +0 -1
  183. package/dist/tooth-44-CazyQucj.cjs +0 -1
  184. package/dist/tooth-45-B4HQtc8n.cjs +0 -1
  185. package/dist/tooth-46-BPM40gbG.cjs +0 -1
  186. package/dist/tooth-47-Dvr20dlh.cjs +0 -1
  187. package/dist/tooth-48-Bd8ljGsF.cjs +0 -1
  188. package/dist/tooth-51-OBpwCOF3.cjs +0 -1
  189. package/dist/tooth-52-aKxyHcmq.cjs +0 -1
  190. package/dist/tooth-53-vCwJjTOc.cjs +0 -1
  191. package/dist/tooth-54-DsWu2iFy.cjs +0 -1
  192. package/dist/tooth-55-BxC1X2Dn.cjs +0 -1
  193. package/dist/tooth-61-BbLvxMQi.cjs +0 -1
  194. package/dist/tooth-62-CmTkWczP.cjs +0 -1
  195. package/dist/tooth-63-DI7l_2qI.cjs +0 -1
  196. package/dist/tooth-64-B21sOsJh.cjs +0 -1
  197. package/dist/tooth-65-D2ZC2VEr.cjs +0 -1
  198. package/dist/tooth-71-D473PPO5.cjs +0 -1
  199. package/dist/tooth-72-Drh1wnNu.cjs +0 -1
  200. package/dist/tooth-73-DzlwYI23.cjs +0 -1
  201. package/dist/tooth-74-8aGvcZPg.cjs +0 -1
  202. package/dist/tooth-75-BFK7At_r.cjs +0 -1
  203. package/dist/tooth-81-BZmR-I0M.cjs +0 -1
  204. package/dist/tooth-82-euVfUUZV.cjs +0 -1
  205. package/dist/tooth-83-KV010j64.cjs +0 -1
  206. package/dist/tooth-84-BBg1RjhZ.cjs +0 -1
  207. package/dist/tooth-85-Cr-kc1wM.cjs +0 -1
  208. package/dist/vuetifyConfig.umd.cjs +0 -1
  209. package/src/composables/date/tests/useDateFormat.spec.ts +0 -67
  210. package/src/composables/date/useDateFormat.ts +0 -110
  211. package/src/composables/date/useDateInitialization.ts +0 -92
@@ -1,6 +1,7 @@
1
1
  import { mount } from '@vue/test-utils'
2
- import { describe, expect, it } from 'vitest'
2
+ import { describe, expect, it, vi } from 'vitest'
3
3
  import { VList } from 'vuetify/components'
4
+ import { nextTick } from 'vue'
4
5
  import SySelect from '../SySelect.vue'
5
6
 
6
7
  type ItemType = {
@@ -129,7 +130,7 @@ describe('SySelect.vue', () => {
129
130
  it('renders error messages when provided', () => {
130
131
  const errorMessages = ['Error 1']
131
132
  const wrapper = mount(SySelect, {
132
- props: { errorMessages, hideMessages: false },
133
+ props: { errorMessages, hideDetails: false },
133
134
  attachTo: document.body,
134
135
  })
135
136
  const message = wrapper.find('.v-messages__message')
@@ -139,6 +140,99 @@ describe('SySelect.vue', () => {
139
140
  wrapper.unmount()
140
141
  })
141
142
 
143
+ describe('hideDetails', () => {
144
+ it('masque les messages de validation quand hideDetails est true', async () => {
145
+ const wrapper = mount(SySelect, {
146
+ props: {
147
+ errorMessages: ['Erreur de test'],
148
+ hideDetails: true,
149
+ },
150
+ attachTo: document.body,
151
+ })
152
+
153
+ expect(wrapper.find('.v-messages__message').exists()).toBe(false)
154
+
155
+ wrapper.unmount()
156
+ })
157
+
158
+ it('affiche les messages de validation quand hideDetails est false', async () => {
159
+ const wrapper = mount(SySelect, {
160
+ props: {
161
+ errorMessages: ['Erreur de test'],
162
+ hideDetails: false,
163
+ },
164
+ attachTo: document.body,
165
+ })
166
+
167
+ const message = wrapper.find('.v-messages__message')
168
+ expect(message.exists()).toBe(true)
169
+ expect(message.text()).toContain('Erreur de test')
170
+
171
+ wrapper.unmount()
172
+ })
173
+
174
+ it('affiche la zone de messages par défaut (hideDetails vaut false par défaut)', () => {
175
+ const wrapper = mount(SySelect, {
176
+ attachTo: document.body,
177
+ })
178
+
179
+ expect(wrapper.find('.v-messages').exists()).toBe(true)
180
+
181
+ wrapper.unmount()
182
+ })
183
+
184
+ it('n\'affiche pas le helpText en dessous du champ quand hideDetails est true et qu\'il y a des erreurs', () => {
185
+ const wrapper = mount(SySelect, {
186
+ props: {
187
+ helpText: 'Texte d\'aide',
188
+ errorMessages: ['Erreur de test'],
189
+ hideDetails: true,
190
+ },
191
+ attachTo: document.body,
192
+ })
193
+
194
+ expect(wrapper.find('.help-text-below').exists()).toBe(false)
195
+
196
+ wrapper.unmount()
197
+ })
198
+
199
+ it('affiche le helpText en dessous du champ quand hideDetails est false et qu\'il y a des erreurs', () => {
200
+ const wrapper = mount(SySelect, {
201
+ props: {
202
+ helpText: 'Texte d\'aide',
203
+ errorMessages: ['Erreur de test'],
204
+ hideDetails: false,
205
+ },
206
+ attachTo: document.body,
207
+ })
208
+
209
+ expect(wrapper.find('.help-text-below').exists()).toBe(true)
210
+ expect(wrapper.find('.help-text-below').text()).toContain('Texte d\'aide')
211
+
212
+ wrapper.unmount()
213
+ })
214
+ })
215
+
216
+ it('keeps the label active when a validation error is displayed', async () => {
217
+ const wrapper = mount(SySelect, {
218
+ props: {
219
+ items: [{ text: 'Option 1', value: '1' }],
220
+ label: 'Option',
221
+ required: true,
222
+ },
223
+ attachTo: document.body,
224
+ })
225
+
226
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- exposed method is not part of the inferred public instance type
227
+ const isValid = await (wrapper.vm as any).validateOnSubmit()
228
+ await nextTick()
229
+
230
+ expect(isValid).toBe(false)
231
+ expect(wrapper.find('.v-field').classes()).toContain('v-field--active')
232
+
233
+ wrapper.unmount()
234
+ })
235
+
142
236
  it('does not render error messages when not provided', () => {
143
237
  const wrapper = mount(SySelect, {
144
238
  attachTo: document.body,
@@ -522,25 +616,170 @@ describe('SySelect.vue', () => {
522
616
  wrapper.unmount()
523
617
  })
524
618
 
525
- it('n\'affiche pas d\'erreur quand disableErrorHandling est true', async () => {
619
+ describe('disableErrorHandling', () => {
620
+ it('n\'affiche pas d\'erreur pour un champ requis sans valeur quand disableErrorHandling est true', async () => {
621
+ const wrapper = mount(SySelect, {
622
+ props: {
623
+ required: true,
624
+ label: 'Test Label',
625
+ modelValue: undefined,
626
+ disableErrorHandling: true,
627
+ },
628
+ attachTo: document.body,
629
+ })
630
+
631
+ await wrapper.find('.v-field').trigger('click')
632
+ await wrapper.vm.$nextTick()
633
+ await wrapper.find('.v-field').trigger('click')
634
+ await wrapper.vm.$nextTick()
635
+
636
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- This is a generic type
637
+ const instance = wrapper.vm as any
638
+ expect(instance.hasError).toBe(false)
639
+
640
+ wrapper.unmount()
641
+ })
642
+
643
+ it('ignore les errorMessages quand disableErrorHandling est true', () => {
644
+ const wrapper = mount(SySelect, {
645
+ props: {
646
+ errorMessages: ['Erreur forcée'],
647
+ disableErrorHandling: true,
648
+ },
649
+ attachTo: document.body,
650
+ })
651
+
652
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- This is a generic type
653
+ const instance = wrapper.vm as any
654
+ expect(instance.hasError).toBe(false)
655
+ expect(wrapper.find('.v-messages__message').exists()).toBe(false)
656
+
657
+ wrapper.unmount()
658
+ })
659
+
660
+ it('n\'évalue pas les customRules quand disableErrorHandling est true', async () => {
661
+ const wrapper = mount(SySelect, {
662
+ props: {
663
+ modelValue: '1',
664
+ disableErrorHandling: true,
665
+ isValidateOnBlur: false,
666
+ customRules: [{
667
+ type: 'custom',
668
+ options: {
669
+ validate: () => false,
670
+ message: 'Toujours invalide',
671
+ },
672
+ }],
673
+ },
674
+ attachTo: document.body,
675
+ })
676
+
677
+ await wrapper.vm.$nextTick()
678
+
679
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- This is a generic type
680
+ const instance = wrapper.vm as any
681
+ expect(instance.hasError).toBe(false)
682
+ expect(wrapper.find('.v-messages__message').exists()).toBe(false)
683
+
684
+ wrapper.unmount()
685
+ })
686
+
687
+ it('affiche les erreurs normalement quand disableErrorHandling est false', async () => {
688
+ const wrapper = mount(SySelect, {
689
+ props: {
690
+ errorMessages: ['Erreur visible'],
691
+ disableErrorHandling: false,
692
+ },
693
+ attachTo: document.body,
694
+ })
695
+
696
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- This is a generic type
697
+ const instance = wrapper.vm as any
698
+ expect(instance.hasError).toBe(true)
699
+ expect(wrapper.find('.v-messages__message').text()).toContain('Erreur visible')
700
+
701
+ wrapper.unmount()
702
+ })
703
+ })
704
+
705
+ it('valide immédiatement quand isValidateOnBlur est false', async () => {
526
706
  const wrapper = mount(SySelect, {
527
707
  props: {
528
- required: true,
708
+ items: [
709
+ { text: 'Option 1', value: '1' },
710
+ { text: 'Option 2', value: '2' },
711
+ ],
529
712
  label: 'Test Label',
530
713
  modelValue: undefined,
531
- disableErrorHandling: true,
714
+ isValidateOnBlur: false,
715
+ customRules: [{
716
+ type: 'custom',
717
+ options: {
718
+ validate: (value: unknown) => value === '2',
719
+ message: 'Test error message',
720
+ },
721
+ }],
532
722
  },
533
723
  attachTo: document.body,
534
724
  })
535
725
 
726
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- This is a generic type
727
+ const instance = wrapper.vm as any
728
+ expect(instance.hasError).toBe(false)
729
+
730
+ // Sélection de Option 1 via interaction utilisateur
536
731
  await wrapper.find('.v-field').trigger('click')
537
732
  await wrapper.vm.$nextTick()
733
+ await wrapper.findComponent(VList).findAll('.v-list-item').at(0)!.trigger('click')
734
+ await wrapper.setProps({ modelValue: '1' })
735
+
736
+ await vi.waitUntil(() => instance.hasError === true)
737
+ expect(wrapper.find('.v-messages').text()).toContain('Test error message')
738
+
739
+ // Sélection de Option 2 via interaction utilisateur
538
740
  await wrapper.find('.v-field').trigger('click')
539
741
  await wrapper.vm.$nextTick()
742
+ await wrapper.findComponent(VList).findAll('.v-list-item').at(1)!.trigger('click')
743
+ await wrapper.setProps({ modelValue: '2' })
744
+
745
+ await vi.waitUntil(() => instance.hasError === false)
746
+ expect(wrapper.find('.v-messages').text()).not.toContain('Test error message')
747
+
748
+ wrapper.unmount()
749
+ })
750
+
751
+ it('masque le message de succes mais conserve l\'etat de succes quand showSuccessMessages est false', async () => {
752
+ const wrapper = mount(SySelect, {
753
+ props: {
754
+ items: [
755
+ { text: 'Option 1', value: '1' },
756
+ { text: 'Option 2', value: '2' },
757
+ ],
758
+ label: 'Test Label',
759
+ modelValue: undefined,
760
+ isValidateOnBlur: false,
761
+ showSuccessMessages: false,
762
+ customSuccessRules: [{
763
+ type: 'custom',
764
+ options: {
765
+ validate: (value: unknown) => value === '2',
766
+ successMessage: 'Test success message',
767
+ },
768
+ }],
769
+ },
770
+ attachTo: document.body,
771
+ })
540
772
 
541
773
  // eslint-disable-next-line @typescript-eslint/no-explicit-any -- This is a generic type
542
774
  const instance = wrapper.vm as any
543
- expect(instance.hasError).toBe(false)
775
+ expect(instance.hasSuccess).toBe(false)
776
+
777
+ await wrapper.setProps({ modelValue: '2' })
778
+
779
+ await vi.waitUntil(() => instance.hasSuccess === true)
780
+ expect(wrapper.find('.success-field').exists()).toBe(true)
781
+ expect(wrapper.findAll('.v-messages__message')).toHaveLength(0)
782
+ expect(wrapper.text()).not.toContain('Test success message')
544
783
 
545
784
  wrapper.unmount()
546
785
  })
@@ -175,7 +175,7 @@
175
175
 
176
176
  const errors = computed(() => validation.errors.value)
177
177
  const warnings = computed(() => validation.warnings.value)
178
- const successes = computed(() => validation.successes.value)
178
+ const displaySuccesses = computed(() => validation.displaySuccesses.value)
179
179
 
180
180
  const labelId = computed(() => (props.id ? `${props.id}-label` : undefined))
181
181
  const computedAriaLabelledby = computed(() => {
@@ -267,8 +267,8 @@
267
267
  >
268
268
  <VMessages
269
269
  ref="messagesRef"
270
- :active="hasError || hasWarning || (hasSuccess && props.showSuccessMessages)"
271
- :messages="hasError ? errors : (hasWarning ? warnings : (hasSuccess && props.showSuccessMessages ? successes : []))"
270
+ :active="hasError || hasWarning || (hasSuccess && displaySuccesses.length > 0)"
271
+ :messages="hasError ? errors : (hasWarning ? warnings : (hasSuccess ? displaySuccesses : []))"
272
272
  />
273
273
  </div>
274
274
 
@@ -204,7 +204,7 @@
204
204
 
205
205
  const errors = computed(() => validation.errors.value)
206
206
  const warnings = computed(() => validation.warnings.value)
207
- const successes = computed(() => validation.successes.value)
207
+ const displaySuccesses = computed(() => validation.displaySuccesses.value)
208
208
 
209
209
  const ariaChecked = computed(() => {
210
210
  if (internalIndeterminate.value) return 'mixed'
@@ -347,6 +347,11 @@
347
347
  :aria-labelledby="props.ariaLabelledby"
348
348
  :title="props.title"
349
349
  :color="props.color"
350
+ :class="{
351
+ 'success-field': hasSuccess && !hasError && !hasWarning,
352
+ 'warning-field': hasWarning && !hasError,
353
+ 'error-field': hasError,
354
+ }"
350
355
  :style="{ color: labelColor }"
351
356
  :disabled="props.disabled"
352
357
  :readonly="props.readonly"
@@ -354,7 +359,7 @@
354
359
  :density="props.density"
355
360
  :error="hasError"
356
361
  :error-messages="errors"
357
- :messages="hasError ? errors : (hasWarning ? warnings : (hasSuccess && props.showSuccessMessages ? successes : []))"
362
+ :messages="hasError ? errors : (hasWarning ? warnings : (hasSuccess ? displaySuccesses : []))"
358
363
  :indeterminate="internalIndeterminate"
359
364
  :true-value="props.trueValue"
360
365
  :false-value="props.falseValue"
@@ -387,6 +392,22 @@
387
392
  </template>
388
393
 
389
394
  <style scoped>
395
+ .success-field :deep(.v-messages__message) {
396
+ color: rgb(var(--v-theme-success)) !important;
397
+ }
398
+
399
+ .success-field :deep(.v-selection-control__input) {
400
+ color: rgb(var(--v-theme-success));
401
+ }
402
+
403
+ .warning-field :deep(.v-messages__message) {
404
+ color: rgb(var(--v-theme-borderWarning)) !important;
405
+ }
406
+
407
+ .warning-field :deep(.v-selection-control__input) {
408
+ color: rgb(var(--v-theme-borderWarning));
409
+ }
410
+
390
411
  :deep(.v-input--dirty .v-selection-control__input) {
391
412
  color: v-bind('props.color');
392
413
  }
@@ -2,6 +2,7 @@
2
2
 
3
3
  import { computed, nextTick, onMounted, onUpdated, ref, watch } from 'vue'
4
4
  import type { VRadioGroup } from 'vuetify/components'
5
+ import { VMessages } from 'vuetify/components'
5
6
  import { useValidation, type ValidationRule } from '@/composables/validation/useValidation'
6
7
  import { useValidatable } from '@/composables/validation/useValidatable'
7
8
  import { locales } from './locales'
@@ -148,7 +149,7 @@
148
149
 
149
150
  const errors = computed(() => validation.errors.value)
150
151
  const warnings = computed(() => validation.warnings.value)
151
- const successes = computed(() => validation.successes.value)
152
+ const displaySuccesses = computed(() => validation.displaySuccesses.value)
152
153
 
153
154
  const getAriaChecked = (value: PropertyKey) => {
154
155
  return model.value === value ? 'true' : 'false'
@@ -224,10 +225,6 @@
224
225
  :error="hasError"
225
226
  :error-messages="hasError ? errors : undefined"
226
227
  :aria-describedby="messageId"
227
- :messages="hasError ? errors :
228
- hasWarning ? warnings :
229
- (hasSuccess && props.showSuccessMessages ? successes : [])
230
- "
231
228
  >
232
229
  <v-radio
233
230
  v-for="opt in props.options"
@@ -258,14 +255,35 @@
258
255
  {{ locales.labelledbyMessage }} <span v-if="props.label">{{ props.label + (props.displayAsterisk ? '*' : '')
259
256
  }}</span>.
260
257
  </span>
258
+ <template
259
+ v-if="!hasError && (hasWarning || hasSuccess)"
260
+ #details
261
+ >
262
+ <div class="v-input__details sy-radio-group__messages">
263
+ <VMessages
264
+ :active="hasWarning || (hasSuccess && displaySuccesses.length > 0)"
265
+ :messages="hasWarning ? warnings : displaySuccesses"
266
+ />
267
+ </div>
268
+ </template>
261
269
  </v-radio-group>
262
270
  </template>
263
271
 
264
272
  <style scoped>
273
+ :deep(.v-input__details) {
274
+ display: block !important;
275
+ padding-inline: 0 !important;
276
+ margin-top: -10px !important;
277
+ }
278
+
265
279
  :deep(.v-selection-control--error .v-selection-control__input) {
266
280
  color: rgb(var(--v-theme-error));
267
281
  }
268
282
 
283
+ .sy-radio-group__messages {
284
+ align-items: flex-start;
285
+ }
286
+
269
287
  .sb-show-main.sb-main-centered #storybook-root {
270
288
  margin: none !important;
271
289
  }
@@ -118,7 +118,7 @@ export const WithVModel: Story = {
118
118
  Onglet actif: {{ activeTab }}
119
119
  <button
120
120
  class="ml-4 px-2 py-1 bg-primary text-white rounded"
121
- @click="activeTab = activeTab === 'tab1' ? 'tab2' : 'tab1'"
121
+ @click="activeTab = activeTab === 'tab1' ? 'tab2' : activeTab === 'tab2' ? 'tab3' : 'tab1'"
122
122
  >
123
123
  Changer d'onglet
124
124
  </button>
@@ -138,7 +138,7 @@ export const WithVModel: Story = {
138
138
  Onglet actif: {{ activeTab }}
139
139
  <button
140
140
  class="ml-4 px-2 py-1 bg-primary text-white rounded"
141
- @click="activeTab = activeTab === 'tab1' ? 'tab2' : 'tab1'"
141
+ @click="activeTab = activeTab === 'tab1' ? 'tab2' : activeTab === 'tab2' ? 'tab3' : 'tab1'"
142
142
  >
143
143
  Changer d'onglet
144
144
  </button>
@@ -476,9 +476,9 @@ const items = [
476
476
  export const NavigationMode: Story = {
477
477
  args: {
478
478
  items: [
479
- { label: 'Accueil', value: 'home', href: '#' },
480
- { label: 'Profil', value: 'profile', href: '#profile' },
481
- { label: 'Paramètres', value: 'settings', href: '#settings' },
479
+ { label: 'Accueil', value: 'home', href: 'javascript:void(0)' },
480
+ { label: 'Profil', value: 'profile', href: 'javascript:void(0)' },
481
+ { label: 'Paramètres', value: 'settings', href: 'javascript:void(0)' },
482
482
  ],
483
483
  },
484
484
  parameters: {
@@ -9,9 +9,9 @@ export const config = {
9
9
  'show-arrows': true,
10
10
  },
11
11
  tab: {
12
- 'base-color': '#0C419A',
13
- 'active-color': '#0C419A',
14
- 'slider-color': '#0C419A',
12
+ 'base-color': 'rgb(var(--v-theme-colorPrimary))',
13
+ 'active-color': 'rgb(var(--v-theme-colorPrimary))',
14
+ 'slider-color': 'rgb(var(--v-theme-colorPrimary))',
15
15
  'ripple': false,
16
16
  },
17
17
  }
@@ -275,6 +275,13 @@
275
275
  return 'rgba(0, 0, 0, 1)'
276
276
  })
277
277
 
278
+ const clearButtonColorClass = computed(() => {
279
+ if (hasError.value) return 'error-field'
280
+ if (hasWarning.value) return 'warning-field'
281
+ if (hasSuccess.value) return 'success-field'
282
+ return 'text-iconBase'
283
+ })
284
+
278
285
  const handlePrependIconClick = () => {
279
286
  emit('prepend-icon-click')
280
287
  }
@@ -346,7 +353,7 @@
346
353
  const validationIcon = computed(() => {
347
354
  if (hasError.value) return ICONS['error']
348
355
  if (hasWarning.value) return ICONS['warning']
349
- if (hasSuccess.value && props.showSuccessMessages) return ICONS['success']
356
+ if (hasSuccess.value) return ICONS['success']
350
357
  return null
351
358
  })
352
359
 
@@ -361,7 +368,7 @@
361
368
  // Détermine s'il y a des messages d'erreur ou d'état
362
369
  const hasMessages = computed(() => {
363
370
  if (props.disableErrorHandling) return false
364
- return (props.errorMessages?.length ?? 0) > 0 || hasError.value || hasWarning.value || hasSuccess.value
371
+ return (props.errorMessages?.length ?? 0) > 0 || hasError.value || hasWarning.value || (hasSuccess.value && props.showSuccessMessages)
365
372
  })
366
373
 
367
374
  // Détermine si le helpText doit être affiché à la position du message ou en dessous
@@ -634,7 +641,7 @@
634
641
  :maxlength="props.maxlength"
635
642
  :max-errors="props.maxErrors"
636
643
  :max-width="props.maxWidth"
637
- :messages="hasError ? errors : (hasWarning ? warnings : (hasSuccess && props.showSuccessMessages ? successes : messages))"
644
+ :messages="hasError ? errors : (hasWarning ? warnings : (hasSuccess ? (props.showSuccessMessages ? successes : []) : messages))"
638
645
  :min-width="props.minWidth"
639
646
  :name="props.name"
640
647
  :persistent-clear="props.displayPersistentClear"
@@ -778,7 +785,8 @@
778
785
  <!-- Keyboard-focusable clear button -->
779
786
  <VBtn
780
787
  v-if="showClear"
781
- class="v-btn v-btn--density-compact mr-1 text-iconBase"
788
+ class="v-btn v-btn--density-compact mr-1"
789
+ :class="clearButtonColorClass"
782
790
  :aria-label="props.label ? `Vider ${props.label}` : 'Vider'"
783
791
  :title="props.label ? `Vider ${props.label}` : 'Vider'"
784
792
  :icon="mdiClose"
@@ -849,7 +857,11 @@
849
857
  :deep(.v-field) {
850
858
  color: rgb(var(--v-theme-borderWarning)) !important;
851
859
 
860
+ --v-medium-emphasis-opacity: 1;
861
+
852
862
  .v-field__outline {
863
+ --v-field-border-opacity: 1;
864
+
853
865
  color: rgb(var(--v-theme-borderWarning)) !important;
854
866
  }
855
867
  }
@@ -863,6 +875,13 @@
863
875
  }
864
876
  }
865
877
 
878
+ /* stylelint-disable-next-line selector-class-pattern */
879
+ .text-iconBase {
880
+ :deep(.v-icon__svg) {
881
+ fill: rgb(var(--v-theme-borderAccentPrimary)) !important;
882
+ }
883
+ }
884
+
866
885
  .error-field {
867
886
  :deep(.v-input__details > .v-icon),
868
887
  :deep(.v-input__prepend > .v-icon),
@@ -870,10 +889,16 @@
870
889
  opacity: 1 !important;
871
890
  }
872
891
 
892
+ :deep(.v-icon__svg) {
893
+ fill: rgb(var(--v-theme-textError)) !important;
894
+ }
895
+
873
896
  :deep(.v-field) {
874
897
  color: rgb(var(--v-theme-borderError)) !important;
875
898
 
876
899
  .v-field__outline {
900
+ --v-field-border-opacity: 1;
901
+
877
902
  color: rgb(var(--v-theme-borderError)) !important;
878
903
  }
879
904
  }
@@ -904,6 +929,8 @@
904
929
  --v-medium-emphasis-opacity: 1;
905
930
 
906
931
  .v-field__outline {
932
+ --v-field-border-opacity: 1;
933
+
907
934
  color: rgb(var(--v-theme-borderSuccess)) !important;
908
935
  }
909
936
  }
@@ -172,7 +172,7 @@ const meta = {
172
172
  },
173
173
  'birthDate': {
174
174
  control: 'boolean',
175
- description: 'Alias pour isBirthDate (pour compatibilité avec l\'attribut kebab-case birth-date dans les templates). ⚠️ Utiliser soit birthDate soit isBirthDate, mais pas les deux.',
175
+ description: '⚠️ **DEPRECATED** Utilisez `isBirthDate` à la place.',
176
176
  defaultValue: false,
177
177
  },
178
178
  'isOutlined': {