@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,1194 @@
1
+ import type { Meta, StoryObj } from '@storybook/vue3'
2
+ import { fn, userEvent, within } from '@storybook/test'
3
+ import { ref, watch } from 'vue'
4
+ import Captcha from '../Captcha.vue'
5
+ import { VBtn, VCard, VForm } from 'vuetify/components'
6
+ import SyForm from '../../Customs/SyForm/SyForm.vue'
7
+ import { getValidationDocumentation } from '@/composables/unifyValidation/documentationValidationProps'
8
+
9
+ const meta: Meta = {
10
+ title: 'Composants/Formulaires/Captcha/Validation',
11
+ component: Captcha,
12
+ argTypes: {
13
+ ...getValidationDocumentation('string'),
14
+ },
15
+ parameters: {
16
+ layout: 'centered',
17
+ docs: {
18
+ description: {
19
+ component: 'Exemples de validation pour le composant Captcha.',
20
+ },
21
+ },
22
+ controls: { exclude: ['onUpdate:modelValue', 'onUpdate:type', 'onImageError', 'onAudioError', 'onCreationError'] },
23
+ },
24
+ args: {
25
+ 'onUpdate:modelValue': fn(),
26
+ 'onUpdate:type': fn(),
27
+ 'onImageError': fn(),
28
+ 'onAudioError': fn(),
29
+ 'onCreationError': fn(),
30
+ 'readonly': false,
31
+ 'disabled': false,
32
+ 'required': false,
33
+ 'isValidateOnBlur': true,
34
+ 'showSuccessMessages': true,
35
+ 'disableErrorHandling': false,
36
+ 'customRules': [],
37
+ 'customWarningRules': [],
38
+ 'customSuccessRules': [],
39
+ 'errorMessages': null,
40
+ 'warningMessages': null,
41
+ 'successMessages': null,
42
+ 'hasError': false,
43
+ 'hasWarning': false,
44
+ 'hasSuccess': false,
45
+ 'useVuetifyValidation': false,
46
+ 'maxErrors': 1,
47
+ 'urlCreate': 'https://free.mockerapi.com/mock/0adac32b-e832-4553-aa7f-0011b7f35f0c',
48
+ 'urlGetImage': '/captcha/captcha.png',
49
+ 'urlGetAudio': '/captcha/captcha.mp3',
50
+ 'type': 'image',
51
+ 'modelValue': 'abc',
52
+ },
53
+ }
54
+
55
+ export default meta
56
+ type StoryArgs = {
57
+ modelValue?: string
58
+ } & Record<string, unknown>
59
+
60
+ type Story = StoryObj<StoryArgs>
61
+
62
+ export const WithError: Story = {
63
+ parameters: {
64
+ a11y: {
65
+ disable: true,
66
+ },
67
+ docs: {
68
+ description: {
69
+ story: 'Validation bloquante via `customRules` (message d\'erreur personnalisé).',
70
+ },
71
+ },
72
+ sourceCode: [
73
+ {
74
+ name: 'Template',
75
+ code: `
76
+ <Captcha
77
+ v-model="modelValue"
78
+ required
79
+ :custom-rules="[
80
+ {
81
+ type: 'custom',
82
+ options: {
83
+ validate: (v) => (v as string).length >= 6,
84
+ message: 'Le captcha doit contenir au moins 6 caractères.',
85
+ successMessage: 'Le captcha contient au moins 6 caractères.'
86
+ }
87
+ }
88
+ ]"
89
+ />
90
+ `,
91
+ },
92
+ {
93
+ name: 'Script',
94
+ code: `
95
+ import { ref } from 'vue'
96
+ import { Captcha } from '@cnamts/synapse'
97
+
98
+ const modelValue = ref('ytq')
99
+ `,
100
+ },
101
+ ],
102
+ },
103
+ render: (args) => {
104
+ return {
105
+ components: { Captcha, VCard },
106
+ setup() {
107
+ const captchaValue = ref(args.modelValue)
108
+ watch(() => args.modelValue, () => {
109
+ captchaValue.value = args.modelValue
110
+ })
111
+
112
+ return { args, captchaValue }
113
+ },
114
+ template: `
115
+ <VCard class="pa-8" max-width="400" min-width="400">
116
+ <Captcha
117
+ v-bind="args"
118
+ v-model="captchaValue"
119
+ />
120
+ </VCard>
121
+ `,
122
+ }
123
+ },
124
+ args: {
125
+ modelValue: 'ytq',
126
+ // Validation à la saisie pour que l'état (erreur/succès) se mette à jour
127
+ // immédiatement quand on modifie le champ, sans attendre le blur.
128
+ isValidateOnBlur: false,
129
+ customRules: [
130
+ {
131
+ type: 'custom',
132
+ options: {
133
+ validate: (v: unknown) => {
134
+ return (String(v || '')).length >= 6
135
+ },
136
+ message: 'Le captcha doit contenir au moins 6 caractères.',
137
+ successMessage: 'Le captcha contient au moins 6 caractères.',
138
+ },
139
+ },
140
+ ],
141
+ },
142
+ play: async ({ canvasElement }) => {
143
+ const input = within(canvasElement).getByRole('textbox')
144
+ await userEvent.clear(input)
145
+ await userEvent.type(input, 'ytq')
146
+ input.blur()
147
+ },
148
+ }
149
+
150
+ export const WithWarning: Story = {
151
+ ...WithError,
152
+ parameters: {
153
+ a11y: {
154
+ disable: true,
155
+ },
156
+ docs: {
157
+ description: {
158
+ story: 'Validation non bloquante via `customWarningRules` (message d\'avertissement personnalisé).',
159
+ },
160
+ },
161
+ sourceCode: [
162
+ {
163
+ name: 'Template',
164
+ code: `
165
+ <Captcha
166
+ v-model="modelValue"
167
+ required
168
+ :custom-warning-rules="[
169
+ {
170
+ type: 'custom',
171
+ options: {
172
+ validate: (v) => (String(v || '')).length >= 6,
173
+ warningMessage: 'Le captcha devrait idéalement contenir au moins 6 caractères.'
174
+ }
175
+ }
176
+ ]"
177
+ />
178
+ `,
179
+ },
180
+ {
181
+ name: 'Script',
182
+ code: `
183
+ import { ref } from 'vue'
184
+ import { Captcha } from '@cnamts/synapse'
185
+
186
+ const modelValue = ref('ytq')
187
+ `,
188
+ },
189
+ ],
190
+ },
191
+ args: {
192
+ ...WithError.args,
193
+ customRules: [],
194
+ customWarningRules: [
195
+ {
196
+ type: 'custom',
197
+ options: {
198
+ validate: (v: unknown) => {
199
+ return (String(v || '')).length >= 6
200
+ },
201
+ warningMessage: 'Le captcha devrait idéalement contenir au moins 6 caractères.',
202
+ },
203
+ },
204
+ ],
205
+ },
206
+ }
207
+
208
+ export const WithSuccess: Story = {
209
+ ...WithError,
210
+ parameters: {
211
+ a11y: {
212
+ disable: true,
213
+ },
214
+ docs: {
215
+ description: {
216
+ story: 'Validation de succès via `customSuccessRules` (message de succès personnalisé).',
217
+ },
218
+ },
219
+ sourceCode: [
220
+ {
221
+ name: 'Template',
222
+ code: `
223
+ <Captcha
224
+ v-model="modelValue"
225
+ required
226
+ :custom-rules="[
227
+ {
228
+ type: 'custom',
229
+ options: {
230
+ validate: (v) => (String(v || '')).length >= 6,
231
+ message: 'Le captcha doit contenir au moins 6 caractères.'
232
+ }
233
+ }
234
+ ]"
235
+ :custom-success-rules="[
236
+ {
237
+ type: 'custom',
238
+ options: {
239
+ validate: (v) => (String(v || '')).length >= 6,
240
+ successMessage: 'Le captcha est correctement renseigné.'
241
+ }
242
+ }
243
+ ]"
244
+ />
245
+ `,
246
+ },
247
+ {
248
+ name: 'Script',
249
+ code: `
250
+ import { ref } from 'vue'
251
+ import { Captcha } from '@cnamts/synapse'
252
+
253
+ const modelValue = ref('ytqZNq')
254
+ `,
255
+ },
256
+ ],
257
+ },
258
+ args: {
259
+ ...WithError.args,
260
+ customRules: [
261
+ {
262
+ type: 'custom',
263
+ options: {
264
+ validate: (v: unknown) => (String(v || '')).length >= 6,
265
+ message: 'Le captcha doit contenir au moins 6 caractères.',
266
+ },
267
+ },
268
+ ],
269
+ customSuccessRules: [
270
+ {
271
+ type: 'custom',
272
+ options: {
273
+ validate: (v: unknown) => (String(v || '')).length >= 6,
274
+ successMessage: 'Le captcha est correctement renseigné.',
275
+ },
276
+ },
277
+ ],
278
+ },
279
+ play: async ({ canvasElement }) => {
280
+ const input = within(canvasElement).getByRole('textbox')
281
+ await userEvent.clear(input)
282
+ await userEvent.type(input, 'ytqZNq')
283
+ input.blur()
284
+ },
285
+ }
286
+
287
+ export const ValidateOnInput: Story = {
288
+ parameters: {
289
+ a11y: {
290
+ disable: true,
291
+ },
292
+ docs: {
293
+ description: {
294
+ story: 'Validation déclenchée à chaque frappe via `isValidateOnBlur=false`.',
295
+ },
296
+ },
297
+ sourceCode: [
298
+ {
299
+ name: 'Template',
300
+ code: `
301
+ <Captcha
302
+ v-model="value"
303
+ required
304
+ :is-validate-on-blur="false"
305
+ :custom-rules="[
306
+ {
307
+ type: 'custom',
308
+ options: {
309
+ validate: (value) => String(value || '').length >= 6,
310
+ message: 'Le captcha doit contenir au moins 6 caractères.'
311
+ }
312
+ },
313
+ {
314
+ type: 'custom',
315
+ options: {
316
+ validate: async (value) => {
317
+ try {
318
+ const r = await verifyCaptcha()
319
+ return r.response.data.message === 'Success'
320
+ }
321
+ catch {
322
+ return false
323
+ }
324
+ },
325
+ message: 'Le captcha est incorrect',
326
+ successMessage: 'Le captcha est correct'
327
+ }
328
+ }
329
+ ]"
330
+ />
331
+ `,
332
+ },
333
+ {
334
+ name: 'Script',
335
+ code: `
336
+ import { ref } from 'vue'
337
+ import { Captcha } from '@cnamts/synapse'
338
+
339
+ const value = ref('abc')
340
+
341
+ const verifyCaptcha = () => {
342
+ // call the API to verify the captcha and return the response
343
+ }
344
+ `,
345
+ },
346
+ ],
347
+ },
348
+ render: (args) => {
349
+ return {
350
+ components: { Captcha, VCard },
351
+ setup() {
352
+ const captchaValue = ref(args.modelValue)
353
+ watch(() => args.modelValue, () => {
354
+ captchaValue.value = args.modelValue
355
+ })
356
+ const verifyCaptcha = () => {
357
+ if (captchaValue.value === 'ytqZNq' || captchaValue.value === '941335') {
358
+ return Promise.resolve({ response: { data: { message: 'Success' } } })
359
+ }
360
+ return Promise.reject({ response: { data: { message: 'Le captcha est incorrect' } } })
361
+ }
362
+
363
+ return { args, captchaValue, verifyCaptcha }
364
+ },
365
+ template: `
366
+ <VCard class="pa-8" max-width="400" min-width="400">
367
+ <p class="mb-4">La validation se déclenche à chaque modification de la valeur.</p>
368
+ <Captcha
369
+ v-bind="args"
370
+ v-model="captchaValue"
371
+ :is-validate-on-blur="false"
372
+ :custom-rules="[
373
+ {
374
+ type: 'custom',
375
+ options: {
376
+ validate: (value) => String(value || '').length >= 6,
377
+ message: 'Le captcha doit contenir au moins 6 caractères.',
378
+ },
379
+ },
380
+ {
381
+ type: 'custom',
382
+ options: {
383
+ validate: async (value) => {
384
+ try {
385
+ const r = await verifyCaptcha()
386
+ return r.response.data.message === 'Success'
387
+ }
388
+ catch {
389
+ return false
390
+ }
391
+ },
392
+ message: 'Le captcha est incorrect',
393
+ successMessage: 'Le captcha est correct',
394
+ }
395
+ },
396
+ ]"
397
+ />
398
+ </VCard>
399
+ `,
400
+ }
401
+ },
402
+ play: async ({ canvasElement }) => {
403
+ const input = within(canvasElement).getByRole('textbox')
404
+ await userEvent.clear(input)
405
+ // Saisie de la bonne réponse : la validation se met à jour à chaque frappe
406
+ // (erreur tant que < 6 caractères, puis succès une fois le bon captcha saisi).
407
+ await userEvent.type(input, 'ytqZNq')
408
+ },
409
+ }
410
+
411
+ export const ExternalMessages: Story = {
412
+ parameters: {
413
+ a11y: {
414
+ disable: true,
415
+ },
416
+ docs: {
417
+ description: {
418
+ story: 'Messages injectés par le parent via `errorMessages`, `warningMessages` et `successMessages`.',
419
+ },
420
+ },
421
+ sourceCode: [
422
+ {
423
+ name: 'Template',
424
+ code: `
425
+ <VCard class="pa-8" max-width="400">
426
+ <Captcha
427
+ v-model="value"
428
+ :error-messages="errorMessages"
429
+ :warning-messages="warningMessages"
430
+ :success-messages="successMessages"
431
+ />
432
+ <div class="mt-4 d-flex flex-wrap ga-2">
433
+ <VBtn @click="setError">Simuler une erreur</VBtn>
434
+ <VBtn @click="setWarning">Simuler un avertissement</VBtn>
435
+ <VBtn @click="setSuccess">Simuler un succès</VBtn>
436
+ <VBtn @click="reset">Réinitialiser</VBtn>
437
+ </div>
438
+ </VCard>
439
+ `,
440
+ },
441
+ ],
442
+ },
443
+ render: args => ({
444
+ components: { Captcha, VBtn, VCard },
445
+ setup() {
446
+ const captchaValue = ref(args.modelValue)
447
+ watch(() => args.modelValue, (newValue) => {
448
+ captchaValue.value = newValue
449
+ })
450
+ const errorMessages = ref<string[] | null>(null)
451
+ const warningMessages = ref<string[] | null>(null)
452
+ const successMessages = ref<string[] | null>(null)
453
+
454
+ const verifyCaptcha = () => {
455
+ if (captchaValue.value === 'ytqZNq' || captchaValue.value === '941335') {
456
+ return Promise.resolve({ response: { data: { message: 'Success' } } })
457
+ }
458
+ return Promise.reject({ response: { data: { message: 'Le captcha est incorrect' } } })
459
+ }
460
+
461
+ function setError() {
462
+ errorMessages.value = ['Ce captcha est déjà utilisé']
463
+ warningMessages.value = null
464
+ successMessages.value = null
465
+ }
466
+
467
+ function setWarning() {
468
+ errorMessages.value = null
469
+ warningMessages.value = ['Ce captcha pourrait être plus lisible']
470
+ successMessages.value = null
471
+ }
472
+
473
+ function setSuccess() {
474
+ errorMessages.value = null
475
+ warningMessages.value = null
476
+ successMessages.value = ['Captcha accepté']
477
+ }
478
+
479
+ function reset() {
480
+ errorMessages.value = null
481
+ warningMessages.value = null
482
+ successMessages.value = null
483
+ }
484
+
485
+ return { args, captchaValue, errorMessages, warningMessages, successMessages, verifyCaptcha, setError, setWarning, setSuccess, reset }
486
+ },
487
+ template: `
488
+ <VCard class="pa-8" max-width="400" min-width="400">
489
+ <p class="mb-4">Les messages sont fournis directement par le parent sans déclencher de règle de validation.</p>
490
+ <Captcha
491
+ v-bind="args"
492
+ v-model="captchaValue"
493
+ :error-messages="errorMessages"
494
+ :warning-messages="warningMessages"
495
+ :success-messages="successMessages"
496
+ />
497
+ </VCard>
498
+ <div class="mt-4 d-flex flex-wrap ga-2">
499
+ <VBtn color="error" @click="setError">Simuler une erreur</VBtn>
500
+ <VBtn color="warning" @click="setWarning">Simuler un avertissement</VBtn>
501
+ <VBtn color="success" @click="setSuccess">Simuler un succès</VBtn>
502
+ <VBtn color="black" @click="reset">Réinitialiser</VBtn>
503
+ </div>
504
+ `,
505
+ }),
506
+ }
507
+
508
+ export const DisableErrorHandling: Story = {
509
+ parameters: {
510
+ a11y: {
511
+ disable: true,
512
+ },
513
+ docs: {
514
+ description: {
515
+ story: 'Désactivation de la gestion des erreurs via `disableErrorHandling`.',
516
+ },
517
+ },
518
+ sourceCode: [
519
+ {
520
+ name: 'Template',
521
+ code: `<template>
522
+ <Captcha
523
+ v-model="value1"
524
+ required
525
+ :custom-rules="customRules"
526
+ />
527
+
528
+ <Captcha
529
+ v-model="value2"
530
+ required
531
+ disable-error-handling
532
+ :custom-rules="customRules"
533
+ />
534
+ </template>`,
535
+ },
536
+ {
537
+ name: 'Script',
538
+ code: `<script setup lang="ts">
539
+ import { ref } from 'vue'
540
+ import { Captcha } from '@cnamts/synapse'
541
+ const value1 = ref('')
542
+ const value2 = ref('')
543
+
544
+ const verifyCaptcha = (captchaValue: string | null) => {
545
+ // Simulate API call to verify captcha
546
+ }
547
+
548
+ const customRules = [
549
+ {
550
+ type: 'custom',
551
+ options: {
552
+ validate: (value: string) => {
553
+ if (!value || value.trim().length === 0) {
554
+ return false
555
+ }
556
+ return true
557
+ },
558
+ message: 'Ce champ est requis.',
559
+ }
560
+ },
561
+ {
562
+ type: 'custom',
563
+ options: {
564
+ validate: async (value: string) => {
565
+ try {
566
+ const r = await verifyCaptcha(value)
567
+ return r.response.data.message === 'Success'
568
+ }
569
+ catch {
570
+ return false
571
+ }
572
+ },
573
+ message: 'Le captcha est incorrect.',
574
+ }
575
+ },
576
+ ]
577
+ </script>`,
578
+ },
579
+ ],
580
+ },
581
+ render: args => ({
582
+ components: { Captcha, VCard },
583
+ setup() {
584
+ const value1 = ref('')
585
+ const value2 = ref('')
586
+
587
+ const verifyCaptcha = (captchaValue: string | null) => {
588
+ if (captchaValue === 'ytqZNq' || captchaValue === '941335') {
589
+ return Promise.resolve({ response: { data: { message: 'Success' } } })
590
+ }
591
+ return Promise.reject({ response: { data: { message: 'Le captcha est incorrect' } } })
592
+ }
593
+
594
+ const customRules = [
595
+ {
596
+ type: 'custom',
597
+ options: {
598
+ validate: (value: string) => {
599
+ if (!value || value.trim().length === 0) {
600
+ return false
601
+ }
602
+ return true
603
+ },
604
+ message: 'Ce champ est requis.',
605
+ fieldIdentifier: 'captcha',
606
+ },
607
+ },
608
+ {
609
+ type: 'custom',
610
+ options: {
611
+ validate: async (value: string) => {
612
+ try {
613
+ const r = await verifyCaptcha(value)
614
+ return r.response.data.message === 'Success'
615
+ }
616
+ catch {
617
+ return false
618
+ }
619
+ },
620
+ message: 'Le captcha est incorrect.',
621
+ },
622
+ },
623
+ ]
624
+
625
+ return { args, value1, value2, customRules }
626
+ },
627
+ template: `
628
+ <div>
629
+ <p class="mb-4">Cette démonstration compare un Captcha standard et un avec <code>disableErrorHandling=true</code>.</p>
630
+
631
+ <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 64px; margin-bottom: 16px;">
632
+ <div>
633
+ <p class="text-subtitle-2 mb-2">Validation normale</p>
634
+ <VCard class="pa-8" max-width="400" min-width="400">
635
+ <Captcha
636
+ v-bind="args"
637
+ v-model="value1"
638
+ required
639
+ :custom-rules="customRules"
640
+ />
641
+ </VCard>
642
+ </div>
643
+
644
+ <div>
645
+ <p class="text-subtitle-2 mb-2">Sans gestion d'erreurs</p>
646
+ <VCard class="pa-8" max-width="400" min-width="400">
647
+ <Captcha
648
+ v-bind="args"
649
+ v-model="value2"
650
+ required
651
+ disable-error-handling
652
+ :custom-rules="customRules"
653
+ />
654
+ </VCard>
655
+ </div>
656
+ </div>
657
+
658
+ <div class="mt-4 text-body-2">
659
+ <p>Instructions :</p>
660
+ <ol>
661
+ <li class="ml-4">Cliquez dans un champ puis en dehors pour déclencher la validation</li>
662
+ <li class="ml-4">Le champ de gauche affichera une erreur requise, mais pas celui de droite</li>
663
+ </ol>
664
+ </div>
665
+ </div>
666
+ `,
667
+ }),
668
+ }
669
+
670
+ export const SyFormValidation: Story = {
671
+ parameters: {
672
+ a11y: {
673
+ disable: true,
674
+ },
675
+ docs: {
676
+ description: {
677
+ story: 'Intégration avec `SyForm` pour soumettre un Captcha validé par règles personnalisées.',
678
+ },
679
+ },
680
+ sourceCode: [
681
+ {
682
+ name: 'Template',
683
+ code: `<template>
684
+ <SyForm @submit="handleSubmit">
685
+ <VCard class="pa-8" max-width="400">
686
+ <Captcha
687
+ v-model="value"
688
+ :custom-rules="customRules"
689
+ required
690
+ />
691
+ <div class="mt-4">
692
+ <VBtn type="submit" color="primary">Valider</VBtn>
693
+ </div>
694
+ </VCard>
695
+ </SyForm>
696
+ </template>`,
697
+ },
698
+ {
699
+ name: 'Script',
700
+ code: `<script setup lang="ts">
701
+ import { ref } from 'vue'
702
+ import { Captcha, SyForm } from '@cnamts/synapse'
703
+
704
+ const value = ref('')
705
+
706
+ const verifyCaptcha = () => {
707
+ // Simulate API call to verify captcha
708
+ }
709
+
710
+ const customRules = [
711
+ {
712
+ type: 'custom',
713
+ options: {
714
+ validate: (value: string) => String(value || '').length >= 6,
715
+ message: 'Le captcha doit contenir au moins 6 caractères.',
716
+ fieldIdentifier: 'captcha',
717
+ },
718
+ },
719
+ {
720
+ type: 'custom',
721
+ options: {
722
+ validate: async (value: string) => {
723
+ try {
724
+ const r = await verifyCaptcha()
725
+ return r.response.data.message === 'Success'
726
+ }
727
+ catch {
728
+ return false
729
+ }
730
+ },
731
+ message: 'Le captcha est incorrect',
732
+ fieldIdentifier: 'captcha',
733
+ successMessage: 'Le captcha est correct',
734
+ },
735
+ },
736
+ ]
737
+
738
+ function handleSubmit(e: { isValid: boolean }) {
739
+ alert(e.isValid ? 'Valeur valide !' : 'Veuillez corriger les erreurs.')
740
+ }
741
+ </script>`,
742
+ },
743
+ ],
744
+ },
745
+ render: args => ({
746
+ components: { Captcha, VBtn, SyForm },
747
+ setup() {
748
+ const captchaValue = ref(args.modelValue)
749
+ watch(() => args.modelValue, (newValue) => {
750
+ captchaValue.value = newValue
751
+ })
752
+
753
+ const verifyCaptcha = () => {
754
+ if (captchaValue.value === 'ytqZNq' || captchaValue.value === '941335') {
755
+ return Promise.resolve({ response: { data: { message: 'Success' } } })
756
+ }
757
+ return Promise.reject({ response: { data: { message: 'Le captcha est incorrect' } } })
758
+ }
759
+
760
+ const customRules = [
761
+ {
762
+ type: 'custom',
763
+ options: {
764
+ validate: (value: string) => String(value || '').length >= 6,
765
+ message: 'Le captcha doit contenir au moins 6 caractères.',
766
+ fieldIdentifier: 'captcha',
767
+ },
768
+ },
769
+ {
770
+ type: 'custom',
771
+ options: {
772
+ validate: async () => {
773
+ try {
774
+ const r = await verifyCaptcha()
775
+ return r.response.data.message === 'Success'
776
+ }
777
+ catch {
778
+ return false
779
+ }
780
+ },
781
+ message: 'Le captcha est incorrect',
782
+ fieldIdentifier: 'captcha',
783
+ successMessage: 'Le captcha est correct',
784
+ },
785
+ },
786
+ ]
787
+
788
+ function handleSubmit(e: { isValid: boolean }) {
789
+ alert(e.isValid ? 'Valeur valide !' : 'Veuillez corriger les erreurs.')
790
+ }
791
+
792
+ return { args, captchaValue, customRules, handleSubmit }
793
+ },
794
+ template: `
795
+ <div>
796
+ <p class="mb-4">Il faut privilégier l'utilisation de <code>SyForm</code> pour bénéficier de l'intégration de validation.</p>
797
+ <SyForm @submit="handleSubmit">
798
+ <VCard class="pa-8" max-width="400" min-width="400">
799
+ <Captcha
800
+ v-bind="args"
801
+ v-model="captchaValue"
802
+ :custom-rules="customRules"
803
+ width="400px"
804
+ required
805
+ />
806
+ <div class="mt-4">
807
+ <VBtn type="submit" color="primary">Valider</VBtn>
808
+ </div>
809
+ </VCard>
810
+ </SyForm>
811
+ </div>
812
+ `,
813
+ }),
814
+ }
815
+
816
+ export const VFormValidation: Story = {
817
+ parameters: {
818
+ a11y: {
819
+ disable: true,
820
+ },
821
+ docs: {
822
+ description: {
823
+ story: 'Validation déclenchée à la soumission du formulaire via l\'API exposée `validate()` du Captcha.',
824
+ },
825
+ },
826
+ sourceCode: [
827
+ {
828
+ name: 'Template',
829
+ code: `<template>
830
+ <VForm @submit.prevent="handleSubmit">
831
+ <VCard class="pa-8" max-width="400">
832
+ <Captcha
833
+ ref="captchaRef"
834
+ v-model="value"
835
+ required
836
+ :custom-rules="customRules"
837
+ />
838
+ <VBtn type="submit" color="primary" class="mt-4">Valider</VBtn>
839
+ </VCard>
840
+ </VForm>
841
+ </template>`,
842
+ },
843
+ {
844
+ name: 'Script',
845
+ code: `<script setup lang="ts">
846
+ import { ref } from 'vue'
847
+ import { Captcha } from '@cnamts/synapse'
848
+ import { VBtn, VForm } from 'vuetify/components'
849
+
850
+ const value = ref('')
851
+ const captchaRef = ref()
852
+
853
+ const customRules = [
854
+ {
855
+ type: 'custom',
856
+ options: {
857
+ validate: (value: string) => String(value || '').length >= 6,
858
+ message: 'Le captcha doit contenir au moins 6 caractères.',
859
+ fieldIdentifier: 'captcha',
860
+ },
861
+ },
862
+ {
863
+ type: 'custom',
864
+ options: {
865
+ validate: async (value: string) => {
866
+ try {
867
+ const r = await verifyCaptcha()
868
+ return r.response.data.message === 'Success'
869
+ }
870
+ catch {
871
+ return false
872
+ }
873
+ },
874
+ message: 'Le captcha est incorrect',
875
+ successMessage: 'Le captcha est correct',
876
+ },
877
+ },
878
+ ]
879
+
880
+ async function handleSubmit() {
881
+ if (captchaRef.value) {
882
+ const result = await captchaRef.value.validateOnSubmit()
883
+ alert(result ? 'Valeur valide !' : 'Veuillez corriger les erreurs.')
884
+ }
885
+ }
886
+ </script>`,
887
+ },
888
+ ],
889
+ },
890
+ render: args => ({
891
+ components: { Captcha, VBtn, VForm },
892
+ setup() {
893
+ const value = ref(args.modelValue)
894
+ watch(() => args.modelValue, (newValue) => {
895
+ value.value = newValue
896
+ })
897
+ const captchaRef = ref()
898
+
899
+ const verifyCaptcha = () => {
900
+ if (value.value === 'ytqZNq' || value.value === '941335') {
901
+ return Promise.resolve({ response: { data: { message: 'Success' } } })
902
+ }
903
+ return Promise.reject({ response: { data: { message: 'Le captcha est incorrect' } } })
904
+ }
905
+
906
+ const customRules = [
907
+ {
908
+ type: 'custom',
909
+ options: {
910
+ validate: (captchaValue: string) => String(captchaValue || '').length >= 6,
911
+ message: 'Le captcha doit contenir au moins 6 caractères.',
912
+ fieldIdentifier: 'captcha',
913
+ },
914
+ },
915
+ {
916
+ type: 'custom',
917
+ options: {
918
+ validate: async () => {
919
+ try {
920
+ const r = await verifyCaptcha()
921
+ return r.response.data.message === 'Success'
922
+ }
923
+ catch {
924
+ return false
925
+ }
926
+ },
927
+ message: 'Le captcha est incorrect',
928
+ successMessage: 'Le captcha est correct',
929
+ fieldIdentifier: 'captcha',
930
+ },
931
+ },
932
+ ]
933
+
934
+ async function handleSubmit() {
935
+ if (captchaRef.value) {
936
+ const result = await captchaRef.value.validateOnSubmit()
937
+ alert(result ? 'Valeur valide !' : 'Veuillez corriger les erreurs.')
938
+ }
939
+ }
940
+
941
+ return { args, value, captchaRef, customRules, handleSubmit }
942
+ },
943
+ template: `
944
+ <div>
945
+ <p class="mb-4">Il faut privilégier l'utilisation du composant SyForm pour déclencher la validation à la soumission.</p>
946
+ <VForm @submit.prevent="handleSubmit">
947
+ <VCard class="pa-8" max-width="400" min-width="400">
948
+ <Captcha
949
+ ref="captchaRef"
950
+ v-bind="args"
951
+ v-model="value"
952
+ :custom-rules="customRules"
953
+ required
954
+ width="400px"
955
+ />
956
+ <VBtn type="submit" color="primary" class="mt-4">Valider</VBtn>
957
+ </VCard>
958
+ </VForm>
959
+ </div>
960
+ `,
961
+ }),
962
+ }
963
+
964
+ export const SyFormVuetifyValidation: Story = {
965
+ parameters: {
966
+ a11y: {
967
+ disable: true,
968
+ },
969
+ docs: {
970
+ description: {
971
+ story: 'Validation native Vuetify (`useVuetifyValidation=true` + `rules`) intégrée automatiquement à un `SyForm`.',
972
+ },
973
+ },
974
+ sourceCode: [
975
+ {
976
+ name: 'Template',
977
+ code: `<template>
978
+ <SyForm @submit="handleSubmit">
979
+ <VCard class="pa-8" max-width="400">
980
+ <Captcha
981
+ v-model="value"
982
+ :use-vuetify-validation="true"
983
+ :rules="rules"
984
+ required
985
+ />
986
+ <div class="mt-4">
987
+ <VBtn type="submit" color="primary">Valider</VBtn>
988
+ </div>
989
+ </VCard>
990
+ </SyForm>
991
+ </template>`,
992
+ },
993
+ {
994
+ name: 'Script',
995
+ code: `<script setup lang="ts">
996
+ import { ref } from 'vue'
997
+ import { Captcha, SyForm } from '@cnamts/synapse'
998
+
999
+ const value = ref('')
1000
+
1001
+ const rules = [
1002
+ (value: string) => !!value || 'Ce champ est requis',
1003
+ (value: string) => String(value || '').length >= 6 || 'Le captcha doit contenir au moins 6 caractères',
1004
+ async () => {
1005
+ try {
1006
+ const r = await verifyCaptcha()
1007
+ return r.response.data.message === 'Success' || 'Le captcha est incorrect'
1008
+ }
1009
+ catch {
1010
+ return 'Le captcha est incorrect'
1011
+ }
1012
+ },
1013
+ ]
1014
+
1015
+ function handleSubmit(e: { isValid: boolean }) {
1016
+ alert(e.isValid ? 'Valeur valide !' : 'Veuillez corriger les erreurs.')
1017
+ }
1018
+ </script>`,
1019
+ },
1020
+ ],
1021
+ },
1022
+ render: args => ({
1023
+ components: { Captcha, VBtn, SyForm },
1024
+ setup() {
1025
+ const captchaValue = ref(args.modelValue)
1026
+ watch(() => args.modelValue, (newValue) => {
1027
+ captchaValue.value = newValue
1028
+ })
1029
+
1030
+ const verifyCaptcha = () => {
1031
+ if (captchaValue.value === 'ytqZNq' || captchaValue.value === '941335') {
1032
+ return Promise.resolve({ response: { data: { message: 'Success' } } })
1033
+ }
1034
+ return Promise.reject({ response: { data: { message: 'Le captcha est incorrect' } } })
1035
+ }
1036
+
1037
+ const rules = [
1038
+ (value: string) => !!value || 'Ce champ est requis',
1039
+ (value: string) => String(value || '').length >= 6 || 'Le captcha doit contenir au moins 6 caractères',
1040
+ async () => {
1041
+ try {
1042
+ const r = await verifyCaptcha()
1043
+ return r.response.data.message === 'Success' || 'Le captcha est incorrect'
1044
+ }
1045
+ catch {
1046
+ return 'Le captcha est incorrect'
1047
+ }
1048
+ },
1049
+ ]
1050
+
1051
+ function handleSubmit(e: { isValid: boolean }) {
1052
+ alert(e.isValid ? 'Valeur valide !' : 'Veuillez corriger les erreurs.')
1053
+ }
1054
+
1055
+ return { args, captchaValue, rules, handleSubmit }
1056
+ },
1057
+ template: `
1058
+ <div>
1059
+ <p class="mb-4">Validation Vuetify native intégrée au <code>SyForm</code> via <code>useVuetifyValidation</code>.</p>
1060
+ <SyForm @submit="handleSubmit">
1061
+ <VCard class="pa-8" max-width="400" min-width="400">
1062
+ <Captcha
1063
+ v-bind="args"
1064
+ v-model="captchaValue"
1065
+ :use-vuetify-validation="true"
1066
+ :rules="rules"
1067
+ width="400px"
1068
+ required
1069
+ />
1070
+ <div class="mt-4">
1071
+ <VBtn type="submit" color="primary">Valider</VBtn>
1072
+ </div>
1073
+ </VCard>
1074
+ </SyForm>
1075
+ </div>
1076
+ `,
1077
+ }),
1078
+ }
1079
+
1080
+ export const VFormVuetifyValidation: Story = {
1081
+ parameters: {
1082
+ a11y: {
1083
+ disable: true,
1084
+ },
1085
+ docs: {
1086
+ description: {
1087
+ story: 'Validation de style Vuetify avec `useVuetifyValidation=true` et des règles natives.',
1088
+ },
1089
+ },
1090
+ sourceCode: [
1091
+ {
1092
+ name: 'Template',
1093
+ code: `<template>
1094
+ <VForm @submit.prevent="handleSubmit">
1095
+ <VCard class="pa-8" max-width="400">
1096
+ <Captcha
1097
+ v-model="value"
1098
+ :use-vuetify-validation="true"
1099
+ :rules="rules"
1100
+ required
1101
+ />
1102
+ <div class="mt-4">
1103
+ <VBtn type="submit" color="primary">Valider</VBtn>
1104
+ </div>
1105
+ </VCard>
1106
+ </VForm>
1107
+ </template>`,
1108
+ },
1109
+ {
1110
+ name: 'Script',
1111
+ code: `<script setup lang="ts">
1112
+ import { ref } from 'vue'
1113
+ import { Captcha } from '@cnamts/synapse'
1114
+
1115
+ const value = ref('')
1116
+
1117
+ const rules = [
1118
+ (value: string) => !!value || 'Ce champ est requis',
1119
+ (value: string) => String(value || '').length >= 6 || 'Le captcha doit contenir au moins 6 caractères',
1120
+ async () => {
1121
+ try {
1122
+ const r = await verifyCaptcha()
1123
+ return r.response.data.message === 'Success' || 'Le captcha est incorrect'
1124
+ }
1125
+ catch {
1126
+ return 'Le captcha est incorrect'
1127
+ }
1128
+ },
1129
+ ]
1130
+
1131
+ async function handleSubmit(e) {
1132
+ alert((await e).valid ? 'Valeur valide !' : 'Veuillez corriger les erreurs.')
1133
+ }
1134
+ </script>`,
1135
+ },
1136
+ ],
1137
+ },
1138
+ render: args => ({
1139
+ components: { Captcha, VBtn, VForm },
1140
+ setup() {
1141
+ const captchaValue = ref(args.modelValue)
1142
+ watch(() => args.modelValue, (newValue) => {
1143
+ captchaValue.value = newValue
1144
+ })
1145
+
1146
+ const rules = [
1147
+ (value: string) => !!value || 'Ce champ est requis',
1148
+ (value: string) => String(value || '').length >= 6 || 'Le captcha doit contenir au moins 6 caractères',
1149
+ async () => {
1150
+ try {
1151
+ const r = await verifyCaptcha()
1152
+ return r.response.data.message === 'Success' || 'Le captcha est incorrect'
1153
+ }
1154
+ catch {
1155
+ return 'Le captcha est incorrect'
1156
+ }
1157
+ },
1158
+ ]
1159
+
1160
+ const verifyCaptcha = () => {
1161
+ if (captchaValue.value === 'ytqZNq' || captchaValue.value === '941335') {
1162
+ return Promise.resolve({ response: { data: { message: 'Success' } } })
1163
+ }
1164
+ return Promise.reject({ response: { data: { message: 'Le captcha est incorrect' } } })
1165
+ }
1166
+
1167
+ async function handleSubmit(e: Promise<{ valid: boolean }>) {
1168
+ const result = await e
1169
+ alert(result.valid ? 'Valeur valide !' : 'Veuillez corriger les erreurs.')
1170
+ }
1171
+
1172
+ return { args, captchaValue, rules, handleSubmit }
1173
+ },
1174
+ template: `
1175
+ <div>
1176
+ <p class="mb-4">Les règles suivent le contrat Vuetify natif: <code>(value) =&gt; true | 'message'</code>.</p>
1177
+ <VForm @submit.prevent="handleSubmit">
1178
+ <VCard class="pa-8" max-width="400" min-width="400">
1179
+ <Captcha
1180
+ v-bind="args"
1181
+ v-model="captchaValue"
1182
+ :use-vuetify-validation="true"
1183
+ :rules="rules"
1184
+ width="400px"
1185
+ />
1186
+ <div class="mt-4">
1187
+ <VBtn type="submit" color="primary">Valider</VBtn>
1188
+ </div>
1189
+ </VCard>
1190
+ </VForm>
1191
+ </div>
1192
+ `,
1193
+ }),
1194
+ }