@cnamts/synapse 0.0.15-alpha → 1.0.0

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 (133) hide show
  1. package/dist/components/Accordion/Accordion.d.ts +39 -0
  2. package/dist/components/Accordion/config.d.ts +9 -0
  3. package/dist/components/ChipList/ChipList.d.ts +1 -1
  4. package/dist/components/CookiesSelection/CookiesSelection.d.ts +26 -26
  5. package/dist/components/CopyBtn/CopyBtn.d.ts +2 -0
  6. package/dist/components/Customs/SyInputSelect/SyInputSelect.d.ts +12 -0
  7. package/dist/components/Customs/SySelect/SySelect.d.ts +43 -16
  8. package/dist/components/Customs/SyTextField/SyTextField.d.ts +1391 -1
  9. package/dist/components/DatePicker/DatePicker.d.ts +2810 -16
  10. package/dist/components/DatePicker/DateTextInput.d.ts +1401 -4
  11. package/dist/components/DiacriticPicker/DiacriticPicker.d.ts +27 -0
  12. package/dist/components/DiacriticPicker/config.d.ts +14 -0
  13. package/dist/components/DiacriticPicker/locales.d.ts +6 -0
  14. package/dist/components/DownloadBtn/DownloadBtn.d.ts +1 -1
  15. package/dist/components/FooterBar/FooterBar.d.ts +1 -1
  16. package/dist/components/LangBtn/LangBtn.d.ts +4 -4
  17. package/dist/components/NirField/NirField.d.ts +2796 -4
  18. package/dist/components/NotificationBar/NotificationBar.d.ts +1 -1
  19. package/dist/components/PasswordField/PasswordField.d.ts +1 -1
  20. package/dist/components/PeriodField/PeriodField.d.ts +5636 -48
  21. package/dist/components/PhoneField/PhoneField.d.ts +1 -0
  22. package/dist/components/PhoneField/tests/types.d.ts +18 -0
  23. package/dist/components/SyAlert/SyAlert.d.ts +72 -1
  24. package/dist/components/SyTextArea/SyTextArea.d.ts +900 -0
  25. package/dist/components/SyTextArea/locales.d.ts +3 -0
  26. package/dist/components/SyTextArea/trimStartOnUpdate.d.ts +1 -0
  27. package/dist/components/SyTextArea/useTextActions.d.ts +13 -0
  28. package/dist/components/SyTextArea/wrapText.d.ts +1 -0
  29. package/dist/components/TableToolbar/TableToolbar.d.ts +10 -4
  30. package/dist/components/TableToolbar/config.d.ts +3 -2
  31. package/dist/components/UploadWorkflow/UploadWorkflow.d.ts +26 -26
  32. package/dist/components/index.d.ts +4 -0
  33. package/dist/composables/date/useDateFormat.d.ts +2 -2
  34. package/dist/composables/date/useDateFormatDayjs.d.ts +23 -0
  35. package/dist/composables/date/useDateInitializationDayjs.d.ts +18 -0
  36. package/dist/composables/date/useHolidayDay.d.ts +36 -0
  37. package/dist/design-system-v3.js +5106 -4208
  38. package/dist/design-system-v3.umd.cjs +4 -1
  39. package/dist/designTokens/tokens/pa/paLightTheme.d.ts +1 -32
  40. package/dist/style.css +1 -1
  41. package/dist/utils/rules/index.d.ts +1 -0
  42. package/dist/utils/rules/isHolidayDay/index.d.ts +11 -0
  43. package/dist/utils/rules/isHolidayDay/locales.d.ts +2 -0
  44. package/package.json +3 -2
  45. package/src/assets/settings.scss +12 -0
  46. package/src/components/Accordion/Accordion.mdx +69 -0
  47. package/src/components/Accordion/Accordion.stories.ts +262 -0
  48. package/src/components/Accordion/Accordion.vue +319 -0
  49. package/src/components/Accordion/config.ts +9 -0
  50. package/src/components/Accordion/tests/__snapshots__/accordion.spec.ts.snap +155 -0
  51. package/src/components/Accordion/tests/accordion.spec.ts +492 -0
  52. package/src/components/CopyBtn/CopyBtn.stories.ts +189 -0
  53. package/src/components/CopyBtn/CopyBtn.vue +29 -1
  54. package/src/components/CopyBtn/tests/CopyBtn.spec.ts +102 -0
  55. package/src/components/Customs/SyInputSelect/SyInputSelect.stories.ts +155 -1
  56. package/src/components/Customs/SyInputSelect/SyInputSelect.vue +97 -14
  57. package/src/components/Customs/SyInputSelect/tests/SyInputSelect.spec.ts +386 -106
  58. package/src/components/Customs/SySelect/SySelect.stories.ts +121 -2
  59. package/src/components/Customs/SySelect/SySelect.vue +33 -8
  60. package/src/components/Customs/SySelect/tests/SySelect.spec.ts +290 -1
  61. package/src/components/Customs/SyTextField/Accessibilite.stories.ts +7 -0
  62. package/src/components/Customs/SyTextField/SyTextField.stories.ts +13 -0
  63. package/src/components/Customs/SyTextField/SyTextField.vue +87 -20
  64. package/src/components/DatePicker/ComplexDatePicker/ComplexDatePicker.vue +795 -0
  65. package/src/components/DatePicker/DatePicker.stories.ts +432 -1
  66. package/src/components/DatePicker/DatePicker.vue +82 -27
  67. package/src/components/DatePicker/DatePickerValidation.stories.ts +9 -1
  68. package/src/components/DatePicker/DateTextInput.vue +101 -138
  69. package/src/components/DatePicker/docExamples/DatePickerBidirectionalValidation.vue +282 -0
  70. package/src/components/DatePicker/examples/DatePickerHolidayRule.vue +130 -0
  71. package/src/components/DatePicker/tests/DatePicker.spec.ts +33 -32
  72. package/src/components/DatePicker/tests/DateTextInput.spec.ts +81 -33
  73. package/src/components/DiacriticPicker/DiacriticPicker.mdx +104 -0
  74. package/src/components/DiacriticPicker/DiacriticPicker.stories.ts +447 -0
  75. package/src/components/DiacriticPicker/DiacriticPicker.vue +262 -0
  76. package/src/components/DiacriticPicker/config.ts +15 -0
  77. package/src/components/DiacriticPicker/locales.ts +6 -0
  78. package/src/components/DiacriticPicker/tests/DiatriticPicker.spec.ts +132 -0
  79. package/src/components/DialogBox/DialogBox.vue +1 -3
  80. package/src/components/NirField/NirField.stories.ts +172 -0
  81. package/src/components/NirField/NirField.vue +15 -7
  82. package/src/components/NotificationBar/Accessibilite.stories.ts +1 -1
  83. package/src/components/NotificationBar/NotificationBar.stories.ts +14 -0
  84. package/src/components/NotificationBar/NotificationBar.vue +26 -3
  85. package/src/components/NotificationBar/{options.ts → config.ts} +0 -1
  86. package/src/components/PaginatedTable/PaginatedTable.vue +0 -11
  87. package/src/components/PasswordField/PasswordField.stories.ts +4 -3
  88. package/src/components/PasswordField/PasswordField.vue +26 -18
  89. package/src/components/PasswordField/tests/PasswordField.spec.ts +1 -10
  90. package/src/components/PhoneField/PhoneField.stories.ts +143 -0
  91. package/src/components/PhoneField/PhoneField.vue +88 -30
  92. package/src/components/PhoneField/tests/PhoneField.additional.spec.ts +266 -0
  93. package/src/components/PhoneField/tests/PhoneField.spec.ts +248 -28
  94. package/src/components/PhoneField/tests/types.d.ts +19 -0
  95. package/src/components/SyAlert/Accessibilite.stories.ts +4 -0
  96. package/src/components/SyAlert/SyAlert.mdx +3 -7
  97. package/src/components/SyAlert/SyAlert.stories.ts +19 -12
  98. package/src/components/SyAlert/SyAlert.vue +88 -51
  99. package/src/components/SyAlert/tests/SyAlert.spec.ts +20 -2
  100. package/src/components/SyAlert/tests/__snapshots__/SyAlert.spec.ts.snap +83 -75
  101. package/src/components/SyTextArea/SyTextArea.mdx +17 -0
  102. package/src/components/SyTextArea/SyTextArea.stories.ts +322 -0
  103. package/src/components/SyTextArea/SyTextArea.vue +113 -0
  104. package/src/components/SyTextArea/locales.ts +3 -0
  105. package/src/components/SyTextArea/tests/SyTextArea.spec.ts +194 -0
  106. package/src/components/SyTextArea/trimStartOnUpdate.ts +12 -0
  107. package/src/components/SyTextArea/useTextActions.ts +52 -0
  108. package/src/components/SyTextArea/wrapText.ts +42 -0
  109. package/src/components/TableToolbar/TableToolbar.mdx +86 -1
  110. package/src/components/TableToolbar/TableToolbar.stories.ts +422 -74
  111. package/src/components/TableToolbar/TableToolbar.vue +25 -8
  112. package/src/components/TableToolbar/config.ts +3 -2
  113. package/src/components/TableToolbar/tests/__snapshots__/TableToolbar.spec.ts.snap +35 -12
  114. package/src/components/index.ts +4 -0
  115. package/src/composables/date/useDateFormat.ts +17 -1
  116. package/src/composables/date/useDateFormatDayjs.ts +84 -0
  117. package/src/composables/date/useDateInitializationDayjs.ts +133 -0
  118. package/src/composables/date/useHolidayDay.ts +98 -0
  119. package/src/composables/rules/useFieldValidation.ts +16 -3
  120. package/src/composables/validation/useValidation.ts +2 -1
  121. package/src/designTokens/tokens/pa/paLightTheme.ts +10 -41
  122. package/src/stories/Accessibilite/Avancement/Avancement.mdx +12 -0
  123. package/src/stories/Accessibilite/Avancement/Avancement.stories.ts +134 -0
  124. package/src/stories/Accessibilite/Introduction.mdx +5 -2
  125. package/src/stories/DesignTokens/colors.stories.ts +100 -41
  126. package/src/utils/rules/index.ts +1 -0
  127. package/src/utils/rules/isHolidayDay/IsHolidayDay.mdx +52 -0
  128. package/src/utils/rules/isHolidayDay/IsHolidayDay.stories.ts +129 -0
  129. package/src/utils/rules/isHolidayDay/index.ts +36 -0
  130. package/src/utils/rules/isHolidayDay/locales.ts +5 -0
  131. package/src/utils/rules/isHolidayDay/tests/isHolidayDay.spec.ts +35 -0
  132. /package/dist/components/NotificationBar/{options.d.ts → config.d.ts} +0 -0
  133. /package/src/components/DatePicker/{DatePickerValidationExamples.vue → docExamples/DatePickerValidationExamples.vue} +0 -0
