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