@cnamts/synapse 1.0.26 → 1.0.27

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 (253) hide show
  1. package/dist/{AutocompleteFilter-BPR-a55G.js → AutocompleteFilter-C9eLKyW8.js} +3 -3
  2. package/dist/{DateFilter-CknrJWs2.js → DateFilter-y-GLkAkn.js} +8 -8
  3. package/dist/{NumberFilter-DJ-yNlzv.js → NumberFilter-DN6hIBS7.js} +1 -1
  4. package/dist/{PeriodFilter-CiB5Oa9Z.js → PeriodFilter-MoUUp9qS.js} +1 -1
  5. package/dist/{SelectFilter-EiafX97M.js → SelectFilter-bCbrdLmu.js} +1 -1
  6. package/dist/{TextFilter-BzOmpdxj.js → TextFilter-CvjgEaoM.js} +4 -4
  7. package/dist/apLightTheme2026-ug4Y23ns.js +611 -0
  8. package/dist/components/Customs/Selects/SyAutocomplete/SyAutocomplete.d.ts +2369 -353
  9. package/dist/components/Customs/Selects/SyAutocomplete/composables/useSyAutocompleteValidation.d.ts +18 -0
  10. package/dist/components/Customs/Selects/SyAutocomplete/utils/ariaManager.d.ts +1 -1
  11. package/dist/components/Customs/Selects/SyAutocomplete/utils/useKeyboardHandler.d.ts +3 -1
  12. package/dist/components/Customs/Selects/SySelect/SySelect.d.ts +9 -10
  13. package/dist/components/Customs/Selects/SySelect/composables/useSySelectKeyboard.d.ts +1 -0
  14. package/dist/components/Customs/Selects/SySelect/composables/useSySelectValidation.d.ts +15 -0
  15. package/dist/components/Customs/SyCheckBoxGroup/SyCheckBoxGroup.d.ts +3 -3
  16. package/dist/components/Customs/SyCheckbox/SyCheckbox.d.ts +3 -3
  17. package/dist/components/Customs/SyIconButton/SyIconButton.d.ts +18 -0
  18. package/dist/components/Customs/SyRadioGroup/SyRadioGroup.d.ts +20 -38
  19. package/dist/components/Customs/SyRadioGroup/composables/useSyRadioGroupValidation.d.ts +50 -0
  20. package/dist/components/Customs/SyTextField/SyTextField.d.ts +6 -6
  21. package/dist/components/DatePicker/CalendarMode/DatePicker.d.ts +147 -136
  22. package/dist/components/DatePicker/ComplexDatePicker/ComplexDatePicker.d.ts +62 -54
  23. package/dist/components/DatePicker/DateTextInput/DateTextInput.d.ts +27 -24
  24. package/dist/components/DatePicker/composables/index.d.ts +1 -0
  25. package/dist/components/DatePicker/composables/useDatePickerValidationBridge.d.ts +51 -0
  26. package/dist/components/MonthPicker/MonthPicker.d.ts +23 -23
  27. package/dist/components/MonthPicker/MonthPickerText/MonthPickerInput.d.ts +23 -23
  28. package/dist/components/NirField/NirField.d.ts +56 -56
  29. package/dist/components/PasswordField/PasswordField.d.ts +3 -3
  30. package/dist/components/PeriodField/PeriodField.d.ts +236 -212
  31. package/dist/components/PhoneField/PhoneField.d.ts +23 -23
  32. package/dist/components/SyTextArea/SyTextArea.d.ts +25 -15
  33. package/dist/components/SyTextArea/composables/useSyTextAreaValidation.d.ts +20 -0
  34. package/dist/components/SyTextArea/locales.d.ts +1 -0
  35. package/dist/components/Tables/SyServerTable/SyServerTable.d.ts +1 -0
  36. package/dist/components/Tables/SyTable/SyTable.d.ts +1 -0
  37. package/dist/components/Tables/common/SyTablePagination.d.ts +25 -25
  38. package/dist/components/Tables/common/types.d.ts +2 -0
  39. package/dist/components/index.d.ts +1 -0
  40. package/dist/composables/unifyValidation/documentationValidationProps.d.ts +160 -160
  41. package/dist/composables/unifyValidation/useValidation.d.ts +16 -14
  42. package/dist/design-system-v3.js +81 -80
  43. package/dist/designTokens/tokens/amelipro/apContextual.d.ts +6 -6
  44. package/dist/designTokens/tokens/amelipro/apDarkTheme.d.ts +3 -1
  45. package/dist/designTokens/tokens/amelipro/apLightTheme.d.ts +53 -100
  46. package/dist/designTokens/tokens/baseContextualTokens.d.ts +0 -6
  47. package/dist/designTokens/tokens/baseTokens.d.ts +232 -0
  48. package/dist/designTokens/tokens/cnam/cnamContextual.d.ts +6 -6
  49. package/dist/designTokens/tokens/cnam/cnamDarkTheme.d.ts +1 -1
  50. package/dist/designTokens/tokens/cnam/cnamLightTheme.d.ts +57 -101
  51. package/dist/designTokens/tokens/pa/paContextual.d.ts +0 -6
  52. package/dist/designTokens/tokens/pa/paDarkTheme.d.ts +1 -1
  53. package/dist/designTokens/tokens/pa/paLightTheme.d.ts +53 -97
  54. package/dist/designTokens/tokens/pa/paSemantic.d.ts +1 -0
  55. package/dist/designTokens/tokens/semanticTokens.d.ts +112 -0
  56. package/dist/main-CI6Q9nmO.js +39234 -0
  57. package/dist/synapse.css +1 -1
  58. package/dist/vuetifyConfig.js +208 -72
  59. package/package.json +4 -2
  60. package/src/assets/overrides/_icons.scss +5 -4
  61. package/src/assets/overrides/_otp.scss +4 -4
  62. package/src/assets/overrides/_typography.scss +2 -1
  63. package/src/assets/overrides/_utilities.scss +1 -42
  64. package/src/components/ChipList/ChipList.vue +30 -18
  65. package/src/components/ChipList/tests/chipList.spec.ts +4 -4
  66. package/src/components/CopyBtn/CopyBtn.vue +2 -2
  67. package/src/components/Customs/Selects/SelectBtnField/SelectBtnField.stories.ts +4 -0
  68. package/src/components/Customs/Selects/SelectBtnField/SelectBtnField.vue +7 -6
  69. package/src/components/Customs/Selects/SelectBtnField/tests/SelectBtnField.spec.ts +223 -0
  70. package/src/components/Customs/Selects/SyAutocomplete/SyAutocomplete.stories.ts +283 -351
  71. package/src/components/Customs/Selects/SyAutocomplete/SyAutocomplete.vue +182 -218
  72. package/src/components/Customs/Selects/SyAutocomplete/composables/useSyAutocompleteValidation.ts +101 -0
  73. package/src/components/Customs/Selects/SyAutocomplete/tests/SyAutocomplete.spec.ts +761 -1
  74. package/src/components/Customs/Selects/SyAutocomplete/utils/ariaManager.ts +3 -1
  75. package/src/components/Customs/Selects/SyAutocomplete/utils/useKeyboardHandler.ts +79 -5
  76. package/src/components/Customs/Selects/SyAutocomplete/validation/Validation.stories.ts +1029 -0
  77. package/src/components/Customs/Selects/SySelect/SySelect.stories.ts +9 -491
  78. package/src/components/Customs/Selects/SySelect/SySelect.vue +46 -79
  79. package/src/components/Customs/Selects/SySelect/composables/useSySelectKeyboard.ts +3 -0
  80. package/src/components/Customs/Selects/SySelect/composables/useSySelectValidation.ts +64 -0
  81. package/src/components/Customs/Selects/SySelect/tests/SySelect.spec.ts +196 -0
  82. package/src/components/Customs/Selects/SySelect/validation/Validation.stories.ts +1026 -0
  83. package/src/components/Customs/SyCheckBoxGroup/SyCheckBoxGroup.stories.ts +18 -7
  84. package/src/components/Customs/SyCheckBoxGroup/SyCheckBoxGroup.vue +2 -2
  85. package/src/components/Customs/SyCheckbox/SyCheckbox.stories.ts +8 -8
  86. package/src/components/Customs/SyCheckbox/SyCheckbox.vue +8 -8
  87. package/src/components/Customs/SyCheckbox/tests/SyCheckbox.spec.ts +1 -1
  88. package/src/components/Customs/SyIcon/accessibilite/Accessibility.mdx +0 -6
  89. package/src/components/Customs/SyIcon/utils/tests/iconUtils.spec.ts +107 -0
  90. package/src/components/Customs/SyRadioGroup/SyRadioGroup.mdx +2 -2
  91. package/src/components/Customs/SyRadioGroup/SyRadioGroup.stories.ts +395 -200
  92. package/src/components/Customs/SyRadioGroup/SyRadioGroup.vue +82 -127
  93. package/src/components/Customs/SyRadioGroup/composables/useSyRadioGroupValidation.ts +127 -0
  94. package/src/components/Customs/SyRadioGroup/tests/SyRadioGroup.a11y.spec.ts +93 -1
  95. package/src/components/Customs/SyRadioGroup/tests/SyRadioGroup.spec.ts +146 -9
  96. package/src/components/Customs/SyRadioGroup/tests/SyRadioGroup.visual.cy.ts +165 -0
  97. package/src/components/Customs/SyRadioGroup/validation/Validation.stories.ts +773 -0
  98. package/src/components/Customs/SyTabs/config.ts +3 -3
  99. package/src/components/Customs/SyTabs/tests/SyTabs.spec.ts +265 -0
  100. package/src/components/Customs/SyTabs/tests/useTabTransition.spec.ts +188 -0
  101. package/src/components/Customs/SyTextField/SyTextField.stories.ts +10 -29
  102. package/src/components/Customs/SyTextField/SyTextField.vue +23 -15
  103. package/src/components/DataList/DataList.stories.ts +1 -1
  104. package/src/components/DataListItem/tests/DataListItem.spec.ts +3 -1
  105. package/src/components/DatePicker/CalendarMode/DatePicker.vue +37 -142
  106. package/src/components/DatePicker/CalendarMode/tests/DatePicker.coverage.spec.ts +156 -0
  107. package/src/components/DatePicker/CalendarMode/tests/DatePicker.spec.ts +495 -4
  108. package/src/components/DatePicker/ComplexDatePicker/ComplexDatePicker.vue +47 -66
  109. package/src/components/DatePicker/ComplexDatePicker/tests/ComplexDatePicker.spec.ts +206 -0
  110. package/src/components/DatePicker/ComplexDatePicker/tests/bridge-integration.regression.spec.ts +210 -0
  111. package/src/components/DatePicker/ComplexDatePicker/tests/calendar-navigation.regression.spec.ts +214 -0
  112. package/src/components/DatePicker/ComplexDatePicker/tests/validation-cross.regression.spec.ts +194 -0
  113. package/src/components/DatePicker/ComplexDatePicker/tests/validation-success-messages.regression.spec.ts +83 -0
  114. package/src/components/DatePicker/DateTextInput/DateTextInput.vue +129 -54
  115. package/src/components/DatePicker/DateTextInput/tests/DateTextInput.spec.ts +320 -0
  116. package/src/components/DatePicker/composables/index.ts +1 -0
  117. package/src/components/DatePicker/composables/tests/useCalendarKeyboardNavigation.spec.ts +360 -0
  118. package/src/components/DatePicker/composables/tests/useDatePickerValidationBridge.spec.ts +129 -0
  119. package/src/components/DatePicker/composables/useDatePickerValidationBridge.ts +205 -0
  120. package/src/components/DatePicker/docExamples/BidirectionalComplexValidation.vue +1 -1
  121. package/src/components/DatePicker/docExamples/DatePickerBidirectionalValidation.vue +1 -1
  122. package/src/components/DatePicker/tests/exposed-methods.coverage.spec.ts +75 -0
  123. package/src/components/DialogBox/DialogBox.vue +1 -1
  124. package/src/components/FileList/UploadItem/UploadItem.vue +4 -4
  125. package/src/components/FileUpload/FileUpload.vue +2 -2
  126. package/src/components/FileUpload/FileUploadContent.vue +1 -1
  127. package/src/components/FilterInline/FilterInline.mdx +2 -2
  128. package/src/components/FilterSideBar/FilterSideBar.stories.ts +1 -1
  129. package/src/components/FilterSideBar/FilterSideBar.vue +2 -2
  130. package/src/components/FooterBar/FooterBar.vue +7 -7
  131. package/src/components/FranceConnectBtn/FranceConnectBtn.vue +1 -1
  132. package/src/components/HeaderBar/HeaderBurgerMenu/HeaderMenuItem/HeaderMenuItem.vue +2 -2
  133. package/src/components/HeaderBar/HeaderBurgerMenu/HeaderSubMenu/HeaderSubMenu.vue +7 -7
  134. package/src/components/HeaderBar/HeaderMenuBtn/HeaderMenuBtn.vue +2 -2
  135. package/src/components/HeaderLoading/tests/HeaderLoading.spec.ts +87 -8
  136. package/src/components/HeaderNavigationBar/HorizontalNavbar/HorizontalNavbar.vue +3 -3
  137. package/src/components/HeaderNavigationBar/HorizontalNavbar/tests/HorizontalNavbar.spec.ts +589 -0
  138. package/src/components/HeaderToolbar/tests/HeaderToolBar.spec.ts +153 -1
  139. package/src/components/HeaderToolbar/tests/useMobileRightMenu.spec.ts +258 -0
  140. package/src/components/LogoBrandSection/tests/LogoBrandSection.spec.ts +2 -2
  141. package/src/components/LogoBrandSection/tests/__snapshots__/LogoBrandSection.spec.ts.snap +1 -1
  142. package/src/components/LunarCalendar/tests/useLunarCalendarRules.spec.ts +184 -0
  143. package/src/components/MonthPicker/MonthPickerVisual/MonthSelector.vue +3 -3
  144. package/src/components/MonthPicker/MonthPickerVisual/VisualPickerFooter.vue +1 -1
  145. package/src/components/MonthPicker/MonthPickerVisual/VisualPickerHeader.vue +2 -2
  146. package/src/components/MonthPicker/MonthPickerVisual/YearSelector.vue +1 -1
  147. package/src/components/NirField/NirField.vue +3 -3
  148. package/src/components/NotificationBar/Notification/Notification.vue +12 -12
  149. package/src/components/NotificationBar/NotificationBar.stories.ts +8 -8
  150. package/src/components/PaginatedTable/Pagination.vue +2 -2
  151. package/src/components/PasswordField/PasswordField.vue +8 -8
  152. package/src/components/PasswordField/tests/PasswordField.spec.ts +3 -3
  153. package/src/components/RangeField/RangeSlider/RangeSlider.vue +2 -2
  154. package/src/components/RangeField/RangeSlider/Tooltip/Tooltip.vue +1 -1
  155. package/src/components/StatusPage/tests/StatusPage.spec.ts +149 -0
  156. package/src/components/SubHeader/SubHeader.vue +1 -1
  157. package/src/components/SyAlert/SyAlert.vue +23 -23
  158. package/src/components/SyTextArea/SyTextArea.stories.ts +177 -131
  159. package/src/components/SyTextArea/SyTextArea.vue +235 -83
  160. package/src/components/SyTextArea/composables/useSyTextAreaValidation.ts +81 -0
  161. package/src/components/SyTextArea/locales.ts +1 -0
  162. package/src/components/SyTextArea/tests/SyTextArea.spec.ts +449 -1
  163. package/src/components/SyTextArea/useDefaultValidationRules.ts +2 -7
  164. package/src/components/SyTextArea/validation/Validation.stories.ts +856 -0
  165. package/src/components/TableToolbar/TableToolbar.vue +6 -6
  166. package/src/components/TableToolbar/accessibilite/Accessibility.mdx +81 -7
  167. package/src/components/Tables/SyServerTable/SyServerTable.stories.ts +163 -0
  168. package/src/components/Tables/SyServerTable/SyServerTable.vue +2 -1
  169. package/src/components/Tables/SyServerTable/tests/SyServerTable.spec.ts +67 -0
  170. package/src/components/Tables/SyTable/SyTable.stories.ts +94 -0
  171. package/src/components/Tables/SyTable/SyTable.vue +2 -1
  172. package/src/components/Tables/SyTable/tests/SyTable.spec.ts +64 -0
  173. package/src/components/Tables/common/TableHeader.vue +2 -2
  174. package/src/components/Tables/common/filters/logics/tests/NumberFilterLogic.spec.ts +176 -0
  175. package/src/components/Tables/common/filters/logics/tests/SelectFilterLogic.spec.ts +111 -0
  176. package/src/components/Tables/common/tableStyles.scss +6 -6
  177. package/src/components/Tables/common/types.ts +2 -0
  178. package/src/components/UploadWorkflow/tests/UploadWorkflow.spec.ts +2 -0
  179. package/src/components/index.ts +1 -0
  180. package/src/composables/date/tests/useDateFormatDayjs.spec.ts +31 -0
  181. package/src/composables/date/tests/useHolidayDay.spec.ts +109 -0
  182. package/src/composables/rules/tests/useFieldValidation.spec.ts +374 -0
  183. package/src/composables/tests/useError.spec.ts +30 -0
  184. package/src/composables/tests/useFormFieldErrorHandling.spec.ts +234 -0
  185. package/src/composables/unifyValidation/documentationValidationProps.ts +5 -5
  186. package/src/composables/unifyValidation/tests/documentationValidationProps.spec.ts +177 -0
  187. package/src/composables/unifyValidation/tests/useCustomValidation.spec.ts +30 -0
  188. package/src/composables/unifyValidation/tests/useValidation.spec.ts +6 -2
  189. package/src/composables/unifyValidation/useCustomValidation.ts +19 -9
  190. package/src/composables/unifyValidation/useValidation.ts +18 -21
  191. package/src/composables/useFilterable/useFilterable.spec.ts +42 -0
  192. package/src/composables/useFilterable/useFilterable.ts +11 -7
  193. package/src/composables/useFormFieldErrorHandling.ts +2 -2
  194. package/src/composantsVuetify/VBtn/VBtn.mdx +9 -39
  195. package/src/composantsVuetify/VBtn/v-btn.stories.ts +26 -86
  196. package/src/designTokens/tokens/amelipro/apContextual.ts +6 -0
  197. package/src/designTokens/tokens/amelipro/apDarkTheme.ts +2 -2
  198. package/src/designTokens/tokens/amelipro/apLightTheme.ts +72 -103
  199. package/src/designTokens/tokens/amelipro/apSemantic.ts +1 -1
  200. package/src/designTokens/tokens/baseContextualTokens.ts +1 -6
  201. package/src/designTokens/tokens/baseTokens.ts +232 -0
  202. package/src/designTokens/tokens/cnam/cnamContextual.ts +6 -0
  203. package/src/designTokens/tokens/cnam/cnamDarkTheme.ts +2 -2
  204. package/src/designTokens/tokens/cnam/cnamLightTheme.ts +76 -104
  205. package/src/designTokens/tokens/pa/paDarkTheme.ts +2 -2
  206. package/src/designTokens/tokens/pa/paLightTheme.ts +73 -99
  207. package/src/designTokens/tokens/pa/paSemantic.ts +2 -0
  208. package/src/designTokens/tokens/semanticTokens.ts +114 -0
  209. package/src/stories/Components/Components.stories.ts +7 -3
  210. package/src/stories/DesignTokens/ColorIntegrationExample.vue +2 -3
  211. package/src/stories/DesignTokens/Colors.mdx +6 -8
  212. package/src/stories/DesignTokens/colors.stories.ts +244 -1081
  213. package/src/utils/amelipro/toKebabCase/tests/toKebabCase.spec.ts +52 -0
  214. package/src/utils/formatNir/tests/formatNir.spec.ts +34 -0
  215. package/src/utils/tests/insertAt.spec.ts +44 -0
  216. package/dist/apLightTheme-DS0Uy44H.js +0 -954
  217. package/dist/components/RatingPicker/tests/RatingPicker.a11y.spect.d.ts +0 -1
  218. package/dist/main-BsJ9ec3i.js +0 -38954
  219. package/src/components/BackBtn/tests/__snapshots__/back-btn-custom-bg.snap.png +0 -0
  220. package/src/components/BackBtn/tests/__snapshots__/back-btn-dark-mode.snap.png +0 -0
  221. package/src/components/BackBtn/tests/__snapshots__/back-btn-default.snap.png +0 -0
  222. package/src/components/BackBtn/tests/__snapshots__/back-btn-no-icon.snap.png +0 -0
  223. package/src/components/DatePicker/CalendarMode/tests/DatePicker.events.spec.ts +0 -178
  224. package/src/components/DialogBox/tests/__snapshots__/dialog-box-custom-texts.snap.png +0 -0
  225. package/src/components/DialogBox/tests/__snapshots__/dialog-box-default.snap.png +0 -0
  226. package/src/components/DialogBox/tests/__snapshots__/dialog-box-no-actions.snap.png +0 -0
  227. package/src/components/HeaderBar/HeaderBurgerMenu/tests/__snapshots__/header-burger-menu-generated-submenu-open.snap.png +0 -0
  228. package/src/components/HeaderBar/HeaderBurgerMenu/tests/__snapshots__/header-burger-menu-generated.snap.png +0 -0
  229. package/src/components/HeaderBar/tests/__snapshots__/header-bar-custom-width.snap.png +0 -0
  230. package/src/components/HeaderBar/tests/__snapshots__/header-bar-default.snap.png +0 -0
  231. package/src/components/HeaderBar/tests/__snapshots__/header-bar-no-sticky.snap.png +0 -0
  232. package/src/components/HeaderBar/tests/__snapshots__/header-bar-with-prepend.snap.png +0 -0
  233. package/src/components/HeaderBar/tests/__snapshots__/header-bar-with-side.snap.png +0 -0
  234. package/src/components/HeaderBar/tests/__snapshots__/header-bar-with-subtitle.snap.png +0 -0
  235. package/src/components/Logo/tests/__snapshots__/logo-avatar.snap.png +0 -0
  236. package/src/components/Logo/tests/__snapshots__/logo-dark.snap.png +0 -0
  237. package/src/components/Logo/tests/__snapshots__/logo-default.snap.png +0 -0
  238. package/src/components/Logo/tests/__snapshots__/logo-no-organism.snap.png +0 -0
  239. package/src/components/Logo/tests/__snapshots__/logo-no-signature.snap.png +0 -0
  240. package/src/components/Logo/tests/__snapshots__/logo-risque-pro.snap.png +0 -0
  241. package/src/components/RangeField/tests/__snapshots__/range-field-custom-bg.snap.png +0 -0
  242. package/src/components/RangeField/tests/__snapshots__/range-field-custom-range.snap.png +0 -0
  243. package/src/components/RangeField/tests/__snapshots__/range-field-default.snap.png +0 -0
  244. package/src/components/RangeField/tests/__snapshots__/range-field-step.snap.png +0 -0
  245. package/src/components/RangeField/tests/__snapshots__/range-field-with-label.snap.png +0 -0
  246. package/src/components/SyAlert/tests/__snapshots__/sy-alert-closable.snap.png +0 -0
  247. package/src/components/SyAlert/tests/__snapshots__/sy-alert-error.snap.png +0 -0
  248. package/src/components/SyAlert/tests/__snapshots__/sy-alert-info.snap.png +0 -0
  249. package/src/components/SyAlert/tests/__snapshots__/sy-alert-success.snap.png +0 -0
  250. package/src/components/SyAlert/tests/__snapshots__/sy-alert-variant-outlined.snap.png +0 -0
  251. package/src/components/SyAlert/tests/__snapshots__/sy-alert-variant-tonal.snap.png +0 -0
  252. package/src/components/SyAlert/tests/__snapshots__/sy-alert-warning.snap.png +0 -0
  253. /package/src/components/RatingPicker/tests/{RatingPicker.a11y.spect.ts → RatingPicker.a11y.spec.ts} +0 -0
