@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,69 +1,51 @@
1
1
  <script lang="ts" setup>
2
2
 
3
- import { computed, nextTick, onMounted, onUpdated, ref, watch } from 'vue'
3
+ import { computed, nextTick, onMounted, onUpdated, ref } from 'vue'
4
4
  import type { VRadioGroup } from 'vuetify/components'
5
5
  import { VMessages } from 'vuetify/components'
6
- import { useValidation, type ValidationRule } from '@/composables/validation/useValidation'
6
+ import { validationPropsDefaults, type FieldValidationProps } from '@/composables/unifyValidation/useValidation'
7
7
  import { useValidatable } from '@/composables/validation/useValidatable'
8
+ import { useSyRadioGroupValidation } from './composables/useSyRadioGroupValidation'
8
9
  import { locales } from './locales'
9
10
 
10
11
  const props = withDefaults(
11
12
  defineProps<{
12
- modelValue?: PropertyKey | null
13
- label?: string
14
- displayAsterisk?: boolean
15
13
  ariaLabel?: string
16
14
  ariaLabelledby?: string
17
- title?: string
18
15
  color?: string
19
- disabled?: boolean
20
- readonly?: boolean
21
- hideDetails?: boolean | 'auto'
22
16
  density?: 'default' | 'comfortable' | 'compact'
23
- options?: Array<{ label: string, value: PropertyKey }>
24
- name?: string
17
+ displayAsterisk?: boolean
18
+ helpText?: string
19
+ hideDetails?: boolean | 'auto'
25
20
  id?: string
26
- required?: boolean
27
- errorMessages?: string[] | null
28
- warningMessages?: string[] | null
29
- successMessages?: string[] | null
30
- customRules?: ValidationRule[]
31
- customWarningRules?: ValidationRule[]
32
- customSuccessRules?: ValidationRule[]
33
- showSuccessMessages?: boolean
34
- isValidateOnBlur?: boolean
35
- disableErrorHandling?: boolean
36
- }>(),
21
+ label?: string
22
+ modelValue?: PropertyKey | null
23
+ name?: string
24
+ options?: Array<{ label: string, value: PropertyKey }>
25
+ title?: string
26
+ } & FieldValidationProps>(),
37
27
  {
38
- modelValue: null,
39
- label: undefined,
40
- displayAsterisk: false,
41
28
  ariaLabel: undefined,
42
29
  ariaLabelledby: undefined,
43
- title: undefined,
44
30
  color: 'primary',
45
- disabled: false,
46
- readonly: false,
47
- hideDetails: 'auto',
48
31
  density: 'default',
49
- options: () => [],
50
- name: undefined,
32
+ displayAsterisk: false,
33
+ helpText: '',
34
+ hideDetails: 'auto',
51
35
  id: undefined,
52
- required: false,
53
- errorMessages: null,
54
- warningMessages: null,
55
- successMessages: null,
56
- customRules: () => [],
57
- customWarningRules: () => [],
58
- customSuccessRules: () => [],
59
- showSuccessMessages: true,
60
- isValidateOnBlur: false,
61
- disableErrorHandling: false,
36
+ label: undefined,
37
+ modelValue: null,
38
+ name: undefined,
39
+ options: () => [],
40
+ title: undefined,
41
+ ...validationPropsDefaults,
42
+ isValidateOnBlur: false, // La validation se déclenche immédiatement à la sélection pour les radios
62
43
  },
63
44
  )
64
45
 
65
46
  const emit = defineEmits(['update:modelValue', 'change'])
66
47
  const radioGroupRef = ref<VRadioGroup | null>(null)
