@cnamts/synapse 1.1.0 → 1.1.1

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 (202) hide show
  1. package/dist/{AutocompleteFilter-DXd4szWO.js → AutocompleteFilter-CGF33skz.js} +1 -1
  2. package/dist/{DateFilter-BD59Kgwf.js → DateFilter-D7-MsKtx.js} +1 -1
  3. package/dist/{NumberFilter-BSMZE7uw.js → NumberFilter-bjQPPfsj.js} +1 -1
  4. package/dist/{PeriodFilter-keUdSSk0.js → PeriodFilter-B3wJpK8-.js} +1 -1
  5. package/dist/{SelectFilter-Dhvvwazl.js → SelectFilter-BN6DbKAV.js} +1 -1
  6. package/dist/{TextFilter-CU8FpXz0.js → TextFilter-BffP0J2f.js} +1 -1
  7. package/dist/{apLightTheme2026-DbS7BPUf.js → apLightTheme2026-C4ygwMHC.js} +11 -11
  8. package/dist/components/Amelipro/AmeliproAutoCompleteField/AmeliproAutoCompleteField.d.ts +6 -6
  9. package/dist/components/Amelipro/AmeliproSelect/AmeliproSelect.d.ts +6 -6
  10. package/dist/components/Amelipro/AmeliproTabs/AmeliproTabs.d.ts +6 -6
  11. package/dist/components/Captcha/Captcha.d.ts +27 -16
  12. package/dist/components/Captcha/CaptchaForm.d.ts +29 -3
  13. package/dist/components/Captcha/types.d.ts +14 -0
  14. package/dist/components/Captcha/useCaptchaValidation.d.ts +37 -0
  15. package/dist/components/Customs/Selects/SelectBtnField/SelectBtnField.d.ts +33 -13
  16. package/dist/components/Customs/Selects/SelectBtnField/composables/useSelectBtnFieldValidation.d.ts +23 -0
  17. package/dist/components/Customs/Selects/SyAutocomplete/composables/useSyAutocompleteValidation.d.ts +2 -2
  18. package/dist/components/Customs/Selects/SySelect/composables/useSySelectValidation.d.ts +2 -2
  19. package/dist/components/Customs/SyCheckBoxGroup/SyCheckBoxGroup.d.ts +17 -48
  20. package/dist/components/Customs/SyCheckBoxGroup/composables/useSyCheckBoxGroupValidation.d.ts +29 -0
  21. package/dist/components/Customs/SyCheckBoxGroup/types.d.ts +46 -0
  22. package/dist/components/Customs/SyCheckbox/SyCheckbox.d.ts +16 -51
  23. package/dist/components/Customs/SyCheckbox/composables/useSyCheckboxValidation.d.ts +27 -0
  24. package/dist/components/Customs/SyCheckbox/types.d.ts +49 -0
  25. package/dist/components/Customs/SyTextField/FieldState.d.ts +5 -0
  26. package/dist/components/Customs/SyTextField/useSyTextFieldValidation.d.ts +3 -3
  27. package/dist/components/DialogBox/DialogBox.d.ts +2 -0
  28. package/dist/components/DialogBox/locales.d.ts +1 -0
  29. package/dist/components/FilterSideBar/FilterSideBar.d.ts +4 -0
  30. package/dist/components/LunarCalendar/LunarCalendar.d.ts +43 -14
  31. package/dist/components/LunarCalendar/types.d.ts +35 -0
  32. package/dist/components/LunarCalendar/useLunarCalendarValidation.d.ts +11 -12
  33. package/dist/components/MonthPicker/MonthPicker.d.ts +72 -1747
  34. package/dist/components/MonthPicker/MonthPickerText/MonthPickerInput.d.ts +21 -1733
  35. package/dist/components/MonthPicker/MonthPickerText/useTextField.d.ts +5 -0
  36. package/dist/components/MonthPicker/locales.d.ts +1 -0
  37. package/dist/components/MonthPicker/types.d.ts +11 -0
  38. package/dist/components/MonthPicker/useMonthPickerValidation.d.ts +37 -24
  39. package/dist/components/NirField/NirField.d.ts +6 -4
  40. package/dist/components/NirField/useNirValidation.d.ts +7 -5
  41. package/dist/components/PageContainer/PageContainer.d.ts +8 -0
  42. package/dist/components/PasswordField/PasswordField.d.ts +2 -2
  43. package/dist/components/PasswordField/usePasswordFieldValidation.d.ts +2 -2
  44. package/dist/components/PhoneField/PhoneField.d.ts +960 -1938
  45. package/dist/components/PhoneField/indicatifs.d.ts +715 -8
  46. package/dist/components/PhoneField/locales.d.ts +7 -0
  47. package/dist/components/PhoneField/types.d.ts +29 -0
  48. package/dist/components/PhoneField/usePhoneFieldValidation.d.ts +45 -0
  49. package/dist/components/PhoneField/usePhoneIndicatifs.d.ts +947 -0
  50. package/dist/components/SyTextArea/composables/useSyTextAreaValidation.d.ts +2 -2
  51. package/dist/composables/unifyValidation/documentationValidationProps.d.ts +1 -1
  52. package/dist/composables/unifyValidation/useValidation.d.ts +4 -5
  53. package/dist/design-system-v3.js +2 -2
  54. package/dist/designTokens/tokens/amelipro/apLightTheme.d.ts +10 -10
  55. package/dist/designTokens/tokens/baseTokens.d.ts +18 -18
  56. package/dist/designTokens/tokens/cnam/cnamLightTheme.d.ts +10 -10
  57. package/dist/designTokens/tokens/pa/paLightTheme.d.ts +10 -10
  58. package/dist/designTokens/tokens/semanticTokens.d.ts +14 -14
  59. package/dist/{main-D8ryUoS5.js → main-C4wAktOs.js} +13718 -12991
  60. package/dist/synapse.css +1 -1
  61. package/dist/vuetifyConfig.js +1 -1
  62. package/package.json +7 -7
  63. package/src/assets/compat/_legacy-tokens.scss +91 -0
  64. package/src/assets/overrides/_utilities.scss +23 -0
  65. package/src/components/Accordion/Accordion.stories.ts +121 -1
  66. package/src/components/BackBtn/BackBtn.mdx +1 -1
  67. package/src/components/BackToTopBtn/BackToTopBtn.mdx +0 -1
  68. package/src/components/Captcha/Captcha.stories.ts +134 -31
  69. package/src/components/Captcha/Captcha.vue +95 -28
  70. package/src/components/Captcha/CaptchaForm.vue +51 -22
  71. package/src/components/Captcha/tests/Captcha.focus.spec.ts +214 -0
  72. package/src/components/Captcha/tests/Captcha.spec.ts +233 -24
  73. package/src/components/Captcha/tests/CaptchaForm.spec.ts +82 -0
  74. package/src/components/Captcha/tests/__snapshots__/Captcha.spec.ts.snap +16 -42
  75. package/src/components/Captcha/types.ts +15 -0
  76. package/src/components/Captcha/useCaptchaValidation.ts +87 -0
  77. package/src/components/Captcha/validation/validation.stories.ts +1194 -0
  78. package/src/components/ChipList/ChipList.mdx +0 -1
  79. package/src/components/CollapsibleList/CollapsibleList.mdx +0 -1
  80. package/src/components/CookieBanner/CookieBanner.mdx +0 -1
  81. package/src/components/CopyBtn/CopyBtn.mdx +0 -1
  82. package/src/components/Customs/Selects/SelectBtnField/SelectBtnField.stories.ts +123 -439
  83. package/src/components/Customs/Selects/SelectBtnField/SelectBtnField.vue +147 -41
  84. package/src/components/Customs/Selects/SelectBtnField/Validation/Validation.stories.ts +600 -0
  85. package/src/components/Customs/Selects/SelectBtnField/composables/useSelectBtnFieldValidation.ts +87 -0
  86. package/src/components/Customs/Selects/SelectBtnField/tests/SelectBtnField.spec.ts +402 -33
  87. package/src/components/Customs/Selects/SelectBtnField/tests/__snapshots__/SelectBtnField.spec.ts.snap +52 -38
  88. package/src/components/Customs/SyCheckBoxGroup/SyCheckBoxGroup.stories.ts +342 -162
  89. package/src/components/Customs/SyCheckBoxGroup/SyCheckBoxGroup.vue +77 -129
  90. package/src/components/Customs/SyCheckBoxGroup/Validation/Validation.stories.ts +1008 -0
  91. package/src/components/Customs/SyCheckBoxGroup/composables/useSyCheckBoxGroupValidation.ts +107 -0
  92. package/src/components/Customs/SyCheckBoxGroup/tests/SyCheckBoxGroup.spec.ts +180 -7
  93. package/src/components/Customs/SyCheckBoxGroup/types.ts +49 -0
  94. package/src/components/Customs/SyCheckbox/SyCheckbox.stories.ts +41 -161
  95. package/src/components/Customs/SyCheckbox/SyCheckbox.vue +71 -148
  96. package/src/components/Customs/SyCheckbox/Validation/Validation.stories.ts +654 -0
  97. package/src/components/Customs/SyCheckbox/composables/useSyCheckboxValidation.ts +105 -0
  98. package/src/components/Customs/SyCheckbox/tests/SyCheckbox.spec.ts +106 -0
  99. package/src/components/Customs/SyCheckbox/tests/useSyCheckboxValidation.spec.ts +98 -0
  100. package/src/components/Customs/SyCheckbox/types.ts +51 -0
  101. package/src/components/Customs/SyTextField/FieldState.vue +50 -0
  102. package/src/components/Customs/SyTextField/SyTextField.vue +12 -9
  103. package/src/components/Customs/SyTextField/useSyTextFieldValidation.ts +2 -11
  104. package/src/components/DataList/DataList.mdx +0 -1
  105. package/src/components/DataListGroup/DataListGroup.mdx +0 -1
  106. package/src/components/DiacriticPicker/DiacriticPicker.mdx +0 -1
  107. package/src/components/DialogBox/DialogBox.mdx +0 -1
  108. package/src/components/DialogBox/DialogBox.stories.ts +399 -4
  109. package/src/components/DialogBox/DialogBox.vue +20 -0
  110. package/src/components/DialogBox/locales.ts +1 -0
  111. package/src/components/DialogBox/tests/DialogBox.spec.ts +73 -0
  112. package/src/components/DialogBox/tests/DialogBox.visual.cy.ts +24 -0
  113. package/src/components/ErrorPage/ErrorPage.mdx +1 -1
  114. package/src/components/ExternalLinks/ExternalLinks.mdx +0 -1
  115. package/src/components/FileList/FileList.mdx +0 -1
  116. package/src/components/FilterInline/FilterInline.mdx +0 -1
  117. package/src/components/FilterSideBar/FilterSideBar.mdx +8 -1
  118. package/src/components/FilterSideBar/FilterSideBar.stories.ts +133 -1
  119. package/src/components/FilterSideBar/FilterSideBar.vue +19 -2
  120. package/src/components/FilterSideBar/tests/FilterSideBar.spec.ts +55 -0
  121. package/src/components/FooterBar/FooterBar.mdx +0 -1
  122. package/src/components/FranceConnectBtn/FranceConnectBtn.mdx +0 -1
  123. package/src/components/HeaderBar/HeaderBar.mdx +0 -1
  124. package/src/components/HeaderLoading/HeaderLoading.mdx +0 -1
  125. package/src/components/LangBtn/LangBtn.mdx +0 -1
  126. package/src/components/Logo/Logo.mdx +1 -1
  127. package/src/components/LunarCalendar/LunarCalendar.mdx +6 -9
  128. package/src/components/LunarCalendar/LunarCalendar.stories.ts +243 -46
  129. package/src/components/LunarCalendar/LunarCalendar.vue +61 -26
  130. package/src/components/LunarCalendar/Validation/Validation.stories.ts +717 -0
  131. package/src/components/LunarCalendar/tests/LunarCalendar.a11y.spec.ts +1 -1
  132. package/src/components/LunarCalendar/tests/LunarCalendar.spec.ts +197 -6
  133. package/src/components/LunarCalendar/tests/useLunarCalendarValidation.spec.ts +287 -0
  134. package/src/components/LunarCalendar/types.ts +39 -0
  135. package/src/components/LunarCalendar/useLunarCalendarValidation.ts +115 -39
  136. package/src/components/MonthPicker/MonthPicker.stories.ts +38 -281
  137. package/src/components/MonthPicker/MonthPicker.vue +66 -17
  138. package/src/components/MonthPicker/MonthPickerText/MonthPickerInput.vue +44 -20
  139. package/src/components/MonthPicker/MonthPickerText/useTextField.ts +5 -0
  140. package/src/components/MonthPicker/Validation/Validation.stories.ts +1117 -0
  141. package/src/components/MonthPicker/locales.ts +1 -0
  142. package/src/components/MonthPicker/tests/MonthPicker.spec.ts +353 -2
  143. package/src/components/MonthPicker/tests/__snapshots__/MonthPicker.spec.ts.snap +12 -8
  144. package/src/components/MonthPicker/types.ts +16 -0
  145. package/src/components/MonthPicker/useMonthPickerValidation.ts +64 -27
  146. package/src/components/NirField/NirField.mdx +120 -66
  147. package/src/components/NirField/NirField.stories.ts +216 -0
  148. package/src/components/NirField/useNirValidation.ts +16 -17
  149. package/src/components/NotFoundPage/tests/__snapshots__/NotFoundPage.spec.ts.snap +263 -245
  150. package/src/components/NotificationBar/NotificationBar.mdx +0 -1
  151. package/src/components/PageContainer/PageContainer.mdx +0 -1
  152. package/src/components/PageContainer/PageContainer.stories.ts +170 -2
  153. package/src/components/PageContainer/PageContainer.vue +63 -8
  154. package/src/components/PageContainer/tests/__snapshots__/PageContainer.spec.ts.snap +19 -11
  155. package/src/components/PaginatedTable/PaginatedTable.mdx +0 -1
  156. package/src/components/PeriodField/PeriodField.mdx +0 -1
  157. package/src/components/PhoneField/PhoneField.mdx +2 -3
  158. package/src/components/PhoneField/PhoneField.stories.ts +227 -410
  159. package/src/components/PhoneField/PhoneField.vue +204 -438
  160. package/src/components/PhoneField/indicatifs.ts +1 -1
  161. package/src/components/PhoneField/locales.ts +7 -0
  162. package/src/components/PhoneField/tests/PhoneField.a11y.spec.ts +0 -1
  163. package/src/components/PhoneField/tests/PhoneField.spec.ts +517 -220
  164. package/src/components/PhoneField/types.ts +30 -0
  165. package/src/components/PhoneField/usePhoneFieldValidation.ts +119 -0
  166. package/src/components/PhoneField/usePhoneIndicatifs.ts +89 -0
  167. package/src/components/PhoneField/validation/validation.stories.ts +717 -0
  168. package/src/components/RangeField/RangeField.mdx +0 -1
  169. package/src/components/RatingPicker/RatingPicker.mdx +0 -1
  170. package/src/components/SocialMediaLinks/SocialMediaLinks.mdx +0 -1
  171. package/src/components/StatusPage/StatusPage.vue +1 -0
  172. package/src/components/StatusPage/tests/__snapshots__/StatusPage.spec.ts.snap +248 -230
  173. package/src/components/SubHeader/SubHeader.mdx +5 -6
  174. package/src/components/Tables/common/tests/SyTableFilter.spec.ts +11 -12
  175. package/src/components/UploadWorkflow/UploadWorkflow.mdx +0 -1
  176. package/src/components/UserMenuBtn/UserMenuBtn.mdx +0 -1
  177. package/src/components/UserMenuBtn/UserMenuBtn.stories.ts +177 -0
  178. package/src/composables/unifyValidation/documentationValidationProps.ts +1 -1
  179. package/src/composables/unifyValidation/tests/useValidation.spec.ts +13 -1
  180. package/src/composables/unifyValidation/useValidation.ts +37 -33
  181. package/src/composantsVuetify/VCard/VCard.mdx +4 -0
  182. package/src/composantsVuetify/VCard/v-card.stories.ts +93 -1
  183. package/src/composantsVuetify/VCarousel/VCarousel.mdx +74 -0
  184. package/src/composantsVuetify/VCarousel/v-carousel.stories.ts +531 -0
  185. package/src/composantsVuetify/VNavigationDrawer/VNavgationDrawer.mdx +53 -0
  186. package/src/composantsVuetify/VNavigationDrawer/v-navigation-drawer.stories.ts +310 -0
  187. package/src/composantsVuetify/VSlideGroup/VSlideGroup.mdx +105 -0
  188. package/src/composantsVuetify/VSlideGroup/v-slide-group.stories.ts +463 -0
  189. package/src/designTokens/tokens/baseColors.ts +1 -1
  190. package/src/designTokens/tokens/baseTokens.ts +18 -18
  191. package/src/stories/Components/Components.stories.ts +34 -1
  192. package/src/stories/Demarrer/Releases.stories.ts +16 -2
  193. package/src/stories/DesignTokens/Arrondis.mdx +1 -1
  194. package/src/stories/DesignTokens/Correspondances.mdx +219 -0
  195. package/src/stories/DesignTokens/UtiliserLesTokens.mdx +235 -0
  196. package/src/stories/DesignTokens/colors.stories.ts +569 -569
  197. package/src/stories/GuideDuDev/Amelipro.stories.ts +335 -267
  198. package/dist/components/LunarCalendar/useLunarCalendarRules.d.ts +0 -5
  199. package/dist/components/PhoneField/tests/types.d.ts +0 -18
  200. package/src/components/LunarCalendar/tests/useLunarCalendarRules.spec.ts +0 -184
  201. package/src/components/LunarCalendar/useLunarCalendarRules.ts +0 -96
  202. package/src/components/PhoneField/tests/types.d.ts +0 -19