@@ -1,43 +1,42 @@
1
1
  <script lang="ts" setup>
2
2
  import {
3
- ref,
3
+ type ComponentPublicInstance,
4
4
  computed,
5
- watch,
6
- onMounted,
7
- onBeforeUnmount,
8
5
  nextTick,
9
- type ComponentPublicInstance,
6
+ onBeforeUnmount,
7
+ onMounted,
8
+ ref,
10
9
  type Ref,
10
+ watch,
11
11
  } from 'vue'
12
12
  import {
13
- useDateInitialization,
14
13
  type DateInput,
15
14
  type DateModelValue,
15
+ useDateInitialization,
16
16
  } from '@/composables/date/useDateInitializationDayjs'
17
17
  import {
18
18
  useAsteriskDisplay,
19
+ useCalendarKeyboardNavigation,
20
+ useDateFormatValidation,
19
21
  useDatePickerFocusTrap,
20
22
  useDatePickerState,
21
- useDateFormatValidation,
22
- useDateValidation,
23
+ useDatePickerValidationBridge,
23
24
  useDatePickerViewMode,
24
25
  useDatePickerVisibility,
25
26
  useDateRangeValidation,
26
27
  useDateSelection,
27
28
  useDisplayedDateString,
29
+ useHolidayHighlighting,
28
30
  useInputBlurHandler,
29
31
  useManualDateValidation,
30
32
  useMonthButtonCustomization,
31
33
  useTodayButton,
32
- useHolidayHighlighting,
33
- useCalendarKeyboardNavigation,
34
34
  } from '../composables'
