@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
@@ -0,0 +1,105 @@
1
+ import { computed, ref, toRef, type ComputedRef, type Ref } from 'vue'
2
+ import { useValidation, type ValidationRule } from '@/composables/unifyValidation/useValidation'
3
+ import type { SyCheckboxValidationProps } from '../types'
4
+
5
+ export interface UseSyCheckboxValidationReturn {
6
+ validate: () => Promise<boolean>
7
+ validateOnSubmit: () => Promise<boolean>
8
+ clearValidation: () => void
9
+ errors: Ref<string[]>
10
+ warnings: Ref<string[]>
11
+ successes: Ref<string[]>
12
+ hasError: ComputedRef<boolean | undefined>
13
+ hasWarning: ComputedRef<boolean | undefined>
14
+ hasSuccess: ComputedRef<boolean | undefined>
15
+ defaultRules: ComputedRef<ValidationRule[]>
16
+ focused: Ref<boolean>
17
+ }
18
+
19
+ /**
20
+ * Composable de validation du composant SyCheckbox
21
+ *
22
+ * Version simplifiée du système unifié : pour une case à cocher, « required » signifie
23
+ * que la case doit être cochée (`value === true`). On utilise donc une règle `custom`
24
+ * plutôt que la règle `required` générique, qui considère `false` comme une valeur valide.
25
+ *
26
+ * @example
27
+ * const { validate, errors, hasError } = useSyCheckboxValidation(props, model, focused)
28
+ */
29
+ export function useSyCheckboxValidation(
30
+ props: SyCheckboxValidationProps,
31
+ model: Ref<boolean | null>,
32
+ focused?: Ref<boolean>,
33
+ ): UseSyCheckboxValidationReturn {
34
+ const focusedRef = focused !== undefined ? focused : ref(false)
35
+
36
+ // « required » pour une case = doit être cochée (true)
37
+ const defaultRules = computed<ValidationRule[]>(() =>
38
+ props.required
39
+ ? [{
40
+ type: 'custom',
41
+ options: {
42
+ validate: (value: unknown) => value === true,
43
+ message: `Le champ ${props.label || 'ce champ'} est requis.`,
44
+ fieldIdentifier: props.label,
45
+ },
46
+ }]
47
+ : [],
48
+ )
49
+
50
+ // Vuetify ne gère pas les messages de succès : on les désactive en mode Vuetify
51
+ const effectiveShowSuccessMessages = computed(() =>
52
+ props.useVuetifyValidation ? false : (props.showSuccessMessages ?? false),
53
+ )
54
+
55
+ const {
56
+ validate,
57
+ clearValidation,
58
+ errors,
59
+ warnings,
60
+ successes,
61
+ hasError,
62
+ hasWarning,
63
+ hasSuccess,
64
+ } = useValidation({
65
+ modelValue: model,
66
+ readonly: toRef(() => props.readonly ?? false),
67
+ disabled: toRef(() => props.disabled ?? false),
68
+ required: toRef(() => props.required ?? false),
69
+ isValidateOnBlur: toRef(() => props.isValidateOnBlur ?? false),
70
+ showSuccessMessages: effectiveShowSuccessMessages,
71
+ disableErrorHandling: toRef(() => props.disableErrorHandling ?? false),
72
+ useVuetifyValidation: toRef(() => props.useVuetifyValidation ?? false),
73
+ label: toRef(() => props.label ?? ''),
74
+ rules: toRef(() => props.rules),
75
+ customRules: computed(() => [...defaultRules.value, ...(props.customRules ?? [])]),
76
+ customWarningRules: toRef(() => props.customWarningRules ?? []),
77
+ customSuccessRules: toRef(() => props.customSuccessRules ?? []),
78
+ errorMessages: toRef(() => props.errorMessages ?? null),
79
+ warningMessages: toRef(() => props.warningMessages ?? null),
80
+ successMessages: toRef(() => props.successMessages ?? null),
81
+ hasErrorProp: toRef(() => props.hasError ?? false),
82
+ hasWarningProp: toRef(() => props.hasWarning ?? false),
83
+ hasSuccessProp: toRef(() => props.hasSuccess ?? false),
84
+ maxErrors: toRef(() => props.maxErrors ?? 1),
85
+ focused: focusedRef,
86
+ })
87
+
88
+ const validateOnSubmit = async (): Promise<boolean> => {
89
+ return await validate()
90
+ }
91
+
92
+ return {
93
+ validate,
94
+ validateOnSubmit,
95
+ clearValidation,
96
+ errors,
97
+ warnings,
98
+ successes,
99
+ hasError,
100
+ hasWarning,
101
+ hasSuccess,
102
+ defaultRules,
103
+ focused: focusedRef,
104
+ }
105
+ }
@@ -178,4 +178,110 @@ describe('SyCheckbox', () => {
178
178
  await wrapper.find('.v-checkbox').trigger('click')
179
179
  expect(wrapper.emitted('update:modelValue')).toBeFalsy()
180
180
  })