@@ -4,137 +4,417 @@ import SyInputSelect from '../SyInputSelect.vue'
4
4
  import { vuetify } from '@tests/unit/setup'
5
5
 
6
6
  describe('SyInputSelect', () => {
7
- it('renders the component with default props', () => {
8
- const wrapper = mount(SyInputSelect, {
9
- global: {
10
- plugins: [vuetify],
11
- },
12
- })
13
- expect(wrapper.exists()).toBe(true)
14
- expect(wrapper.find('.sy-input-select').text()).toBe('Sélectionnez une option')
15
- })
7
+ describe('Rendu et affichage', () => {
8
+ it('renders the component with default props', () => {
9
+ const wrapper = mount(SyInputSelect, {
10
+ global: {
11
+ plugins: [vuetify],
12
+ },
13
+ })
14
+ expect(wrapper.exists()).toBe(true)
15
+ expect(wrapper.find('.sy-input-select').text()).toBe('Sélectionnez une option')
16
+ })
16
17
 
17
- it('displays the selected item text', async () => {
18
- const items = [{ text: 'Option 1', value: '1' }, { text: 'Option 2', value: '2' }]
19
- const wrapper = mount(SyInputSelect, {
20
- props: { items, modelValue: { text: 'Option 1', value: '1' } },
21
- global: {
22
- plugins: [vuetify],
23
- },
18
+ it('displays the selected item text', async () => {
19
+ const items = [{ text: 'Option 1', value: '1' }, { text: 'Option 2', value: '2' }]
20
+ const wrapper = mount(SyInputSelect, {
21
+ props: { items, modelValue: { text: 'Option 1', value: '1' } },
22
+ global: {
23
+ plugins: [vuetify],
24
+ },
25
+ })
26
+ expect(wrapper.find('.sy-input-select').text()).toContain('Option 1')
24
27
  })
25
- expect(wrapper.find('.sy-input-select').text()).toContain('Option 1')
26
- })
27
28
 
28
- it('does not render error messages when not provided', () => {
29
- const wrapper = mount(SyInputSelect, {
30
- global: {
31
- plugins: [vuetify],
32
- },
29
+ it('does not render error messages when not provided', () => {
30
+ const wrapper = mount(SyInputSelect, {
31
+ global: {
32
+ plugins: [vuetify],
33
+ },
34
+ })
35
+ expect(wrapper.find('.v-messages__message').exists()).toBe(false)
33
36
  })
34
- expect(wrapper.find('.v-messages__message').exists()).toBe(false)
35
- })
36
37
 
37
- it('does not render the label when not provided', () => {
38
- const wrapper = mount(SyInputSelect, {
39
- global: {
40
- plugins: [vuetify],
41
- },
38
+ it('does not render the label when not provided', () => {
39
+ const wrapper = mount(SyInputSelect, {
40
+ global: {
41
+ plugins: [vuetify],
42
+ },
43
+ })
44
+ expect(wrapper.find('label').exists()).toBe(false)
42
45
  })
43
- expect(wrapper.find('label').exists()).toBe(false)
44
- })
45
46
 
46
- it('formats items correctly', () => {
47
- const items = ['Option 1', 'Option 2']
48
- const wrapper = mount(SyInputSelect, {
49
- props: { items, textKey: 'text', valueKey: 'value' },
50
- global: {
51
- plugins: [vuetify],
52
- },
53
- })
54
- // eslint-disable-next-line @typescript-eslint/no-explicit-any -- This is a generic type
55
- const formattedItems = (wrapper.vm as any).formattedItems
56
- expect(formattedItems).toEqual([
57
- { text: 'Option 1', value: 'Option 1' },
58
- { text: 'Option 2', value: 'Option 2' },
59
- ])
60
- })
47
+ it('formats items correctly', () => {
48
+ const items = ['Option 1', 'Option 2']
49
+ const wrapper = mount(SyInputSelect, {
50
+ props: { items, textKey: 'text', valueKey: 'value' },
51
+ global: {
52
+ plugins: [vuetify],
53
+ },
54
+ })
55
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- This is a generic type
56
+ const formattedItems = (wrapper.vm as any).formattedItems
57
+ expect(formattedItems).toEqual([
58
+ { text: 'Option 1', value: 'Option 1' },
59
+ { text: 'Option 2', value: 'Option 2' },
60
+ ])
61
+ })
62
+
63
+ it('applies the correct button class when outlined is true', () => {
64
+ const wrapper = mount(SyInputSelect, {
65
+ props: { outlined: true },
66
+ global: {
67
+ plugins: [vuetify],
68
+ },
69
+ })
70
+ expect(wrapper.find('.sy-input-select').classes()).toContain('v-btn--variant-outlined')
71
+ })
72
+
73
+ it('toggles the menu when the button is clicked', async () => {
74
+ const wrapper = mount(SyInputSelect, {
75
+ global: {
76
+ plugins: [vuetify],
77
+ },
78
+ })
79
+ const button = wrapper.find('.sy-input-select')
80
+ await button.trigger('click')
81
+ expect(wrapper.vm.isOpen).toBe(true)
82
+ await button.trigger('click')
83
+ expect(wrapper.vm.isOpen).toBe(false)
84
+ })
85
+
86
+ it('use closeList method', async () => {
87
+ const wrapper = mount(SyInputSelect, {
88
+ global: {
89
+ plugins: [vuetify],
90
+ },
91
+ })
92
+ await wrapper.vm.closeList()
93
+ expect(wrapper.vm.isOpen).toBe(false)
94
+ })
95
+
96
+ it('selectItem method', async () => {
97
+ const wrapper = mount(SyInputSelect, {
98
+ global: {
99
+ plugins: [vuetify],
100
+ },
101
+ })
102
+ await wrapper.vm.selectItem({ text: 'Option 1', value: '1' })
103
+ expect(wrapper.vm.isOpen).toBe(false)
104
+ expect(wrapper.vm.selectedItem).toEqual({ text: 'Option 1', value: '1' })
105
+ })
106
+
107
+ it('getItemText method', async () => {
108
+ const wrapper = mount(SyInputSelect, {
109
+ global: {
110
+ plugins: [vuetify],
111
+ },
112
+ })
113
+ const item = { text: 'Option 1', value: '1' }
114
+ const text = wrapper.vm.getItemText(item)
115
+ expect(text).toBe('Option 1')
116
+ })
117
+
118
+ it('watch modelValue', async () => {
119
+ const wrapper = mount(SyInputSelect, {
120
+ props: { modelValue: { text: 'Option 1', value: '1' } },
121
+ global: {
122
+ plugins: [vuetify],
123
+ },
124
+ })
125
+ expect(wrapper.vm.selectedItem).toEqual({ text: 'Option 1', value: '1' })
126
+ await wrapper.setProps({ modelValue: { text: 'Option 2', value: '2' } })
127
+ expect(wrapper.vm.selectedItem).toEqual({ text: 'Option 2', value: '2' })
128
+ })
61
129
 
62
- it('applies the correct button class when outlined is true', () => {
63
- const wrapper = mount(SyInputSelect, {
64
- props: { outlined: true },
65
- global: {
66
- plugins: [vuetify],
67
- },
130
+ it('watch errorMessages', async () => {
131
+ const wrapper = mount(SyInputSelect, {
132
+ props: { errorMessages: ['Error message'] },
133
+ global: {
134
+ plugins: [vuetify],
135
+ },
136
+ })
137
+ expect(wrapper.find('.v-messages__message').exists()).toBe(true)
138
+ await wrapper.setProps({ errorMessages: [] })
139
+ expect(wrapper.find('.v-messages__message').exists()).toBe(false)
68
140
  })
69
- expect(wrapper.find('.sy-input-select').classes()).toContain('v-btn--variant-outlined')
70
141
  })
71
142
 
72
- it('toggles the menu when the button is clicked', async () => {
73
- const wrapper = mount(SyInputSelect, {
74
- global: {
75
- plugins: [vuetify],
76
- },
77
- })
78
- const button = wrapper.find('.sy-input-select')
79
- await button.trigger('click')
80
- expect(wrapper.vm.isOpen).toBe(true)
81
- await button.trigger('click')
82
- expect(wrapper.vm.isOpen).toBe(false)
143
+ describe('Validation', () => {
144
+ it('validateField valide correctement un champ requis avec une valeur', async () => {
145
+ const wrapper = mount(SyInputSelect, {
146
+ props: {
147
+ required: true,
148
+ modelValue: { text: 'Option 1', value: '1' },
149
+ },
150
+ global: {
151
+ plugins: [vuetify],
152
+ },
153
+ })
154
+
155
+ const result = wrapper.vm.validateField({ text: 'Option 1', value: '1' })
156
+ expect(result).toBe(true)
157
+ expect(wrapper.find('.v-messages__message').exists()).toBe(false)
158
+ })
159
+
160
+ it('validateField échoue pour un champ requis sans valeur', async () => {
161
+ const wrapper = mount(SyInputSelect, {
162
+ props: {
163
+ required: true,
164
+ label: 'Test Label',
165
+ errorMessages: [],
166
+ },
167
+ global: {
168
+ plugins: [vuetify],
169
+ },
170
+ })
171
+
172
+ const result = wrapper.vm.validateField(null)
173
+ expect(result).toBe(false)
174
+
175
+ await wrapper.setProps({ errorMessages: ['Test Label est requis'] })
176
+ await wrapper.vm.$nextTick()
177
+
178
+ expect(wrapper.find('.v-messages__message').exists()).toBe(true)
179
+ expect(wrapper.find('.v-messages__message').text()).toContain('Test Label est requis')
180
+ })
181
+
182
+ it('validateOnSubmit retourne le résultat de validation', async () => {
183
+ const wrapper = mount(SyInputSelect, {
184
+ props: { required: true },
185
+ global: {
186
+ plugins: [vuetify],
187
+ },
188
+ })
189
+
190
+ const result = wrapper.vm.validateOnSubmit()
191
+ expect(result).toBe(false)
192
+
193
+ await wrapper.setProps({ modelValue: { text: 'Option 1', value: '1' } })
194
+ const resultWithValue = wrapper.vm.validateOnSubmit()
195
+ expect(resultWithValue).toBe(true)
196
+ })
197
+
198
+ it('vérifie que checkForErrors retourne le résultat de la validation', () => {
199
+ const wrapper1 = mount(SyInputSelect, {
200
+ props: {
201
+ required: true,
202
+ modelValue: null,
203
+ },
204
+ global: {
205
+ plugins: [vuetify],
206
+ },
207
+ })
208
+
209
+ const result1 = wrapper1.vm.checkForErrors()
210
+ expect(result1).toBe(false)
211
+
212
+ const wrapper2 = mount(SyInputSelect, {
213
+ props: {
214
+ required: true,
215
+ modelValue: { text: 'Option 1', value: '1' },
216
+ },
217
+ global: {
218
+ plugins: [vuetify],
219
+ },
220
+ })
221
+
222
+ const result2 = wrapper2.vm.checkForErrors()
223
+ expect(result2).toBe(true)
224
+ })
83
225
  })
84
226
 
85
- it('use closeList method', async () => {
86
- const wrapper = mount(SyInputSelect, {
87
- global: {
88
- plugins: [vuetify],
89
- },
227
+ describe('Mode readonly', () => {
228
+ it('désactive la validation en mode readonly', () => {
229
+ const wrapper = mount(SyInputSelect, {
230
+ props: {
231
+ readonly: true,
232
+ required: true,
233
+ },
234
+ global: {
235
+ plugins: [vuetify],
236
+ },
237
+ })
238
+
239
+ const result = wrapper.vm.validateField(null)
240
+ expect(result).toBe(true)
241
+ expect(wrapper.find('.v-messages__message').exists()).toBe(false)
242
+ })
243
+
244
+ it('empêche l\'ouverture du menu en mode readonly', async () => {
245
+ const wrapper = mount(SyInputSelect, {
246
+ props: { readonly: true },
247
+ global: {
248
+ plugins: [vuetify],
249
+ },
250
+ })
251
+
252
+ const button = wrapper.find('.sy-input-select')
253
+ await button.trigger('click')
254
+ expect(wrapper.vm.isOpen).toBe(false)
90
255
  })
91
- await wrapper.vm.closeList()
92
- expect(wrapper.vm.isOpen).toBe(false)
93
256
  })
94
257
 
95
- it('selectItem method', async () => {
96
- const wrapper = mount(SyInputSelect, {
97
- global: {
98
- plugins: [vuetify],
99
- },
258
+ describe('Option clearable', () => {
259
+ it('affiche l\'icône de suppression quand clearable est true et une valeur est sélectionnée', async () => {
260
+ const wrapper = mount(SyInputSelect, {
261
+ props: {
262
+ clearable: true,
263
+ modelValue: { text: 'Option 1', value: '1' },
264
+ },
265
+ global: {
266
+ plugins: [vuetify],
267
+ },
268
+ })
269
+
270
+ const clearIcon = wrapper.find('.sy-input-select .v-icon[aria-label="Supprimer"]')
271
+ expect(clearIcon.exists()).toBe(true)
272
+ })
273
+
274
+ it('n\'affiche pas l\'icône de suppression quand clearable est false', async () => {
275
+ const wrapper = mount(SyInputSelect, {
276
+ props: {
277
+ clearable: false,
278
+ modelValue: { text: 'Option 1', value: '1' },
279
+ },
280
+ global: {
281
+ plugins: [vuetify],
282
+ },
283
+ })
284
+
285
+ const clearIcon = wrapper.find('.sy-input-select .v-icon[aria-label="Supprimer"]')
286
+ expect(clearIcon.exists()).toBe(false)
287
+ })
288
+
289
+ it('efface la valeur sélectionnée quand l\'icône de suppression est cliquée', async () => {
290
+ const wrapper = mount(SyInputSelect, {
291
+ props: {
292
+ clearable: true,
293
+ modelValue: { text: 'Option 1', value: '1' },
294
+ },
295
+ global: {
296
+ plugins: [vuetify],
297
+ },
298
+ })
299
+
300
+ const clearIcon = wrapper.find('.sy-input-select .v-icon[aria-label="Supprimer"]')
301
+ await clearIcon.trigger('click')
302
+ expect(wrapper.emitted('update:modelValue')?.[0]).toEqual([null])
100
303
  })
101
- await wrapper.vm.selectItem({ text: 'Option 1', value: '1' })
102
- expect(wrapper.vm.isOpen).toBe(false)
103
- expect(wrapper.vm.selectedItem).toEqual({ text: 'Option 1', value: '1' })
104
304
  })
105
305
 
106
- it('getItemText method', async () => {
107
- const wrapper = mount(SyInputSelect, {
108
- global: {
109
- plugins: [vuetify],
110
- },
306
+ describe('Affichage de l\'astérisque', () => {
307
+ it('affiche l\'astérisque quand displayAsterisk et required sont true', () => {
308
+ const wrapper = mount(SyInputSelect, {
309
+ props: {
310
+ displayAsterisk: true,
311
+ required: true,
312
+ label: 'Test Label',
313
+ },
314
+ global: {
315
+ plugins: [vuetify],
316
+ },
317
+ })
318
+
319
+ const html = wrapper.html()
320
+ expect(html).toContain('Test Label *')
321
+ })
322
+
323
+ it('n\'affiche pas l\'astérisque quand displayAsterisk est false', () => {
324
+ const wrapper = mount(SyInputSelect, {
325
+ props: {
326
+ displayAsterisk: false,
327
+ required: true,
328
+ label: 'Test Label',
329
+ },
330
+ global: {
331
+ plugins: [vuetify],
332
+ },
333
+ })
334
+
335
+ const html = wrapper.html()
336
+ expect(html).not.toContain('Test Label *')
337
+ expect(html).toContain('Test Label')
338
+ })
339
+
340
+ it('n\'affiche pas l\'astérisque quand required est false', () => {
341
+ const wrapper = mount(SyInputSelect, {
342
+ props: {
343
+ displayAsterisk: true,
344
+ required: false,
345
+ label: 'Test Label',
346
+ },
347
+ global: {
348
+ plugins: [vuetify],
349
+ },
350
+ })
351
+
352
+ const html = wrapper.html()
353
+ expect(html).not.toContain('Test Label *')
354
+ expect(html).toContain('Test Label')
111
355
  })
112
- const item = { text: 'Option 1', value: '1' }
113
- const text = wrapper.vm.getItemText(item)
114
- expect(text).toBe('Option 1')
115
356
  })
116
357
 
117
- it('watch modelValue', async () => {
118
- const wrapper = mount(SyInputSelect, {
119
- props: { modelValue: { text: 'Option 1', value: '1' } },
120
- global: {
121
- plugins: [vuetify],
122
- },
358
+ describe('Événements émis', () => {
359
+ it('émet update:modelValue lors de la sélection d\'un élément', async () => {
360
+ const wrapper = mount(SyInputSelect, {
361
+ props: {
362
+ items: [{ text: 'Option 1', value: '1' }, { text: 'Option 2', value: '2' }],
363
+ },
364
+ global: {
365
+ plugins: [vuetify],
366
+ },
367
+ })
368
+
369
+ await wrapper.vm.selectItem({ text: 'Option 1', value: '1' })
370
+ expect(wrapper.emitted('update:modelValue')?.[0]).toEqual([{ text: 'Option 1', value: '1' }])
371
+ })
372
+
373
+ it('émet update:errorMessages après validation', async () => {
374
+ const wrapper = mount(SyInputSelect, {
375
+ props: {
376
+ required: true,
377
+ label: 'Test Label',
378
+ },
379
+ global: {
380
+ plugins: [vuetify],
381
+ },
382
+ })
383
+
384
+ await wrapper.vm.validateField(null)
385
+ await wrapper.vm.selectItem(null)
386
+
387
+ expect(wrapper.emitted('update:errorMessages')).toBeTruthy()
388
+ expect(wrapper.emitted('update:errorMessages')?.[0][0]).toContainEqual(expect.stringContaining('Test Label est requis'))
123
389
  })
124
- expect(wrapper.vm.selectedItem).toEqual({ text: 'Option 1', value: '1' })
125
- await wrapper.setProps({ modelValue: { text: 'Option 2', value: '2' } })
126
- expect(wrapper.vm.selectedItem).toEqual({ text: 'Option 2', value: '2' })
127
390
  })
128
391
 
129
- it('watch errorMessages', async () => {
130
- const wrapper = mount(SyInputSelect, {
131
- props: { errorMessages: ['Error message'] },
132
- global: {
133
- plugins: [vuetify],
134
- },
392
+ describe('Comportement du menu', () => {
393
+ it('ouvre le menu quand on clique sur le bouton', async () => {
394
+ const wrapper = mount(SyInputSelect, {
395
+ global: {
396
+ plugins: [vuetify],
397
+ },
398
+ })
399
+
400
+ const button = wrapper.find('.sy-input-select')
401
+ await button.trigger('click')
402
+
403
+ expect(wrapper.find('.v-list').exists()).toBe(true)
404
+ })
405
+
406
+ it('applique des styles différents pour isHeaderToolbar', async () => {
407
+ const wrapper = mount(SyInputSelect, {
408
+ props: { isHeaderToolbar: true },
409
+ global: {
410
+ plugins: [vuetify],
411
+ },
412
+ })
413
+
414
+ const button = wrapper.find('.sy-input-select')
415
+ await button.trigger('click')
416
+
417
+ expect(wrapper.find('.v-list').attributes('is-header-toolbar')).toBeTruthy()
135
418
  })
136
- expect(wrapper.find('.v-messages__message').exists()).toBe(true)
137
- await wrapper.setProps({ errorMessages: [] })
138
- expect(wrapper.find('.v-messages__message').exists()).toBe(false)
139
419
  })
140
420
  })
@@ -1,7 +1,7 @@
1
1
  import type { Meta, StoryObj } from '@storybook/vue3'
2
2
  import SySelect from '@/components/Customs/SySelect/SySelect.vue'
3
3
  import SyAlert from '@/components/SyAlert/SyAlert.vue'
4
- import { VBtn, VMenu, VList, VListItem, VListItemTitle } from 'vuetify/components'
4
+ import { VBtn, VMenu, VList, VListItem, VListItemTitle, VForm } from 'vuetify/components'
5
5
  import { ref } from 'vue'
6
6
  import { fn } from '@storybook/test'
7
7
 
@@ -34,6 +34,19 @@ const meta: Meta<typeof SySelect> = {
34
34
  control: 'boolean',
35
35
  description: 'Permet de vider la sélection',
36
36
  },
37
+ hideMessages: {
38
+ control: 'boolean',
39
+ description: 'Masque les messages d\'erreur',
40
+ },
41
+ density: {
42
+ control: 'select',
43
+ options: ['default', 'comfortable', 'compact'],
44
+ description: 'Définit la densité du champ de sélection',
45
+ },
46
+ width: {
47
+ control: 'text',
48
+ description: 'Permet de définir une largeur personnalisée pour le champ de sélection (en px)',
49
+ },
37
50
  },
38
51
  } as Meta<typeof SySelect>
39
52
 
@@ -361,9 +374,115 @@ export const Info: Story = {
361
374
  <li>- Si les items sont un tableau de string, le composant les utilisera directement.</li>
362
375
  </ul>
363
376
  </template>
364
- </SyAlert>
377
+ </SyAlert>
365
378
  `,
366
379
  }
367
380
  },
368
381
  tags: ['!dev'],
369
382
  }
383
+
384
+ export const FormValidation: Story = {
385
+ parameters: {
386
+ docs: {
387
+ description: {
388
+ story: 'Exemple d\'utilisation du SySelect dans un formulaire.',
389
+ },
390
+ },
391
+ sourceCode: [
392
+ {
393
+ name: 'Template',
394
+ code: `
395
+ <template>
396
+ <VForm @submit.prevent="submitForm">
397
+ <SySelect
398
+ v-model="formData.option"
399
+ :items="options"
400
+ label="Option"
401
+ required
402
+ display-asterisk
403
+ class="mb-4"
404
+ />
405
+ <VBtn
406
+ type="submit"
407
+ color="primary"
408
+ class="mt-4"
409
+ >
410
+ Soumettre
411
+ </VBtn>
412
+ </VForm>
413
+ </template>
414
+ `,
415
+ },
416
+ {
417
+ name: 'Script',
418
+ code: `
419
+ <script setup lang="ts">
420
+ import { ref } from 'vue'
421
+ import SySelect from '@cnamts/synapse'
422
+ import { VBtn, VForm } from 'vuetify/components'
423
+
424
+ const formData = ref({
425
+ option: ''
426
+ })
427
+
428
+ const options = [
429
+ { text: 'Option 1', value: '1' },
430
+ { text: 'Option 2', value: '2' },
431
+ { text: 'Option 3', value: '3' },
432
+ ]
433
+
434
+ const submitForm = () => {
435
+ // Traitement du formulaire
436
+ console.log('Formulaire soumis:', formData.value)
437
+ }
438
+ </script>
439
+ `,
440
+ },
441
+ ],
442
+ },
443
+ args: {
444
+ 'items': [
445
+ { text: 'Option 1', value: '1' },
446
+ { text: 'Option 2', value: '2' },
447
+ { text: 'Option 3', value: '3' },
448
+ ],
449
+ 'label': 'Option',
450
+ 'required': true,
451
+ 'displayAsterisk': true,
452
+ 'onUpdate:modelValue': fn(),
453
+ },
454
+ render: (args) => {
455
+ return {
456
+ components: { SySelect, VBtn, VForm },
457
+ setup() {
458
+ const formData = ref({
459
+ option: '',
460
+ })
461
+
462
+ const submitForm = () => {
463
+ console.log('Formulaire soumis:', formData.value)
464
+ }
465
+
466
+ return { args, formData, submitForm }
467
+ },
468
+ template: `
469
+ <div class="pa-4">
470
+ <VForm @submit.prevent="submitForm">
471
+ <SySelect
472
+ v-model="formData.option"
473
+ v-bind="args"
474
+ class="mb-4"
475
+ />
476
+ <VBtn
477
+ type="submit"
478
+ color="primary"
479
+ class="mt-4"
480
+ >
481
+ Soumettre
482
+ </VBtn>
483
+ </VForm>
484
+ </div>
485
+ `,
486
+ }
487
+ },
488
+ }