35
35
  import dayjs from 'dayjs'
36
36
  import SyTextField from '@/components/Customs/SyTextField/SyTextField.vue'
37
37
  import DateTextInput from '../DateTextInput/DateTextInput.vue'
38
38
  import { VDatePicker } from 'vuetify/components'
39
39
  import { useInputHandler } from '../composables/useInputHandler'
40
- import { useValidation } from '@/composables/validation/useValidation'
41
40
  import { useValidatable } from '@/composables/validation/useValidatable'
42
41
  import { useDateFormat } from '@/composables/date/useDateFormatDayjs'
43
42
  import type { DateObjectValue } from '../types'
@@ -232,22 +231,6 @@
232
231
  * Validation + messages
233
232
  */
234
233
  const isDatePickerVisible = ref(false)
235
- const validation = useValidation({
236
- showSuccessMessages: props.showSuccessMessages,
237
- fieldIdentifier: 'Date',
238
- disableErrorHandling: props.disableErrorHandling,
239
- })
240
- const { errors, warnings, successes, validateField, clearValidation } = validation
241
- const errorMessages = computed(() => errors.value)
242
- const warningMessages = computed(() => warnings.value)
243
- const successMessages = computed(() => successes.value)
244
-
245
- const getMessageClasses = () => ({
246
- 'dp-width': true,
247
- 'v-messages__message--error': errorMessages.value.length > 0,
248
- 'v-messages__message--warning': warningMessages.value.length > 0 && errorMessages.value.length === 0,
249
- 'v-messages__message--success': successMessages.value.length > 0 && errorMessages.value.length === 0 && warningMessages.value.length === 0,
250
- })
251
234
 