181
+
182
+ it('affiche le helpText quand aucun message de validation n\'est présent', () => {
183
+ const wrapper = mount(SyCheckbox, {
184
+ props: {
185
+ label: 'CGU',
186
+ helpText: 'Texte d\'aide',
187
+ },
188
+ })
189
+
190
+ const help = wrapper.find('.help-text-below')
191
+ expect(help.exists()).toBe(true)
192
+ expect(help.text()).toContain('Texte d\'aide')
193
+ })
194
+
195
+ it('masque le helpText et affiche l\'erreur quand la validation échoue', async () => {
196
+ const wrapper = mount(SyCheckbox, {
197
+ props: {
198
+ label: 'CGU',
199
+ helpText: 'Texte d\'aide',
200
+ required: true,
201
+ modelValue: false,
202
+ },
203
+ })
204
+
205
+ await wrapper.vm.validateOnSubmit()
206
+ await nextTick()
207
+
208
+ expect(wrapper.find('.help-text-below').exists()).toBe(false)
209
+ expect(wrapper.find('.v-messages').text()).toContain('CGU est requis')
210
+ })
211
+
212
+ it('disableErrorHandling : aucune erreur affichée même si requis et décoché', async () => {
213
+ const wrapper = mount(SyCheckbox, {
214
+ props: {
215
+ label: 'CGU',
216
+ required: true,
217
+ disableErrorHandling: true,
218
+ modelValue: false,
219
+ },
220
+ })
221
+
222
+ const isValid = await wrapper.vm.validateOnSubmit()
223
+ await nextTick()
224
+
225
+ expect(isValid).toBe(true)
226
+ expect(wrapper.find('.error-field').exists()).toBe(false)
227
+ expect(wrapper.find('.v-messages__message').exists()).toBe(false)
228
+ })
229
+
230
+ it('affiche un message d\'avertissement via customWarningRules', async () => {
231
+ const wrapper = mount(SyCheckbox, {
232
+ props: {
233
+ label: 'CGU',
234
+ modelValue: false,
235
+ customWarningRules: [{
236
+ type: 'custom',
237
+ options: {
238
+ validate: (value: boolean) => value === true,
239
+ warningMessage: 'Avertissement de test',
240
+ },
241
+ }],
242
+ },
243
+ })
244
+
245
+ await wrapper.vm.validateOnSubmit()
246
+ await nextTick()
247
+
248
+ expect(wrapper.find('.warning-field').exists()).toBe(true)
249
+ expect(wrapper.find('.v-messages').text()).toContain('Avertissement de test')
250
+ })
251
+
252
+ it('affiche un message de succès via customSuccessRules et showSuccessMessages', async () => {
253
+ const wrapper = mount(SyCheckbox, {
254
+ props: {
255
+ label: 'CGU',
256
+ modelValue: true,
257
+ showSuccessMessages: true,
258
+ customSuccessRules: [{
259
+ type: 'custom',
260
+ options: {
261
+ validate: (value: boolean) => value === true,
262
+ successMessage: 'Succès de test',
263
+ },
264
+ }],
265
+ },
266
+ })
267
+
268
+ await wrapper.vm.validateOnSubmit()
269
+ await nextTick()
270
+
271
+ expect(wrapper.find('.success-field').exists()).toBe(true)
272
+ expect(wrapper.find('.v-messages').text()).toContain('Succès de test')
273
+ })
274
+
275
+ it('affiche les messages externes (errorMessages + hasError)', () => {
276
+ const wrapper = mount(SyCheckbox, {
277
+ props: {
278
+ label: 'CGU',
279
+ hasError: true,
280
+ errorMessages: ['Erreur externe'],
281
+ },
282
+ })
283
+
284
+ expect(wrapper.find('.error-field').exists()).toBe(true)
285
+ expect(wrapper.find('.v-messages').text()).toContain('Erreur externe')
286
+ })
181
287
  })
