@cnamts/synapse 1.0.23 → 1.0.24

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 (292) hide show
  1. package/dist/AutocompleteFilter-BWLR3U7W.js +114 -0
  2. package/dist/AutocompleteFilter-D9jzRzAL.cjs +1 -0
  3. package/dist/{DateFilter-Dc-gSGwk.js → DateFilter-BpwFexzi.js} +1 -1
  4. package/dist/DateFilter-DTUl8hb1.cjs +1 -0
  5. package/dist/{NumberFilter-vP38Wp6j.js → NumberFilter-Bz_NTdX9.js} +3 -3
  6. package/dist/NumberFilter-MAEojdk0.cjs +1 -0
  7. package/dist/PeriodFilter-CC4WgIhl.cjs +1 -0
  8. package/dist/{PeriodFilter-Ba1uYUnT.js → PeriodFilter-DX_wy9g-.js} +1 -1
  9. package/dist/SelectFilter-BR3fvl-a.cjs +1 -0
  10. package/dist/SelectFilter-xqiPtPgX.js +135 -0
  11. package/dist/{TextFilter-B84dpnoq.js → TextFilter-BBl3JFqK.js} +7 -7
  12. package/dist/TextFilter-CCfYFl5F.cjs +1 -0
  13. package/dist/apLightTheme-CFSRrjv2.cjs +1 -0
  14. package/dist/apLightTheme-D1P4jcD0.js +1231 -0
  15. package/dist/components/Amelipro/AmeliproAutoCompleteField/AmeliproAutoCompleteField.d.ts +7022 -9616
  16. package/dist/components/Amelipro/AmeliproCarousel/AmeliproCarousel.d.ts +2 -2
  17. package/dist/components/Amelipro/AmeliproIconBtn/AmeliproIconBtn.d.ts +2 -2
  18. package/dist/components/Amelipro/AmeliproPostalAddressField/AmeliproPostalAddressCityRow/AmeliproPostalAddressCityRow.d.ts +40 -40
  19. package/dist/components/Amelipro/AmeliproPostalAddressField/AmeliproPostalAddressField.d.ts +60 -60
  20. package/dist/components/Amelipro/AmeliproSelect/AmeliproSelect.d.ts +7168 -9762
  21. package/dist/components/Amelipro/AmeliproStepper/AmeliproStepper.d.ts +2 -2
  22. package/dist/components/Amelipro/AmeliproTabs/AmeliproTabs.d.ts +7501 -10095
  23. package/dist/components/Amelipro/AmeliproTextArea/AmeliproTextArea.d.ts +21 -21
  24. package/dist/components/Amelipro/AmeliproTextField/AmeliproTextField.d.ts +41 -41
  25. package/dist/components/Amelipro/StructureMenu/StructureTabs/StructureTabs.d.ts +2 -2
  26. package/dist/components/CookiesSelection/CookiesInformation/CookiesInformation.d.ts +20 -498
  27. package/dist/components/Customs/Selects/SyAutocomplete/SyAutocomplete.d.ts +108 -146
  28. package/dist/components/Customs/Selects/SyInputSelect/SyInputSelect.d.ts +5 -5
  29. package/dist/components/Customs/Selects/SySelect/SySelect.d.ts +12 -16
  30. package/dist/components/Customs/SyCheckBoxGroup/SyCheckBoxGroup.d.ts +8 -8
  31. package/dist/components/Customs/SyCheckbox/SyCheckbox.d.ts +28 -506
  32. package/dist/components/Customs/SyRadioGroup/SyRadioGroup.d.ts +28 -506
  33. package/dist/components/Customs/SyTextField/SyTextField.d.ts +65 -85
  34. package/dist/components/DatePicker/CalendarMode/DatePicker.d.ts +627 -771
  35. package/dist/components/DatePicker/ComplexDatePicker/ComplexDatePicker.d.ts +315 -402
  36. package/dist/components/DatePicker/DateTextInput/DateTextInput.d.ts +112 -155
  37. package/dist/components/DatePicker/composables/index.d.ts +1 -0
  38. package/dist/components/DatePicker/composables/useDatePickerFocusTrap.d.ts +11 -0
  39. package/dist/components/DatePicker/composables/useDateTextField.d.ts +4 -4
  40. package/dist/components/DatePicker/composables/useDateValidation.d.ts +3 -3
  41. package/dist/components/DatePicker/composables/useInputBlurHandler.d.ts +2 -2
  42. package/dist/components/DatePicker/composables/useManualDateValidation.d.ts +2 -2
  43. package/dist/components/HeaderNavigationBar/HeaderNavigationBar.d.ts +4 -4
  44. package/dist/components/HeaderToolbar/HeaderToolbar.d.ts +20 -28
  45. package/dist/components/LunarCalendar/useLunarCalendarValidation.d.ts +3 -3
  46. package/dist/components/MonthPicker/MonthPicker.d.ts +86 -122
  47. package/dist/components/MonthPicker/MonthPickerText/MonthPickerInput.d.ts +85 -121
  48. package/dist/components/NirField/NirField.d.ts +206 -270
  49. package/dist/components/NirField/locales.d.ts +10 -10
  50. package/dist/components/NirField/useNirValidation.d.ts +64 -0
  51. package/dist/components/PasswordField/PasswordField.d.ts +8 -9
  52. package/dist/components/PeriodField/PeriodField.d.ts +1352 -1640
  53. package/dist/components/PhoneField/PhoneField.d.ts +88 -124
  54. package/dist/components/RangeField/RangeSlider/RangeSlider.d.ts +12 -12
  55. package/dist/components/SyTextArea/SyTextArea.d.ts +34 -14
  56. package/dist/components/SyTextArea/useDefaultValidationRules.d.ts +11 -0
  57. package/dist/components/Tables/SyServerTable/SyServerTable.d.ts +9 -6
  58. package/dist/components/Tables/SyTable/SyTable.d.ts +9 -6
  59. package/dist/components/Tables/common/SyTableFilter.d.ts +2 -3
  60. package/dist/components/Tables/common/SyTablePagination.d.ts +17 -19
  61. package/dist/components/Tables/common/filters/AutocompleteFilter.d.ts +120 -0
  62. package/dist/components/Tables/common/filters/locales.d.ts +0 -1
  63. package/dist/components/Tables/common/types.d.ts +19 -3
  64. package/dist/components/Tables/common/useClickableTableRow.d.ts +17 -0
  65. package/dist/components/Tables/common/usePagination.d.ts +3 -1
  66. package/dist/components/Tables/common/usePinnedColumns.d.ts +31 -0
  67. package/dist/components/Tables/common/useTableHeaders.d.ts +2 -0
  68. package/dist/components/Tables/common/useTableRowCheckboxAccessibility.d.ts +5 -0
  69. package/dist/components/UploadWorkflow/UploadWorkflow.d.ts +6 -6
  70. package/dist/composables/date/useDatePickerAccessibility.d.ts +1 -1
  71. package/dist/composables/rules/useFieldValidation.d.ts +4 -4
  72. package/dist/composables/unifyValidation/useCustomValidation.d.ts +8 -0
  73. package/dist/composables/unifyValidation/useValidation.d.ts +102 -0
  74. package/dist/composables/unifyValidation/useVuetifyValidation.d.ts +18 -0
  75. package/dist/composables/useFormFieldErrorHandling.d.ts +2 -2
  76. package/dist/composables/validation/useFormValidation.d.ts +11 -2
  77. package/dist/composables/validation/useValidation.d.ts +15 -9
  78. package/dist/design-system-v3.d.ts +2 -0
  79. package/dist/design-system-v3.js +186 -187
  80. package/dist/design-system-v3.umd.cjs +1 -1066
  81. package/dist/{main-aLKwdMi1.js → main-BtTqyn4z.js} +16434 -15672
  82. package/dist/main-C1e3eoxd.cjs +1067 -0
  83. package/dist/main.d.ts +0 -1
  84. package/dist/synapse.css +1 -0
  85. package/dist/tooth-11-D3sLWv2n.cjs +1 -0
  86. package/dist/tooth-12-CXrLuH03.cjs +1 -0
  87. package/dist/tooth-13-BSfo5fpT.cjs +1 -0
  88. package/dist/tooth-14-DMzulx0h.cjs +1 -0
  89. package/dist/tooth-15-BKRFVi-9.cjs +1 -0
  90. package/dist/tooth-16-CpuxAbuM.cjs +1 -0
  91. package/dist/tooth-17-BPoahUdg.cjs +1 -0
  92. package/dist/tooth-18-DhHJz8sy.cjs +1 -0
  93. package/dist/tooth-21-Dgd5hn_X.cjs +1 -0
  94. package/dist/tooth-22-C2Tn19sB.cjs +1 -0
  95. package/dist/tooth-23-C9uaaSGb.cjs +1 -0
  96. package/dist/tooth-24-BrK9UGpf.cjs +1 -0
  97. package/dist/tooth-25-CE_EfGNp.cjs +1 -0
  98. package/dist/tooth-26-Ctv4i9Fy.cjs +1 -0
  99. package/dist/tooth-27-C5J7JkWM.cjs +1 -0
  100. package/dist/tooth-28-Z9oWqjo0.cjs +1 -0
  101. package/dist/tooth-31-BrYqmkTi.cjs +1 -0
  102. package/dist/tooth-32-BNNR0oCZ.cjs +1 -0
  103. package/dist/tooth-33-DuxvqO2J.cjs +1 -0
  104. package/dist/tooth-34-BCSCXMB6.cjs +1 -0
  105. package/dist/tooth-35-BLUXkX88.cjs +1 -0
  106. package/dist/tooth-36-IrKHYqlA.cjs +1 -0
  107. package/dist/tooth-37-BYqpdMwo.cjs +1 -0
  108. package/dist/tooth-38-B_eNXXdu.cjs +1 -0
  109. package/dist/tooth-41-Ddva4Ot8.cjs +1 -0
  110. package/dist/tooth-42-szcDqlM0.cjs +1 -0
  111. package/dist/tooth-43-B3ka6rQm.cjs +1 -0
  112. package/dist/tooth-44-CazyQucj.cjs +1 -0
  113. package/dist/tooth-45-B4HQtc8n.cjs +1 -0
  114. package/dist/tooth-46-BPM40gbG.cjs +1 -0
  115. package/dist/tooth-47-Dvr20dlh.cjs +1 -0
  116. package/dist/tooth-48-Bd8ljGsF.cjs +1 -0
  117. package/dist/tooth-51-OBpwCOF3.cjs +1 -0
  118. package/dist/tooth-52-aKxyHcmq.cjs +1 -0
  119. package/dist/tooth-53-vCwJjTOc.cjs +1 -0
  120. package/dist/tooth-54-DsWu2iFy.cjs +1 -0
  121. package/dist/tooth-55-BxC1X2Dn.cjs +1 -0
  122. package/dist/tooth-61-BbLvxMQi.cjs +1 -0
  123. package/dist/tooth-62-CmTkWczP.cjs +1 -0
  124. package/dist/tooth-63-DI7l_2qI.cjs +1 -0
  125. package/dist/tooth-64-B21sOsJh.cjs +1 -0
  126. package/dist/tooth-65-D2ZC2VEr.cjs +1 -0
  127. package/dist/tooth-71-D473PPO5.cjs +1 -0
  128. package/dist/tooth-72-Drh1wnNu.cjs +1 -0
  129. package/dist/tooth-73-DzlwYI23.cjs +1 -0
  130. package/dist/tooth-74-8aGvcZPg.cjs +1 -0
  131. package/dist/tooth-75-BFK7At_r.cjs +1 -0
  132. package/dist/tooth-81-BZmR-I0M.cjs +1 -0
  133. package/dist/tooth-82-euVfUUZV.cjs +1 -0
  134. package/dist/tooth-83-KV010j64.cjs +1 -0
  135. package/dist/tooth-84-BBg1RjhZ.cjs +1 -0
  136. package/dist/tooth-85-Cr-kc1wM.cjs +1 -0
  137. package/dist/vuetifyConfig.js +561 -0
  138. package/dist/vuetifyConfig.umd.cjs +1 -0
  139. package/package.json +10 -4
  140. package/src/assets/overrides/_btns.scss +0 -6
  141. package/src/assets/overrides/_icons.scss +9 -1
  142. package/src/assets/overrides/_typography.scss +0 -10
  143. package/src/components/Amelipro/AmeliproAutoCompleteField/__tests__/__snapshots__/AmeliproAutoCompleteField.spec.ts.snap +2 -2
  144. package/src/components/Amelipro/AmeliproHeader/AmeliproHeaderBar/AmeliproHeaderBrandSection/__tests__/__snapshots__/AmeliproHeaderBrandSection.spec.ts.snap +1 -1
  145. package/src/components/Amelipro/AmeliproTextArea/__tests__/__snapshots__/AmeliproTextArea.spec.ts.snap +2 -2
  146. package/src/components/Captcha/accessibilite/Accessibility.mdx +86 -8
  147. package/src/components/Captcha/tests/__snapshots__/Captcha.spec.ts.snap +12 -12
  148. package/src/components/ChipList/ChipList.stories.ts +0 -15
  149. package/src/components/ChipList/ChipList.vue +5 -1
  150. package/src/components/ChipList/accessibilite/Accessibility.mdx +83 -10
  151. package/src/components/ChipList/tests/ChipList.a11y.spec.ts +41 -0
  152. package/src/components/Customs/Selects/SelectBtnField/accessibilite/Accessibility.mdx +0 -9
  153. package/src/components/Customs/Selects/SyAutocomplete/SyAutocomplete.vue +22 -5
  154. package/src/components/Customs/Selects/SyAutocomplete/tests/SyAutocomplete.spec.ts +143 -0
  155. package/src/components/Customs/Selects/SyAutocomplete/utils/ariaManager.ts +14 -10
  156. package/src/components/Customs/Selects/SyInputSelect/SyInputSelect.stories.ts +4 -4
  157. package/src/components/Customs/Selects/SyInputSelect/SyInputSelect.vue +8 -9
  158. package/src/components/Customs/Selects/SyInputSelect/tests/SyInputSelect.spec.ts +10 -10
  159. package/src/components/Customs/Selects/SySelect/SySelect.vue +14 -11
  160. package/src/components/Customs/Selects/SySelect/tests/SySelect.spec.ts +54 -0
  161. package/src/components/Customs/SyCheckBoxGroup/SyCheckBoxGroup.vue +6 -9
  162. package/src/components/Customs/SyCheckbox/SyCheckbox.stories.ts +10 -16
  163. package/src/components/Customs/SyCheckbox/SyCheckbox.vue +16 -11
  164. package/src/components/Customs/SyCheckbox/accessibilite/Accessibility.mdx +35 -0
  165. package/src/components/Customs/SyCheckbox/tests/SyCheckbox.a11y.spec.ts +134 -2
  166. package/src/components/Customs/SyForm/SyForm.stories.ts +31 -5
  167. package/src/components/Customs/SyRadioGroup/SyRadioGroup.vue +4 -7
  168. package/src/components/Customs/SyTextField/SyTextField.mdx +1 -1
  169. package/src/components/Customs/SyTextField/SyTextField.stories.ts +29 -27
  170. package/src/components/Customs/SyTextField/SyTextField.vue +154 -157
  171. package/src/components/Customs/SyTextField/tests/SyTextField.a11y.spec.ts +32 -0
  172. package/src/components/Customs/SyTextField/tests/SyTextField.spec.ts +120 -11
  173. package/src/components/DatePicker/CalendarMode/DatePicker.stories.ts +62 -58
  174. package/src/components/DatePicker/CalendarMode/DatePicker.vue +330 -223
  175. package/src/components/DatePicker/CalendarMode/accessibilite/Accessibility.mdx +82 -0
  176. package/src/components/DatePicker/CalendarMode/tests/DatePicker.a11y.spec.ts +141 -0
  177. package/src/components/DatePicker/ComplexDatePicker/ComplexDatePicker.stories.ts +2 -56
  178. package/src/components/DatePicker/ComplexDatePicker/ComplexDatePicker.vue +195 -159
  179. package/src/components/DatePicker/ComplexDatePicker/accessibilite/Accessibility.mdx +76 -0
  180. package/src/components/DatePicker/ComplexDatePicker/tests/ComplexDatePicker.spec.ts +10 -10
  181. package/src/components/DatePicker/DatePickerValidationExample/CalendarMode.stories.ts +8 -8
  182. package/src/components/DatePicker/DatePickerValidationExample/ComplexDatePicker.stories.ts +106 -8
  183. package/src/components/DatePicker/DatePickerValidationExample/DateTextInput.stories.ts +12 -11
  184. package/src/components/DatePicker/DatePickerValidationExample/MultiMode.stories.ts +12 -12
  185. package/src/components/DatePicker/DateTextInput/DateRange.stories.ts +0 -12
  186. package/src/components/DatePicker/DateTextInput/DateTextInput.vue +63 -57
  187. package/src/components/DatePicker/DateTextInput/NoCalendar.stories.ts +3 -0
  188. package/src/components/DatePicker/DateTextInput/accessibilite/Accessibility.mdx +66 -0
  189. package/src/components/DatePicker/DateTextInput/tests/DateTextInput.spec.ts +52 -1
  190. package/src/components/DatePicker/composables/index.ts +1 -0
  191. package/src/components/DatePicker/composables/tests/useCalendarKeyboardNavigation.spec.ts +109 -65
  192. package/src/components/DatePicker/composables/tests/useDatePickerFocusTrap.spec.ts +138 -0
  193. package/src/components/DatePicker/composables/tests/useDateValidation.spec.ts +74 -18
  194. package/src/components/DatePicker/composables/tests/useInputBlurHandler.spec.ts +39 -0
  195. package/src/components/DatePicker/composables/tests/useManualDateValidation.spec.ts +91 -0
  196. package/src/components/DatePicker/composables/useCalendarKeyboardNavigation.ts +442 -36
  197. package/src/components/DatePicker/composables/useDatePickerFocusTrap.ts +92 -0
  198. package/src/components/DatePicker/composables/useDateTextField.ts +7 -6
  199. package/src/components/DatePicker/composables/useDateValidation.ts +36 -35
  200. package/src/components/DatePicker/composables/useInputBlurHandler.ts +3 -3
  201. package/src/components/DatePicker/composables/useManualDateValidation.ts +6 -2
  202. package/src/components/DiacriticPicker/accessibilite/Accessibility.mdx +76 -8
  203. package/src/components/HeaderBar/HeaderBar.stories.ts +14 -2
  204. package/src/components/Logo/accessibilite/Accessibility.mdx +73 -11
  205. package/src/components/LogoBrandSection/accessibilite/Accessibility.mdx +85 -9
  206. package/src/components/LunarCalendar/tests/LunarCalendar.spec.ts +3 -1
  207. package/src/components/LunarCalendar/useLunarCalendarValidation.ts +4 -5
  208. package/src/components/MonthPicker/tests/MonthPicker.spec.ts +2 -1
  209. package/src/components/MonthPicker/tests/__snapshots__/MonthPicker.spec.ts.snap +7 -7
  210. package/src/components/NirField/NirField.stories.ts +4 -0
  211. package/src/components/NirField/NirField.vue +64 -260
  212. package/src/components/NirField/accessibilite/Accessibility.mdx +2 -2
  213. package/src/components/NirField/locales.ts +1 -1
  214. package/src/components/NirField/tests/NirField.spec.ts +6 -0
  215. package/src/components/NirField/useNirValidation.ts +271 -0
  216. package/src/components/PasswordField/PasswordField.stories.ts +4 -4
  217. package/src/components/PasswordField/PasswordField.vue +18 -24
  218. package/src/components/PasswordField/tests/PasswordField.spec.ts +6 -3
  219. package/src/components/PeriodField/PeriodField.stories.ts +4 -4
  220. package/src/components/PeriodField/PeriodField.vue +57 -57
  221. package/src/components/PeriodField/__tests__/PeriodField.async.spec.ts +32 -0
  222. package/src/components/PeriodField/accessibilite/Accessibility.mdx +68 -8
  223. package/src/components/PeriodField/tests/PeriodField.spec.ts +28 -2
  224. package/src/components/PhoneField/PhoneField.vue +5 -6
  225. package/src/components/PhoneField/tests/PhoneField.spec.ts +1 -0
  226. package/src/components/RangeField/RangeField.vue +6 -0
  227. package/src/components/SyTextArea/SyTextArea.stories.ts +138 -2
  228. package/src/components/SyTextArea/SyTextArea.vue +53 -23
  229. package/src/components/SyTextArea/tests/SyTextArea.spec.ts +126 -3
  230. package/src/components/SyTextArea/useDefaultValidationRules.ts +74 -0
  231. package/src/components/Tables/SyServerTable/SyServerTable.mdx +25 -0
  232. package/src/components/Tables/SyServerTable/SyServerTable.stories.ts +673 -1
  233. package/src/components/Tables/SyServerTable/SyServerTable.vue +148 -91
  234. package/src/components/Tables/SyServerTable/tests/SyServerTable.a11y.spec.ts +58 -0
  235. package/src/components/Tables/SyServerTable/tests/SyServerTable.spec.ts +122 -0
  236. package/src/components/Tables/SyTable/SyTable.mdx +25 -0
  237. package/src/components/Tables/SyTable/SyTable.stories.ts +452 -1
  238. package/src/components/Tables/SyTable/SyTable.vue +130 -56
  239. package/src/components/Tables/SyTable/tests/SyTable.a11y.spec.ts +57 -0
  240. package/src/components/Tables/SyTable/tests/SyTable.spec.ts +108 -0
  241. package/src/components/Tables/common/SyTableFilter.vue +22 -2
  242. package/src/components/Tables/common/TableHeader.vue +5 -1
  243. package/src/components/Tables/common/filters/AutocompleteFilter.vue +160 -0
  244. package/src/components/Tables/common/filters/NumberFilter.vue +1 -1
  245. package/src/components/Tables/common/filters/SelectFilter.vue +10 -11
  246. package/src/components/Tables/common/filters/TextFilter.vue +1 -1
  247. package/src/components/Tables/common/filters/getFilterComponent.ts +8 -1
  248. package/src/components/Tables/common/filters/locales.ts +0 -1
  249. package/src/components/Tables/common/filters/tests/AutocompleteFilter.a11y.spec.ts +110 -0
  250. package/src/components/Tables/common/filters/tests/AutocompleteFilter.spec.ts +203 -0
  251. package/src/components/Tables/common/filters/tests/SelectFilter.a11y.spec.ts +104 -0
  252. package/src/components/Tables/common/filters/tests/SelectFilter.spec.ts +152 -16
  253. package/src/components/Tables/common/tableFilterUtils.ts +3 -0
  254. package/src/components/Tables/common/tableStyles.scss +48 -4
  255. package/src/components/Tables/common/tests/filterByRange.spec.ts +2 -1
  256. package/src/components/Tables/common/types.ts +13 -4
  257. package/src/components/Tables/common/useClickableTableRow.ts +103 -0
  258. package/src/components/Tables/common/usePagination.ts +13 -0
  259. package/src/components/Tables/common/usePinnedColumns.ts +237 -0
  260. package/src/components/Tables/common/useTableHeaders.ts +3 -3
  261. package/src/components/Tables/common/useTableRowCheckboxAccessibility.ts +41 -0
  262. package/src/composables/date/tests/useDatePickerAccessibility.spec.ts +2 -6
  263. package/src/composables/date/useDatePickerAccessibility.ts +42 -207
  264. package/src/composables/rules/tests/useFieldValidation.spec.ts +120 -120
  265. package/src/composables/rules/useFieldValidation.ts +34 -17
  266. package/src/composables/unifyValidation/tests/useCustomValidation.spec.ts +601 -0
  267. package/src/composables/unifyValidation/tests/useValidation.spec.ts +2048 -0
  268. package/src/composables/unifyValidation/tests/useVuetifyValidation.spec.ts +184 -0
  269. package/src/composables/unifyValidation/useCustomValidation.ts +95 -0
  270. package/src/composables/unifyValidation/useValidation.ts +190 -0
  271. package/src/composables/unifyValidation/useVuetifyValidation.ts +54 -0
  272. package/src/composables/useFormFieldErrorHandling.ts +4 -7
  273. package/src/composables/validation/tests/useFormValidation.spec.ts +14 -0
  274. package/src/composables/validation/tests/useValidation.spec.ts +116 -21
  275. package/src/composables/validation/useFormValidation.ts +20 -13
  276. package/src/composables/validation/useValidatable.ts +8 -1
  277. package/src/composables/validation/useValidation.ts +135 -99
  278. package/src/composantsVuetify/Introduction.mdx +48 -0
  279. package/src/composantsVuetify/VBtn/VBtn.mdx +72 -0
  280. package/src/composantsVuetify/VBtn/v-btn.stories.ts +121 -0
  281. package/src/composantsVuetify/VTooltip/VTooltip.mdx +32 -0
  282. package/src/composantsVuetify/VTooltip/v-tooltip.stories.ts +95 -0
  283. package/src/designTokens/tokens/cnam/cnamSemantic.ts +2 -2
  284. package/src/main.ts +0 -2
  285. package/src/stories/Components/Components.stories.ts +74 -9
  286. package/src/stories/Demarrer/Accueil.stories.ts +3 -3
  287. package/src/stories/GuideDuDev/Amelipro.mdx +15 -0
  288. package/src/stories/GuideDuDev/Amelipro.stories.ts +209 -0
  289. package/src/stories/GuideDuDev/vuetifyOptions.mdx +3 -3
  290. package/dist/SelectFilter-BioGT6Nn.js +0 -136
  291. package/dist/style.css +0 -1
  292. package/src/components/DatePicker/Accessibilite.mdx +0 -14