252
235
  /**
253
236
  * Selection state
@@ -275,22 +258,36 @@
275
258
  disableErrorHandling: props.disableErrorHandling,
276
259
  })
277
260
 
278
- const { validateDates } = useDateValidation({
261
+ const {
262
+ errors,
263
+ warnings,
264
+ successes,
265
+ validateField,
266
+ clearValidation,
267
+ validateDates,
268
+ } = useDatePickerValidationBridge({
269
+ showSuccessMessages: props.showSuccessMessages,
270
+ disableErrorHandling: props.disableErrorHandling,
279
271
  noCalendar: props.noCalendar,
280
272
  required: props.required,
281
273
  displayRange: props.displayRange,
282
- disableErrorHandling: props.disableErrorHandling,
283
274
  customRules: computed(() => props.customRules),
284
275
  customWarningRules: computed(() => props.customWarningRules),
285
276
  selectedDates,
286
277
  isUpdatingFromInternal,
287
278
  currentRangeIsValid,
288
279
  getRangeValidationError,
289
- clearValidation,
290
- validateField,
291
- errors,
292
- warnings,
293
- successes,
280
+ revalidateOnCustomRulesChange: true,
281
+ })
282
+ const errorMessages = computed(() => errors.value)
283
+ const warningMessages = computed(() => warnings.value)
284
+ const successMessages = computed(() => successes.value)
285
+
286
+ const getMessageClasses = () => ({
287
+ 'dp-width': true,
288
+ 'v-messages__message--error': errorMessages.value.length > 0,
289
+ 'v-messages__message--warning': warningMessages.value.length > 0 && errorMessages.value.length === 0,
290
+ 'v-messages__message--success': successMessages.value.length > 0 && errorMessages.value.length === 0 && warningMessages.value.length === 0,
294
291
  })
295
292
 
296
293
  const {
@@ -343,30 +340,12 @@
343
340
  }
344
341
  }
345
342
  else {
346
- const dateObject = parseDate(value, returnFormat.value)
347
- selectedDates.value = dateObject
343
+ selectedDates.value = parseDate(value, returnFormat.value)
348
344
  }
349
345
 
350
346
  // 3) Re-emit upward
351
347
  emit('date-selected', value)
352
348
  }
353
- // Watcher pour re-valider quand les customRules changent
354
- watch(() => props.customRules, () => {
355
- if (selectedDates.value !== null) {
356
- // Retarder légèrement pour s'assurer que les computed sont mis à jour
357
- setTimeout(async () => {
358
- clearValidation()
359
- const datesToValidate = Array.isArray(selectedDates.value) ? selectedDates.value : [selectedDates.value]
360
- for (const date of datesToValidate) {
361
- await Promise.resolve(validateField(
362
- date,
363
- props.customRules,
364
- props.customWarningRules,
365
- ))
366
- }
367
- }, 5)
368
- }
369
- }, { deep: true })
370
349
  // Range handling
371
350
  const rangeBoundaryDates = ref<[Date | null, Date | null] | null>(null)
372
351
  const dateSelectionResult = useDateSelection(parseDate, selectedDates, props.format, props.displayRange)
@@ -728,8 +707,7 @@
728
707
 
729
708
  if (!charBeforeCursor || !/\d/.test(charBeforeCursor)) {
730
709
  event.preventDefault()
731
- const newValue = input.value.substring(0, cursorPos - 2) + input.value.substring(cursorPos)
732
- displayFormattedDate.value = newValue
710
+ displayFormattedDate.value = input.value.substring(0, cursorPos - 2) + input.value.substring(cursorPos)
733
711
  queueMicrotask(() => {
734
712
  const newCursorPos = cursorPos - 2
735
713
  input.setSelectionRange(newCursorPos, newCursorPos)
@@ -901,9 +879,9 @@
901
879
  }
902
880
  }
903
881
  finally {
904
- setTimeout(() => {
882
+ queueMicrotask(() => {
905
883
  isUpdatingFromInternal.value = false
906
- }, 0)
884
+ })
907
885
  }
908
886
  }
909
887
 
@@ -1073,6 +1051,7 @@
1073
1051
  :title="props.title"
1074
1052
  :hint="props.hint"
1075
1053
  :persistent-hint="props.persistentHint"
1054
+ :skip-internal-validation="true"
1076
1055
  @focus="emit('focus')"
1077
1056
  @blur="emit('blur')"
1078
1057
  />
@@ -1106,6 +1085,7 @@
1106
1085
  ref="dateCalendarTextInputRef"
1107
1086
  :key="fieldKey"
1108
1087
  :model-value="textInputValue"
1088
+ :skip-internal-validation="true"
1109
1089
  :label="labelWithAsterisk"
1110
1090
  :placeholder="props.placeholder"
1111
1091
  :format="props.format"
@@ -1273,40 +1253,40 @@
1273
1253
  .v-messages__message--success {
1274
1254
  :deep(.v-input__control),
1275
1255
  :deep(.v-messages__message) {
1276
- color: rgb(var(--v-theme-textSuccess)) !important;
1256
+ color: rgb(var(--v-theme-success)) !important;
1277
1257
 
1278
1258
  --v-medium-emphasis-opacity: 1;
1279
1259
  }
1280
1260
 
1281
1261
  .v-field--active & {
1282
- color: rgb(var(--v-theme-borderSuccess)) !important;
1262
+ color: rgb(var(--v-theme-success)) !important;
1283
1263
  }
1284
1264
  }
1285
1265
 
1286
1266
  .v-messages__message--error {
1287
1267
  :deep(.v-input__control),
1288
1268
  :deep(.v-messages__message) {
1289
- color: rgb(var(--v-theme-textError)) !important;
1269
+ color: rgb(var(--v-theme-error)) !important;
1290
1270
  }
1291
1271
 
1292
1272
  .v-field--active & {
1293
- color: rgb(var(--v-theme-borderError)) !important;
1273
+ color: rgb(var(--v-theme-error)) !important;
1294
1274
  }
1295
1275
  }
1296
1276
 
1297
1277
  .v-messages__message--warning {
1298
1278
  :deep(.v-input__control) {
1299
- color: rgb(var(--v-theme-textWarning)) !important;
1279
+ color: rgb(var(--v-theme-warning)) !important;
1300
1280
 
1301
1281
  --v-medium-emphasis-opacity: 1;
1302
1282
  }
1303
1283
 
1304
1284
  :deep(.v-messages__message) {
1305
- color: rgb(var(--v-theme-textWarning)) !important;
1285
+ color: rgb(var(--v-theme-warning)) !important;
1306
1286
  }
1307
1287
 
1308
1288
  .v-field--active & {
1309
- color: rgb(var(--v-theme-textWarning)) !important;
1289
+ color: rgb(var(--v-theme-warning)) !important;
1310
1290
  }
1311
1291
  }
1312
1292
 
@@ -1342,7 +1322,8 @@
1342
1322
  }
1343
1323
 
1344
1324
  :deep(.v-date-picker-month__day .v-btn:hover) {
1345
- background-color: rgb(var(--v-theme-backgroundMain));
1325
+ // background-color: rgb(var(--v-theme-background));
1326
+ background: green !important;
1346
1327
  }
1347
1328
 
1348
1329
  :deep(.v-date-picker-month__day--selected, .v-date-picker-month__day--adjacent) {
@@ -1350,7 +1331,7 @@
1350
1331
  }
1351
1332
 
1352
1333
  :deep(.v-date-picker-month__day--selected .v-btn:hover) {
1353
- background-color: rgb(var(--v-theme-backgroundAccentContrasted)) !important;
1334
+ background-color: rgb(var(--v-theme-primaryVariant)) !important;
1354
1335
  }
1355
1336
 
1356
1337
  :deep(.weekend .v-date-picker-month__day--week-end .v-btn) {
@@ -59,6 +59,24 @@ describe('ComplexDatePicker.clean', () => {
59
59
  expect(wrapper.vm.currentMonthName).toBeTruthy()
60
60
  })
61
61
 
62
+ it('preserves autoClamp in text input mode', async () => {
63
+ const wrapper = mountComponent({
64
+ label: 'Date Field',
65
+ format: 'DD/MM/YYYY',
66
+ autoClamp: true,
67
+ })
68
+
69
+ const input = wrapper.find('input')
70
+ await input.setValue('31/04/2025')
71
+ await input.trigger('blur')
72
+ await flushPromises()
73
+
74
+ expect(input.element.value).toBe('30/04/2025')
75
+ const emitted = wrapper.emitted('update:modelValue')
76
+ expect(emitted).toBeTruthy()
77
+ expect(emitted && emitted[emitted.length - 1]?.[0]).toBe('30/04/2025')
78
+ })
79
+
62
80
  it('respects disabled and readonly props when opening the calendar', async () => {
63
81
  const wrapper = mountComponent({
64
82
  label: 'Date Field',
@@ -319,6 +337,29 @@ describe('ComplexDatePicker.clean', () => {
319
337
  expect(wrapper.vm.errorMessages.length).toBeGreaterThan(0)
320
338
  })
321
339
 
340
+ it('surfaces custom warning rules without blocking submit in calendar mode', async () => {
341
+ const wrapper = mountComponent({
342
+ label: 'Date Field',
343
+ format: 'DD/MM/YYYY',
344
+ customWarningRules: [
345
+ {
346
+ type: 'custom',
347
+ options: {
348
+ validate: () => false,
349
+ warningMessage: 'Warning de contrat ComplexDatePicker',
350
+ },
351
+ },
352
+ ],
353
+ })
354
+
355
+ wrapper.vm.selectedDates = new Date(2025, 0, 1)
356
+ const result = await wrapper.vm.validateOnSubmit()
357
+
358
+ expect(result).toBe(true)
359
+ expect(wrapper.vm.errorMessages).toEqual([])
360
+ expect(wrapper.vm.warningMessages).toContain('Warning de contrat ComplexDatePicker')
361
+ })
362
+
322
363
  it('validateDates flags an error when end date is before start date in range mode', async () => {
323
364
  const wrapper = mountComponent({
324
365
  label: 'Date Field',
@@ -379,4 +420,169 @@ describe('ComplexDatePicker.clean', () => {
379
420
  expect(wrapper.vm.errorMessages.length).toBe(0)
380
421
  expect(wrapper.vm.isDatePickerVisible).toBe(false)
381
422
  })
423
+
424
+ it('handleDateSelected avec value null efface la sélection', async () => {
425
+ const wrapper = mountComponent({ label: 'Test', format: 'DD/MM/YYYY' })
426
+ wrapper.vm.selectedDates = new Date(2025, 0, 1)
427
+ await wrapper.vm.handleDateSelected(null)
428
+ await flushPromises()
429
+ expect(wrapper.vm.selectedDates).toBeNull()
430
+ })
431
+
432
+ it('handleDateSelected avec tableau range met à jour selectedDates', async () => {
433
+ const wrapper = mountComponent({ label: 'Test', format: 'DD/MM/YYYY', displayRange: true })
434
+ await wrapper.vm.handleDateSelected(['01/01/2025', '10/01/2025'])
435
+ await flushPromises()
436
+ expect(wrapper.vm.selectedDates).not.toBeNull()
437
+ })
438
+
439
+ it('watcher selectedDates null remet les dates à aujourd hui', async () => {
440
+ const wrapper = mountComponent({ label: 'Test', format: 'DD/MM/YYYY' })
441
+ wrapper.vm.selectedDates = new Date(2025, 0, 1)
442
+ await nextTick()
443
+ wrapper.vm.selectedDates = null
444
+ await flushPromises()
445
+ // currentYear doit être une année valide (reset vers today)
446
+ const year = Number(wrapper.vm.currentYear)
447
+ expect(year).toBeGreaterThanOrEqual(2025)
448
+ })
449
+
450
+ it('syncFromModelValue initialise depuis un array range', async () => {
451
+ const wrapper = mountComponent({
452
+ label: 'Test',
453
+ format: 'DD/MM/YYYY',
454
+ displayRange: true,
455
+ modelValue: ['01/01/2025', '10/01/2025'],
456
+ })
457
+ await flushPromises()
458
+ expect(wrapper.vm.selectedDates).not.toBeNull()
459
+ expect(wrapper.vm.displayFormattedDate).toContain('01/01/2025')
460
+ })
461
+
462
+ it('syncFromModelValue initialise depuis une string', async () => {
463
+ const wrapper = mountComponent({
464
+ label: 'Test',
465
+ format: 'DD/MM/YYYY',
466
+ modelValue: '15/06/2025',
467
+ })
468
+ await flushPromises()
469
+ expect(wrapper.vm.displayFormattedDate).toBe('15/06/2025')
470
+ })
471
+
472
+ it('reset avec disabled incrémente fieldKey', async () => {
473
+ const wrapper = mountComponent({ label: 'Test', format: 'DD/MM/YYYY', disabled: true })
474
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
475
+ const before = (wrapper.vm as any).fieldKey
476
+ wrapper.vm.reset()
477
+ await flushPromises()
478
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
479
+ expect((wrapper.vm as any).fieldKey).toBe(before + 1)
480
+ })
481
+
482
+ it('navigation année : bridge Dec→Jan quand currentMonth=11 et année monte', async () => {
483
+ const wrapper = mountComponent({ label: 'Test', format: 'DD/MM/YYYY' })
484
+ wrapper.vm.isDatePickerVisible = true
485
+ // Simuler mois=11 (décembre) et année qui monte
486
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
487
+ ;(wrapper.vm as any).currentMonth = '11'
488
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
489
+ ;(wrapper.vm as any).currentYear = '2024'
490
+ await nextTick()
491
+ // Émettre update:year avec une année supérieure
492
+ const dp = wrapper.findComponent({ name: 'VDatePicker' })
493
+ if (dp.exists()) {
494
+ await dp.vm.$emit('update:year', '2025')
495
+ await nextTick()
496
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
497
+ expect((wrapper.vm as any).currentMonth).toBe('0')
498
+ }
499
+ else {
500
+ // VDatePicker non rendu sans le calendrier ouvert – appel direct
501
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
502
+ ;(wrapper.vm as any).handleYearUpdate?.()
503
+ }
504
+ })
505
+
506
+ const makeKeydownEvent = (key: string, inputProps: Partial<HTMLInputElement> = {}) => {
507
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
508
+ const fakeInput = Object.assign(document.createElement('input'), inputProps) as any
509
+ fakeInput.setSelectionRange = () => {}
510
+ const event = new KeyboardEvent('keydown', { key, bubbles: true })
511
+ Object.defineProperty(event, 'target', { value: fakeInput, writable: false })
512
+ return event
513
+ }
514
+
515
+ it('handleKeydown Backspace sur séparateur supprime le séparateur', async () => {
516
+ const wrapper = mountComponent({ label: 'Test', format: 'DD/MM/YYYY' })
517
+ const event = makeKeydownEvent('Backspace', { value: '01/', selectionStart: 3, selectionEnd: 3 })
518
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
519
+ ;(wrapper.vm as any).handleKeydown(event)
520
+ await nextTick()
521
+ expect(wrapper.exists()).toBe(true)
522
+ })
523
+
524
+ it('handleKeydown ArrowLeft saute le séparateur', async () => {
525
+ const wrapper = mountComponent({ label: 'Test', format: 'DD/MM/YYYY' })
526
+ const event = makeKeydownEvent('ArrowLeft', { value: '01/01/2025', selectionStart: 3, selectionEnd: 3 })
527
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
528
+ ;(wrapper.vm as any).handleKeydown(event)
529
+ await nextTick()
530
+ expect(wrapper.exists()).toBe(true)
531
+ })
532
+
533
+ it('handleKeydown ArrowRight saute le séparateur', async () => {
534
+ const wrapper = mountComponent({ label: 'Test', format: 'DD/MM/YYYY' })
535
+ const event = makeKeydownEvent('ArrowRight', { value: '01/01/2025', selectionStart: 2, selectionEnd: 2 })
536
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
537
+ ;(wrapper.vm as any).handleKeydown(event)
538
+ await nextTick()
539
+ expect(wrapper.exists()).toBe(true)
540
+ })
541
+
542
+ it('handleDateTextInputUpdate en mode noCalendar met à jour le modèle depuis une string', async () => {
543
+ const wrapper = mountComponent({ label: 'Test', format: 'DD/MM/YYYY', noCalendar: true })
544
+ const input = wrapper.find('input')
545
+ await input.setValue('15/06/2025')
546
+ await input.trigger('blur')
547
+ await flushPromises()
548
+ const emitted = wrapper.emitted('update:modelValue')
549
+ expect(emitted).toBeTruthy()
550
+ })
551
+
552
+ it('handleDateTextInputUpdate avec valeur null efface selectedDates via reset', async () => {
553
+ const wrapper = mountComponent({ label: 'Test', format: 'DD/MM/YYYY', noCalendar: true })
554
+ wrapper.vm.selectedDates = new Date(2025, 0, 1)
555
+ wrapper.vm.reset()
556
+ await flushPromises()
557
+ expect(wrapper.vm.selectedDates).toBeNull()
558
+ })
559
+
560
+ it('handleDateTextInputUpdate avec array range startDate only via noCalendar', async () => {
561
+ const wrapper = mountComponent({ label: 'Test', format: 'DD/MM/YYYY', noCalendar: true, displayRange: true })
562
+ const input = wrapper.find('input')
563
+ await input.setValue('01/01/2025 - ')
564
+ await input.trigger('blur')
565
+ await flushPromises()
566
+ expect(wrapper.exists()).toBe(true)
567
+ })
568
+
569
+ it('handleKeydown readonly ne fait rien', async () => {
570
+ const wrapper = mountComponent({ label: 'Test', format: 'DD/MM/YYYY', readonly: true })
571
+ const event = makeKeydownEvent('Backspace', { value: '01/01/2025', selectionStart: 3, selectionEnd: 3 })
572
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
573
+ ;(wrapper.vm as any).handleKeydown(event)
574
+ await nextTick()
575
+ expect(wrapper.exists()).toBe(true)
576
+ })
577
+
578
+ it('keeps deprecated birthDate prop as an alias for birth date mode', () => {
579
+ const wrapper = mountComponent({
580
+ label: 'Date Field',
581
+ birthDate: true,
582
+ format: 'DD/MM/YYYY',
583
+ })
584
+
585
+ expect(wrapper.props('birthDate')).toBe(true)
586
+ expect(wrapper.vm.currentViewMode).toBe('year')
587
+ })
382
588
  })
@@ -0,0 +1,210 @@
1
+ import { mount, flushPromises } from '@vue/test-utils'
2
+ import { describe, it, expect, vi } from 'vitest'
3
+ import { nextTick } from 'vue'
4
+ import ComplexDatePicker from '../ComplexDatePicker.vue'
5
+ import DateTextInput from '../../DateTextInput/DateTextInput.vue'
6
+
7
+ /**
8
+ * Tests de régression pour l'intégration Bridge + validation
9
+ * Couvrent les cas critiques non testés précédemment
10
+ */
11
+ describe('Bridge Integration Regression Tests', () => {
12
+ /**
13
+ * Test 1: Réactivité des customRules
14
+ * Bug potentiel : Si les règles ne sont pas réactives,
15
+ * la validation croisée (dateA -> dateB) ne fonctionne pas
16
+ */
17
+ it('réagit aux changements de customRules dynamiques', async () => {
18
+ const wrapper = mount(ComplexDatePicker, {
19
+ props: {
20
+ modelValue: '',
21
+ label: 'Date',
22
+ format: 'DD/MM/YYYY',
23
+ // Première règle qui passe toujours
24
+ customRules: [{ type: 'custom', options: { validate: () => true, message: 'Erreur' } }],
25
+ },
26
+ })
27
+
28
+ // Changer les règles via setProps (comme le ferait le parent)
29
+ await wrapper.setProps({
30
+ customRules: [{ type: 'custom', options: { validate: () => false, message: 'Nouvelle erreur' } }],
31
+ })
32
+ await nextTick()
33
+
34
+ // La nouvelle règle doit être prise en compte
35
+ const input = wrapper.find('input')
36
+ await input.setValue('15/05/2025')
37
+ await input.trigger('blur')
38
+ await flushPromises()
39
+
40
+ // Avec la nouvelle règle qui retourne false, on doit avoir une erreur
41
+ const errors = wrapper.findAll('.v-messages__message--error')
42
+ expect(errors.length).toBeGreaterThan(0)
43
+
44
+ wrapper.unmount()
45
+ })
46
+
47
+ /**
48
+ * Test 2: Mode readonly - pas de validation active
49
+ * Bug potentiel : Validation qui s'exécute quand même en readonly
50
+ */
51
+ it('ne valide pas quand readonly est true', async () => {
52
+ const validateSpy = vi.fn(() => true)
53
+
54
+ const wrapper = mount(DateTextInput, {
55
+ props: {
56
+ modelValue: '',
57
+ label: 'Date',
58
+ format: 'DD/MM/YYYY',
59
+ readonly: true,
60
+ showSuccessMessages: true,
61
+ customRules: [{ type: 'custom', options: { validate: validateSpy } }],
62
+ },
63
+ })
64
+
65
+ // Attendre un tick pour s'assurer que la validation ne s'est pas déclenchée
66
+ await nextTick()
67
+
68
+ // La règle ne doit pas avoir été appelée
69
+ expect(validateSpy).not.toHaveBeenCalled()
70
+
71
+ wrapper.unmount()
72
+ })
73
+
74
+ /**
75
+ * Test 3: Coordonnation parent/enfant avec skipInternalValidation
76
+ * Bug potentiel : DateTextInput valide en parallèle du parent
77
+ */
78
+ it('ComplexDatePicker contrôle la validation quand DateTextInput a skipInternalValidation', async () => {
79
+ const parentValidate = vi.fn(() => ({ hasError: false, hasWarning: false, hasSuccess: true, state: { errors: [], warnings: [], successes: ['OK'] } }))
80
+
81
+ // Ce test vérifie que le parent gère la validation, pas l'enfant
82
+ const wrapper = mount(ComplexDatePicker, {
83
+ props: {
84
+ modelValue: '',
85
+ label: 'Date parent',
86
+ format: 'DD/MM/YYYY',
87
+ showSuccessMessages: true,
88
+ customRules: [{ type: 'custom', options: { validate: parentValidate } }],
89
+ },
90
+ })
91
+
92
+ const input = wrapper.find('input')
93
+ await input.setValue('20/06/2025')
94
+ await input.trigger('blur')
95
+ await flushPromises()
96
+
97
+ // Vérifier qu'on a un message de succès (du parent)
98
+ const successMessages = wrapper.findAll('.v-messages__message--success')
99
+ expect(successMessages.length).toBeLessThanOrEqual(1)
100
+
101
+ wrapper.unmount()
102
+ })
103
+
104
+ /**
105
+ * Test 4: Pas de double validation avec queueMicrotask
106
+ * Bug potentiel : queueMicrotask pourrait causer des validations en cascade
107
+ */
108
+ it('ne déclenche pas de validation en cascade avec queueMicrotask', async () => {
109
+ let validationCount = 0
110
+ const validateRule = () => {
111
+ validationCount++
112
+ return true
113
+ }
114
+
115
+ const wrapper = mount(DateTextInput, {
116
+ props: {
117
+ modelValue: '',
118
+ label: 'Date',
119
+ format: 'DD/MM/YYYY',
120
+ customRules: [{ type: 'custom', options: { validate: validateRule } }],
121
+ showSuccessMessages: true,
122
+ },
123
+ })
124
+
125
+ // Attendre que les microtasks soient traitées
126
+ await nextTick()
127
+ await new Promise(resolve => queueMicrotask(() => resolve(undefined)))
128
+
129
+ // La validation ne doit pas avoir été déclenchée automatiquement
130
+ // (seulement au blur ou changement de valeur)
131
+ expect(validationCount).toBe(0)
132
+
133
+ wrapper.unmount()
134
+ })
135
+
136
+ /**
137
+ * Test 5: Clear reset correctement les messages
138
+ * Bug potentiel : Messages qui restent après clear
139
+ */
140
+ it('reset les messages de validation après clear', async () => {
141
+ const wrapper = mount(DateTextInput, {
142
+ props: {
143
+ modelValue: '15/05/2025',
144
+ label: 'Date',
145
+ format: 'DD/MM/YYYY',
146
+ showSuccessMessages: true,
147
+ required: true,
148
+ },
149
+ })
150
+
151
+ // D'abord s'assurer qu'on a un message de succès
152
+ await flushPromises()
153
+
154
+ // Puis clear
155
+ await wrapper.setProps({ modelValue: '' })
156
+ await flushPromises()
157
+ await nextTick()
158
+
159
+ // Plus de message de succès
160
+ const successMessages = wrapper.findAll('.v-messages__message--success')
161
+ expect(successMessages.length).toBe(0)
162
+
163
+ wrapper.unmount()
164
+ })
165
+
166
+ /**
167
+ * Test 6: useDatePickerValidationBridge expose les bonnes méthodes
168
+ * Bug potentiel : Methods exposées qui ne fonctionnent pas
169
+ */
170
+ it('expose validateOnSubmit qui retourne un booléen', async () => {
171
+ const wrapper = mount(DateTextInput, {
172
+ props: {
173
+ modelValue: '',
174
+ label: 'Date',
175
+ format: 'DD/MM/YYYY',
176
+ required: true,
177
+ },
178
+ })
179
+
180
+ // Appeler validateOnSubmit exposé
181
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Testing internal API
182
+ const result = await (wrapper.vm as any).validateOnSubmit?.()
183
+
184
+ // Doit retourner false car champ vide et required
185
+ expect(result).toBe(false)
186
+
187
+ wrapper.unmount()
188
+ })
189
+
190
+ /**
191
+ * Test 7: Pas de fuite mémoire avec les watchers
192
+ * Bug potentiel : Watchers qui s'accumulent
193
+ */
194
+ it('nettoie les watchers quand le composant est détruit', async () => {
195
+ const wrapper = mount(DateTextInput, {
196
+ props: {
197
+ modelValue: '',
198
+ label: 'Date',
199
+ format: 'DD/MM/YYYY',
200
+ },
201
+ })
202
+
203
+ // Détruire le composant
204
+ wrapper.unmount()
205
+
206
+ // Aucune erreur ne doit être levée
207
+ // (les watchers sont nettoyés automatiquement par Vue)
208
+ expect(() => wrapper.vm).not.toThrow()
209
+ })
210
+ })