@@ -0,0 +1,98 @@
1
+ import { describe, it, expect } from 'vitest'
2
+ import { defineComponent, ref, nextTick, type Ref } from 'vue'
3
+ import { mount } from '@vue/test-utils'
4
+ import { useSyCheckboxValidation, type UseSyCheckboxValidationReturn } from '../composables/useSyCheckboxValidation'
5
+ import type { SyCheckboxValidationProps } from '../types'
6
+
7
+ function createProps(overrides: Partial<SyCheckboxValidationProps> = {}): SyCheckboxValidationProps {
8
+ return {
9
+ label: 'Conditions générales',
10
+ required: false,
11
+ readonly: false,
12
+ disabled: false,
13
+ customRules: [],
14
+ customWarningRules: [],
15
+ customSuccessRules: [],
16
+ errorMessages: null,
17
+ warningMessages: null,
18
+ successMessages: null,
19
+ showSuccessMessages: false,
20
+ isValidateOnBlur: false,
21
+ ...overrides,
22
+ }
23
+ }
24
+
25
+ // Exécute le composable dans un contexte de setup (requis par les composables Vuetify internes)
26
+ function withValidation(props: SyCheckboxValidationProps, model: Ref<boolean | null>): UseSyCheckboxValidationReturn {
27
+ let result!: UseSyCheckboxValidationReturn
28
+ const Harness = defineComponent({
29
+ setup() {
30
+ result = useSyCheckboxValidation(props, model)
31
+ return () => null
32
+ },
33
+ })
34
+ mount(Harness)
35
+ return result
36
+ }
37
+
38
+ describe('useSyCheckboxValidation', () => {
39
+ describe('required (la case doit être cochée)', () => {
40
+ it('échoue quand la case est décochée', async () => {
41
+ const model = ref<boolean | null>(false)
42
+ const { validate, errors, hasError } = withValidation(createProps({ required: true }), model)
43
+
44
+ expect(await validate()).toBe(false)
45
+ await nextTick()
46
+ expect(hasError.value).toBe(true)
47
+ expect(errors.value.join(' ')).toContain('Conditions générales est requis')
48
+ })
49
+
50
+ it('réussit quand la case est cochée', async () => {
51
+ const model = ref<boolean | null>(true)
52
+ const { validate, hasError } = withValidation(createProps({ required: true }), model)
53
+
54
+ expect(await validate()).toBe(true)
55
+ await nextTick()
56
+ expect(hasError.value).toBeFalsy()
57
+ })
58
+ })
59
+
60
+ it('non requis : valide même décochée', async () => {
61
+ const model = ref<boolean | null>(false)
62
+ const { validate, hasError } = withValidation(createProps({ required: false }), model)
63
+
64
+ expect(await validate()).toBe(true)
65
+ expect(hasError.value).toBeFalsy()
66
+ })
67
+
68
+ it('customRules : la case doit être cochée', async () => {
69
+ const model = ref<boolean | null>(false)
70
+ const { validate, errors } = withValidation(
71
+ createProps({
72
+ customRules: [{
73
+ type: 'custom',
74
+ options: {
75
+ validate: (value: unknown) => value === true,
76
+ message: 'Doit être coché',
77
+ },
78
+ }],
79
+ }),
80
+ model,
81
+ )
82
+
83
+ expect(await validate()).toBe(false)
84
+ await nextTick()
85
+ expect(errors.value.join(' ')).toContain('Doit être coché')
86
+ })
87
+
88
+ it('disableErrorHandling : ne produit pas d\'erreur', async () => {
89
+ const model = ref<boolean | null>(false)
90
+ const { validate, hasError } = withValidation(
91
+ createProps({ required: true, disableErrorHandling: true }),
92
+ model,
93
+ )
94
+
95
+ expect(await validate()).toBe(true)
96
+ expect(hasError.value).toBeFalsy()
97
+ })
98
+ })
@@ -0,0 +1,51 @@
1
+ import type { FieldValidationProps, ValidationRule, VuetifyValidationRule } from '@/composables/unifyValidation/useValidation'
2
+
3
+ /**
4
+ * Props du composant SyCheckbox
5
+ */
6
+ export interface SyCheckboxProps extends FieldValidationProps {
7
+ modelValue?: boolean | null
8
+ indeterminate?: boolean
9
+ label?: string
10
+ helpText?: string
11
+ ariaLabel?: string
12
+ ariaLabelledby?: string
13
+ title?: string
14
+ color?: string
15
+ hideDetails?: boolean | 'auto'
16
+ density?: 'default' | 'comfortable' | 'compact'
17
+ id?: string
18
+ name?: string
19
+ value?: unknown
20
+ trueValue?: unknown
21
+ falseValue?: unknown
22
+ controlsIds?: string[]
23
+ displayAsterisk?: boolean
24
+ decorative?: boolean
25
+ }
26
+
27
+ /**
28
+ * Props de validation passées au composable dédié
29
+ */
30
+ export interface SyCheckboxValidationProps extends FieldValidationProps {
31
+ modelValue?: boolean | null
32
+ required?: boolean
33
+ readonly?: boolean
34
+ disabled?: boolean
35
+ label?: string
36
+ customRules?: ValidationRule[]
37
+ customWarningRules?: ValidationRule[]
38
+ customSuccessRules?: ValidationRule[]
39
+ isValidateOnBlur?: boolean
40
+ showSuccessMessages?: boolean
41
+ useVuetifyValidation?: boolean
42
+ rules?: VuetifyValidationRule[]
43
+ errorMessages?: string[] | null
44
+ warningMessages?: string[] | null
45
+ successMessages?: string[] | null
46
+ hasError?: boolean
47
+ hasWarning?: boolean
48
+ hasSuccess?: boolean
49
+ maxErrors?: number
50
+ disableErrorHandling?: boolean
51
+ }
@@ -0,0 +1,50 @@
1
+ <script setup lang="ts">
2
+ import { mdiAlertOutline, mdiCheck, mdiAlertCircle } from '@mdi/js'
3
+ import { computed } from 'vue'
4
+ import SyIcon from '@/components/Customs/SyIcon/SyIcon.vue'
5
+
6
+ const props = defineProps<{
7
+ state?: 'default' | 'error' | 'success' | 'warning'
8
+ }>()
9
+
10
+ const validationIcon = computed(() => {
11
+ if (props.state === 'error') return mdiAlertCircle
12
+ if (props.state === 'warning') return mdiAlertOutline
13
+ if (props.state === 'success') return mdiCheck
14
+ return null
15
+ })
16
+
17
+ const className = computed(() => {
18
+ if (props.state === 'error') return 'error-icon'
19
+ if (props.state === 'warning') return 'warning-icon'
20
+ if (props.state === 'success') return 'success-icon'
21
+ return null
22
+ })
23
+ </script>
24
+ <template>
25
+ <SyIcon
26
+ v-if="validationIcon"
27
+ :icon="validationIcon"
28
+ :class="className"
29
+ class="field-state-icon"
30
+ decorative
31
+ />
32
+ </template>
33
+ <style scoped lang="scss">
34
+ .field-state-icon :deep(.v-icon__svg) {
35
+ opacity: 1;
36
+ }
37
+
38
+ .success-icon :deep(.v-icon__svg) {
39
+ fill: rgb(var(--v-theme-onSuccessVariant)) !important;
40
+ }
41
+
42
+ .warning-icon :deep(.v-icon__svg) {
43
+ fill: rgb(var(--v-theme-onWarningVariant)) !important;
44
+ }
45
+
46
+ .error-icon :deep(.v-icon__svg) {
47
+ fill: rgb(var(--v-theme-error)) !important;
48
+ opacity: 1;
49
+ }
50
+ </style>
@@ -22,6 +22,7 @@
22
22
  import { useNumberField } from './useNumberField'