48
+ const focused = ref(false)
67
49
  const model = computed({
68
50
  get() {
69
51
  return props.modelValue
@@ -75,81 +57,29 @@
75
57
  })
76
58
 
77
59
  const generatedLabel = computed(() =>
78
- (props.label || '') + (props.displayAsterisk ? '*' : ''),
60
+ (props.label || '') + (props.displayAsterisk ? ' *' : ''),
79
61
  )
80
62
 
81
- // Initialisation du composable de validation
82
- // Variable pour suivre si le formulaire a été soumis
83
- const isSubmitted = ref(false)
84
-
85
- const validation = useValidation({
86
- showSuccessMessages: props.showSuccessMessages,
87
- fieldIdentifier: props.label,
88
- disableErrorHandling: props.disableErrorHandling,
89
- })
63
+ // Utilisation du composable de validation dédié
64
+ const {
65
+ validateOnSubmit,
66
+ errors,
67
+ warnings,
68
+ successes,
69
+ hasError,
70
+ hasWarning,
71
+ hasSuccess,
72
+ } = useSyRadioGroupValidation(props, model, focused)
90
73
 
91
- // Synchronisation des messages externes
92
- watch(() => props.errorMessages, value => (validation.errors.value = value || []), { immediate: true })
93
- watch(() => props.warningMessages, value => (validation.warnings.value = value || []), { immediate: true })
94
- watch(() => props.successMessages, value => (validation.successes.value = value || []), { immediate: true })
74
+ // Intégration avec le système de validation du formulaire
75
+ useValidatable(validateOnSubmit)
95
76
 
96
- // Construction des règles de validation
97
- const defaultRules = computed<ValidationRule[]>(() =>
98
- props.required
99
- ? [{
100
- type: 'required',
101
- options: {
102
- message: `Le champ ${props.label || 'ce champ'} est requis.`,
103
- fieldIdentifier: props.label,
104
- },
105
- }]
106
- : [],
77
+ const hasMessages = computed(() =>
78
+ errors.value.length > 0 || warnings.value.length > 0 || successes.value.length > 0,
107
79
  )
108
80
 
109
- const validateField = async (value: PropertyKey | null) => {
110
- // const stringValue = value != null ? String(value) : null
111
-
112
- if (props.readonly) {
113
- validation.clearValidation()
114
- return true
115
- }
116
-
117
- if (value === null && !props.required) {
118
- validation.clearValidation()
119
- return true
120
- }
121
-
122
- const result = await validation.validateField(
123
- value,
124
- [...defaultRules.value, ...props.customRules],
125
- props.customWarningRules,
126
- props.customSuccessRules,
127
- )
128
- return !result.hasError
129
- }
130
-
131
- const validateOnSubmit = async () => {
132
- isSubmitted.value = true
133
- return await validateField(model.value)
134
- }
135
-
136
- const checkErrorOnBlur = () => {
137
- validateField(model.value)
138
- }
139
-
140
- watch(model, (newValue) => {
141
- if (!props.isValidateOnBlur) {
142
- validateField(newValue)
143
- }
144
- })
145
-
146
- const hasError = computed(() => validation.hasError.value)
147
- const hasWarning = computed(() => validation.hasWarning.value)
148
- const hasSuccess = computed(() => validation.hasSuccess.value)
149
-
150
- const errors = computed(() => validation.errors.value)
151
- const warnings = computed(() => validation.warnings.value)
152
- const displaySuccesses = computed(() => validation.displaySuccesses.value)
81
+ const showHelpTextAsMessage = computed(() => !!props.helpText && !hasMessages.value)
82
+ const showHelpTextBelow = computed(() => !!props.helpText && hasMessages.value && props.hideDetails !== true)
153
83
 
154
84
  const getAriaChecked = (value: PropertyKey) => {
155
85
  return model.value === value ? 'true' : 'false'
@@ -168,6 +98,9 @@
168
98
  return undefined
169
99
  })
170
100
 
101
+ // Workaround Vuetify: Vuetify ajoute aria-disabled="false" sur tous les radios non désactivés
102
+ // Ce n'est pas nécessaire car la spécification ARIA ne requiert pas aria-disabled="false"
103
+ // On supprime cet attribut pour éviter le bruit dans les lecteurs d'écran
171
104
  const removeAriaAttributesForRadio = () => {
172
105
  nextTick(() => {
173
106
  if (radioGroupRef.value) {
@@ -182,22 +115,14 @@
182
115
  // Appliquer la correction lors du montage et de la mise à jour du composant
183
116
  onMounted(() => {
184
117
  removeAriaAttributesForRadio()
185
- if (!props.isValidateOnBlur && !props.required) {
186
- validateField(model.value)
187
- }
188
118
  })
189
119
 
190
120
  onUpdated(() => {
191
121
  removeAriaAttributesForRadio()
192
122
  })
193
123
 
194
- // Intégration avec le système de validation du formulaire
195
- useValidatable(validateOnSubmit)
196
-
197
124
  defineExpose({
198
- validation,
199
125
  validateOnSubmit,
200
- checkErrorOnBlur,
201
126
  })
202
127
 
203
128
  </script>
@@ -220,7 +145,7 @@
220
145
  :color="props.color"
221
146
  :disabled="props.disabled"
222
147
  :readonly="props.readonly"
223
- :hide-details="props.hideDetails"
148
+ :hide-details="showHelpTextAsMessage ? false : props.hideDetails"
224
149
  :density="props.density"
225
150
  :error="hasError"
226
151
  :error-messages="hasError ? errors : undefined"
@@ -233,7 +158,8 @@
233
158
  role="radio"
234
159
  :label="opt.label"
235
160
  :aria-checked="getAriaChecked(opt.value)"
236
- @blur="checkErrorOnBlur"
161
+ @focus="focused = true"
162
+ @blur="focused = false"
237
163
  />
238
164
  <template
239
165
  v-if="$slots.label"
@@ -256,17 +182,32 @@
256
182
  }}</span>.
257
183
  </span>
258
184
  <template
259
- v-if="!hasError && (hasWarning || hasSuccess)"
185
+ v-if="(!hasError && (hasWarning || hasSuccess) && props.showSuccessMessages) || showHelpTextAsMessage"
260
186
  #details
261
187
  >
262
188
  <div class="v-input__details sy-radio-group__messages">
263
189
  <VMessages
264
- :active="hasWarning || (hasSuccess && displaySuccesses.length > 0)"
265
- :messages="hasWarning ? warnings : displaySuccesses"
190
+ v-if="!hasError && (hasWarning || hasSuccess) && props.showSuccessMessages"
191
+ :active="hasWarning || (hasSuccess && successes.length > 0)"
192
+ :messages="hasWarning ? warnings : successes"
266
193
  />
194
+ <div
195
+ v-if="showHelpTextAsMessage"
196
+ class="sy-radio-group__help-text"
197
+ :class="{ 'text-disabled': props.disabled }"
198
+ >
199
+ {{ props.helpText }}
200
+ </div>
267
201
  </div>
268
202
  </template>
269
203
  </v-radio-group>
204
+ <div
205
+ v-if="showHelpTextBelow"
206
+ class="help-text-below px-1 mt-1"
207
+ :class="{ 'text-disabled': props.disabled }"
208
+ >
209
+ {{ props.helpText }}
210
+ </div>
270
211
  </template>
271
212
 
272
213
  <style scoped>
@@ -280,21 +221,31 @@
280
221
  color: rgb(var(--v-theme-error));
281
222
  }
282
223
 
283
- .sy-radio-group__messages {
224
+ :deep(.sy-radio-group__messages) {
284
225
  align-items: flex-start;
226
+ margin-top: -22px !important;
227
+ }
228
+
229
+ .sy-radio-group__help-text {
230
+ font-size: 0.75rem;
231
+ color: rgba(var(--v-theme-on-surface), var(--v-medium-emphasis-opacity));
285
232
  }
286
233
 
287
234
  .sb-show-main.sb-main-centered #storybook-root {
288
235
  margin: none !important;
289
236
  }
290
237
 
238
+ :deep(.v-messages) {
239
+ opacity: 1 !important;
240
+ }
241
+
291
242
  .warning-field {
292
243
  :deep(.v-messages__message) {
293
- color: rgb(var(--v-theme-warning)) !important;
244
+ color: rgb(var(--v-theme-onWarningVariant)) !important;
294
245
  }
295
246
 
296
247
  :deep(.v-selection-control__input) {
297
- color: rgb(var(--v-theme-warning));
248
+ color: rgb(var(--v-theme-onWarningVariant));
298
249
  }
299
250
  }
300
251
 
@@ -306,7 +257,7 @@
306
257
 
307
258
  .success-field {
308
259
  :deep(.v-messages__message) {
309
- color: rgb(var(--v-theme-success)) !important;
260
+ color: rgb(var(--v-theme-onSuccessVariant)) !important;
310
261
  }
311
262
  }
312
263
 
@@ -314,6 +265,10 @@
314
265
  animation: sy-messages-in 0.25s cubic-bezier(0.4, 0, 0.2, 1) !important;
315
266
  }
316
267
 
268
+ :deep(.v-label) {
269
+ margin-inline-start: 0 !important;
270
+ }
271
+
317
272
  @keyframes sy-messages-in {
318
273
  from {
319
274
  opacity: 0;
@@ -0,0 +1,127 @@
1
+ import { computed, ref, toRef, type ComputedRef, type Ref } from 'vue'
2
+ import { useValidation, type FieldValidationProps, type ValidationRule, type VuetifyValidationRule } from '@/composables/unifyValidation/useValidation'
3
+
4
+ export interface SyRadioGroupValidationProps extends FieldValidationProps {
5
+ modelValue?: PropertyKey | null
6
+ required?: boolean
7
+ readonly?: boolean
8
+ disabled?: boolean
9
+ customRules?: ValidationRule[]
10
+ customWarningRules?: ValidationRule[]
11
+ customSuccessRules?: ValidationRule[]
12
+ isValidateOnBlur?: boolean
13
+ showSuccessMessages?: boolean
14
+ useVuetifyValidation?: boolean
15
+ rules?: VuetifyValidationRule[]
16
+ errorMessages?: string[] | null
17
+ warningMessages?: string[] | null
18
+ successMessages?: string[] | null
19
+ hasError?: boolean
20
+ hasWarning?: boolean
21
+ hasSuccess?: boolean
22
+ maxErrors?: number
23
+ disableErrorHandling?: boolean
24
+ fieldIdentifier?: string
25
+ }
26
+
27
+ export interface UseSyRadioGroupValidationReturn {
28
+ validate: () => Promise<boolean>
29
+ validateOnSubmit: () => Promise<boolean>
30
+ errors: Ref<string[]>
31
+ warnings: Ref<string[]>
32
+ successes: Ref<string[]>
33
+ hasError: ComputedRef<boolean | undefined>
34
+ hasWarning: ComputedRef<boolean | undefined>
35
+ hasSuccess: ComputedRef<boolean | undefined>
36
+ defaultRules: ComputedRef<ValidationRule[]>
37
+ focused: Ref<boolean>
38
+ }
39
+
40
+ /**
41
+ * Composable pour gérer la validation du composant SyRadioGroup
42
+ *
43
+ * Ce composable encapsule toute la logique de validation spécifique aux groupes de radios :
44
+ * - Validation required avec message personnalisé
45
+ * - Validation immédiate à la sélection (isValidateOnBlur = false par défaut)
46
+ * - Support des customRules, customWarningRules, customSuccessRules
47
+ * - Désactivation automatique des messages de succès en mode Vuetify
48
+ * - Intégration avec useValidation du design system
49
+ *
50
+ * @example
51
+ * const { validate, errors, hasError, defaultRules } = useSyRadioGroupValidation(props, model)
52
+ */
53
+ export function useSyRadioGroupValidation(
54
+ props: SyRadioGroupValidationProps,
55
+ model: Ref<PropertyKey | null>,
56
+ focused?: Ref<boolean>,
57
+ ): UseSyRadioGroupValidationReturn {
58
+ // Utiliser la variable focused passée en paramètre, sinon en créer une locale
59
+ const focusedRef = focused || ref(false)
60
+
61
+ // Construction des règles de validation par défaut (required)
62
+ const defaultRules = computed<ValidationRule[]>(() =>
63
+ props.required
64
+ ? [{
65
+ type: 'required',
66
+ options: {
67
+ message: `Le champ ${props.fieldIdentifier || props.label || 'ce champ'} est requis.`,
68
+ fieldIdentifier: props.label,
69
+ },
70
+ }]
71
+ : [],
72
+ )
73
+
74
+ // Vuetify ne gère pas les messages de succès, on désactive automatiquement en mode Vuetify
75
+ const effectiveShowSuccessMessages = computed(() =>
76
+ props.useVuetifyValidation ? false : (props.showSuccessMessages ?? true),
77
+ )
78
+
79
+ const {
80
+ validate,
81
+ errors,
82
+ warnings,
83
+ successes,
84
+ hasError,
85
+ hasWarning,
86
+ hasSuccess,
87
+ } = useValidation({
88
+ modelValue: model,
89
+ readonly: toRef(() => props.readonly ?? false),
90
+ disabled: toRef(() => props.disabled ?? false),
91
+ required: toRef(() => props.required ?? false),
92
+ isValidateOnBlur: toRef(() => props.isValidateOnBlur ?? false), // false par défaut pour les radios
93
+ showSuccessMessages: effectiveShowSuccessMessages,
94
+ disableErrorHandling: toRef(() => props.disableErrorHandling ?? false),
95
+ useVuetifyValidation: toRef(() => props.useVuetifyValidation ?? false),
96
+ label: toRef(() => props.label ?? ''),
97
+ rules: toRef(() => props.rules),
98
+ customRules: computed(() => [...defaultRules.value, ...(props.customRules || [])]),
99
+ customWarningRules: toRef(() => props.customWarningRules ?? []),
100
+ customSuccessRules: toRef(() => props.customSuccessRules ?? []),
101
+ errorMessages: toRef(() => props.errorMessages ?? null),
102
+ warningMessages: toRef(() => props.warningMessages ?? null),
103
+ successMessages: toRef(() => props.successMessages ?? null),
104
+ hasErrorProp: toRef(() => props.hasError ?? false),
105
+ hasWarningProp: toRef(() => props.hasWarning ?? false),
106
+ hasSuccessProp: toRef(() => props.hasSuccess ?? false),
107
+ maxErrors: toRef(() => props.maxErrors ?? 1),
108
+ focused: focusedRef,
109
+ })
110
+
111
+ const validateOnSubmit = async (): Promise<boolean> => {
112
+ return await validate()
113
+ }
114
+
115
+ return {
116
+ validate,
117
+ validateOnSubmit,
118
+ errors,
119
+ warnings,
120
+ successes,
121
+ hasError,
122
+ hasWarning,
123
+ hasSuccess,
124
+ defaultRules,
125
+ focused: focusedRef,
126
+ }
127
+ }
@@ -6,7 +6,7 @@ import { axe } from 'vitest-axe'
6
6
  import { assertNoA11yViolations } from '@tests/unit/accessibility/axeUtils'
7
7
  import SyRadioGroup from '../SyRadioGroup.vue'
8
8
 
9
- // Scénario daccessibilité : groupe de boutons radio requis avec label.
9
+ // Scénarios d'accessibilité pour le groupe de boutons radio
10
10
 
11
11
  describe('SyRadioGroup – accessibility (axe)', () => {
12
12
  it('has no obvious axe violations for required radio group', async () => {
@@ -27,4 +27,96 @@ describe('SyRadioGroup – accessibility (axe)', () => {
27
27
  ignoreRules: ['region'],
28
28
  })
29
29
  })
30
+
31
+ it('has no obvious axe violations with error state', async () => {
32
+ const wrapper = mount(SyRadioGroup, {
33
+ props: {
34
+ label: 'Choisissez une option',
35
+ modelValue: null,
36
+ required: true,
37
+ errorMessages: ['Veuillez sélectionner une option'],
38
+ options: [
39
+ { label: 'Option A', value: 'A' },
40
+ { label: 'Option B', value: 'B' },
41
+ ],
42
+ },
43
+ })
44
+
45
+ const results = await axe(wrapper.element as HTMLElement)
46
+ assertNoA11yViolations(results, 'SyRadioGroup – error state', {
47
+ ignoreRules: ['region'],
48
+ })
49
+ })
50
+
51
+ it('has no obvious axe violations when disabled', async () => {
52
+ const wrapper = mount(SyRadioGroup, {
53
+ props: {
54
+ label: 'Choisissez une option',
55
+ modelValue: 'A',
56
+ disabled: true,
57
+ options: [
58
+ { label: 'Option A', value: 'A' },
59
+ { label: 'Option B', value: 'B' },
60
+ ],
61
+ },
62
+ })
63
+
64
+ const results = await axe(wrapper.element as HTMLElement)
65
+ assertNoA11yViolations(results, 'SyRadioGroup – disabled state', {
66
+ ignoreRules: ['region'],
67
+ })
68
+ })
69
+
70
+ it('has no obvious axe violations with aria-label', async () => {
71
+ const wrapper = mount(SyRadioGroup, {
72
+ props: {
73
+ ariaLabel: 'Groupe de boutons radio personnalisé',
74
+ label: 'Radio avec aria-label',
75
+ modelValue: null,
76
+ options: [
77
+ { label: 'Option A', value: 'A' },
78
+ { label: 'Option B', value: 'B' },
79
+ ],
80
+ },
81
+ })
82
+
83
+ const results = await axe(wrapper.element as HTMLElement)
84
+ assertNoA11yViolations(results, 'SyRadioGroup – avec aria-label', {
85
+ ignoreRules: ['region'],
86
+ })
87
+ })
88
+
89
+ it('has no obvious axe violations with aria-labelledby', async () => {
90
+ const container = document.createElement('div')
91
+ document.body.appendChild(container)
92
+
93
+ const heading = document.createElement('h2')
94
+ heading.id = 'custom-heading'
95
+ heading.textContent = 'Titre du groupe'
96
+ container.appendChild(heading)
97
+
98
+ const wrapper = mount(SyRadioGroup, {
99
+ attachTo: container,
100
+ props: {
101
+ ariaLabelledby: 'custom-heading',
102
+ label: 'Radio avec aria-labelledby',
103
+ modelValue: null,
104
+ options: [
105
+ { label: 'Option A', value: 'A' },
106
+ { label: 'Option B', value: 'B' },
107
+ ],
108
+ },
109
+ })
110
+
111
+ try {
112
+ const results = await axe(container)
113
+ assertNoA11yViolations(results, 'SyRadioGroup – avec aria-labelledby', {
114
+ ignoreRules: ['region'],
115
+ })
116
+ }
117
+ finally {
118
+ wrapper.unmount()
119
+ document.body.removeChild(container)
120
+ }
121
+ })
30
122
  })