@@ -1,7 +1,9 @@
1
1
  import { describe, it, expect } from 'vitest'
2
- import { mount } from '@vue/test-utils'
2
+ import { mount, flushPromises } from '@vue/test-utils'
3
+ import { ref } from 'vue'
3
4
 
4
5
  import SelectBtnField from '../SelectBtnField.vue'
6
+ import SyForm from '@/components/Customs/SyForm/SyForm.vue'
5
7
 
6
8
  describe('SelectBtnField', () => {
7
9
  it('renders correctly', () => {
@@ -14,7 +16,7 @@ describe('SelectBtnField', () => {
14
16
  const wrapper = mount(SelectBtnField, {
15
17
  props: {
16
18
  label: 'Test',
17
- hint: 'Test',
19
+ helpText: 'Test',
18
20
  items: [
19
21
  {
20
22
  text: 'Test',
@@ -39,7 +41,7 @@ describe('SelectBtnField', () => {
39
41
  const wrapper = mount(SelectBtnField, {
40
42
  props: {
41
43
  label: 'Test',
42
- hint: 'Test',
44
+ helpText: 'Test',
43
45
  items: [
44
46
  {
45
47
  text: 'Test',
@@ -65,7 +67,7 @@ describe('SelectBtnField', () => {
65
67
  const wrapper = mount(SelectBtnField, {
66
68
  props: {
67
69
  label: 'Test',
68
- hint: 'Test',
70
+ helpText: 'Test',
69
71
  items: [
70
72
  {
71
73
  text: 'Test',
@@ -88,7 +90,7 @@ describe('SelectBtnField', () => {
88
90
  const wrapper = mount(SelectBtnField, {
89
91
  props: {
90
92
  label: 'Test',
91
- hint: 'Test',
93
+ helpText: 'Test',
92
94
  items: [
93
95
  {
94
96
  text: 'Test',
@@ -122,7 +124,7 @@ describe('SelectBtnField', () => {
122
124
  const wrapper = mount(SelectBtnField, {
123
125
  props: {
124
126
  label: 'Test',
125
- hint: 'Test',
127
+ helpText: 'Test',
126
128
  items: [
127
129
  {
128
130
  text: 'Test',
@@ -153,7 +155,7 @@ describe('SelectBtnField', () => {
153
155
  const wrapper = mount(SelectBtnField, {
154
156
  props: {
155
157
  label: 'Test',
156
- hint: 'Test',
158
+ helpText: 'Test',
157
159
  items: [
158
160
  {
159
161
  text: 'Test',
@@ -168,7 +170,6 @@ describe('SelectBtnField', () => {
168
170
  value: 'test3',
169
171
  },
170
172
  ],
171
- error: true,
172
173
  errorMessages: ['Test'],
173
174
  },
174
175
  })
@@ -230,7 +231,7 @@ describe('SelectBtnField', () => {
230
231
  return {
231
232
  props: {
232
233
  label: 'Test',
233
- hint: 'Test',
234
+ helpText: 'Test',
234
235
  items: [
235
236
  {
236
237
  text: 'Test 1',
@@ -241,7 +242,6 @@ describe('SelectBtnField', () => {
241
242
  value: 'test2',
242
243
  },
243
244
  ],
244
- error: true,
245
245
  errorMessages: ['Test'],
246
246
  multiple: true,
247
247
  inline: true,
@@ -258,7 +258,7 @@ describe('SelectBtnField', () => {
258
258
  expect(wrapper.html()).toMatchSnapshot()
259
259
  })
260
260
 
261
- it(`display correctly with in dark mode with an hint`, () => {
261
+ it(`display correctly with in dark mode with a help text`, () => {
262
262
  const DarkMode = {
263
263
  template: `
264
264
  <v-app>
@@ -276,7 +276,7 @@ describe('SelectBtnField', () => {
276
276
  return {
277
277
  props: {
278
278
  label: 'Test',
279
- hint: 'Test',
279
+ helpText: 'Test',
280
280
  items: [
281
281
  {
282
282
  text: 'Test 1',
@@ -303,7 +303,7 @@ describe('SelectBtnField', () => {
303
303
  const wrapper = mount(SelectBtnField, {
304
304
  props: {
305
305
  label: 'Test',
306
- hint: 'Test',
306
+ helpText: 'Test',
307
307
  items: [
308
308
  {
309
309
  text: 'Test 1',
@@ -322,21 +322,6 @@ describe('SelectBtnField', () => {
322
322
  expect(wrapper.emitted()).not.toHaveProperty('update:modelValue')
323
323
  })
324
324
 
325
- it('emits update:error and update:error-messages when an item is selected', async () => {
326
- const wrapper = mount(SelectBtnField, {
327
- props: {
328
- items: [{ text: 'Test', value: 'test' }],
329
- error: true,
330
- errorMessages: ['Champ requis'],
331
- },
332
- })
333
-
334
- await wrapper.find('[role="option"]').trigger('click')
335
-
336
- expect(wrapper.emitted('update:error')).toEqual([[false]])
337
- expect(wrapper.emitted('update:error-messages')).toEqual([[undefined]])
338
- })
339
-
340
325
  it('filters out items with null or undefined value', () => {
341
326
  const wrapper = mount(SelectBtnField, {
342
327
  props: {
@@ -399,7 +384,7 @@ describe('SelectBtnField', () => {
399
384
  label: 'Choix',
400
385
  multiple: true,
401
386
  inline: true,
402
- error: true,
387
+ errorMessages: ['Erreur'],
403
388
  },
404
389
  })
405
390
 
@@ -521,10 +506,10 @@ describe('SelectBtnField', () => {
521
506
  expect(emitted![emitted!.length - 1]![0]).toEqual([])
522
507
  })
523
508
 
524
- it('displays hint when no errorMessages', () => {
509
+ it('displays helpText when no validation message', () => {
525
510
  const wrapper = mount(SelectBtnField, {
526
511
  props: {
527
- hint: 'Aide contextuelle',
512
+ helpText: 'Aide contextuelle',
528
513
  items: [{ text: 'A', value: 'a' }],
529
514
  },
530
515
  })
@@ -532,10 +517,10 @@ describe('SelectBtnField', () => {
532
517
  expect(wrapper.text()).toContain('Aide contextuelle')
533
518
  })
534
519
 
535
- it('displays errorMessages instead of hint when both are provided', () => {
520
+ it('displays errorMessages instead of helpText when both are provided', () => {
536
521
  const wrapper = mount(SelectBtnField, {
537
522
  props: {
538
- hint: 'Aide contextuelle',
523
+ helpText: 'Aide contextuelle',
539
524
  errorMessages: ['Champ invalide'],
540
525
  items: [{ text: 'A', value: 'a' }],
541
526
  },
@@ -544,4 +529,388 @@ describe('SelectBtnField', () => {
544
529
  expect(wrapper.text()).toContain('Champ invalide')
545
530
  expect(wrapper.text()).not.toContain('Aide contextuelle')
546
531
  })
532
+
533
+ describe('validation', () => {
534
+ it('exposes validateOnSubmit and returns false for an empty required field', async () => {
535
+ const wrapper = mount(SelectBtnField, {
536
+ props: {
537
+ label: 'Moyen de contact',
538
+ required: true,
539
+ items: [{ text: 'Email', value: 'email' }],
540
+ },
541
+ })
542
+
543
+ const vm = wrapper.vm as unknown as { validateOnSubmit: () => Promise<boolean>, errors: string[] }
544
+ const result = await vm.validateOnSubmit()
545
+
546
+ expect(result).toBe(false)
547
+ expect(vm.errors).toContain('Le champ Moyen de contact est requis.')
548
+
549
+ wrapper.unmount()
550
+ })
551
+
552
+ it('validateOnSubmit returns true when a required field is filled', async () => {
553
+ const wrapper = mount(SelectBtnField, {
554
+ props: {
555
+ label: 'Moyen de contact',
556
+ required: true,
557
+ modelValue: 'email',
558
+ items: [{ text: 'Email', value: 'email' }],
559
+ },
560
+ })
561
+
562
+ const vm = wrapper.vm as unknown as { validateOnSubmit: () => Promise<boolean> }
563
+ expect(await vm.validateOnSubmit()).toBe(true)
564
+
565
+ wrapper.unmount()
566
+ })
567
+
568
+ it('displays an error from a customRule', async () => {
569
+ const wrapper = mount(SelectBtnField, {
570
+ props: {
571
+ label: 'Moyen de contact',
572
+ modelValue: 'sms',
573
+ items: [
574
+ { text: 'Email', value: 'email' },
575
+ { text: 'SMS', value: 'sms' },
576
+ ],
577
+ customRules: [{
578
+ type: 'custom',
579
+ options: {
580
+ validate: (v: unknown) => v !== 'sms',
581
+ message: 'Le SMS n’est pas disponible.',
582
+ },
583
+ }],
584
+ },
585
+ })
586
+ const vm = wrapper.vm as unknown as { validateOnSubmit: () => Promise<boolean>, hasError: boolean, errors: string[] }
587
+ await vm.validateOnSubmit()
588
+ await flushPromises()
589
+
590
+ expect(vm.hasError).toBe(true)
591
+ expect(vm.errors).toContain('Le SMS n’est pas disponible.')
592
+ expect(wrapper.find('[role="listbox"]').attributes('aria-invalid')).toBe('true')
593
+
594
+ wrapper.unmount()
595
+ })
596
+
597
+ it('displays a warning and a success message from custom rules', async () => {
598
+ const warningWrapper = mount(SelectBtnField, {
599
+ props: {
600
+ label: 'Moyen de contact',
601
+ modelValue: 'courrier',
602
+ items: [{ text: 'Courrier', value: 'courrier' }],
603
+ customWarningRules: [{
604
+ type: 'custom',
605
+ options: {
606
+ validate: (v: unknown) => v !== 'courrier',
607
+ warningMessage: 'Délais allongés.',
608
+ },
609
+ }],
610
+ },
611
+ })
612
+ const warningVm = warningWrapper.vm as unknown as { validateOnSubmit: () => Promise<boolean>, hasWarning: boolean, warnings: string[] }
613
+ await warningVm.validateOnSubmit()
614
+ await flushPromises()
615
+ expect(warningVm.hasWarning).toBe(true)
616
+ expect(warningVm.warnings).toContain('Délais allongés.')
617
+ warningWrapper.unmount()
618
+
619
+ const successWrapper = mount(SelectBtnField, {
620
+ props: {
621
+ label: 'Moyen de contact',
622
+ modelValue: 'email',
623
+ showSuccessMessages: true,
624
+ items: [{ text: 'Email', value: 'email' }],
625
+ customSuccessRules: [{
626
+ type: 'custom',
627
+ options: {
628
+ validate: (v: unknown) => v === 'email',
629
+ successMessage: 'Choix optimal.',
630
+ },
631
+ }],
632
+ },
633
+ })
634
+ const successVm = successWrapper.vm as unknown as { validateOnSubmit: () => Promise<boolean>, hasSuccess: boolean, successes: string[] }
635
+ await successVm.validateOnSubmit()
636
+ await flushPromises()
637
+ expect(successVm.hasSuccess).toBe(true)
638
+ expect(successVm.successes).toContain('Choix optimal.')
639
+ successWrapper.unmount()
640
+ })
641
+
642
+ it('sets aria-required when required', () => {
643
+ const wrapper = mount(SelectBtnField, {
644
+ props: { label: 'Choix', required: true },
645
+ })
646
+ expect(wrapper.find('[role="listbox"]').attributes('aria-required')).toBe('true')
647
+ wrapper.unmount()
648
+ })
649
+
650
+ it('does not display errors when disableErrorHandling is true', async () => {
651
+ const wrapper = mount(SelectBtnField, {
652
+ props: {
653
+ label: 'Moyen de contact',
654
+ required: true,
655
+ disableErrorHandling: true,
656
+ items: [{ text: 'Email', value: 'email' }],
657
+ },
658
+ })
659
+
660
+ const vm = wrapper.vm as unknown as { validateOnSubmit: () => Promise<boolean>, errors: string[] }
661
+ await vm.validateOnSubmit()
662
+ await flushPromises()
663
+
664
+ expect(vm.errors).toHaveLength(0)
665
+ expect(wrapper.find('[role="listbox"]').attributes('aria-invalid')).toBe('false')
666
+
667
+ wrapper.unmount()
668
+ })
669
+
670
+ it('registers with a parent SyForm and is validated on submit', async () => {
671
+ const wrapper = mount({
672
+ components: { SyForm, SelectBtnField },
673
+ template: `
674
+ <SyForm ref="form">
675
+ <SelectBtnField v-model="value" label="Moyen de contact" required :items="items" />
676
+ </SyForm>
677
+ `,
678
+ setup() {
679
+ return {
680
+ value: ref<string | null>(null),
681
+ items: [{ text: 'Email', value: 'email' }],
682
+ }
683
+ },
684
+ })
685
+
686
+ const form = wrapper.getComponent(SyForm)
687
+ const isValid = await (form.vm as unknown as { validate: () => Promise<boolean> }).validate()
688
+ await flushPromises()
689
+
690
+ expect(isValid).toBe(false)
691
+ expect(wrapper.text()).toContain('Le champ Moyen de contact est requis.')
692
+
693
+ wrapper.unmount()
694
+ })
695
+
696
+ it('validates on selection change when isValidateOnBlur is false', async () => {
697
+ const wrapper = mount({
698
+ components: { SelectBtnField },
699
+ template: `
700
+ <SelectBtnField
701
+ ref="field"
702
+ v-model="value"
703
+ label="Moyen de contact"
704
+ :items="items"
705
+ :custom-rules="customRules"
706
+ />
707
+ `,
708
+ setup() {
709
+ return {
710
+ value: ref<string | null>(null),
711
+ items: [
712
+ { text: 'Email', value: 'email' },
713
+ { text: 'SMS', value: 'sms' },
714
+ ],
715
+ customRules: [{
716
+ type: 'custom',
717
+ options: {
718
+ validate: (v: unknown) => v !== 'sms',
719
+ message: 'Le SMS n’est pas disponible.',
720
+ },
721
+ }],
722
+ }
723
+ },
724
+ })
725
+ const vm = wrapper.findComponent(SelectBtnField).vm as unknown as { hasError: boolean, errors: string[] }
726
+
727
+ // Sélection d'une valeur invalide -> validation immédiate
728
+ await wrapper.find('li:nth-child(2)[role="option"]').trigger('click')
729
+ await flushPromises()
730
+ expect(vm.hasError).toBe(true)
731
+ expect(vm.errors).toContain('Le SMS n’est pas disponible.')
732
+
733
+ // Re-clic pour désélectionner -> l'erreur disparaît
734
+ await wrapper.find('li:nth-child(2)[role="option"]').trigger('click')
735
+ await flushPromises()
736
+ expect(vm.hasError).toBe(false)
737
+
738
+ wrapper.unmount()
739
+ })
740
+
741
+ it('supports Vuetify native validation (useVuetifyValidation)', async () => {
742
+ const wrapper = mount(SelectBtnField, {
743
+ props: {
744
+ label: 'Moyen de contact',
745
+ useVuetifyValidation: true,
746
+ rules: [(v: unknown) => !!v || 'Le moyen de contact est requis.'],
747
+ items: [{ text: 'Email', value: 'email' }],
748
+ },
749
+ })
750
+
751
+ const vm = wrapper.vm as unknown as { validateOnSubmit: () => Promise<boolean>, errors: string[] }
752
+ expect(await vm.validateOnSubmit()).toBe(false)
753
+ await flushPromises()
754
+ expect(vm.errors).toContain('Le moyen de contact est requis.')
755
+
756
+ wrapper.unmount()
757
+ })
758
+
759
+ it('clears the validation state via clearValidation', async () => {
760
+ const wrapper = mount(SelectBtnField, {
761
+ props: {
762
+ label: 'Moyen de contact',
763
+ required: true,
764
+ items: [{ text: 'Email', value: 'email' }],
765
+ },
766
+ })
767
+ const vm = wrapper.vm as unknown as {
768
+ validateOnSubmit: () => Promise<boolean>
769
+ clearValidation: () => void
770
+ errors: string[]
771
+ }
772
+
773
+ await vm.validateOnSubmit()
774
+ await flushPromises()
775
+ expect(vm.errors.length).toBeGreaterThan(0)
776
+
777
+ vm.clearValidation()
778
+ await flushPromises()
779
+ expect(vm.errors).toHaveLength(0)
780
+
781
+ wrapper.unmount()
782
+ })
783
+
784
+ it('renders external error, warning and success messages', async () => {
785
+ const errorWrapper = mount(SelectBtnField, {
786
+ props: {
787
+ label: 'Moyen de contact',
788
+ errorMessages: ['Erreur externe'],
789
+ items: [{ text: 'Email', value: 'email' }],
790
+ },
791
+ })
792
+ expect(errorWrapper.text()).toContain('Erreur externe')
793
+ expect(errorWrapper.find('.select-btn-field__message--error').exists()).toBe(true)
794
+ errorWrapper.unmount()
795
+
796
+ const warningWrapper = mount(SelectBtnField, {
797
+ props: {
798
+ label: 'Moyen de contact',
799
+ warningMessages: ['Avertissement externe'],
800
+ items: [{ text: 'Email', value: 'email' }],
801
+ },
802
+ })
803
+ expect(warningWrapper.text()).toContain('Avertissement externe')
804
+ expect(warningWrapper.find('.select-btn-field__message--warning').exists()).toBe(true)
805
+ warningWrapper.unmount()
806
+
807
+ const successWrapper = mount(SelectBtnField, {
808
+ props: {
809
+ label: 'Moyen de contact',
810
+ successMessages: ['Succès externe'],
811
+ showSuccessMessages: true,
812
+ items: [{ text: 'Email', value: 'email' }],
813
+ },
814
+ })
815
+ expect(successWrapper.text()).toContain('Succès externe')
816
+ expect(successWrapper.find('.select-btn-field__message--success').exists()).toBe(true)
817
+ successWrapper.unmount()
818
+ })
819
+
820
+ it('applies the matching state class on the listbox container', async () => {
821
+ const wrapper = mount(SelectBtnField, {
822
+ props: {
823
+ label: 'Moyen de contact',
824
+ errorMessages: ['Erreur'],
825
+ items: [{ text: 'Email', value: 'email' }],
826
+ },
827
+ })
828
+ expect(wrapper.find('.select-btn-field__options--error').exists()).toBe(true)
829
+ wrapper.unmount()
830
+ })
831
+
832
+ it('does not render the messages area when hideDetails is true', () => {
833
+ const wrapper = mount(SelectBtnField, {
834
+ props: {
835
+ label: 'Moyen de contact',
836
+ hideDetails: true,
837
+ errorMessages: ['Erreur masquée'],
838
+ items: [{ text: 'Email', value: 'email' }],
839
+ },
840
+ })
841
+
842
+ expect(wrapper.find('.select-btn-field__messages').exists()).toBe(false)
843
+ expect(wrapper.text()).not.toContain('Erreur masquée')
844
+
845
+ wrapper.unmount()
846
+ })
847
+
848
+ it('does not display success message text when showSuccessMessages is false', async () => {
849
+ const wrapper = mount(SelectBtnField, {
850
+ props: {
851
+ label: 'Moyen de contact',
852
+ modelValue: 'email',
853
+ items: [{ text: 'Email', value: 'email' }],
854
+ customSuccessRules: [{
855
+ type: 'custom',
856
+ options: {
857
+ validate: (v: unknown) => v === 'email',
858
+ successMessage: 'Choix optimal.',
859
+ },
860
+ }],
861
+ },
862
+ })
863
+ const vm = wrapper.vm as unknown as { validateOnSubmit: () => Promise<boolean>, hasSuccess: boolean }
864
+ await vm.validateOnSubmit()
865
+ await flushPromises()
866
+
867
+ // L'état de succès est actif mais le message texte reste masqué
868
+ expect(vm.hasSuccess).toBe(true)
869
+ expect(wrapper.text()).not.toContain('Choix optimal.')
870
+
871
+ wrapper.unmount()
872
+ })
873
+
874
+ it('does not enter success state when filled without an explicit success source', async () => {
875
+ const wrapper = mount({
876
+ components: { SelectBtnField },
877
+ template: `<SelectBtnField ref="field" v-model="value" label="Moyen de contact" :items="items" show-success-messages />`,
878
+ setup() {
879
+ return {
880
+ value: ref<string | null>(null),
881
+ items: [{ text: 'Email', value: 'email' }],
882
+ }
883
+ },
884
+ })
885
+
886
+ // Sélection d'un item (champ rempli) sans aucune règle/source de succès
887
+ await wrapper.find('[role="option"]').trigger('click')
888
+ await flushPromises()
889
+
890
+ const field = wrapper.findComponent(SelectBtnField)
891
+ const vm = field.vm as unknown as { hasSuccess: boolean, successes: string[] }
892
+ expect(vm.hasSuccess).toBe(false)
893
+ expect(vm.successes).toHaveLength(0)
894
+ // Pas de coloration verte de l'item sélectionné
895
+ expect(wrapper.find('.select-btn-field__options--success').exists()).toBe(false)
896
+
897
+ wrapper.unmount()
898
+ })
899
+
900
+ it('does not allow selection when disabled', async () => {
901
+ const wrapper = mount(SelectBtnField, {
902
+ props: {
903
+ label: 'Moyen de contact',
904
+ disabled: true,
905
+ items: [{ text: 'Email', value: 'email' }],
906
+ },
907
+ })
908
+
909
+ await wrapper.find('[role="option"]').trigger('click')
910
+ expect(wrapper.emitted()).not.toHaveProperty('update:modelValue')
911
+ expect(wrapper.find('[role="listbox"]').attributes('aria-disabled')).toBe('true')
912
+
913
+ wrapper.unmount()
914
+ })
915
+ })
547
916
  })
@@ -74,17 +74,21 @@ exports[`SelectBtnField > display correctly in dark mode with an error 1`] = `
74
74
  </div>
75
75
  </li>
76
76
  </ul>
77
- <p class="
78
- mb-0
77
+ <div class="
79
78
  mt-2
80
- opacity-100
81
79
  px-3
82
- text-error
83
- theme--light
84
- v-messages
80
+ select-btn-field__messages
85
81
  ">
86
- Test
87
- </p>
82
+ <p class="
83
+ mb-0
84
+ opacity-100
85
+ select-btn-field__message
86
+ select-btn-field__message--error
87
+ v-messages
88
+ ">
89
+ Test
90
+ </p>
91
+ </div>
88
92
  </div>
89
93
  </div>
90
94
  </div>
@@ -180,21 +184,25 @@ exports[`SelectBtnField > display correctly with an error 1`] = `
180
184
  </div>
181
185
  </li>
182
186
  </ul>
183
- <p class="
184
- mb-0
187
+ <div class="
185
188
  mt-2
186
- opacity-100
187
189
  px-3
188
- text-error
189
- theme--light
190
- v-messages
190
+ select-btn-field__messages
191
191
  ">
192
- Test
193
- </p>
192
+ <p class="
193
+ mb-0
194
+ opacity-100
195
+ select-btn-field__message
196
+ select-btn-field__message--error
197
+ v-messages
198
+ ">
199
+ Test
200
+ </p>
201
+ </div>
194
202
  </div>
195
203
  `;
196
204
 
197
- exports[`SelectBtnField > display correctly with in dark mode with an hint 1`] = `
205
+ exports[`SelectBtnField > display correctly with in dark mode with a help text 1`] = `
198
206
  <div class="
199
207
  v-application
200
208
  v-layout
@@ -267,16 +275,18 @@ exports[`SelectBtnField > display correctly with in dark mode with an hint 1`] =
267
275
  </div>
268
276
  </li>
269
277
  </ul>
270
- <p class="
271
- mb-0
278
+ <div class="
272
279
  mt-2
273
- opacity-100
274
280
  px-3
275
- theme--light
276
- v-messages
281
+ select-btn-field__messages
277
282
  ">
278
- Test
279
- </p>
283
+ <p class="
284
+ mb-0
285
+ select-btn-field__help-text
286
+ ">
287
+ Test
288
+ </p>
289
+ </div>
280
290
  </div>
281
291
  </div>
282
292
  </div>
@@ -371,16 +381,18 @@ exports[`SelectBtnField > render correctly in multiple mode 1`] = `
371
381
  </div>
372
382
  </li>
373
383
  </ul>
374
- <p class="
375
- mb-0
384
+ <div class="
376
385
  mt-2
377
- opacity-100
378
386
  px-3
379
- theme--light
380
- v-messages
387
+ select-btn-field__messages
381
388
  ">
382
- Test
383
- </p>
389
+ <p class="
390
+ mb-0
391
+ select-btn-field__help-text
392
+ ">
393
+ Test
394
+ </p>
395
+ </div>
384
396
  </div>
385
397
  `;
386
398
 
@@ -490,15 +502,17 @@ exports[`SelectBtnField > renders correctly with props 1`] = `
490
502
  </div>
491
503
  </li>
492
504
  </ul>
493
- <p class="
494
- mb-0
505
+ <div class="
495
506
  mt-2
496
- opacity-100
497
507
  px-3
498
- theme--light
499
- v-messages
508
+ select-btn-field__messages
500
509
  ">
501
- Test
502
- </p>
510
+ <p class="
511
+ mb-0
512
+ select-btn-field__help-text
513
+ ">
514
+ Test
515
+ </p>
516
+ </div>
503
517
  </div>
504
518
  `;