23
23
  import { locales as defaultLocales } from './locales'
24
24
  import type { SyTextFieldProps } from './types'
25
+ import FieldState from './FieldState.vue'
25
26
 
26
27
  const props = withDefaults(
27
28
  defineProps<SyTextFieldProps>(),
@@ -157,7 +158,7 @@
157
158
  }
158
159
 
159
160
  const focused = ref(false)
160
- const { validate, errors, warnings, successes, hasError, hasWarning, hasSuccess, iconColor, clearButtonColorClass, validationIcon, hasMessages } = useSyTextFieldValidation({
161
+ const { validate, errors, warnings, successes, hasError, hasWarning, hasSuccess, iconColor, clearButtonColorClass, state, hasMessages } = useSyTextFieldValidation({
161
162
  modelValue: model,
162
163
  readonly: toRef(props, 'readonly'),
163
164
  disabled: toRef(props, 'disabled'),
@@ -246,6 +247,10 @@
246
247
  return
247
248
  }
248
249
 
250
+ if (event.inputType === 'insertFromPaste') {
251
+ return
252
+ }
253
+
249
254
  const hasDisallowed = props.type === 'number'
250
255
  ? hasDisallowedNumberCharacter(event.data)
251
256
  : event.data.replace(TEL_ALLOWED_CHARACTERS_PATTERN, '') !== event.data
@@ -274,7 +279,7 @@
274
279
  ? isAllowedNumberCharacter(event.key)
275
280
  : TEL_ALLOWED_SINGLE_CHARACTER_PATTERN.test(event.key)
276
281
 
277
- if (!allowedNonCharacterKeys.includes(event.key) && event.key.length === 1 && !isAllowedCharacter) {
282
+ if (!allowedNonCharacterKeys.includes(event.key) && event.key?.length === 1 && !isAllowedCharacter) {
278
283
  event.preventDefault()
279
284
  }
280
285
  }
@@ -293,14 +298,13 @@
293
298
  return isShouldDisplayAsterisk.value ? `${props.label} *` : props.label
294
299
  })
295
300
 
296
- // Détermine si le helpText doit être affiché à la position du message ou en dessous
301
+ // Détermine si le helpText doit être affiché dans le composant VTextField ou en dessous
297
302
  const showHelpTextAsMessage = computed(() => {
298
- // Afficher à la position du message si pas de messages d'erreur
299
303
  return props.helpText && !hasMessages.value
300
304
  })
301
305
 
302
306
  const showHelpTextBelow = computed(() => {
303
- // Afficher en dessous si il y a des messages d'erreur ET hideMessages n'est pas activé
307
+ // Afficher en dessous si il y a des messages d'erreur ET hideDetails n'est pas activé
304
308
  return props.helpText && hasMessages.value && !props.hideDetails
305
309
  })
306
310
 
@@ -718,10 +722,9 @@
718
722
  @keydown.enter.stop
719
723
  @keydown.space.stop
720
724
  />
721
- <SyIcon
722
- v-if="validationIcon && !props.appendInnerIcon"
723
- :icon="validationIcon"
724
- :decorative="true"
725
+ <FieldState
726
+ v-if="!props.appendInnerIcon"
727
+ :state="state"
725
728
  />
726
729
  <SyIcon
727
730
  v-if="props.appendInnerIcon && !props.noIcon"
@@ -1,7 +1,6 @@
1
1
  import { computed, type Ref } from 'vue'
2
2
  import { useValidation } from '@/composables/unifyValidation/useValidation'
3
3
  import type { ValidationRule as SyValidationRule, VuetifyValidationRule } from '@/composables/unifyValidation/useValidation'
4
- import { mdiAlertOutline, mdiCheck, mdiAlertCircle } from '@mdi/js'
5
4
 
6
5
  export function useSyTextFieldValidation(params: {
7
6
  modelValue: Ref<string | number | null | undefined>
@@ -38,7 +37,7 @@ export function useSyTextFieldValidation(params: {
38
37
  : [],
39
38
  )
40
39
 
41
- const { validate, errors, warnings, successes, hasError, hasWarning, hasSuccess, clearValidation } = useValidation({
40
+ const { validate, errors, warnings, successes, hasError, hasWarning, hasSuccess, state, clearValidation } = useValidation({
42
41
  modelValue: params.modelValue,
43
42
  readonly: params.readonly,
44
43
  disabled: params.disabled,
@@ -75,15 +74,7 @@ export function useSyTextFieldValidation(params: {
75
74
  // Le bouton clear garde toujours une couleur neutre, quel que soit l'état de validation
76
75
  const clearButtonColorClass = computed(() => 'text-iconBase')
77
76
 
78
- const validationIcon = computed(() => {
79
- if (hasError.value) return mdiAlertCircle
80
- if (hasWarning.value) return mdiAlertOutline
81
- if (hasSuccess.value) return mdiCheck
82
- return null
83
- })
84
-
85
77
  const hasMessages = computed(() => {
86
- if (params.disableErrorHandling.value) return false
87
78
  return (params.errorMessages.value?.length ?? 0) > 0 || hasError.value || hasWarning.value || (hasSuccess.value && params.showSuccessMessages.value)
88
79
  })
89
80
 
@@ -96,8 +87,8 @@ export function useSyTextFieldValidation(params: {
96
87
  hasSuccess,
97
88
  iconColor,
98
89
  clearButtonColorClass,
99
- validationIcon,
100
90
  hasMessages,
91
+ state,
101
92
  validate,
102
93
  clearValidation,
103
94
  }
@@ -73,7 +73,6 @@ import * as DataList from './DataList.stories';
73
73
  },
74
74
  ]
75
75
  </script>
76
- import '../../stories/styles/shared.css';
77
76
 
78
77
  <template>
79
78
  <DataList :items="items" />
@@ -71,7 +71,6 @@ import * as DataListGroup from './DataListGroup.stories';
71
71
  },
72
72
  ]
73
73
  </script>
74
- import '../../stories/styles/shared.css';
75
74
 
76
75
  <template>
77
76
  <DataListGroup :items="items" />
@@ -90,7 +90,6 @@ Vous pouvez personnaliser la liste des caractères diacritiques affichés dans l
90
90
  import { ref } from 'vue'
91
91
 
92
92
  const text = ref('')
93
- import '../../stories/styles/shared.css';
94
93
  const caracteres = ['é', 'è', 'ê', 'à', 'ç', 'ù']
95
94
  </script>
96
95
 
@@ -22,7 +22,6 @@ import * as DialogBoxStories from './DialogBox.stories';
22
22
  import { ref } from 'vue'
23
23
 
24
24
  const dialogOpen = ref(false)
25
- import '../../stories/styles/shared.css';
26
25
  </script>
27
26
 
28
27
  <template>