@cnamts/synapse 0.0.11-alpha → 0.0.12-alpha
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/design-system-v3.js +3878 -3189
- package/dist/design-system-v3.umd.cjs +1 -1
- package/dist/src/components/Amelipro/types/languages.d.ts +6 -0
- package/dist/src/components/Amelipro/types/types.d.ts +65 -0
- package/dist/src/components/CookieBanner/CookieBanner.d.ts +1 -1
- package/dist/src/components/Customs/SyInputSelect/SyInputSelect.d.ts +2 -0
- package/dist/src/components/Customs/SyTextField/SyTextField.d.ts +29 -23
- package/dist/src/components/Customs/SyTextField/types.d.ts +1 -0
- package/dist/src/components/DatePicker/DatePicker.d.ts +70 -59
- package/dist/src/components/DatePicker/DateTextInput.d.ts +67 -56
- package/dist/src/components/ErrorPage/ErrorPage.d.ts +1 -1
- package/dist/src/components/FileList/FileList.d.ts +1 -0
- package/dist/src/components/FileList/UploadItem/UploadItem.d.ts +1 -1
- package/dist/src/components/FilterSideBar/FilterSideBar.d.ts +31 -0
- package/dist/src/components/FilterSideBar/locales.d.ts +7 -0
- package/dist/src/components/FilterSideBar/tests/FilterSideBar.spec.d.ts +1 -0
- package/dist/src/components/LangBtn/LangBtn.d.ts +2 -2
- package/dist/src/components/NirField/NirField.d.ts +940 -0
- package/dist/src/components/NotificationBar/NotificationBar.d.ts +1 -1
- package/dist/src/components/PasswordField/PasswordField.d.ts +40 -8
- package/dist/src/components/PeriodField/PeriodField.d.ts +142 -120
- package/dist/src/components/PhoneField/PhoneField.d.ts +11 -2
- package/dist/src/components/RatingPicker/EmotionPicker/EmotionPicker.d.ts +1 -1
- package/dist/src/components/RatingPicker/NumberPicker/NumberPicker.d.ts +1 -1
- package/dist/src/components/RatingPicker/StarsPicker/StarsPicker.d.ts +1 -1
- package/dist/src/components/UploadWorkflow/config.d.ts +29 -0
- package/dist/src/components/UploadWorkflow/locales.d.ts +7 -0
- package/dist/src/components/UploadWorkflow/tests/UploadWorkflow.spec.d.ts +1 -0
- package/dist/src/components/UploadWorkflow/types.d.ts +19 -0
- package/dist/src/components/UploadWorkflow/useFileList.d.ts +10 -0
- package/dist/src/components/UploadWorkflow/useFileUploadJourney.d.ts +9 -0
- package/dist/src/components/index.d.ts +2 -0
- package/dist/src/composables/rules/useFieldValidation.d.ts +1 -0
- package/dist/src/composables/validation/tests/useValidation.spec.d.ts +1 -0
- package/dist/src/composables/validation/useValidation.d.ts +39 -0
- package/dist/src/designTokens/index.d.ts +3 -1
- package/dist/src/vuetifyConfig.d.ts +81 -0
- package/dist/style.css +1 -1
- package/package.json +1 -1
- package/src/assets/_elevations.scss +89 -0
- package/src/assets/_fonts.scss +6 -0
- package/src/assets/_radius.scss +86 -0
- package/src/assets/_spacers.scss +149 -0
- package/src/assets/settings.scss +7 -3
- package/src/assets/tokens.scss +32 -29
- package/src/components/Amelipro/types/languages.d.ts +6 -0
- package/src/components/Amelipro/types/types.d.ts +65 -0
- package/src/components/Customs/SyInputSelect/SyInputSelect.stories.ts +65 -0
- package/src/components/Customs/SyInputSelect/SyInputSelect.vue +13 -3
- package/src/components/Customs/SySelect/SySelect.stories.ts +88 -5
- package/src/components/Customs/SySelect/SySelect.vue +36 -10
- package/src/components/Customs/SySelect/tests/SySelect.spec.ts +135 -2
- package/src/components/Customs/SyTextField/SyTextField.stories.ts +576 -85
- package/src/components/Customs/SyTextField/SyTextField.vue +132 -104
- package/src/components/Customs/SyTextField/tests/SyTextField.spec.ts +190 -38
- package/src/components/Customs/SyTextField/types.d.ts +1 -0
- package/src/components/DatePicker/DatePicker.vue +405 -137
- package/src/components/DatePicker/DateTextInput.vue +15 -0
- package/src/components/DatePicker/tests/DatePicker.spec.ts +8 -15
- package/src/components/FileList/FileList.vue +2 -1
- package/src/components/FileList/UploadItem/UploadItem.vue +10 -0
- package/src/components/FileUpload/FileUpload.stories.ts +84 -0
- package/src/components/FileUpload/FileUpload.vue +1 -0
- package/src/components/FileUpload/tests/FileUpload.spec.ts +4 -4
- package/src/components/FilterInline/FilterInline.mdx +180 -34
- package/src/components/FilterInline/FilterInline.stories.ts +363 -6
- package/src/components/FilterSideBar/FilterSideBar.mdx +237 -0
- package/src/components/FilterSideBar/FilterSideBar.stories.ts +798 -0
- package/src/components/FilterSideBar/FilterSideBar.vue +193 -0
- package/src/components/FilterSideBar/locales.ts +8 -0
- package/src/components/FilterSideBar/tests/FilterSideBar.spec.ts +305 -0
- package/src/components/FilterSideBar/tests/__snapshots__/FilterSideBar.spec.ts.snap +39 -0
- package/src/components/HeaderBar/Usages.mdx +1 -1
- package/src/components/NirField/NirField.stories.ts +573 -29
- package/src/components/NirField/NirField.vue +397 -359
- package/src/components/NirField/tests/NirField.spec.ts +88 -52
- package/src/components/NirField/tests//342/200/257dataset/342/200/257.md +12 -0
- package/src/components/NotificationBar/Accessibilite.stories.ts +4 -0
- package/src/components/NotificationBar/NotificationBar.stories.ts +18 -13
- package/src/components/PasswordField/PasswordField.mdx +129 -47
- package/src/components/PasswordField/PasswordField.stories.ts +924 -120
- package/src/components/PasswordField/PasswordField.vue +209 -99
- package/src/components/PasswordField/tests/PasswordField.spec.ts +138 -9
- package/src/components/PeriodField/PeriodField.vue +55 -54
- package/src/components/PhoneField/PhoneField.stories.ts +69 -0
- package/src/components/PhoneField/PhoneField.vue +3 -0
- package/src/components/PhoneField/indicatifs.ts +1 -1
- package/src/components/UploadWorkflow/UploadWorkflow.mdx +75 -0
- package/src/components/UploadWorkflow/UploadWorkflow.stories.ts +943 -0
- package/src/components/UploadWorkflow/UploadWorkflow.vue +230 -0
- package/src/components/UploadWorkflow/config.ts +29 -0
- package/src/components/UploadWorkflow/locales.ts +8 -0
- package/src/components/UploadWorkflow/tests/UploadWorkflow.spec.ts +257 -0
- package/src/components/UploadWorkflow/tests/__snapshots__/UploadWorkflow.spec.ts.snap +54 -0
- package/src/components/UploadWorkflow/types.ts +21 -0
- package/src/components/UploadWorkflow/useFileList.ts +84 -0
- package/src/components/UploadWorkflow/useFileUploadJourney.ts +18 -0
- package/src/components/index.ts +2 -0
- package/src/composables/rules/useFieldValidation.ts +5 -2
- package/src/composables/validation/tests/useValidation.spec.ts +154 -0
- package/src/composables/validation/useValidation.ts +165 -0
- package/src/designTokens/index.ts +4 -0
- package/src/stories/Demarrer/Accueil.mdx +1 -1
- package/src/stories/DesignTokens/ThemePA.mdx +4 -30
- package/src/stories/GuideDuDev/UtiliserLesRules.mdx +319 -76
- package/src/stories/GuideDuDev/moduleDeNotification.mdx +1 -1
- package/src/vuetifyConfig.ts +61 -0
- package/src/composables/useFilterable/__snapshots__/useFilterable.spec.ts.snap +0 -3
|
@@ -2,26 +2,54 @@
|
|
|
2
2
|
import { ref, computed, watch } from 'vue'
|
|
3
3
|
import { config } from './config'
|
|
4
4
|
import { locales } from './locales'
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
|
|
5
|
+
import { useValidation, type ValidationRule } from '@/composables/validation/useValidation'
|
|
6
|
+
import {
|
|
7
|
+
mdiEye,
|
|
8
|
+
mdiEyeOff,
|
|
9
|
+
mdiAlertCircle,
|
|
10
|
+
mdiAlert,
|
|
11
|
+
mdiCheckCircle,
|
|
12
|
+
} from '@mdi/js'
|
|
8
13
|
import useCustomizableOptions, { type CustomizableOptions } from '@/composables/useCustomizableOptions'
|
|
9
|
-
|
|
10
|
-
type
|
|
14
|
+
import SyTextField from '@/components/Customs/SyTextField/SyTextField.vue'
|
|
15
|
+
import type { ColorType } from '@/components/Customs/SyTextField/types'
|
|
11
16
|
|
|
12
17
|
const props = withDefaults(defineProps<{
|
|
13
18
|
modelValue?: string | null
|
|
14
|
-
|
|
19
|
+
variantStyle?: 'outlined' | 'underlined'
|
|
20
|
+
color?: ColorType
|
|
21
|
+
label?: string
|
|
15
22
|
required?: boolean
|
|
23
|
+
errorMessages?: string[] | null
|
|
24
|
+
warningMessages?: string[] | null
|
|
25
|
+
successMessages?: string[] | null
|
|
26
|
+
isReadOnly?: boolean
|
|
27
|
+
isDisabled?: boolean
|
|
28
|
+
placeholder?: string
|
|
29
|
+
customRules?: ValidationRule[]
|
|
30
|
+
customWarningRules?: ValidationRule[]
|
|
31
|
+
customSuccessRules?: ValidationRule[]
|
|
32
|
+
showSuccessMessages?: boolean
|
|
33
|
+
displayAsterisk?: boolean
|
|
16
34
|
isValidateOnBlur?: boolean
|
|
17
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- This is a generic type
|
|
18
|
-
customRules?: any
|
|
19
35
|
} & CustomizableOptions>(), {
|
|
20
36
|
modelValue: null,
|
|
21
|
-
|
|
37
|
+
variantStyle: 'outlined',
|
|
38
|
+
color: 'primary',
|
|
39
|
+
label: undefined,
|
|
22
40
|
required: false,
|
|
41
|
+
errorMessages: null,
|
|
42
|
+
warningMessages: null,
|
|
43
|
+
successMessages: null,
|
|
44
|
+
isReadOnly: false,
|
|
45
|
+
isDisabled: false,
|
|
46
|
+
placeholder: undefined,
|
|
47
|
+
customRules: () => [],
|
|
48
|
+
customWarningRules: () => [],
|
|
49
|
+
customSuccessRules: () => [],
|
|
50
|
+
showSuccessMessages: true,
|
|
51
|
+
displayAsterisk: false,
|
|
23
52
|
isValidateOnBlur: true,
|
|
24
|
-
customRules: [],
|
|
25
53
|
})
|
|
26
54
|
|
|
27
55
|
const options = useCustomizableOptions(config, props)
|
|
@@ -43,147 +71,229 @@
|
|
|
43
71
|
},
|
|
44
72
|
)
|
|
45
73
|
|
|
46
|
-
|
|
74
|
+
// Construction des règles de validation
|
|
75
|
+
const defaultRules = computed<ValidationRule[]>(() => {
|
|
76
|
+
const rules: ValidationRule[] = []
|
|
47
77
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
? [{
|
|
78
|
+
if (props.required) {
|
|
79
|
+
rules.push({
|
|
51
80
|
type: 'required',
|
|
52
|
-
options: {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
const baseRules = (props.required ? defaultRules : [])
|
|
59
|
-
return props.customRules ? generateRules([...baseRules, ...props.customRules]) : generateRules(baseRules)
|
|
60
|
-
})
|
|
61
|
-
|
|
62
|
-
const errors = ref<string[]>([])
|
|
63
|
-
const successes = ref<string[]>([])
|
|
81
|
+
options: {
|
|
82
|
+
message: 'Le mot de passe est requis',
|
|
83
|
+
fieldIdentifier: props.label || 'password',
|
|
84
|
+
},
|
|
85
|
+
})
|
|
86
|
+
}
|
|
64
87
|
|
|
65
|
-
|
|
88
|
+
// Règle pour le message de succès
|
|
89
|
+
rules.push({
|
|
90
|
+
type: 'custom',
|
|
91
|
+
options: {
|
|
92
|
+
validate: (value: string) => value ? true : 'Ce champ est requis',
|
|
93
|
+
successMessage: 'Mot de passe fort',
|
|
94
|
+
fieldIdentifier: props.label || 'password',
|
|
95
|
+
},
|
|
96
|
+
})
|
|
66
97
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
}, { immediate: true })
|
|
98
|
+
return rules
|
|
99
|
+
})
|
|
70
100
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
101
|
+
// Initialisation du composable de validation
|
|
102
|
+
const { errors, warnings, successes, validateField } = useValidation({
|
|
103
|
+
customRules: defaultRules.value,
|
|
104
|
+
warningRules: props.customWarningRules || [],
|
|
105
|
+
successRules: props.customSuccessRules || [],
|
|
106
|
+
showSuccessMessages: props.showSuccessMessages,
|
|
107
|
+
fieldIdentifier: props.label || 'password',
|
|
108
|
+
})
|
|
78
109
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
},
|
|
84
|
-
{ immediate: true },
|
|
85
|
-
)
|
|
110
|
+
// Computed pour les états de validation
|
|
111
|
+
const hasError = computed(() => errors.value.length > 0)
|
|
112
|
+
const hasWarning = computed(() => warnings.value.length > 0)
|
|
113
|
+
const hasSuccess = computed(() => successes.value.length > 0 && props.showSuccessMessages)
|
|
86
114
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
}
|
|
115
|
+
const validationIcon = computed(() => {
|
|
116
|
+
if (hasError.value) return mdiAlertCircle
|
|
117
|
+
if (hasWarning.value) return mdiAlert
|
|
118
|
+
if (hasSuccess.value) return mdiCheckCircle
|
|
119
|
+
return undefined
|
|
120
|
+
})
|
|
94
121
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
122
|
+
const validationColor = computed(() => {
|
|
123
|
+
if (hasError.value) return 'error'
|
|
124
|
+
if (hasWarning.value) return 'warning'
|
|
125
|
+
if (hasSuccess.value) return 'success'
|
|
126
|
+
return 'rgb(0 0 0 / 100%)'
|
|
127
|
+
})
|
|
98
128
|
|
|
99
|
-
|
|
129
|
+
// Synchronisation des messages externes
|
|
130
|
+
watch(() => props.errorMessages, (newVal) => {
|
|
131
|
+
if (newVal) {
|
|
132
|
+
errors.value = newVal
|
|
133
|
+
}
|
|
134
|
+
}, { immediate: true })
|
|
100
135
|
|
|
101
|
-
|
|
136
|
+
watch(() => props.warningMessages, (newVal) => {
|
|
137
|
+
if (newVal) {
|
|
138
|
+
warnings.value = newVal
|
|
139
|
+
}
|
|
140
|
+
}, { immediate: true })
|
|
102
141
|
|
|
103
|
-
|
|
104
|
-
|
|
142
|
+
watch(() => props.successMessages, (newVal) => {
|
|
143
|
+
if (newVal) {
|
|
144
|
+
successes.value = newVal
|
|
145
|
+
}
|
|
146
|
+
}, { immediate: true })
|
|
105
147
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
}
|
|
148
|
+
watch(() => password.value, () => {
|
|
149
|
+
validateField(password.value, [...defaultRules.value, ...(props.customRules || [])], props.customWarningRules || [], props.customSuccessRules || [])
|
|
150
|
+
emit('update:modelValue', password.value)
|
|
151
|
+
})
|
|
110
152
|
|
|
111
153
|
function handleKeydown(event: KeyboardEvent): void {
|
|
112
154
|
if (event.key === 'Enter') {
|
|
113
|
-
|
|
155
|
+
validateOnSubmit()
|
|
114
156
|
}
|
|
115
157
|
}
|
|
116
158
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
159
|
+
const validateOnSubmit = () => {
|
|
160
|
+
validateField(password.value, [...defaultRules.value, ...(props.customRules || [])], props.customWarningRules || [], props.customSuccessRules || [])
|
|
161
|
+
const isValid = errors.value.length === 0
|
|
162
|
+
if (isValid) {
|
|
163
|
+
emit('submit')
|
|
164
|
+
}
|
|
165
|
+
return isValid
|
|
121
166
|
}
|
|
122
167
|
|
|
123
168
|
defineExpose({
|
|
169
|
+
showEyeIcon,
|
|
170
|
+
errors,
|
|
171
|
+
warnings,
|
|
172
|
+
successes,
|
|
173
|
+
hasError,
|
|
174
|
+
hasWarning,
|
|
175
|
+
hasSuccess,
|
|
124
176
|
validateOnSubmit,
|
|
125
177
|
})
|
|
126
178
|
</script>
|
|
127
179
|
|
|
128
180
|
<template>
|
|
129
|
-
<
|
|
181
|
+
<SyTextField
|
|
130
182
|
v-model="password"
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
183
|
+
v-bind="options"
|
|
184
|
+
:variant-style="props.variantStyle"
|
|
185
|
+
:color="props.color"
|
|
186
|
+
:label="props.label"
|
|
187
|
+
:required="props.required"
|
|
134
188
|
:error-messages="errors"
|
|
135
|
-
:messages="
|
|
189
|
+
:warning-messages="warnings"
|
|
190
|
+
:success-messages="successes"
|
|
191
|
+
:is-read-only="props.isReadOnly"
|
|
192
|
+
:is-disabled="props.isDisabled"
|
|
193
|
+
:placeholder="props.placeholder"
|
|
136
194
|
:type="showEyeIcon ? 'text' : 'password'"
|
|
137
|
-
:
|
|
195
|
+
:display-asterisk="props.displayAsterisk"
|
|
196
|
+
:rules="[...defaultRules, ...props.customRules]"
|
|
138
197
|
class="vd-password"
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
validate-on="blur lazy"
|
|
142
|
-
@blur="validateFields(true)"
|
|
198
|
+
:validate-on="props.isValidateOnBlur ? 'blur lazy' : 'lazy'"
|
|
199
|
+
@blur="props.isValidateOnBlur ? validateField(password, [...defaultRules, ...(props.customRules || [])], props.customWarningRules || [], props.customSuccessRules || []) : () => {}"
|
|
143
200
|
@keydown="handleKeydown"
|
|
144
|
-
@update:model-value="emitChangeEvent"
|
|
145
201
|
>
|
|
146
202
|
<template #append-inner>
|
|
147
|
-
<
|
|
148
|
-
|
|
149
|
-
class="mx-auto"
|
|
203
|
+
<div
|
|
204
|
+
class="d-flex align-center"
|
|
150
205
|
v-bind="options.btn"
|
|
151
|
-
@click="showEyeIcon = !showEyeIcon"
|
|
152
206
|
>
|
|
153
|
-
<VIcon
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
207
|
+
<VIcon
|
|
208
|
+
:icon="validationIcon"
|
|
209
|
+
:color="validationColor"
|
|
210
|
+
class="mr-2"
|
|
211
|
+
/>
|
|
212
|
+
<VIcon
|
|
213
|
+
:icon="showEyeIcon ? eyeIcon : eyeOffIcon"
|
|
214
|
+
color="rgb(0 0 0 / 70%)"
|
|
215
|
+
:aria-label="btnLabel"
|
|
216
|
+
role="button"
|
|
217
|
+
@click="showEyeIcon = !showEyeIcon"
|
|
218
|
+
/>
|
|
219
|
+
</div>
|
|
157
220
|
</template>
|
|
158
|
-
</
|
|
221
|
+
</SyTextField>
|
|
159
222
|
</template>
|
|
160
223
|
|
|
161
224
|
<style lang="scss" scoped>
|
|
162
225
|
@use '@/assets/tokens';
|
|
163
226
|
|
|
164
227
|
.vd-password {
|
|
165
|
-
.v-
|
|
166
|
-
|
|
167
|
-
|
|
228
|
+
:deep(.v-field) {
|
|
229
|
+
.v-field__input {
|
|
230
|
+
padding-right: 48px;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
.warning-field {
|
|
236
|
+
:deep(.v-input__details > .v-icon),
|
|
237
|
+
:deep(.v-input__prepend > .v-icon),
|
|
238
|
+
:deep(.v-input__append > .v-icon) {
|
|
239
|
+
opacity: 1 !important;
|
|
168
240
|
}
|
|
169
241
|
|
|
170
|
-
:deep(.v-field
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
242
|
+
:deep(.v-field) {
|
|
243
|
+
color: tokens.$colors-border-warning !important;
|
|
244
|
+
|
|
245
|
+
.v-field__outline {
|
|
246
|
+
color: tokens.$colors-border-warning !important;
|
|
247
|
+
}
|
|
175
248
|
}
|
|
176
249
|
|
|
177
|
-
:deep(.v-
|
|
178
|
-
|
|
250
|
+
:deep(.v-messages) {
|
|
251
|
+
opacity: 1 !important;
|
|
252
|
+
|
|
253
|
+
.v-messages__message {
|
|
254
|
+
color: tokens.$colors-border-warning !important;
|
|
255
|
+
}
|
|
179
256
|
}
|
|
180
257
|
}
|
|
181
258
|
|
|
182
|
-
.
|
|
183
|
-
|
|
259
|
+
.error-field {
|
|
260
|
+
:deep(.v-input__control),
|
|
261
|
+
:deep(.v-messages__message) {
|
|
262
|
+
color: tokens.$colors-text-error !important;
|
|
263
|
+
}
|
|
184
264
|
|
|
185
265
|
.v-field--active & {
|
|
266
|
+
color: tokens.$colors-border-error !important;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
.success-field {
|
|
271
|
+
:deep(.v-input__details > .v-icon),
|
|
272
|
+
:deep(.v-input__prepend > .v-icon),
|
|
273
|
+
:deep(.v-input__append > .v-icon) {
|
|
274
|
+
opacity: 1 !important;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
:deep(.v-field) {
|
|
186
278
|
color: tokens.$colors-border-success !important;
|
|
279
|
+
|
|
280
|
+
.v-field__outline {
|
|
281
|
+
color: tokens.$colors-border-success !important;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
:deep(.v-messages) {
|
|
286
|
+
opacity: 1 !important;
|
|
287
|
+
|
|
288
|
+
.v-messages__message {
|
|
289
|
+
color: tokens.$colors-border-success !important;
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
.basic-field {
|
|
295
|
+
:deep(.v-icon__svg) {
|
|
296
|
+
fill: rgb(0 0 0 / 70%);
|
|
187
297
|
}
|
|
188
298
|
}
|
|
189
299
|
</style>
|
|
@@ -7,8 +7,12 @@ import { createVuetify } from 'vuetify'
|
|
|
7
7
|
interface PasswordFieldVM {
|
|
8
8
|
showEyeIcon: boolean
|
|
9
9
|
errors: string[]
|
|
10
|
-
isValidating: boolean
|
|
11
10
|
validateOnSubmit: () => boolean
|
|
11
|
+
hasError: boolean
|
|
12
|
+
hasWarning: boolean
|
|
13
|
+
hasSuccess: boolean
|
|
14
|
+
validationIcon: string
|
|
15
|
+
validationIconColor: string
|
|
12
16
|
}
|
|
13
17
|
|
|
14
18
|
describe('PasswordField.vue', () => {
|
|
@@ -36,7 +40,7 @@ describe('PasswordField.vue', () => {
|
|
|
36
40
|
// 2. Cast wrapper.vm as your interface
|
|
37
41
|
const vm = wrapper.vm as unknown as PasswordFieldVM
|
|
38
42
|
|
|
39
|
-
const button = wrapper.find('button')
|
|
43
|
+
const button = wrapper.find('[role="button"]')
|
|
40
44
|
expect(vm.showEyeIcon).toBe(false) // from your interface
|
|
41
45
|
await button.trigger('click')
|
|
42
46
|
expect(vm.showEyeIcon).toBe(true)
|
|
@@ -62,35 +66,160 @@ describe('PasswordField.vue', () => {
|
|
|
62
66
|
},
|
|
63
67
|
props: {
|
|
64
68
|
required: true,
|
|
69
|
+
label: 'Password',
|
|
65
70
|
},
|
|
66
71
|
})
|
|
67
72
|
const vm = wrapper.vm as unknown as PasswordFieldVM
|
|
68
73
|
|
|
69
74
|
const input = wrapper.find('input')
|
|
70
75
|
await input.trigger('blur')
|
|
71
|
-
expect(vm.errors).toContain('Le mot de passe est requis
|
|
76
|
+
expect(vm.errors).toContain('Le mot de passe est requis')
|
|
72
77
|
})
|
|
73
78
|
|
|
74
|
-
it('validates fields on submit
|
|
79
|
+
it('validates fields on submit', async () => {
|
|
75
80
|
const wrapper = mount(PasswordField, {
|
|
76
81
|
global: {
|
|
77
82
|
plugins: [vuetify],
|
|
78
83
|
},
|
|
79
84
|
props: {
|
|
80
85
|
modelValue: '',
|
|
81
|
-
|
|
86
|
+
variantStyle: 'underlined',
|
|
82
87
|
required: true,
|
|
88
|
+
label: 'Password',
|
|
83
89
|
},
|
|
84
90
|
})
|
|
85
91
|
const vm = wrapper.vm as unknown as PasswordFieldVM
|
|
86
92
|
|
|
87
93
|
const result = vm.validateOnSubmit()
|
|
88
|
-
expect(vm.isValidating).toBe(true)
|
|
89
94
|
expect(result).toBe(false)
|
|
95
|
+
expect(vm.errors).toContain('Le mot de passe est requis')
|
|
90
96
|
|
|
91
|
-
//
|
|
92
|
-
await wrapper.
|
|
97
|
+
// Test avec un mot de passe valide
|
|
98
|
+
await wrapper.setProps({ modelValue: 'valid-password' })
|
|
99
|
+
const validResult = vm.validateOnSubmit()
|
|
100
|
+
expect(validResult).toBe(true)
|
|
101
|
+
expect(wrapper.emitted().submit).toBeTruthy()
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
it('displays warning and success messages', async () => {
|
|
105
|
+
const wrapper = mount(PasswordField, {
|
|
106
|
+
global: {
|
|
107
|
+
plugins: [vuetify],
|
|
108
|
+
},
|
|
109
|
+
props: {
|
|
110
|
+
modelValue: 'test',
|
|
111
|
+
warningMessages: ['Attention: mot de passe court'],
|
|
112
|
+
successMessages: ['Mot de passe valide'],
|
|
113
|
+
},
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
const messages = wrapper.findAll('.v-messages__message')
|
|
117
|
+
expect(messages.length).toBe(1) // SyTextField affiche soit les warnings, soit les succès
|
|
118
|
+
expect(messages[0].text()).toBe('Attention: mot de passe court')
|
|
119
|
+
|
|
120
|
+
// Simuler la suppression du warning pour voir le message de succès
|
|
121
|
+
await wrapper.setProps({ warningMessages: [] })
|
|
122
|
+
const successMessages = wrapper.findAll('.v-messages__message')
|
|
123
|
+
expect(successMessages.length).toBe(1)
|
|
124
|
+
expect(successMessages[0].text()).toBe('Mot de passe valide')
|
|
125
|
+
})
|
|
126
|
+
|
|
127
|
+
it('handles custom validation rules', async () => {
|
|
128
|
+
const wrapper = mount(PasswordField, {
|
|
129
|
+
global: {
|
|
130
|
+
plugins: [vuetify],
|
|
131
|
+
},
|
|
132
|
+
props: {
|
|
133
|
+
modelValue: 'test',
|
|
134
|
+
customRules: [{
|
|
135
|
+
type: 'custom',
|
|
136
|
+
options: {
|
|
137
|
+
message: 'Le mot de passe doit contenir au moins 8 caractères',
|
|
138
|
+
validate: (value: string) => value.length >= 8,
|
|
139
|
+
},
|
|
140
|
+
}],
|
|
141
|
+
customWarningRules: [{
|
|
142
|
+
type: 'custom',
|
|
143
|
+
options: {
|
|
144
|
+
warningMessage: 'Le mot de passe pourrait être plus fort',
|
|
145
|
+
validate: (value: string) => /[A-Z]/.test(value),
|
|
146
|
+
},
|
|
147
|
+
}],
|
|
148
|
+
customSuccessRules: [{
|
|
149
|
+
type: 'custom',
|
|
150
|
+
options: {
|
|
151
|
+
successMessage: 'Mot de passe fort',
|
|
152
|
+
validate: (value: string) => value.length >= 12,
|
|
153
|
+
},
|
|
154
|
+
}],
|
|
155
|
+
},
|
|
156
|
+
})
|
|
157
|
+
|
|
158
|
+
const vm = wrapper.vm as unknown as PasswordFieldVM
|
|
159
|
+
|
|
160
|
+
// Forcer la validation initiale
|
|
161
|
+
await wrapper.find('input').trigger('blur')
|
|
162
|
+
expect(vm.errors).toContain('Le mot de passe doit contenir au moins 8 caractères')
|
|
163
|
+
|
|
164
|
+
// Mettons un mot de passe plus long mais sans majuscule -> warning
|
|
165
|
+
await wrapper.setProps({ modelValue: 'testpassword' })
|
|
166
|
+
await wrapper.find('input').trigger('blur')
|
|
167
|
+
const messages = wrapper.findAll('.v-messages__message')
|
|
168
|
+
expect(messages[0].text()).toBe('Le mot de passe pourrait être plus fort')
|
|
169
|
+
|
|
170
|
+
// Mettons un mot de passe fort -> succès
|
|
171
|
+
await wrapper.setProps({ modelValue: 'TestPassword123' })
|
|
172
|
+
await wrapper.find('input').trigger('blur')
|
|
173
|
+
const successMessages = wrapper.findAll('.v-messages__message')
|
|
174
|
+
expect(successMessages[0].text()).toBe('Mot de passe fort')
|
|
175
|
+
})
|
|
176
|
+
|
|
177
|
+
it('displays validation states based on validation rules', async () => {
|
|
178
|
+
const wrapper = mount(PasswordField, {
|
|
179
|
+
global: {
|
|
180
|
+
plugins: [vuetify],
|
|
181
|
+
},
|
|
182
|
+
props: {
|
|
183
|
+
modelValue: 'test',
|
|
184
|
+
customRules: [{
|
|
185
|
+
type: 'custom',
|
|
186
|
+
options: {
|
|
187
|
+
message: 'Le mot de passe doit contenir au moins 8 caractères',
|
|
188
|
+
validate: (value: string) => value.length >= 8,
|
|
189
|
+
},
|
|
190
|
+
}],
|
|
191
|
+
customWarningRules: [{
|
|
192
|
+
type: 'custom',
|
|
193
|
+
options: {
|
|
194
|
+
warningMessage: 'Le mot de passe pourrait être plus fort',
|
|
195
|
+
validate: (value: string) => /[A-Z]/.test(value),
|
|
196
|
+
},
|
|
197
|
+
}],
|
|
198
|
+
customSuccessRules: [{
|
|
199
|
+
type: 'custom',
|
|
200
|
+
options: {
|
|
201
|
+
successMessage: 'Mot de passe fort',
|
|
202
|
+
validate: (value: string) => value.length >= 12,
|
|
203
|
+
},
|
|
204
|
+
}],
|
|
205
|
+
},
|
|
206
|
+
})
|
|
207
|
+
|
|
208
|
+
// Forcer la validation initiale
|
|
209
|
+
await wrapper.find('input').trigger('blur')
|
|
210
|
+
|
|
211
|
+
// État d'erreur
|
|
212
|
+
const vm = wrapper.vm as unknown as PasswordFieldVM
|
|
213
|
+
expect(vm.hasError).toBe(true)
|
|
214
|
+
|
|
215
|
+
// État d'avertissement
|
|
216
|
+
await wrapper.setProps({ modelValue: 'testpassword' })
|
|
217
|
+
await wrapper.find('input').trigger('blur')
|
|
218
|
+
expect(vm.hasWarning).toBe(true)
|
|
93
219
|
|
|
94
|
-
|
|
220
|
+
// État de succès
|
|
221
|
+
await wrapper.setProps({ modelValue: 'TestPassword123' })
|
|
222
|
+
await wrapper.find('input').trigger('blur')
|
|
223
|
+
expect(vm.hasSuccess).toBe(true)
|
|
95
224
|
})
|
|
96
225
|
})
|