@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.
- package/dist/components/Accordion/Accordion.d.ts +39 -0
- package/dist/components/Accordion/config.d.ts +9 -0
- package/dist/components/ChipList/ChipList.d.ts +1 -1
- package/dist/components/CookiesSelection/CookiesSelection.d.ts +26 -26
- package/dist/components/CopyBtn/CopyBtn.d.ts +2 -0
- package/dist/components/Customs/SyInputSelect/SyInputSelect.d.ts +12 -0
- package/dist/components/Customs/SySelect/SySelect.d.ts +43 -16
- package/dist/components/Customs/SyTextField/SyTextField.d.ts +1391 -1
- package/dist/components/DatePicker/DatePicker.d.ts +2810 -16
- package/dist/components/DatePicker/DateTextInput.d.ts +1401 -4
- package/dist/components/DiacriticPicker/DiacriticPicker.d.ts +27 -0
- package/dist/components/DiacriticPicker/config.d.ts +14 -0
- package/dist/components/DiacriticPicker/locales.d.ts +6 -0
- package/dist/components/DownloadBtn/DownloadBtn.d.ts +1 -1
- package/dist/components/FooterBar/FooterBar.d.ts +1 -1
- package/dist/components/LangBtn/LangBtn.d.ts +4 -4
- package/dist/components/NirField/NirField.d.ts +2796 -4
- package/dist/components/NotificationBar/NotificationBar.d.ts +1 -1
- package/dist/components/PasswordField/PasswordField.d.ts +1 -1
- package/dist/components/PeriodField/PeriodField.d.ts +5636 -48
- package/dist/components/PhoneField/PhoneField.d.ts +1 -0
- package/dist/components/PhoneField/tests/types.d.ts +18 -0
- package/dist/components/SyAlert/SyAlert.d.ts +72 -1
- package/dist/components/SyTextArea/SyTextArea.d.ts +900 -0
- package/dist/components/SyTextArea/locales.d.ts +3 -0
- package/dist/components/SyTextArea/trimStartOnUpdate.d.ts +1 -0
- package/dist/components/SyTextArea/useTextActions.d.ts +13 -0
- package/dist/components/SyTextArea/wrapText.d.ts +1 -0
- package/dist/components/TableToolbar/TableToolbar.d.ts +10 -4
- package/dist/components/TableToolbar/config.d.ts +3 -2
- package/dist/components/UploadWorkflow/UploadWorkflow.d.ts +26 -26
- package/dist/components/index.d.ts +4 -0
- package/dist/composables/date/useDateFormat.d.ts +2 -2
- package/dist/composables/date/useDateFormatDayjs.d.ts +23 -0
- package/dist/composables/date/useDateInitializationDayjs.d.ts +18 -0
- package/dist/composables/date/useHolidayDay.d.ts +36 -0
- package/dist/design-system-v3.js +5106 -4208
- package/dist/design-system-v3.umd.cjs +4 -1
- package/dist/designTokens/tokens/pa/paLightTheme.d.ts +1 -32
- package/dist/style.css +1 -1
- package/dist/utils/rules/index.d.ts +1 -0
- package/dist/utils/rules/isHolidayDay/index.d.ts +11 -0
- package/dist/utils/rules/isHolidayDay/locales.d.ts +2 -0
- package/package.json +3 -2
- package/src/assets/settings.scss +12 -0
- package/src/components/Accordion/Accordion.mdx +69 -0
- package/src/components/Accordion/Accordion.stories.ts +262 -0
- package/src/components/Accordion/Accordion.vue +319 -0
- package/src/components/Accordion/config.ts +9 -0
- package/src/components/Accordion/tests/__snapshots__/accordion.spec.ts.snap +155 -0
- package/src/components/Accordion/tests/accordion.spec.ts +492 -0
- package/src/components/CopyBtn/CopyBtn.stories.ts +189 -0
- package/src/components/CopyBtn/CopyBtn.vue +29 -1
- package/src/components/CopyBtn/tests/CopyBtn.spec.ts +102 -0
- package/src/components/Customs/SyInputSelect/SyInputSelect.stories.ts +155 -1
- package/src/components/Customs/SyInputSelect/SyInputSelect.vue +97 -14
- package/src/components/Customs/SyInputSelect/tests/SyInputSelect.spec.ts +386 -106
- package/src/components/Customs/SySelect/SySelect.stories.ts +121 -2
- package/src/components/Customs/SySelect/SySelect.vue +33 -8
- package/src/components/Customs/SySelect/tests/SySelect.spec.ts +290 -1
- package/src/components/Customs/SyTextField/Accessibilite.stories.ts +7 -0
- package/src/components/Customs/SyTextField/SyTextField.stories.ts +13 -0
- package/src/components/Customs/SyTextField/SyTextField.vue +87 -20
- package/src/components/DatePicker/ComplexDatePicker/ComplexDatePicker.vue +795 -0
- package/src/components/DatePicker/DatePicker.stories.ts +432 -1
- package/src/components/DatePicker/DatePicker.vue +82 -27
- package/src/components/DatePicker/DatePickerValidation.stories.ts +9 -1
- package/src/components/DatePicker/DateTextInput.vue +101 -138
- package/src/components/DatePicker/docExamples/DatePickerBidirectionalValidation.vue +282 -0
- package/src/components/DatePicker/examples/DatePickerHolidayRule.vue +130 -0
- package/src/components/DatePicker/tests/DatePicker.spec.ts +33 -32
- package/src/components/DatePicker/tests/DateTextInput.spec.ts +81 -33
- package/src/components/DiacriticPicker/DiacriticPicker.mdx +104 -0
- package/src/components/DiacriticPicker/DiacriticPicker.stories.ts +447 -0
- package/src/components/DiacriticPicker/DiacriticPicker.vue +262 -0
- package/src/components/DiacriticPicker/config.ts +15 -0
- package/src/components/DiacriticPicker/locales.ts +6 -0
- package/src/components/DiacriticPicker/tests/DiatriticPicker.spec.ts +132 -0
- package/src/components/DialogBox/DialogBox.vue +1 -3
- package/src/components/NirField/NirField.stories.ts +172 -0
- package/src/components/NirField/NirField.vue +15 -7
- package/src/components/NotificationBar/Accessibilite.stories.ts +1 -1
- package/src/components/NotificationBar/NotificationBar.stories.ts +14 -0
- package/src/components/NotificationBar/NotificationBar.vue +26 -3
- package/src/components/NotificationBar/{options.ts → config.ts} +0 -1
- package/src/components/PaginatedTable/PaginatedTable.vue +0 -11
- package/src/components/PasswordField/PasswordField.stories.ts +4 -3
- package/src/components/PasswordField/PasswordField.vue +26 -18
- package/src/components/PasswordField/tests/PasswordField.spec.ts +1 -10
- package/src/components/PhoneField/PhoneField.stories.ts +143 -0
- package/src/components/PhoneField/PhoneField.vue +88 -30
- package/src/components/PhoneField/tests/PhoneField.additional.spec.ts +266 -0
- package/src/components/PhoneField/tests/PhoneField.spec.ts +248 -28
- package/src/components/PhoneField/tests/types.d.ts +19 -0
- package/src/components/SyAlert/Accessibilite.stories.ts +4 -0
- package/src/components/SyAlert/SyAlert.mdx +3 -7
- package/src/components/SyAlert/SyAlert.stories.ts +19 -12
- package/src/components/SyAlert/SyAlert.vue +88 -51
- package/src/components/SyAlert/tests/SyAlert.spec.ts +20 -2
- package/src/components/SyAlert/tests/__snapshots__/SyAlert.spec.ts.snap +83 -75
- package/src/components/SyTextArea/SyTextArea.mdx +17 -0
- package/src/components/SyTextArea/SyTextArea.stories.ts +322 -0
- package/src/components/SyTextArea/SyTextArea.vue +113 -0
- package/src/components/SyTextArea/locales.ts +3 -0
- package/src/components/SyTextArea/tests/SyTextArea.spec.ts +194 -0
- package/src/components/SyTextArea/trimStartOnUpdate.ts +12 -0
- package/src/components/SyTextArea/useTextActions.ts +52 -0
- package/src/components/SyTextArea/wrapText.ts +42 -0
- package/src/components/TableToolbar/TableToolbar.mdx +86 -1
- package/src/components/TableToolbar/TableToolbar.stories.ts +422 -74
- package/src/components/TableToolbar/TableToolbar.vue +25 -8
- package/src/components/TableToolbar/config.ts +3 -2
- package/src/components/TableToolbar/tests/__snapshots__/TableToolbar.spec.ts.snap +35 -12
- package/src/components/index.ts +4 -0
- package/src/composables/date/useDateFormat.ts +17 -1
- package/src/composables/date/useDateFormatDayjs.ts +84 -0
- package/src/composables/date/useDateInitializationDayjs.ts +133 -0
- package/src/composables/date/useHolidayDay.ts +98 -0
- package/src/composables/rules/useFieldValidation.ts +16 -3
- package/src/composables/validation/useValidation.ts +2 -1
- package/src/designTokens/tokens/pa/paLightTheme.ts +10 -41
- package/src/stories/Accessibilite/Avancement/Avancement.mdx +12 -0
- package/src/stories/Accessibilite/Avancement/Avancement.stories.ts +134 -0
- package/src/stories/Accessibilite/Introduction.mdx +5 -2
- package/src/stories/DesignTokens/colors.stories.ts +100 -41
- package/src/utils/rules/index.ts +1 -0
- package/src/utils/rules/isHolidayDay/IsHolidayDay.mdx +52 -0
- package/src/utils/rules/isHolidayDay/IsHolidayDay.stories.ts +129 -0
- package/src/utils/rules/isHolidayDay/index.ts +36 -0
- package/src/utils/rules/isHolidayDay/locales.ts +5 -0
- package/src/utils/rules/isHolidayDay/tests/isHolidayDay.spec.ts +35 -0
- /package/dist/components/NotificationBar/{options.d.ts → config.d.ts} +0 -0
- /package/src/components/DatePicker/{DatePickerValidationExamples.vue → docExamples/DatePickerValidationExamples.vue} +0 -0
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
<script lang="ts" setup>
|
|
2
|
+
import { ref, computed } from 'vue'
|
|
3
|
+
import { useFieldValidation } from '@/composables/rules/useFieldValidation'
|
|
4
|
+
import { useHolidayDay } from '@/composables/date/useHolidayDay'
|
|
5
|
+
|
|
6
|
+
const selectedDate = ref('')
|
|
7
|
+
const { generateRules } = useFieldValidation()
|
|
8
|
+
const { isHolidayDay, getJoursFeries } = useHolidayDay()
|
|
9
|
+
|
|
10
|
+
// Année courante pour afficher les jours fériés
|
|
11
|
+
const currentYear = new Date().getFullYear()
|
|
12
|
+
|
|
13
|
+
// Création de la règle qui vérifie qu'une date n'est pas un jour férié
|
|
14
|
+
const holidayRules = generateRules([
|
|
15
|
+
{
|
|
16
|
+
type: 'isHolidayDay',
|
|
17
|
+
options: {
|
|
18
|
+
fieldName: 'La date',
|
|
19
|
+
message: 'Vous ne pouvez pas sélectionner un jour férié.',
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
])
|
|
23
|
+
|
|
24
|
+
// Vérification si la date sélectionnée est un jour férié (pour l'affichage)
|
|
25
|
+
const isDateHoliday = computed(() => {
|
|
26
|
+
if (!selectedDate.value) return false
|
|
27
|
+
return isHolidayDay(selectedDate.value)
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
// Liste des jours fériés de l'année courante
|
|
31
|
+
const currentYearHolidays = computed(() => {
|
|
32
|
+
const holidays = getJoursFeries(currentYear)
|
|
33
|
+
return Array.from(holidays).sort()
|
|
34
|
+
})
|
|
35
|
+
</script>
|
|
36
|
+
|
|
37
|
+
<template>
|
|
38
|
+
<div class="date-picker-holiday-example">
|
|
39
|
+
<h2>DatePicker avec règle de validation pour jours fériés</h2>
|
|
40
|
+
<p>
|
|
41
|
+
Cet exemple montre comment utiliser la règle <code>isHolidayDay</code> pour empêcher
|
|
42
|
+
la sélection de jours fériés dans un DatePicker.
|
|
43
|
+
</p>
|
|
44
|
+
|
|
45
|
+
<div class="date-picker-container">
|
|
46
|
+
<DatePicker
|
|
47
|
+
v-model="selectedDate"
|
|
48
|
+
label="Date (pas de jour férié)"
|
|
49
|
+
:rules="holidayRules"
|
|
50
|
+
error-messages
|
|
51
|
+
placeholder="Sélectionnez une date non fériée"
|
|
52
|
+
/>
|
|
53
|
+
</div>
|
|
54
|
+
|
|
55
|
+
<div class="info-container mt-4">
|
|
56
|
+
<h3>Informations</h3>
|
|
57
|
+
<p>Date sélectionnée : <strong>{{ selectedDate || 'Aucune' }}</strong></p>
|
|
58
|
+
|
|
59
|
+
<div
|
|
60
|
+
v-if="selectedDate"
|
|
61
|
+
class="mt-2"
|
|
62
|
+
>
|
|
63
|
+
<p
|
|
64
|
+
v-if="isDateHoliday"
|
|
65
|
+
class="error-text"
|
|
66
|
+
>
|
|
67
|
+
⚠️ Le {{ selectedDate }} est un jour férié.
|
|
68
|
+
</p>
|
|
69
|
+
<p
|
|
70
|
+
v-else
|
|
71
|
+
class="success-text"
|
|
72
|
+
>
|
|
73
|
+
✅ Le {{ selectedDate }} n'est pas un jour férié.
|
|
74
|
+
</p>
|
|
75
|
+
</div>
|
|
76
|
+
|
|
77
|
+
<div
|
|
78
|
+
v-if="currentYearHolidays.length"
|
|
79
|
+
class="holiday-list mt-4"
|
|
80
|
+
>
|
|
81
|
+
<h4>Jours fériés {{ currentYear }} :</h4>
|
|
82
|
+
<ul>
|
|
83
|
+
<li
|
|
84
|
+
v-for="(date, index) in currentYearHolidays"
|
|
85
|
+
:key="index"
|
|
86
|
+
>
|
|
87
|
+
{{ date }}
|
|
88
|
+
</li>
|
|
89
|
+
</ul>
|
|
90
|
+
</div>
|
|
91
|
+
</div>
|
|
92
|
+
</div>
|
|
93
|
+
</template>
|
|
94
|
+
|
|
95
|
+
<style scoped>
|
|
96
|
+
.date-picker-holiday-example {
|
|
97
|
+
max-width: 800px;
|
|
98
|
+
margin: 0 auto;
|
|
99
|
+
padding: 20px;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
.date-picker-container {
|
|
103
|
+
max-width: 400px;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
.info-container {
|
|
107
|
+
background-color: #f5f5f5;
|
|
108
|
+
padding: 15px;
|
|
109
|
+
border-radius: 4px;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
.error-text {
|
|
113
|
+
color: #d32f2f;
|
|
114
|
+
font-weight: bold;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
.success-text {
|
|
118
|
+
color: #2e7d32;
|
|
119
|
+
font-weight: bold;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
.holiday-list {
|
|
123
|
+
max-height: 200px;
|
|
124
|
+
overflow-y: auto;
|
|
125
|
+
background-color: #fff;
|
|
126
|
+
padding: 10px;
|
|
127
|
+
border-radius: 4px;
|
|
128
|
+
border: 1px solid #e0e0e0;
|
|
129
|
+
}
|
|
130
|
+
</style>
|
|
@@ -60,12 +60,6 @@ describe('DatePicker.vue', () => {
|
|
|
60
60
|
expect(wrapper.emitted('update:modelValue')[0]).toEqual(['01/01/2023'])
|
|
61
61
|
})
|
|
62
62
|
|
|
63
|
-
it('toggles the date picker visibility on focus', async () => {
|
|
64
|
-
const input = wrapper.find('input')
|
|
65
|
-
await input.trigger('focus')
|
|
66
|
-
expect(wrapper.vm.isDatePickerVisible).toBe(true)
|
|
67
|
-
})
|
|
68
|
-
|
|
69
63
|
it('renders the date picker with proper structure', async () => {
|
|
70
64
|
// Ouvrir le DatePicker pour que les éléments soient dans le DOM
|
|
71
65
|
wrapper.vm.isDatePickerVisible = true
|
|
@@ -151,17 +145,15 @@ describe('DatePicker.vue', () => {
|
|
|
151
145
|
plugins: [vuetify],
|
|
152
146
|
},
|
|
153
147
|
props: {
|
|
154
|
-
required: true,
|
|
148
|
+
required: true,
|
|
155
149
|
},
|
|
156
150
|
})
|
|
157
151
|
|
|
158
|
-
// Simule une date valide
|
|
159
152
|
wrapper.vm.selectedDates = [new Date('2023-01-01')]
|
|
160
153
|
await nextTick()
|
|
161
154
|
|
|
162
155
|
const result = wrapper.vm.validateOnSubmit()
|
|
163
156
|
|
|
164
|
-
// Vérifie que validateOnSubmit retourne true et qu'il n'y a pas d'erreurs
|
|
165
157
|
expect(result).toBe(true)
|
|
166
158
|
expect(wrapper.vm.errorMessages).toEqual([])
|
|
167
159
|
})
|
|
@@ -172,34 +164,32 @@ describe('DatePicker.vue', () => {
|
|
|
172
164
|
plugins: [vuetify],
|
|
173
165
|
},
|
|
174
166
|
props: {
|
|
175
|
-
required: true,
|
|
167
|
+
required: true,
|
|
176
168
|
},
|
|
177
169
|
})
|
|
178
170
|
|
|
179
|
-
// Simule l'absence de date sélectionnée
|
|
180
171
|
wrapper.vm.selectedDates = null
|
|
181
172
|
await nextTick()
|
|
182
173
|
|
|
183
174
|
const result = wrapper.vm.validateOnSubmit()
|
|
184
175
|
|
|
185
|
-
// Vérifie que validateOnSubmit retourne false et qu'il y a des erreurs
|
|
186
176
|
expect(result).toBe(false)
|
|
187
177
|
expect(wrapper.vm.errorMessages).toContain('La date est requise.')
|
|
188
178
|
})
|
|
189
179
|
|
|
190
180
|
it('parses a valid date string into a Date instance', () => {
|
|
191
|
-
const modelValue = '15/01/2023'
|
|
181
|
+
const modelValue = '15/01/2023'
|
|
192
182
|
const result = wrapper.vm.initializeSelectedDates(modelValue, 'DD/MM/YYYY')
|
|
193
183
|
|
|
194
|
-
expect(result).toBeInstanceOf(Date)
|
|
195
|
-
expect(result.toISOString().split('T')[0]).toBe('2023-01-15')
|
|
184
|
+
expect(result).toBeInstanceOf(Date)
|
|
185
|
+
expect(result.toISOString().split('T')[0]).toBe('2023-01-15')
|
|
196
186
|
})
|
|
197
187
|
|
|
198
188
|
it('returns null if modelValue is null or undefined', () => {
|
|
199
189
|
const modelValue = null
|
|
200
190
|
const result = wrapper.vm.initializeSelectedDates(modelValue, 'DD/MM/YYYY')
|
|
201
191
|
|
|
202
|
-
expect(result).toBeNull()
|
|
192
|
+
expect(result).toBeNull()
|
|
203
193
|
})
|
|
204
194
|
|
|
205
195
|
it('handles an invalid date string gracefully', () => {
|
|
@@ -210,24 +200,23 @@ describe('DatePicker.vue', () => {
|
|
|
210
200
|
})
|
|
211
201
|
|
|
212
202
|
it('sets selectedDates to null when input is empty', () => {
|
|
213
|
-
wrapper.vm.updateSelectedDates('')
|
|
203
|
+
wrapper.vm.updateSelectedDates('')
|
|
214
204
|
|
|
215
|
-
expect(wrapper.vm.selectedDates).toBeNull()
|
|
205
|
+
expect(wrapper.vm.selectedDates).toBeNull()
|
|
216
206
|
})
|
|
217
207
|
|
|
218
208
|
it('parses a valid date string and updates selectedDates', () => {
|
|
219
209
|
const validInput = '15/01/2023'
|
|
220
|
-
wrapper.vm.updateSelectedDates(validInput)
|
|
210
|
+
wrapper.vm.updateSelectedDates(validInput)
|
|
221
211
|
|
|
222
|
-
expect(wrapper.vm.selectedDates).toBeInstanceOf(Date)
|
|
223
|
-
expect(wrapper.vm.selectedDates.toISOString().split('T')[0]).toBe('2023-01-15')
|
|
212
|
+
expect(wrapper.vm.selectedDates).toBeInstanceOf(Date)
|
|
213
|
+
expect(wrapper.vm.selectedDates.toISOString().split('T')[0]).toBe('2023-01-15')
|
|
224
214
|
})
|
|
225
215
|
|
|
226
216
|
it('does not update selectedDates for invalid date string', () => {
|
|
227
217
|
const invalidInput = 'invalid-date'
|
|
228
|
-
wrapper.vm.updateSelectedDates(invalidInput)
|
|
229
|
-
|
|
230
|
-
expect(wrapper.vm.selectedDates).toBeNull() // Vérifie que selectedDates reste null
|
|
218
|
+
wrapper.vm.updateSelectedDates(invalidInput)
|
|
219
|
+
expect(wrapper.vm.selectedDates).toBeNull()
|
|
231
220
|
})
|
|
232
221
|
|
|
233
222
|
it('toggles date picker visibility correctly', async () => {
|
|
@@ -235,15 +224,12 @@ describe('DatePicker.vue', () => {
|
|
|
235
224
|
global: { plugins: [vuetify] },
|
|
236
225
|
})
|
|
237
226
|
|
|
238
|
-
// État initial : le date picker est caché
|
|
239
227
|
expect(wrapper.vm.isDatePickerVisible).toBe(false)
|
|
240
228
|
|
|
241
|
-
// Ouvrir le date picker
|
|
242
229
|
wrapper.vm.openDatePicker()
|
|
243
230
|
await nextTick()
|
|
244
231
|
expect(wrapper.vm.isDatePickerVisible).toBe(true)
|
|
245
232
|
|
|
246
|
-
// Fermer le date picker
|
|
247
233
|
wrapper.vm.isDatePickerVisible = false
|
|
248
234
|
await nextTick()
|
|
249
235
|
expect(wrapper.vm.isDatePickerVisible).toBe(false)
|
|
@@ -257,7 +243,6 @@ describe('DatePicker.vue', () => {
|
|
|
257
243
|
},
|
|
258
244
|
})
|
|
259
245
|
|
|
260
|
-
// Valider sans date (devrait échouer)
|
|
261
246
|
const result = await wrapper.vm.validateOnSubmit()
|
|
262
247
|
expect(result).toBe(false)
|
|
263
248
|
expect(wrapper.vm.errorMessages.length).toBeGreaterThan(0)
|
|
@@ -299,11 +284,9 @@ describe('DatePicker.vue', () => {
|
|
|
299
284
|
},
|
|
300
285
|
})
|
|
301
286
|
|
|
302
|
-
// Valider sans date pour générer une erreur
|
|
303
287
|
await wrapper.vm.validateOnSubmit()
|
|
304
288
|
await nextTick()
|
|
305
289
|
|
|
306
|
-
// Vérifier que l'erreur est ajoutée à errorMessages
|
|
307
290
|
expect(wrapper.vm.errorMessages.length).toBeGreaterThan(0)
|
|
308
291
|
const errorMessage = wrapper.find('.v-messages__message')
|
|
309
292
|
expect(errorMessage.exists()).toBe(true)
|
|
@@ -318,12 +301,30 @@ describe('DatePicker.vue', () => {
|
|
|
318
301
|
},
|
|
319
302
|
})
|
|
320
303
|
|
|
321
|
-
// Vérifier que le mode birth date est appliqué
|
|
322
304
|
expect(wrapper.props('isBirthDate')).toBe(true)
|
|
323
305
|
|
|
324
|
-
// Ouvrir le picker et vérifier qu'il est visible
|
|
325
306
|
wrapper.vm.openDatePicker()
|
|
326
307
|
await nextTick()
|
|
327
308
|
expect(wrapper.vm.isDatePickerVisible).toBe(true)
|
|
328
309
|
})
|
|
310
|
+
|
|
311
|
+
it('takes into account disabled and readonly limitations', async () => {
|
|
312
|
+
const wrapper = mount(DatePicker, {
|
|
313
|
+
global: { plugins: [vuetify] },
|
|
314
|
+
props: {
|
|
315
|
+
disabled: true,
|
|
316
|
+
},
|
|
317
|
+
})
|
|
318
|
+
|
|
319
|
+
wrapper.vm.openDatePicker()
|
|
320
|
+
await nextTick()
|
|
321
|
+
|
|
322
|
+
expect(wrapper.vm.isDatePickerVisible).toBe(false)
|
|
323
|
+
|
|
324
|
+
await wrapper.setProps({ disabled: false, readonly: true })
|
|
325
|
+
wrapper.vm.openDatePicker()
|
|
326
|
+
await nextTick()
|
|
327
|
+
|
|
328
|
+
expect(wrapper.vm.isDatePickerVisible).toBe(false)
|
|
329
|
+
})
|
|
329
330
|
})
|
|
@@ -38,7 +38,7 @@ describe('DateTextInput.vue', () => {
|
|
|
38
38
|
await input.trigger('blur')
|
|
39
39
|
await wrapper.vm.$nextTick()
|
|
40
40
|
const textField = wrapper.findComponent(SyTextField)
|
|
41
|
-
expect(textField.props('errorMessages')).toContain('Format de date invalide')
|
|
41
|
+
expect(textField.props('errorMessages')).toContain('Format de date invalide (DD/MM/YYYY)')
|
|
42
42
|
})
|
|
43
43
|
|
|
44
44
|
it('accepts valid date format', async () => {
|
|
@@ -116,20 +116,20 @@ describe('DateTextInput.vue', () => {
|
|
|
116
116
|
},
|
|
117
117
|
})
|
|
118
118
|
|
|
119
|
-
// Force
|
|
119
|
+
// Force validation by triggering a blur event
|
|
120
120
|
const input = wrapper.find('input')
|
|
121
121
|
await input.trigger('focus')
|
|
122
122
|
await input.trigger('blur')
|
|
123
123
|
await wrapper.vm.$nextTick()
|
|
124
|
-
await wrapper.vm.$nextTick() // Double nextTick
|
|
125
|
-
//
|
|
124
|
+
await wrapper.vm.$nextTick() // Double nextTick to ensure updates are completed
|
|
125
|
+
// We can also force validation manually
|
|
126
126
|
await wrapper.vm.validateOnSubmit()
|
|
127
127
|
await wrapper.vm.$nextTick()
|
|
128
128
|
const textField = wrapper.findComponent(SyTextField)
|
|
129
129
|
const warningMessages = textField.props('warningMessages') || []
|
|
130
|
-
//
|
|
130
|
+
// Check that the warning message is present
|
|
131
131
|
expect(warningMessages.length).toBeGreaterThan(0)
|
|
132
|
-
//
|
|
132
|
+
// The actual message starts with "Attention:"
|
|
133
133
|
expect(warningMessages[0]).toContain('Attention :')
|
|
134
134
|
})
|
|
135
135
|
|
|
@@ -139,7 +139,7 @@ describe('DateTextInput.vue', () => {
|
|
|
139
139
|
await input.trigger('blur')
|
|
140
140
|
await wrapper.vm.$nextTick()
|
|
141
141
|
const textField = wrapper.findComponent(SyTextField)
|
|
142
|
-
expect(textField.props('errorMessages')).toContain('Format de date invalide')
|
|
142
|
+
expect(textField.props('errorMessages')).toContain('Format de date invalide (DD/MM/YYYY)')
|
|
143
143
|
})
|
|
144
144
|
|
|
145
145
|
it('formats input while typing', async () => {
|
|
@@ -169,7 +169,7 @@ describe('DateTextInput.vue', () => {
|
|
|
169
169
|
const input = wrapper.find('input')
|
|
170
170
|
await input.trigger('blur')
|
|
171
171
|
await wrapper.vm.$nextTick()
|
|
172
|
-
expect(textField.props('errorMessages')).toContain('Format de date invalide')
|
|
172
|
+
expect(textField.props('errorMessages')).toContain('Format de date invalide (DD/MM/YYYY)')
|
|
173
173
|
})
|
|
174
174
|
|
|
175
175
|
it('formats date during input', async () => {
|
|
@@ -180,13 +180,13 @@ describe('DateTextInput.vue', () => {
|
|
|
180
180
|
})
|
|
181
181
|
|
|
182
182
|
it('handles date deletion', async () => {
|
|
183
|
-
//
|
|
183
|
+
// First set a valid date
|
|
184
184
|
const input = wrapper.find('input')
|
|
185
185
|
await input.setValue('01/01/2025')
|
|
186
186
|
await input.trigger('blur')
|
|
187
187
|
await wrapper.vm.$nextTick()
|
|
188
188
|
|
|
189
|
-
//
|
|
189
|
+
// Then delete it
|
|
190
190
|
await input.setValue('')
|
|
191
191
|
await input.trigger('input')
|
|
192
192
|
await input.trigger('blur')
|
|
@@ -266,19 +266,61 @@ describe('DateTextInput.vue', () => {
|
|
|
266
266
|
await wrapper.vm.$nextTick()
|
|
267
267
|
expect(input.element.value).toBe('01/__/____')
|
|
268
268
|
|
|
269
|
-
//
|
|
269
|
+
// Simulate cursor position after "01"
|
|
270
270
|
input.element.setSelectionRange(2, 2)
|
|
271
271
|
await input.trigger('input')
|
|
272
272
|
await wrapper.vm.$nextTick()
|
|
273
273
|
|
|
274
|
-
//
|
|
274
|
+
// The cursor position should remain after "01"
|
|
275
275
|
expect(input.element.selectionStart).toBe(2)
|
|
276
276
|
})
|
|
277
277
|
|
|
278
|
+
it('handles 2 digits year correctly', async () => {
|
|
279
|
+
const customWrapper = mount(DateTextInput, {
|
|
280
|
+
global: {
|
|
281
|
+
plugins: [vuetify],
|
|
282
|
+
},
|
|
283
|
+
props: {
|
|
284
|
+
modelValue: null,
|
|
285
|
+
format: 'DD/MM/YY',
|
|
286
|
+
dateFormatReturn: 'YYYY-MM-DD',
|
|
287
|
+
},
|
|
288
|
+
})
|
|
289
|
+
|
|
290
|
+
const input = customWrapper.find('input')
|
|
291
|
+
await input.setValue('0')
|
|
292
|
+
expect(input.element.value).toBe('0_/__/__')
|
|
293
|
+
|
|
294
|
+
await input.setValue('01/02/99')
|
|
295
|
+
expect(input.element.value).toBe('01/02/99')
|
|
296
|
+
expect(customWrapper.emitted('update:model-value')?.at(-1)).toEqual(['1999-02-01'])
|
|
297
|
+
})
|
|
298
|
+
|
|
299
|
+
it('handles ISO-8601 date format correctly', async () => {
|
|
300
|
+
const customWrapper = mount(DateTextInput, {
|
|
301
|
+
global: {
|
|
302
|
+
plugins: [vuetify],
|
|
303
|
+
},
|
|
304
|
+
props: {
|
|
305
|
+
modelValue: null,
|
|
306
|
+
format: 'YYYY-MM-DD',
|
|
307
|
+
},
|
|
308
|
+
})
|
|
309
|
+
|
|
310
|
+
const input = customWrapper.find('input')
|
|
311
|
+
await input.setValue('2025-')
|
|
312
|
+
expect(input.element.value).toBe('2025-__-__')
|
|
313
|
+
expect(customWrapper.emitted('update:model-value')).toBeFalsy()
|
|
314
|
+
|
|
315
|
+
await input.setValue('2025-01-01')
|
|
316
|
+
expect(input.element.value).toBe('2025-01-01')
|
|
317
|
+
expect(customWrapper.emitted('update:model-value')?.at(-1)).toEqual(['2025-01-01'])
|
|
318
|
+
})
|
|
319
|
+
|
|
278
320
|
it('handles paste event with valid date', async () => {
|
|
279
321
|
const input = wrapper.find('input')
|
|
280
322
|
|
|
281
|
-
//
|
|
323
|
+
// Create a paste event with data
|
|
282
324
|
const clipboardData = {
|
|
283
325
|
getData: () => '01/01/2025',
|
|
284
326
|
}
|
|
@@ -297,26 +339,26 @@ describe('DateTextInput.vue', () => {
|
|
|
297
339
|
await input.trigger('keydown', {
|
|
298
340
|
key: 'Tab',
|
|
299
341
|
})
|
|
300
|
-
//
|
|
342
|
+
// Check that the component doesn't prevent tab navigation
|
|
301
343
|
expect(wrapper.emitted('update:model-value')).toBeFalsy()
|
|
302
344
|
|
|
303
|
-
//
|
|
345
|
+
// Test behavior with Ctrl+V (paste)
|
|
304
346
|
await input.trigger('keydown', {
|
|
305
347
|
key: 'v',
|
|
306
348
|
ctrlKey: true,
|
|
307
349
|
})
|
|
308
|
-
//
|
|
350
|
+
// Default behavior should be preserved
|
|
309
351
|
expect(wrapper.emitted('update:model-value')).toBeFalsy()
|
|
310
352
|
})
|
|
311
353
|
|
|
312
354
|
it('validates on submit correctly', async () => {
|
|
313
355
|
const input = wrapper.find('input')
|
|
314
356
|
|
|
315
|
-
//
|
|
357
|
+
// Case 1: Empty field with required=true
|
|
316
358
|
const emptyResult = await wrapper.vm.validateOnSubmit()
|
|
317
359
|
expect(emptyResult).toBe(false)
|
|
318
360
|
|
|
319
|
-
//
|
|
361
|
+
// Case 2: Valid date
|
|
320
362
|
await input.setValue('01/01/2025')
|
|
321
363
|
await input.trigger('blur')
|
|
322
364
|
await wrapper.vm.$nextTick()
|
|
@@ -326,23 +368,29 @@ describe('DateTextInput.vue', () => {
|
|
|
326
368
|
})
|
|
327
369
|
|
|
328
370
|
it('handles focus and blur methods correctly', async () => {
|
|
329
|
-
//
|
|
330
|
-
const originalQuerySelector = document.querySelector
|
|
371
|
+
// Create a mock for the input element
|
|
331
372
|
const mockInput = { focus: vi.fn(), blur: vi.fn() }
|
|
332
373
|
|
|
333
|
-
//
|
|
334
|
-
|
|
374
|
+
// Mock the component's querySelector method
|
|
375
|
+
const mockQuerySelector = vi.fn().mockReturnValue(mockInput)
|
|
376
|
+
|
|
377
|
+
// Replace the element reference and its querySelector method
|
|
378
|
+
wrapper.vm.inputRef = {
|
|
379
|
+
$el: {
|
|
380
|
+
querySelector: mockQuerySelector,
|
|
381
|
+
},
|
|
382
|
+
}
|
|
335
383
|
|
|
336
|
-
//
|
|
384
|
+
// Call the exposed methods
|
|
337
385
|
wrapper.vm.focus()
|
|
338
386
|
wrapper.vm.blur()
|
|
339
387
|
|
|
340
|
-
//
|
|
388
|
+
// Check that querySelector was called with the right selector
|
|
389
|
+
expect(mockQuerySelector).toHaveBeenCalledWith('input:not([type="hidden"])')
|
|
390
|
+
|
|
391
|
+
// Check that the methods were called
|
|
341
392
|
expect(mockInput.focus).toHaveBeenCalled()
|
|
342
393
|
expect(mockInput.blur).toHaveBeenCalled()
|
|
343
|
-
|
|
344
|
-
// Restaurer document.querySelector
|
|
345
|
-
document.querySelector = originalQuerySelector
|
|
346
394
|
})
|
|
347
395
|
|
|
348
396
|
it('initializes with model value correctly', async () => {
|
|
@@ -383,17 +431,17 @@ describe('DateTextInput.vue', () => {
|
|
|
383
431
|
it('handles partial date input correctly', async () => {
|
|
384
432
|
const input = wrapper.find('input')
|
|
385
433
|
|
|
386
|
-
//
|
|
434
|
+
// Enter only the day
|
|
387
435
|
await input.setValue('01')
|
|
388
436
|
await wrapper.vm.$nextTick()
|
|
389
437
|
expect(input.element.value).toBe('01/__/____')
|
|
390
438
|
|
|
391
|
-
//
|
|
439
|
+
// Add the month
|
|
392
440
|
await input.setValue('01/02')
|
|
393
441
|
await wrapper.vm.$nextTick()
|
|
394
442
|
expect(input.element.value).toBe('01/02/____')
|
|
395
443
|
|
|
396
|
-
//
|
|
444
|
+
// Complete the date
|
|
397
445
|
await input.setValue('01/02/2025')
|
|
398
446
|
await wrapper.vm.$nextTick()
|
|
399
447
|
expect(input.element.value).toBe('01/02/2025')
|
|
@@ -423,16 +471,16 @@ describe('DateTextInput.vue', () => {
|
|
|
423
471
|
await input.trigger('focus')
|
|
424
472
|
await input.trigger('blur')
|
|
425
473
|
await customWrapper.vm.$nextTick()
|
|
426
|
-
await customWrapper.vm.$nextTick() // Double nextTick
|
|
474
|
+
await customWrapper.vm.$nextTick() // Double nextTick for reliability
|
|
427
475
|
|
|
428
|
-
// Force validation
|
|
476
|
+
// Force manual validation
|
|
429
477
|
await customWrapper.vm.validateOnSubmit()
|
|
430
478
|
await customWrapper.vm.$nextTick()
|
|
431
479
|
|
|
432
480
|
const textField = customWrapper.findComponent(SyTextField)
|
|
433
481
|
const successMessages = textField.props('successMessages') || []
|
|
434
482
|
|
|
435
|
-
//
|
|
483
|
+
// Flexible verification
|
|
436
484
|
expect(successMessages.length).toBeGreaterThan(0)
|
|
437
485
|
expect(successMessages[0]).toContain('valide')
|
|
438
486
|
})
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import {Controls, Canvas, Meta, Source} from '@storybook/blocks';
|
|
2
|
+
|
|
3
|
+
import * as DiacriticPickerStories from './DiacriticPicker.stories';
|
|
4
|
+
|
|
5
|
+
<Meta of={DiacriticPickerStories} />
|
|
6
|
+
|
|
7
|
+
# DiacriticPicker
|
|
8
|
+
|
|
9
|
+
Le composant `DiacriticPicker` permet d'ajouter facilement des caractères diacritiques (accents, cédilles, etc.) à un champ de texte ou un textarea. Il ajoute un bouton à côté du champ qui ouvre une boîte de dialogue avec des caractères diacritiques à insérer.
|
|
10
|
+
|
|
11
|
+
<Canvas of={DiacriticPickerStories.Default} />
|
|
12
|
+
|
|
13
|
+
# API
|
|
14
|
+
|
|
15
|
+
<Controls of={DiacriticPickerStories.Default} />
|
|
16
|
+
|
|
17
|
+
## Fonctionnalités
|
|
18
|
+
|
|
19
|
+
### Insertion de caractères
|
|
20
|
+
Cliquer sur un caractère dans la boîte de dialogue l'insère à la position actuelle du curseur dans le champ de texte.
|
|
21
|
+
|
|
22
|
+
### Raccourci clavier
|
|
23
|
+
Le composant offre un raccourci clavier pour changer rapidement entre les variantes d'un même caractère :
|
|
24
|
+
- Tapez un caractère (par exemple "e")
|
|
25
|
+
- Appuyez sur la touche `=` pour parcourir ses variantes diacritiques (é, è, ê, ë)
|
|
26
|
+
|
|
27
|
+
## Personnalisation
|
|
28
|
+
|
|
29
|
+
Le composant utilise le système de personnalisation Vuetify pour les éléments suivants :
|
|
30
|
+
- `btn` : Style du bouton d'ouverture
|
|
31
|
+
- `dialog` : Style de la boîte de dialogue
|
|
32
|
+
|
|
33
|
+
# Exemple d'utilisation
|
|
34
|
+
|
|
35
|
+
<Source dark code={`
|
|
36
|
+
<script setup>
|
|
37
|
+
import { DiacriticPicker } from '@cnamts/synapse'
|
|
38
|
+
import { ref } from 'vue'
|
|
39
|
+
|
|
40
|
+
const nom = ref('')
|
|
41
|
+
</script>
|
|
42
|
+
|
|
43
|
+
<template>
|
|
44
|
+
<DiacriticPicker v-model="nom">
|
|
45
|
+
<v-text-field
|
|
46
|
+
id="diacritic-input"
|
|
47
|
+
v-model="nom"
|
|
48
|
+
label="Nom avec accents"
|
|
49
|
+
color="primary"
|
|
50
|
+
variant="outlined"
|
|
51
|
+
/>
|
|
52
|
+
</DiacriticPicker>
|
|
53
|
+
</template>
|
|
54
|
+
`} />
|
|
55
|
+
|
|
56
|
+
## Utilisation avec un textarea
|
|
57
|
+
|
|
58
|
+
Le composant fonctionne également avec un textarea pour les textes plus longs.
|
|
59
|
+
|
|
60
|
+
<Source dark code={`
|
|
61
|
+
<script setup>
|
|
62
|
+
import { DiacriticPicker } from '@cnamts/synapse'
|
|
63
|
+
import { ref } from 'vue'
|
|
64
|
+
|
|
65
|
+
const adresse = ref('')
|
|
66
|
+
</script>
|
|
67
|
+
|
|
68
|
+
<template>
|
|
69
|
+
<DiacriticPicker v-model="adresse">
|
|
70
|
+
<v-textarea
|
|
71
|
+
id="diacritic-input"
|
|
72
|
+
v-model="adresse"
|
|
73
|
+
label="Adresse"
|
|
74
|
+
color="primary"
|
|
75
|
+
variant="outlined"
|
|
76
|
+
auto-grow
|
|
77
|
+
/>
|
|
78
|
+
</DiacriticPicker>
|
|
79
|
+
</template>
|
|
80
|
+
`} />
|
|
81
|
+
|
|
82
|
+
## Personnalisation des caractères diacritiques
|
|
83
|
+
|
|
84
|
+
Vous pouvez personnaliser la liste des caractères diacritiques affichés dans la boîte de dialogue.
|
|
85
|
+
|
|
86
|
+
<Source dark code={`
|
|
87
|
+
<script setup>
|
|
88
|
+
import { DiacriticPicker } from '@cnamts/synapse'
|
|
89
|
+
import { ref } from 'vue'
|
|
90
|
+
|
|
91
|
+
const text = ref('')
|
|
92
|
+
const caracteres = ['é', 'è', 'ê', 'à', 'ç', 'ù']
|
|
93
|
+
</script>
|
|
94
|
+
|
|
95
|
+
<template>
|
|
96
|
+
<DiacriticPicker v-model="text" :diacritics="caracteres">
|
|
97
|
+
<v-text-field
|
|
98
|
+
v-model="text"
|
|
99
|
+
label="Texte avec accents personnalisés"
|
|
100
|
+
variant="outlined"
|
|
101
|
+
/>
|
|
102
|
+
</DiacriticPicker>
|
|
103
|
+
</template>
|
|
104
|
+
`} />
|