@@ -102,9 +102,6 @@
102
102
  const isSubmitted = ref(false)
103
103
 
104
104
  const validation = useValidation({
105
- customRules: props.customRules,
106
- warningRules: props.customWarningRules,
107
- successRules: props.customSuccessRules,
108
105
  showSuccessMessages: props.showSuccessMessages,
109
106
  fieldIdentifier: props.label,
110
107
  disableErrorHandling: props.disableErrorHandling,
@@ -135,7 +132,7 @@
135
132
  : [],
136
133
  )
137
134
 
138
- const validateField = (value: boolean | null) => {
135
+ const validateField = async (value: boolean | null) => {
139
136
  // Si en lecture seule ou si la valeur est null et non requise, pas de validation
140
137
  if (props.readonly) {
141
138
  validation.clearValidation()
@@ -157,7 +154,7 @@
157
154
  }
158
155
 
159
156
  // Validation standard
160
- const result = validation.validateField(
157
+ const result = await validation.validateField(
161
158
  value,
162
159
  [...defaultRules.value, ...props.customRules],
163
160
  props.customWarningRules,
@@ -175,11 +172,11 @@
175
172
  validateField(model.value)
176
173
  }
177
174
 
178
- watch(model, (newValue) => {
175
+ watch(model, async (newValue) => {
179
176
  if (!props.isValidateOnBlur) {
180
177
  // Si le formulaire a été soumis et que la valeur change, on valide à nouveau
181
178
  if (isSubmitted.value) {
182
- const isValid = validateField(newValue)
179
+ const isValid = await validateField(newValue)
183
180
  if (isValid) {
184
181
  // La validation a réussi, effacer les erreurs
185
182
  validation.clearValidation()
@@ -187,7 +184,7 @@
187
184
  }
188
185
  else {
189
186
  // Comportement normal (hors soumission)
190
- const isValid = validateField(newValue)
187
+ const isValid = await validateField(newValue)
191
188
  // Si la validation réussit, s'assurer que les erreurs sont effacées
192
189
  if (isValid && validation.hasError.value) {
193
190
  validation.clearValidation()
@@ -206,7 +203,7 @@
206
203
 
207
204
  const ariaChecked = computed(() => {
208
205
  if (internalIndeterminate.value) return 'mixed'
209
- return model.value ? 'true' : undefined
206
+ return model.value ? 'true' : 'false'
210
207
  })
211
208
 
212
209
  const labelColor = computed(() => {
@@ -243,9 +240,17 @@
243
240
  const removeAriaAttributes = () => {
244
241
  nextTick(() => {
245
242
  if (checkboxRef.value) {
246
- const checkboxInput = checkboxRef.value.$el.querySelector('input[type="checkbox"][aria-disabled="false"]')
243
+ const checkboxInput = checkboxRef.value.$el?.querySelector('input[type="checkbox"]')
247
244
  if (checkboxInput) {
248
- checkboxInput.removeAttribute('aria-disabled')
245
+ // Supprimer aria-disabled="false" car il est redondant
246
+ if (checkboxInput.getAttribute('aria-disabled') === 'false') {
247
+ checkboxInput.removeAttribute('aria-disabled')
248
+ }
249
+ // Supprimer aria-checked natif de Vuetify pour éviter les conflits
250
+ // Notre composant gère aria-checked au niveau du wrapper VCheckbox
251
+ if (checkboxInput.hasAttribute('aria-checked')) {
252
+ checkboxInput.removeAttribute('aria-checked')
253
+ }
249
254
  }
250
255
  }
251
256
  })
@@ -79,6 +79,23 @@ import '@/stories/styles/shared.css';
79
79
  </div>
80
80
  </div>
81
81
 
82
+ <div className="aria-management-section">
83
+ <h2>Gestion des attributs ARIA</h2>
84
+ <p>
85
+ Pour garantir la conformité ARIA, le composant SyCheckbox gère activement les conflits potentiels
86
+ avec l'implémentation native de Vuetify :
87
+ </p>
88
+ <ul>
89
+ <li><strong>Suppression des attributs conflictuels</strong> : Les attributs ARIA natifs de Vuetify sont automatiquement supprimés de l'élément input pour éviter les doublons</li>
90
+ <li><strong>Gestion centralisée</strong> : L'attribut <code>aria-checked</code> est géré au niveau du composant SyCheckbox et non au niveau de l'input natif</li>
91
+ <li><strong>Conformité garantie</strong> : Cette approche garantit que l'attribut <code>aria-checked</code> reflète toujours l'état réel du composant (true, false ou mixed)</li>
92
+ </ul>
93
+ <p>
94
+ Cette gestion préventive évite les erreurs d'audit d'accessibilité qui pourraient survenir
95
+ lorsque plusieurs attributs ARIA contradictoires sont présents sur le même élément.
96
+ </p>
97
+ </div>
98
+
82
99
  <div className="tri-state-section">
83
100
  <h2>Fonctionnalité tri-état (indéterminé)</h2>
84
101
  <p>
@@ -190,6 +207,7 @@ import '@/stories/styles/shared.css';
190
207
 
191
208
  .criteria-section h2,
192
209
  .demo-section h2,
210
+ .aria-management-section h2,
193
211
  .tri-state-section h2,
194
212
  .parent-child-section h2,
195
213
  .best-practices h2,
@@ -243,6 +261,23 @@ import '@/stories/styles/shared.css';
243
261
  margin-bottom: 40px;
244
262
  }
245
263
 
264
+ .aria-management-section {
265
+ background-color: #fff9e6;
266
+ padding: 20px;
267
+ border-radius: 8px;
268
+ margin-bottom: 30px;
269
+ border-left: 5px solid #ff9800;
270
+ }
271
+
272
+ .aria-management-section ul {
273
+ padding-left: 20px;
274
+ }
275
+
276
+ .aria-management-section li {
277
+ margin-bottom: 8px;
278
+ line-height: 1.5;
279
+ }
280
+
246
281
  .tri-state-section,
247
282
  .parent-child-section {
248
283
  background-color: #f0f7ff;
@@ -6,21 +6,153 @@ import { axe } from 'vitest-axe'
6
6
  import { assertNoA11yViolations } from '@tests/unit/accessibility/axeUtils'
7
7
  import SyCheckbox from '../SyCheckbox.vue'
8
8
 
9
- // Scénario daccessibilité : case à cocher requise avec libellé.
9
+ // Scénarios d'accessibilité : cases à cocher avec différents états et configurations.
10
10
 
11
11
  describe('SyCheckbox – accessibility (axe)', () => {
12
12
  it('has no obvious axe violations for required checkbox with label', async () => {
13
13
  const wrapper = mount(SyCheckbox, {
14
14
  props: {
15
- label: 'Jaccepte les conditions générales',
15
+ label: 'J\'accepte les conditions générales',
16
16
  modelValue: false,
17
17
  required: true,
18
18
  },
19
19
  })
20
20
 
21
+ // Attendre que removeAriaAttributes soit exécuté
22
+ await wrapper.vm.$nextTick()
23
+ await wrapper.vm.$nextTick()
24
+
21
25
  const results = await axe(wrapper.element as HTMLElement)
22
26
  assertNoA11yViolations(results, 'SyCheckbox – required labelled checkbox', {
23
27
  ignoreRules: ['region'],
24
28
  })
25
29
  })
30
+
31
+ it('has no axe violations for indeterminate checkbox (aria-checked="mixed")', async () => {
32
+ const wrapper = mount(SyCheckbox, {
33
+ props: {
34
+ label: 'Sélection partielle',
35
+ modelValue: false,
36
+ indeterminate: true,
37
+ controlsIds: ['child-1', 'child-2', 'child-3'],
38
+ },
39
+ })
40
+
41
+ // Attendre que removeAriaAttributes soit exécuté
42
+ await wrapper.vm.$nextTick()
43
+ await wrapper.vm.$nextTick()
44
+
45
+ const results = await axe(wrapper.element as HTMLElement)
46
+ assertNoA11yViolations(results, 'SyCheckbox – indeterminate checkbox', {
47
+ ignoreRules: ['region'],
48
+ })
49
+ })
50
+
51
+ it('has no axe violations for checked checkbox with aria-checked="true"', async () => {
52
+ const wrapper = mount(SyCheckbox, {
53
+ props: {
54
+ label: 'Option activée',
55
+ modelValue: true,
56
+ required: false,
57
+ },
58
+ })
59
+
60
+ // Attendre que removeAriaAttributes soit exécuté
61
+ await wrapper.vm.$nextTick()
62
+ await wrapper.vm.$nextTick()
63
+
64
+ const results = await axe(wrapper.element as HTMLElement)
65
+ assertNoA11yViolations(results, 'SyCheckbox – checked checkbox', {
66
+ ignoreRules: ['region'],
67
+ })
68
+ })
69
+
70
+ it('has no axe violations for unchecked checkbox with aria-checked="false"', async () => {
71
+ const wrapper = mount(SyCheckbox, {
72
+ props: {
73
+ label: 'Option désactivée',
74
+ modelValue: false,
75
+ required: false,
76
+ },
77
+ })
78
+
79
+ // Attendre que removeAriaAttributes soit exécuté
80
+ await wrapper.vm.$nextTick()
81
+ await wrapper.vm.$nextTick()
82
+
83
+ const results = await axe(wrapper.element as HTMLElement)
84
+ assertNoA11yViolations(results, 'SyCheckbox – unchecked checkbox', {
85
+ ignoreRules: ['region'],
86
+ })
87
+ })
88
+
89
+ it('has no axe violations for disabled checkbox', async () => {
90
+ const wrapper = mount(SyCheckbox, {
91
+ props: {
92
+ label: 'Option désactivée',
93
+ modelValue: true,
94
+ disabled: true,
95
+ },
96
+ })
97
+
98
+ // Attendre que removeAriaAttributes soit exécuté
99
+ await wrapper.vm.$nextTick()
100
+ await wrapper.vm.$nextTick()
101
+
102
+ const results = await axe(wrapper.element as HTMLElement)
103
+ assertNoA11yViolations(results, 'SyCheckbox – disabled checkbox', {
104
+ ignoreRules: ['region'],
105
+ })
106
+ })
107
+
108
+ it('has no axe violations for checkbox with custom aria-label', async () => {
109
+ const wrapper = mount(SyCheckbox, {
110
+ props: {
111
+ ariaLabel: 'Case à cocher personnalisée',
112
+ modelValue: false,
113
+ },
114
+ })
115
+
116
+ // Attendre que removeAriaAttributes soit exécuté
117
+ await wrapper.vm.$nextTick()
118
+ await wrapper.vm.$nextTick()
119
+
120
+ const results = await axe(wrapper.element as HTMLElement)
121
+ assertNoA11yViolations(results, 'SyCheckbox – checkbox with aria-label', {
122
+ ignoreRules: ['region'],
123
+ })
124
+ })
125
+
126
+ it('has no axe violations for checkbox with aria-labelledby', async () => {
127
+ // Créer un conteneur pour le test
128
+ const container = document.createElement('div')
129
+ document.body.appendChild(container)
130
+
131
+ // Créer un élément avec l'ID référencé
132
+ const labelElement = document.createElement('div')
133
+ labelElement.id = 'custom-label-id'
134
+ labelElement.textContent = 'Libellé personnalisé'
135
+ container.appendChild(labelElement)
136
+
137
+ const wrapper = mount(SyCheckbox, {
138
+ props: {
139
+ ariaLabelledby: 'custom-label-id',
140
+ modelValue: false,
141
+ },
142
+ attachTo: container,
143
+ })
144
+
145
+ // Attendre que removeAriaAttributes soit exécuté
146
+ await wrapper.vm.$nextTick()
147
+ await wrapper.vm.$nextTick()
148
+
149
+ const results = await axe(container as HTMLElement)
150
+ assertNoA11yViolations(results, 'SyCheckbox – checkbox with aria-labelledby', {
151
+ ignoreRules: ['region'],
152
+ })
153
+
154
+ // Nettoyer
155
+ wrapper.unmount()
156
+ document.body.removeChild(container)
157
+ })
26
158
  })
@@ -5,6 +5,7 @@ import SyTextField from '@/components/Customs/SyTextField/SyTextField.vue'
5
5
  import SyCheckbox from '@/components/Customs/SyCheckbox/SyCheckbox.vue'
6
6
  import SySelect from '@/components/Customs/Selects/SySelect/SySelect.vue'
7
7
  import { VBtn } from 'vuetify/components'
8
+ import { fn } from '@storybook/test'
8
9
 
9
10
  export default {
10
11
  title: 'Composants/Formulaires/SyForm',
@@ -42,6 +43,10 @@ export default {
42
43
  type Story = StoryObj<typeof SyForm>
43
44
 
44
45
  export const Basic: Story = {
46
+ args: {
47
+ onReset: fn(),
48
+ onSubmit: fn(),
49
+ },
45
50
  render: args => ({
46
51
  components: { SyForm, SyTextField, VBtn },
47
52
  setup() {
@@ -126,6 +131,11 @@ const onSubmit = (event: { isValid: boolean }) => {
126
131
  }
127
132
 
128
133
  export const CustomValidation: Story = {
134
+ args: {
135
+ validateOnSubmit: false,
136
+ onReset: fn(),
137
+ onSubmit: fn(),
138
+ },
129
139
  render: args => ({
130
140
  components: { SyForm, SyTextField, VBtn },
131
141
  setup() {
@@ -142,7 +152,7 @@ export const CustomValidation: Story = {
142
152
 
143
153
  const confirmPasswordRules = computed(() => [
144
154
  { type: 'custom', options: {
145
- validate: value => value === password.value || 'Les mots de passe ne correspondent pas',
155
+ validate: (value: string) => value === password.value,
146
156
  message: 'Les mots de passe ne correspondent pas',
147
157
  } },
148
158
  { type: 'required', options: { message: 'Veuillez confirmer le mot de passe' } },
@@ -150,7 +160,7 @@ export const CustomValidation: Story = {
150
160
 
151
161
  const submitForm = async (e: { isValid: boolean }) => {
152
162
  if (e.isValid) {
153
- alert('Inscription réussie !')
163
+ alert('Inscription réussie ! (validate on submit = ' + args.validateOnSubmit + ')')
154
164
  }
155
165
  else {
156
166
  alert('Formulaire invalide, veuillez corriger les erreurs.')
@@ -171,7 +181,7 @@ export const CustomValidation: Story = {
171
181
  },
172
182
  template: `
173
183
  <div>
174
- <SyForm ref="form" v-bind="args" @submit="submitForm" :validate-on-submit="false">
184
+ <SyForm ref="form" v-bind="args" @submit="submitForm" :validate-on-submit="args.validateOnSubmit">
175
185
  <div class="d-flex flex-column gap-4">
176
186
  <SyTextField v-model="username" label="Nom d'utilisateur" required class="mb-2" />
177
187
  <SyTextField v-model="password" label="Mot de passe" type="password" :custom-rules="passwordRules" class="mb-2" />
@@ -238,7 +248,7 @@ const passwordRules = computed(() => [
238
248
 
239
249
  const confirmPasswordRules = computed(() => [
240
250
  { type: 'custom', options: {
241
- validate: value => value === password.value || 'Les mots de passe ne correspondent pas',
251
+ validate: (value: string) => value === password.value,
242
252
  message: 'Les mots de passe ne correspondent pas',
243
253
  } },
244
254
  { type: 'required', options: { message: 'Veuillez confirmer le mot de passe' } },
@@ -267,6 +277,10 @@ const validateManually = () => {
267
277
  }
268
278
 
269
279
  export const MixedFields: Story = {
280
+ args: {
281
+ onReset: fn(),
282
+ onSubmit: fn(),
283
+ },
270
284
  render: args => ({
271
285
  components: { SyForm, SyTextField, SySelect, SyCheckbox, VBtn },
272
286
  setup() {
@@ -372,6 +386,9 @@ export const MixedFields: Story = {
372
386
  if (event.isValid) {
373
387
  alert('Formulaire valide ! Données: ' + JSON.stringify(formData.value))
374
388
  }
389
+ else {
390
+ alert('Formulaire invalide, veuillez corriger les erreurs.')
391
+ }
375
392
  }
376
393
  </script>
377
394
  `,
@@ -381,6 +398,10 @@ export const MixedFields: Story = {
381
398
  }
382
399
 
383
400
  export const Reset: Story = {
401
+ args: {
402
+ onReset: fn(),
403
+ onSubmit: fn(),
404
+ },
384
405
  render: args => ({
385
406
  components: { SyForm, SyTextField, VBtn },
386
407
  setup() {
@@ -408,7 +429,12 @@ export const Reset: Story = {
408
429
  form.value?.clearValidation()
409
430
  }
410
431
 
411
- return { name, email, emailRules, form, submitForm, clearAll, args }
432
+ function onFormReset() {
433
+ alert('Formulaire réinitialisé !')
434
+ args.onReset?.()
435
+ }
436
+
437
+ return { name, email, emailRules, form, submitForm, clearAll, onFormReset, args }
412
438
  },
413
439
  template: `
414
440
  <SyForm ref="form" v-bind="args" @submit="submitForm" @reset="onFormReset">
@@ -82,9 +82,6 @@
82
82
  const isSubmitted = ref(false)
83
83
 
84
84
  const validation = useValidation({
85
- customRules: props.customRules,
86
- warningRules: props.customWarningRules,
87
- successRules: props.customSuccessRules,
88
85
  showSuccessMessages: props.showSuccessMessages,
89
86
  fieldIdentifier: props.label,
90
87
  disableErrorHandling: props.disableErrorHandling,
@@ -108,7 +105,7 @@
108
105
  : [],
109
106
  )
110
107
 
111
- const validateField = (value: PropertyKey | null) => {
108
+ const validateField = async (value: PropertyKey | null) => {
112
109
  // const stringValue = value != null ? String(value) : null
113
110
 
114
111
  if (props.readonly) {
@@ -121,7 +118,7 @@
121
118
  return true
122
119
  }
123
120
 
124
- const result = validation.validateField(
121
+ const result = await validation.validateField(
125
122
  value,
126
123
  [...defaultRules.value, ...props.customRules],
127
124
  props.customWarningRules,
@@ -130,9 +127,9 @@
130
127
  return !result.hasError
131
128
  }
132
129
 
133
- const validateOnSubmit = () => {
130
+ const validateOnSubmit = async () => {
134
131
  isSubmitted.value = true
135
- return validateField(model.value)
132
+ return await validateField(model.value)
136
133
  }
137
134
 
138
135
  const checkErrorOnBlur = () => {
@@ -62,5 +62,5 @@ Le champ adapte automatiquement son apparence selon son état :
62
62
  `}
63
63
  />
64
64
 
65
- <a href="/?path=/docs/guide-du-dev-guide-de-validation-des-formulaires--docs" className="action-link">Pour plus d'informations sur la validation, consultez le guide de validation des formulaires.</a>
65
+ <a href="/?path=/docs/guide-du-dev-guide-des-formulaires-syform-validation-automatique--docs" className="action-link">Pour plus d'informations sur la validation, consultez le guide de validation des formulaires.</a>
66
66
 
@@ -279,17 +279,9 @@ const meta = {
279
279
  description: 'Largeur du champ',
280
280
  control: { type: 'text' },
281
281
  },
282
- 'validation': {
283
- description: 'Valide le champ avec la valeur donnée',
284
- type: '(value: string | number | null) => void',
285
- },
286
282
  'validateOnSubmit': {
287
- description: 'Valide le champ lors de la soumission du formulaire',
288
- type: '() => void',
289
- },
290
- 'checkErrorOnBlur': {
291
- description: 'Vérifie les erreurs lors de la perte de focus',
292
- type: '() => void',
283
+ description: 'Valide le champ avec la valeur donnée',
284
+ type: '(value: string | number | null) => Promise<void>',
293
285
  },
294
286
  'append': {
295
287
  description: 'Slot pour ajouter du contenu à droite du champ',
@@ -1165,9 +1157,6 @@ export const WithTooltips: Story = {
1165
1157
  `,
1166
1158
  }),
1167
1159
  parameters: {
1168
- a11y: {
1169
- disable: true,
1170
- },
1171
1160
  docs: {
1172
1161
  description: {
1173
1162
  story: 'Exemple de champ avec des tooltips d\'information. Les icônes d\'information apparaissent automatiquement lorsque les props prependTooltip et/ou appendTooltip sont renseignées. La position des tooltips peut être contrôlée avec la prop tooltipLocation.',
@@ -1217,8 +1206,14 @@ export const ValidateOnBlur: Story = {
1217
1206
  const value = ref(args.modelValue)
1218
1207
  const fieldRef = ref()
1219
1208
 
1220
- function handleSubmit() {
1221
- const isValid = fieldRef.value?.validateOnSubmit()
1209
+ watch(() => args.modelValue, (newValue) => {
1210
+ if (value.value !== newValue) {
1211
+ value.value = newValue
1212
+ }
1213
+ })
1214
+
1215
+ async function handleSubmit() {
1216
+ const isValid = await fieldRef.value?.validateOnSubmit()
1222
1217
  alert(isValid ? 'Formulaire valide !' : 'Formulaire invalide, veuillez corriger les erreurs.')
1223
1218
  }
1224
1219
 
@@ -1231,9 +1226,8 @@ export const ValidateOnBlur: Story = {
1231
1226
  </p>
1232
1227
  <SyTextField
1233
1228
  ref="fieldRef"
1234
- v-model="value"
1235
1229
  v-bind="args"
1236
- @update:model-value="args.modelValue = $event"
1230
+ v-model="value"
1237
1231
  />
1238
1232
  <div class="mt-4">
1239
1233
  <VBtn type="submit" color="primary">
@@ -1288,8 +1282,8 @@ import { SyTextField } from '@cnamts/synapse'
1288
1282
  const value = ref('')
1289
1283
  const fieldRef = ref()
1290
1284
 
1291
- function handleSubmit() {
1292
- const isValid = fieldRef.value?.validateOnSubmit()
1285
+ async function handleSubmit() {
1286
+ const isValid = await fieldRef.value?.validateOnSubmit()
1293
1287
  if (!isValid) {
1294
1288
  // Gérer l'erreur
1295
1289
  return
@@ -1336,19 +1330,23 @@ export const FormValidation: Story = {
1336
1330
  },
1337
1331
  }]
1338
1332
 
1339
- const handleSubmit = () => {
1333
+ const handleSubmit = async () => {
1340
1334
  const fields = [
1341
1335
  { ref: nomField, name: 'Nom' },
1342
1336
  { ref: prenomField, name: 'Prénom' },
1343
1337
  { ref: ageField, name: 'Âge' },
1344
1338
  ]
1345
1339
 
1346
- const invalidFields = fields
1347
- .filter(({ ref }) => !ref.value?.validateOnSubmit())
1348
- .map(({ name }) => name)
1340
+ const invalidFields: string[] = []
1341
+ for (const { ref, name } of fields) {
1342
+ const isValid = await ref.value?.validateOnSubmit()
1343
+ if (!isValid) {
1344
+ invalidFields.push(name)
1345
+ }
1346
+ }
1349
1347
 
1350
1348
  if (invalidFields.length > 0) {
1351
- alert(`Les champs suivants sont invalides: ${invalidFields.join('\\n')}`)
1349
+ alert(`Les champs suivants sont invalides: ${invalidFields.join(', ')}`)
1352
1350
  }
1353
1351
  else {
1354
1352
  alert('Formulaire soumis avec succès !')
@@ -1561,9 +1559,13 @@ const nomField = ref()
1561
1559
  { ref: ageField, name: 'Âge' },
1562
1560
  ]
1563
1561
 
1564
- const invalidFields = fields
1565
- .filter(({ ref }) => !ref.value?.validateOnSubmit())
1566
- .map(({ name }) => name)
1562
+ const invalidFields: string[] = []
1563
+ for (const { ref, name } of fields) {
1564
+ const isValid = await ref.value?.validateOnSubmit()
1565
+ if (!isValid) {
1566
+ invalidFields.push(name)
1567
+ }
1568
+ }
1567
1569
 
1568
1570
  if (invalidFields.length > 0) {
1569
1571
  alert('Les champs suivants sont invalides: ' + invalidFields.join('\\n'))