@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,600 @@
1
+ import type { Meta, StoryObj } from '@storybook/vue3'
2
+ import SelectBtnField from '../SelectBtnField.vue'
3
+ import SyForm from '@/components/Customs/SyForm/SyForm.vue'
4
+ import { onMounted, ref } from 'vue'
5
+ import { fn } from '@storybook/test'
6
+ import { VBtn, VForm } from 'vuetify/components'
7
+
8
+ const meta: Meta<typeof SelectBtnField> = {
9
+ title: 'Composants/Formulaires/Selects/SelectBtnField/Validation',
10
+ component: SelectBtnField,
11
+ parameters: {
12
+ layout: 'fullscreen',
13
+ docs: {
14
+ description: {
15
+ component: 'Exemples de validation pour le composant SelectBtnField.',
16
+ },
17
+ },
18
+ },
19
+ args: {
20
+ 'onUpdate:modelValue': fn(),
21
+ },
22
+ } as Meta<typeof SelectBtnField>
23
+
24
+ export default meta
25
+
26
+ type Story = StoryObj<typeof meta>
27
+
28
+ const items = [
29
+ { text: 'Email', value: 'email' },
30
+ { text: 'Courrier', value: 'courrier' },
31
+ { text: 'SMS', value: 'sms' },
32
+ ]
33
+
34
+ export const WithError: Story = {
35
+ parameters: {
36
+ docs: {
37
+ description: {
38
+ story: 'Une `customRule` bloque la sélection « SMS » et affiche un message d’erreur.',
39
+ },
40
+ },
41
+ sourceCode: [
42
+ {
43
+ name: 'Template',
44
+ code: `<template>
45
+ <SelectBtnField
46
+ v-model="value"
47
+ :items="items"
48
+ label="Moyen de contact"
49
+ :custom-rules="customRules"
50
+ />
51
+ </template>`,
52
+ },
53
+ {
54
+ name: 'Script',
55
+ code: `<script setup lang="ts">
56
+ import { ref } from 'vue'
57
+ import { SelectBtnField } from '@cnamts/synapse'
58
+
59
+ const value = ref('sms')
60
+ const items = [
61
+ { text: 'Email', value: 'email' },
62
+ { text: 'Courrier', value: 'courrier' },
63
+ { text: 'SMS', value: 'sms' },
64
+ ]
65
+
66
+ const customRules = [
67
+ {
68
+ type: 'custom',
69
+ options: {
70
+ validate: (v) => v !== 'sms',
71
+ message: 'Le SMS n’est pas disponible pour ce dossier.',
72
+ },
73
+ },
74
+ ]
75
+ </script>`,
76
+ },
77
+ ],
78
+ },
79
+ render: args => ({
80
+ components: { SelectBtnField },
81
+ setup() {
82
+ const value = ref('sms')
83
+ const fieldRef = ref<{ validateOnSubmit: () => Promise<boolean> } | null>(null)
84
+ const customRules = [
85
+ {
86
+ type: 'custom',
87
+ options: {
88
+ validate: (v: unknown) => v !== 'sms',
89
+ message: 'Le SMS n’est pas disponible pour ce dossier.',
90
+ },
91
+ },
92
+ ]
93
+ onMounted(() => {
94
+ fieldRef.value?.validateOnSubmit()
95
+ })
96
+ return { args, value, items, customRules, fieldRef }
97
+ },
98
+ template: `
99
+ <div class="pa-4" style="max-width: 400px">
100
+ <SelectBtnField
101
+ ref="fieldRef"
102
+ v-model="value"
103
+ :items="items"
104
+ label="Moyen de contact"
105
+ :custom-rules="customRules"
106
+ />
107
+ </div>
108
+ `,
109
+ }),
110
+ }
111
+
112
+ export const WithWarning: Story = {
113
+ parameters: {
114
+ docs: {
115
+ description: {
116
+ story: 'Une `customWarningRule` affiche un avertissement non bloquant.',
117
+ },
118
+ },
119
+ sourceCode: [
120
+ {
121
+ name: 'Script',
122
+ code: `<script setup lang="ts">
123
+ import { ref } from 'vue'
124
+ import { SelectBtnField } from '@cnamts/synapse'
125
+
126
+ const value = ref('courrier')
127
+
128
+ const customWarningRules = [
129
+ {
130
+ type: 'custom',
131
+ options: {
132
+ validate: (v) => v !== 'courrier',
133
+ warningMessage: 'Le courrier postal allonge les délais de traitement.',
134
+ },
135
+ },
136
+ ]
137
+ </script>`,
138
+ },
139
+ ],
140
+ },
141
+ render: args => ({
142
+ components: { SelectBtnField },
143
+ setup() {
144
+ const value = ref('courrier')
145
+ const fieldRef = ref<{ validateOnSubmit: () => Promise<boolean> } | null>(null)
146
+ const customWarningRules = [
147
+ {
148
+ type: 'custom',
149
+ options: {
150
+ validate: (v: unknown) => v !== 'courrier',
151
+ warningMessage: 'Le courrier postal allonge les délais de traitement.',
152
+ },
153
+ },
154
+ ]
155
+ onMounted(() => {
156
+ fieldRef.value?.validateOnSubmit()
157
+ })
158
+ return { args, value, items, customWarningRules, fieldRef }
159
+ },
160
+ template: `
161
+ <div class="pa-4" style="max-width: 400px">
162
+ <SelectBtnField
163
+ ref="fieldRef"
164
+ v-model="value"
165
+ :items="items"
166
+ label="Moyen de contact"
167
+ :custom-warning-rules="customWarningRules"
168
+ />
169
+ </div>
170
+ `,
171
+ }),
172
+ }
173
+
174
+ export const WithSuccess: Story = {
175
+ parameters: {
176
+ docs: {
177
+ description: {
178
+ story: 'Une `customSuccessRule` affiche un feedback positif lorsque `showSuccessMessages` est activé.',
179
+ },
180
+ },
181
+ sourceCode: [
182
+ {
183
+ name: 'Script',
184
+ code: `<script setup lang="ts">
185
+ import { ref } from 'vue'
186
+ import { SelectBtnField } from '@cnamts/synapse'
187
+
188
+ const value = ref('email')
189
+
190
+ const customSuccessRules = [
191
+ {
192
+ type: 'custom',
193
+ options: {
194
+ validate: (v) => v === 'email',
195
+ successMessage: 'L’email est le moyen de contact le plus rapide.',
196
+ },
197
+ },
198
+ ]
199
+ </script>`,
200
+ },
201
+ ],
202
+ },
203
+ render: args => ({
204
+ components: { SelectBtnField },
205
+ setup() {
206
+ const value = ref('email')
207
+ const fieldRef = ref<{ validateOnSubmit: () => Promise<boolean> } | null>(null)
208
+ const customSuccessRules = [
209
+ {
210
+ type: 'custom',
211
+ options: {
212
+ validate: (v: unknown) => v === 'email',
213
+ successMessage: 'L’email est le moyen de contact le plus rapide.',
214
+ },
215
+ },
216
+ ]
217
+ onMounted(() => {
218
+ fieldRef.value?.validateOnSubmit()
219
+ })
220
+ return { args, value, items, customSuccessRules, fieldRef }
221
+ },
222
+ template: `
223
+ <div class="pa-4" style="max-width: 400px">
224
+ <SelectBtnField
225
+ ref="fieldRef"
226
+ v-model="value"
227
+ :items="items"
228
+ label="Moyen de contact"
229
+ :custom-success-rules="customSuccessRules"
230
+ show-success-messages
231
+ />
232
+ </div>
233
+ `,
234
+ }),
235
+ }
236
+
237
+ export const DisableErrorHandling: Story = {
238
+ parameters: {
239
+ docs: {
240
+ description: {
241
+ story: 'Avec `disableErrorHandling`, aucune erreur n’est affichée même si une règle échoue.',
242
+ },
243
+ },
244
+ },
245
+ render: args => ({
246
+ components: { SelectBtnField },
247
+ setup() {
248
+ const value = ref<string | null>(null)
249
+ return { args, value, items }
250
+ },
251
+ template: `
252
+ <div class="pa-4" style="max-width: 400px">
253
+ <SelectBtnField
254
+ v-model="value"
255
+ :items="items"
256
+ label="Moyen de contact"
257
+ required
258
+ disable-error-handling
259
+ />
260
+ </div>
261
+ `,
262
+ }),
263
+ }
264
+
265
+ export const ExternalMessages: Story = {
266
+ parameters: {
267
+ docs: {
268
+ description: {
269
+ story: 'Les props `errorMessages` / `warningMessages` / `successMessages` injectent des messages depuis le parent.',
270
+ },
271
+ },
272
+ },
273
+ render: args => ({
274
+ components: { SelectBtnField, VBtn },
275
+ setup() {
276
+ const value = ref<string | null>('email')
277
+ const errorMessages = ref<string[] | null>(null)
278
+ const warningMessages = ref<string[] | null>(null)
279
+ const successMessages = ref<string[] | null>(null)
280
+
281
+ function setError() {
282
+ errorMessages.value = ['Ce moyen de contact est indisponible.']
283
+ warningMessages.value = null
284
+ successMessages.value = null
285
+ }
286
+ function setWarning() {
287
+ errorMessages.value = null
288
+ warningMessages.value = ['Vérifiez vos coordonnées.']
289
+ successMessages.value = null
290
+ }
291
+ function setSuccess() {
292
+ errorMessages.value = null
293
+ warningMessages.value = null
294
+ successMessages.value = ['Moyen de contact validé par le serveur.']
295
+ }
296
+ function reset() {
297
+ errorMessages.value = null
298
+ warningMessages.value = null
299
+ successMessages.value = null
300
+ }
301
+
302
+ return { args, value, items, errorMessages, warningMessages, successMessages, setError, setWarning, setSuccess, reset }
303
+ },
304
+ template: `
305
+ <div class="pa-4" style="max-width: 400px">
306
+ <SelectBtnField
307
+ v-model="value"
308
+ :items="items"
309
+ label="Moyen de contact"
310
+ :error-messages="errorMessages"
311
+ :warning-messages="warningMessages"
312
+ :success-messages="successMessages"
313
+ show-success-messages
314
+ />
315
+ <div class="mt-4 d-flex flex-wrap ga-2">
316
+ <VBtn color="error" @click="setError">Erreur</VBtn>
317
+ <VBtn color="warning" @click="setWarning">Avertissement</VBtn>
318
+ <VBtn color="success" @click="setSuccess">Succès</VBtn>
319
+ <VBtn @click="reset">Réinitialiser</VBtn>
320
+ </div>
321
+ </div>
322
+ `,
323
+ }),
324
+ }
325
+
326
+ export const SyFormValidation: Story = {
327
+ parameters: {
328
+ docs: {
329
+ description: {
330
+ story: 'Intégration automatique au `SyForm` : le champ s’enregistre et est validé à la soumission.',
331
+ },
332
+ },
333
+ sourceCode: [
334
+ {
335
+ name: 'Template',
336
+ code: `<template>
337
+ <SyForm @submit="handleSubmit">
338
+ <SelectBtnField
339
+ v-model="value"
340
+ :items="items"
341
+ label="Moyen de contact"
342
+ required
343
+ />
344
+ <VBtn type="submit" color="primary" class="mt-4">Valider</VBtn>
345
+ </SyForm>
346
+ </template>`,
347
+ },
348
+ {
349
+ name: 'Script',
350
+ code: `<script setup lang="ts">
351
+ import { ref } from 'vue'
352
+ import { SelectBtnField, SyForm } from '@cnamts/synapse'
353
+
354
+ const value = ref(null)
355
+
356
+ function handleSubmit(e) {
357
+ alert(e.isValid ? 'Formulaire valide !' : 'Veuillez corriger les erreurs.')
358
+ }
359
+ </script>`,
360
+ },
361
+ ],
362
+ },
363
+ render: args => ({
364
+ components: { SelectBtnField, SyForm, VBtn },
365
+ setup() {
366
+ const value = ref<string | null>(null)
367
+ function handleSubmit(e: { isValid: boolean }) {
368
+ alert(e.isValid ? 'Formulaire valide !' : 'Veuillez corriger les erreurs.')
369
+ }
370
+ return { args, value, items, handleSubmit }
371
+ },
372
+ template: `
373
+ <div class="pa-4" style="max-width: 400px">
374
+ <SyForm @submit="handleSubmit">
375
+ <SelectBtnField
376
+ v-model="value"
377
+ :items="items"
378
+ label="Moyen de contact"
379
+ required
380
+ />
381
+ <VBtn type="submit" color="primary" class="mt-4">Valider</VBtn>
382
+ </SyForm>
383
+ </div>
384
+ `,
385
+ }),
386
+ }
387
+
388
+ export const VFormValidation: Story = {
389
+ parameters: {
390
+ docs: {
391
+ description: {
392
+ story: 'Validation Synapse (`customRules`) dans un `VForm` Vuetify, déclenchée via `validateOnSubmit` exposé par le composant.',
393
+ },
394
+ },
395
+ sourceCode: [
396
+ {
397
+ name: 'Template',
398
+ code: `<template>
399
+ <VForm @submit.prevent="handleSubmit">
400
+ <SelectBtnField
401
+ ref="fieldRef"
402
+ v-model="value"
403
+ :items="items"
404
+ label="Moyen de contact"
405
+ :custom-rules="customRules"
406
+ required
407
+ />
408
+ <VBtn type="submit" color="primary" class="mt-4">Valider</VBtn>
409
+ </VForm>
410
+ </template>`,
411
+ },
412
+ {
413
+ name: 'Script',
414
+ code: `<script setup lang="ts">
415
+ import { ref } from 'vue'
416
+ import { SelectBtnField } from '@cnamts/synapse'
417
+
418
+ const value = ref(null)
419
+ const fieldRef = ref(null)
420
+
421
+ const customRules = [
422
+ {
423
+ type: 'custom',
424
+ options: {
425
+ validate: (v) => v !== 'sms',
426
+ message: 'Le SMS n’est pas disponible pour ce dossier.',
427
+ },
428
+ },
429
+ ]
430
+
431
+ async function handleSubmit() {
432
+ const isValid = await fieldRef.value?.validateOnSubmit()
433
+ alert(isValid ? 'Valide !' : 'Veuillez corriger les erreurs.')
434
+ }
435
+ </script>`,
436
+ },
437
+ ],
438
+ },
439
+ render: args => ({
440
+ components: { SelectBtnField, VBtn, VForm },
441
+ setup() {
442
+ const value = ref<string | null>(null)
443
+ const fieldRef = ref<{ validateOnSubmit: () => Promise<boolean> } | null>(null)
444
+ const customRules = [
445
+ {
446
+ type: 'custom',
447
+ options: {
448
+ validate: (v: unknown) => v !== 'sms',
449
+ message: 'Le SMS n’est pas disponible pour ce dossier.',
450
+ },
451
+ },
452
+ ]
453
+ async function handleSubmit() {
454
+ const isValid = await fieldRef.value?.validateOnSubmit()
455
+ alert(isValid ? 'Valide !' : 'Veuillez corriger les erreurs.')
456
+ }
457
+ return { args, value, items, customRules, fieldRef, handleSubmit }
458
+ },
459
+ template: `
460
+ <div class="pa-4" style="max-width: 400px">
461
+ <VForm @submit.prevent="handleSubmit">
462
+ <SelectBtnField
463
+ ref="fieldRef"
464
+ v-model="value"
465
+ :items="items"
466
+ label="Moyen de contact"
467
+ :custom-rules="customRules"
468
+ required
469
+ />
470
+ <VBtn type="submit" color="primary" class="mt-4">Valider</VBtn>
471
+ </VForm>
472
+ </div>
473
+ `,
474
+ }),
475
+ }
476
+
477
+ export const SyFormVuetifyValidation: Story = {
478
+ parameters: {
479
+ docs: {
480
+ description: {
481
+ story: 'Validation native Vuetify (`useVuetifyValidation` + `rules`) intégrée automatiquement à un `SyForm`.',
482
+ },
483
+ },
484
+ sourceCode: [
485
+ {
486
+ name: 'Template',
487
+ code: `<template>
488
+ <SyForm @submit="handleSubmit">
489
+ <SelectBtnField
490
+ v-model="value"
491
+ :items="items"
492
+ label="Moyen de contact"
493
+ :use-vuetify-validation="true"
494
+ :rules="rules"
495
+ />
496
+ <VBtn type="submit" color="primary" class="mt-4">Valider</VBtn>
497
+ </SyForm>
498
+ </template>`,
499
+ },
500
+ {
501
+ name: 'Script',
502
+ code: `<script setup lang="ts">
503
+ import { ref } from 'vue'
504
+ import { SelectBtnField, SyForm } from '@cnamts/synapse'
505
+
506
+ const value = ref(null)
507
+
508
+ const rules = [
509
+ (v) => !!v || 'Le moyen de contact est requis.',
510
+ ]
511
+
512
+ function handleSubmit(e) {
513
+ alert(e.isValid ? 'Formulaire valide !' : 'Veuillez corriger les erreurs.')
514
+ }
515
+ </script>`,
516
+ },
517
+ ],
518
+ },
519
+ render: args => ({
520
+ components: { SelectBtnField, SyForm, VBtn },
521
+ setup() {
522
+ const value = ref<string | null>(null)
523
+ const rules = [
524
+ (v: unknown) => !!v || 'Le moyen de contact est requis.',
525
+ ]
526
+ function handleSubmit(e: { isValid: boolean }) {
527
+ alert(e.isValid ? 'Formulaire valide !' : 'Veuillez corriger les erreurs.')
528
+ }
529
+ return { args, value, items, rules, handleSubmit }
530
+ },
531
+ template: `
532
+ <div class="pa-4" style="max-width: 400px">
533
+ <SyForm @submit="handleSubmit">
534
+ <SelectBtnField
535
+ v-model="value"
536
+ :items="items"
537
+ label="Moyen de contact"
538
+ :use-vuetify-validation="true"
539
+ :rules="rules"
540
+ />
541
+ <VBtn type="submit" color="primary" class="mt-4">Valider</VBtn>
542
+ </SyForm>
543
+ </div>
544
+ `,
545
+ }),
546
+ }
547
+
548
+ export const VFormVuetifyValidation: Story = {
549
+ parameters: {
550
+ docs: {
551
+ description: {
552
+ story: 'Avec `useVuetifyValidation`, les règles sont des fonctions Vuetify natives `(value) => true | message`.',
553
+ },
554
+ },
555
+ sourceCode: [
556
+ {
557
+ name: 'Script',
558
+ code: `<script setup lang="ts">
559
+ import { ref } from 'vue'
560
+ import { SelectBtnField } from '@cnamts/synapse'
561
+
562
+ const value = ref(null)
563
+
564
+ const rules = [
565
+ (v) => !!v || 'Le moyen de contact est requis.',
566
+ ]
567
+ </script>`,
568
+ },
569
+ ],
570
+ },
571
+ render: args => ({
572
+ components: { SelectBtnField, VBtn, VForm },
573
+ setup() {
574
+ const value = ref<string | null>(null)
575
+ const rules = [
576
+ (v: unknown) => !!v || 'Le moyen de contact est requis.',
577
+ ]
578
+ async function handleSubmit(e: Promise<{ valid: boolean }>) {
579
+ const result = await e
580
+ alert(result.valid ? 'Valide !' : 'Veuillez corriger les erreurs.')
581
+ }
582
+ return { args, value, items, rules, handleSubmit }
583
+ },
584
+ template: `
585
+ <div class="pa-4" style="max-width: 400px">
586
+ <VForm @submit.prevent="handleSubmit">
587
+ <SelectBtnField
588
+ v-model="value"
589
+ :items="items"
590
+ label="Moyen de contact"
591
+ :use-vuetify-validation="true"
592
+ :rules="rules"
593
+ required
594
+ />
595
+ <VBtn type="submit" color="primary" class="mt-4">Valider</VBtn>
596
+ </VForm>
597
+ </div>
598
+ `,
599
+ }),
600
+ }
@@ -0,0 +1,87 @@
1
+ import { computed, ref } from 'vue'
2
+ import { mdiAlertCircle, mdiAlertOutline, mdiCheck } from '@mdi/js'
3
+ import { useValidation, type FieldValidationProps } from '@/composables/unifyValidation/useValidation'
4
+ import type { ValidationRule } from '@/composables/validation/useValidation'
5
+
6
+ /**
7
+ * Composable de validation dédié au composant SelectBtnField.
8
+ * - règle `required` par défaut avec message personnalisé,
9
+ * - support des customRules / customWarningRules / customSuccessRules,
10
+ * - messages externes (errorMessages / warningMessages / successMessages),
11
+ * - validation immédiate à la sélection (isValidateOnBlur à `false` par défaut, comme un groupe de boutons),
12
+ * - intégration automatique au SyForm via le useValidatable interne à useValidation.
13
+ */
14
+ export function useSelectBtnFieldValidation(props: FieldValidationProps & { modelValue?: unknown }) {
15
+ const focused = ref(false)
16
+
17
+ const defaultRules = computed<ValidationRule[]>(() => props.required
18
+ ? [{
19
+ type: 'required',
20
+ options: {
21
+ message: `Le champ ${props.label || 'ce champ'} est requis.`,
22
+ fieldIdentifier: props.label,
23
+ },
24
+ }]
25
+ : [],
26
+ )
27
+
28
+ const { validate, clearValidation, errors, warnings, successes, hasError, hasWarning, hasSuccess } = useValidation({
29
+ modelValue: computed(() => props.modelValue),
30
+ readonly: computed(() => props.readonly ?? false),
31
+ disabled: computed(() => props.disabled ?? false),
32
+ required: computed(() => props.required ?? false),
33
+ isValidateOnBlur: computed(() => props.isValidateOnBlur ?? false),
34
+ showSuccessMessages: computed(() => props.showSuccessMessages ?? false),
35
+ disableErrorHandling: computed(() => props.disableErrorHandling ?? false),
36
+ useVuetifyValidation: computed(() => props.useVuetifyValidation ?? false),
37
+ label: computed(() => props.label ?? ''),
38
+ rules: computed(() => props.rules ?? []),
39
+ customRules: computed(() => [...defaultRules.value, ...(props.customRules ?? [])]),
40
+ customWarningRules: computed(() => props.customWarningRules ?? []),
41
+ customSuccessRules: computed(() => props.customSuccessRules ?? []),
42
+ errorMessages: computed(() => props.errorMessages ?? []),
43
+ warningMessages: computed(() => props.warningMessages ?? []),
44
+ successMessages: computed(() => props.successMessages ?? []),
45
+ hasErrorProp: computed(() => props.hasError ?? false),
46
+ hasWarningProp: computed(() => props.hasWarning ?? false),
47
+ hasSuccessProp: computed(() => props.hasSuccess ?? false),
48
+ maxErrors: computed(() => props.maxErrors ?? 1),
49
+ focused,
50
+ })
51
+
52
+ // Un champ simplement rempli n'est pas « en succès » : sans cela, le composable legacy
53
+ // marque succès dès qu'une valeur est saisie (successRules vides + valeur remplie),
54
+ // ce qui ferait passer l'item sélectionné en vert sur toutes les stories. On n'autorise
55
+ // donc l'état de succès que s'il existe une source explicite (règle de succès, règle
56
+ // portant un successMessage, message de succès injecté, ou succès forcé).
57
+ const hasExplicitSuccessSource = computed(() =>
58
+ (props.customSuccessRules?.length ?? 0) > 0
59
+ || (props.customRules ?? []).some(rule => !!rule.options?.successMessage)
60
+ || (props.successMessages?.length ?? 0) > 0
61
+ || (props.hasSuccess ?? false),
62
+ )
63
+
64
+ const effectiveHasSuccess = computed(() => hasExplicitSuccessSource.value && hasSuccess.value)
65
+ const effectiveSuccesses = computed(() => hasExplicitSuccessSource.value ? successes.value : [])
66
+
67
+ const validationIcon = computed(() => {
68
+ if (props.useVuetifyValidation) return null
69
+ if (hasError.value) return mdiAlertCircle
70
+ if (hasWarning.value) return mdiAlertOutline
71
+ if (effectiveHasSuccess.value) return mdiCheck
72
+ return null
73
+ })
74
+
75
+ return {
76
+ focused,
77
+ validate,
78
+ clearValidation,
79
+ errors,
80
+ warnings,
81
+ successes: effectiveSuccesses,
82
+ hasError,
83
+ hasWarning,
84
+ hasSuccess: effectiveHasSuccess,
85
+ validationIcon,
86
+ }
87
+ }