@cnamts/synapse 1.0.25 → 1.0.26
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/{AutocompleteFilter-D7qBuCAP.js → AutocompleteFilter-BPR-a55G.js} +1 -1
- package/dist/{DateFilter-BitMWrMU.js → DateFilter-CknrJWs2.js} +2 -2
- package/dist/{NumberFilter-BTLUxw0a.js → NumberFilter-DJ-yNlzv.js} +1 -1
- package/dist/{PeriodFilter-B5rUIPAC.js → PeriodFilter-CiB5Oa9Z.js} +1 -1
- package/dist/{SelectFilter-l4QnRcuk.js → SelectFilter-EiafX97M.js} +1 -1
- package/dist/{TextFilter-C9hj6Qrp.js → TextFilter-BzOmpdxj.js} +1 -1
- package/dist/{apLightTheme-DnIM24Lv.js → apLightTheme-DS0Uy44H.js} +20 -16
- package/dist/components/Customs/Selects/SyAutocomplete/SyAutocomplete.d.ts +4 -2
- package/dist/components/Customs/Selects/SySelect/SySelect.d.ts +60 -289
- package/dist/components/Customs/SyCheckBoxGroup/SyCheckBoxGroup.d.ts +1 -0
- package/dist/components/Customs/SyCheckbox/SyCheckbox.d.ts +1 -0
- package/dist/components/Customs/SyRadioGroup/SyRadioGroup.d.ts +1 -0
- package/dist/components/Customs/SyTextField/SyTextField.d.ts +2 -4
- package/dist/components/DatePicker/CalendarMode/DatePicker.d.ts +50 -49
- package/dist/components/DatePicker/ComplexDatePicker/ComplexDatePicker.d.ts +29 -28
- package/dist/components/DatePicker/DateTextInput/DateTextInput.d.ts +8 -8
- package/dist/components/DatePicker/composables/useDatePickerState.d.ts +3 -3
- package/dist/components/DatePicker/composables/useDateTextField.d.ts +2 -2
- package/dist/components/DatePicker/composables/useInputBlurHandler.d.ts +2 -2
- package/dist/components/DatePicker/types.d.ts +1 -2
- package/dist/components/LunarCalendar/useLunarCalendarValidation.d.ts +1 -0
- package/dist/components/MonthPicker/MonthPicker.d.ts +1 -1
- package/dist/components/MonthPicker/MonthPickerText/MonthPickerInput.d.ts +1 -1
- package/dist/components/NirField/NirField.d.ts +8 -4
- package/dist/components/NirField/useNirValidation.d.ts +6 -2
- package/dist/components/PeriodField/PeriodField.d.ts +102 -102
- package/dist/components/PhoneField/PhoneField.d.ts +11 -1
- package/dist/components/RangeField/RangeSlider/RangeSlider.d.ts +0 -3
- package/dist/components/RatingPicker/EmotionPicker/EmotionPicker.d.ts +3 -1
- package/dist/components/RatingPicker/NumberPicker/NumberPicker.d.ts +4 -3
- package/dist/components/RatingPicker/RatingPicker.d.ts +18 -5
- package/dist/components/RatingPicker/StarsPicker/StarsPicker.d.ts +3 -1
- package/dist/components/RatingPicker/tests/RatingPicker.a11y.spect.d.ts +1 -0
- package/dist/components/RatingPicker/useRatingFocus.d.ts +18 -0
- package/dist/components/Tables/SyServerTable/SyServerTable.d.ts +4 -4
- package/dist/components/Tables/SyTable/SyTable.d.ts +4 -4
- package/dist/components/Tables/common/SyTablePagination.d.ts +152 -364
- package/dist/components/Tables/common/TableHeader.d.ts +1 -1
- package/dist/components/Tables/common/filters/DateFilter.d.ts +4 -4
- package/dist/composables/date/useDateInitializationDayjs.d.ts +3 -1
- package/dist/composables/unifyValidation/useCustomValidation.d.ts +3 -1
- package/dist/composables/unifyValidation/useValidation.d.ts +12 -6
- package/dist/composables/unifyValidation/useVuetifyValidation.d.ts +1 -1
- package/dist/composables/validation/useValidation.d.ts +1 -0
- package/dist/design-system-v3.js +2 -2
- package/dist/designTokens/tokens/amelipro/apLightTheme.d.ts +2 -0
- package/dist/designTokens/tokens/cnam/cnamLightTheme.d.ts +2 -0
- package/dist/{main-Cpx8Co6H.js → main-BsJ9ec3i.js} +9103 -9018
- package/dist/synapse.css +1 -1
- package/dist/vuetifyConfig.js +1 -1
- package/package.json +8 -7
- package/src/assets/overrides/_icons.scss +0 -13
- package/src/assets/overrides/_otp.scss +0 -1
- package/src/components/Accordion/Accordion.vue +2 -0
- package/src/components/CookiesSelection/CookiesInformation/CookiesInformation.vue +2 -1
- package/src/components/CookiesSelection/CookiesSelection.vue +2 -1
- package/src/components/CopyBtn/CopyBtn.vue +9 -0
- package/src/components/Customs/Selects/SyAutocomplete/SyAutocomplete.vue +1 -1
- package/src/components/Customs/Selects/SySelect/SySelect.stories.ts +413 -96
- package/src/components/Customs/Selects/SySelect/SySelect.vue +270 -225
- package/src/components/Customs/Selects/SySelect/tests/SySelect.spec.ts +245 -6
- package/src/components/Customs/SyCheckBoxGroup/SyCheckBoxGroup.vue +3 -3
- package/src/components/Customs/SyCheckbox/SyCheckbox.vue +23 -2
- package/src/components/Customs/SyRadioGroup/SyRadioGroup.vue +23 -5
- package/src/components/Customs/SyTabs/SyTabs.stories.ts +5 -5
- package/src/components/Customs/SyTabs/config.ts +3 -3
- package/src/components/Customs/SyTextField/SyTextField.vue +31 -4
- package/src/components/DatePicker/CalendarMode/DatePicker.stories.ts +1 -1
- package/src/components/DatePicker/CalendarMode/DatePicker.vue +17 -14
- package/src/components/DatePicker/ComplexDatePicker/ComplexDatePicker.stories.ts +1 -1
- package/src/components/DatePicker/ComplexDatePicker/ComplexDatePicker.vue +8 -7
- package/src/components/DatePicker/ComplexDatePicker/tests/ComplexDatePicker.spec.ts +1 -1
- package/src/components/DatePicker/DatePickerValidationExample/DatePickerValidation.stories.ts +1 -1
- package/src/components/DatePicker/DateTextInput/DateTextInput.vue +57 -23
- package/src/components/DatePicker/DateTextInput/NoCalendar.stories.ts +1 -1
- package/src/components/DatePicker/composables/useDatePickerState.ts +33 -14
- package/src/components/DatePicker/composables/useDateRangeInput.ts +2 -1
- package/src/components/DatePicker/composables/useDateSelection.ts +2 -1
- package/src/components/DatePicker/composables/useDateTextField.ts +2 -2
- package/src/components/DatePicker/composables/useInputBlurHandler.ts +2 -2
- package/src/components/DatePicker/types.ts +1 -2
- package/src/components/DialogBox/DialogBox.stories.ts +8 -8
- package/src/components/DialogBox/accessibilite/Accessibility.mdx +86 -22
- package/src/components/FilterSideBar/FilterSideBar.vue +2 -1
- package/src/components/LangBtn/LangBtn.vue +2 -1
- package/src/components/NotificationBar/Notification/Notification.vue +2 -2
- package/src/components/PaginatedTable/PaginatedTable.vue +1 -1
- package/src/components/PaginatedTable/Pagination.vue +1 -1
- package/src/components/PasswordField/PasswordField.vue +7 -3
- package/src/components/PhoneField/PhoneField.vue +4 -2
- package/src/components/RangeField/RangeSlider/RangeSlider.vue +11 -18
- package/src/components/RatingPicker/EmotionPicker/EmotionPicker.vue +32 -48
- package/src/components/RatingPicker/EmotionPicker/tests/__snapshots__/EmotionPicker.spec.ts.snap +5 -0
- package/src/components/RatingPicker/NumberPicker/NumberPicker.vue +48 -53
- package/src/components/RatingPicker/NumberPicker/tests/NumberPicker.spec.ts +2 -1
- package/src/components/RatingPicker/NumberPicker/tests/__snapshots__/NumberPicker.spec.ts.snap +40 -13
- package/src/components/RatingPicker/RatingPicker.stories.ts +65 -88
- package/src/components/RatingPicker/RatingPicker.vue +71 -15
- package/src/components/RatingPicker/StarsPicker/StarsPicker.vue +28 -37
- package/src/components/RatingPicker/StarsPicker/tests/StarsPicker.spec.ts +1 -1
- package/src/components/RatingPicker/StarsPicker/tests/__snapshots__/StarsPicker.spec.ts.snap +5 -0
- package/src/components/RatingPicker/accessibilite/Accessibility.mdx +137 -9
- package/src/components/RatingPicker/tests/RatingPicker.a11y.spect.ts +123 -0
- package/src/components/RatingPicker/tests/RatingPicker.spec.ts +3 -2
- package/src/components/RatingPicker/tests/__snapshots__/RatingPicker.spec.ts.snap +40 -11
- package/src/components/RatingPicker/useRatingFocus.ts +97 -0
- package/src/components/SyTextArea/SyTextArea.vue +32 -1
- package/src/components/Tables/SyServerTable/SyServerTable.vue +1 -1
- package/src/components/Tables/SyTable/SyTable.vue +1 -1
- package/src/components/Tables/common/SyTableFilter.vue +4 -4
- package/src/components/Tables/common/SyTablePagination.vue +1 -0
- package/src/components/Tables/common/TableHeader.vue +1 -1
- package/src/components/Tables/common/filters/DateFilter.vue +2 -2
- package/src/composables/date/tests/useDateFormatDayjs.spec.ts +81 -0
- package/src/composables/date/tests/{useDateInitialization.spec.ts → useDateInitializationDayjs.spec.ts} +39 -3
- package/src/composables/date/useDateInitializationDayjs.ts +4 -1
- package/src/composables/unifyValidation/documentationValidationProps.ts +7 -7
- package/src/composables/unifyValidation/tests/useCustomValidation.spec.ts +2 -1
- package/src/composables/unifyValidation/tests/useValidation.spec.ts +22 -0
- package/src/composables/unifyValidation/useCustomValidation.ts +16 -4
- package/src/composables/unifyValidation/useValidation.ts +46 -15
- package/src/composables/unifyValidation/useVuetifyValidation.ts +2 -2
- package/src/composables/useFormFieldErrorHandling.ts +4 -1
- package/src/composables/validation/tests/useValidation.spec.ts +2 -2
- package/src/composables/validation/useValidation.ts +15 -3
- package/src/composantsVuetify/VCard/VCard.mdx +59 -0
- package/src/composantsVuetify/VCard/v-card.stories.ts +279 -0
- package/src/designTokens/tokens/amelipro/apColors2026.ts +1 -1
- package/src/designTokens/tokens/amelipro/apLightTheme.ts +3 -0
- package/src/designTokens/tokens/cnam/cnamLightTheme.ts +3 -0
- package/src/stories/Accessibilite/Aculturation/SensibilisationAccessibilite.mdx +61 -91
- package/src/stories/Accessibilite/AuditDesignSystem.mdx +5 -8
- package/src/stories/Accessibilite/AuditEtContreAudit/Exemptions-derogations.mdx +1 -1
- package/src/stories/Accessibilite/AuditEtContreAudit/Introduction.mdx +11 -8
- package/src/stories/Accessibilite/AuditEtContreAudit/RGAA.mdx +6 -7
- package/src/stories/Accessibilite/Introduction.mdx +30 -30
- package/src/stories/Accessibilite/KitDePreAudit/Echantillonnage.mdx +168 -78
- package/src/stories/Accessibilite/KitDePreAudit/Introduction.mdx +13 -6
- package/src/stories/Accessibilite/KitDePreAudit/Outils/Introduction.mdx +66 -45
- package/src/stories/Accessibilite/KitDePreAudit/Outils/LecteursDEcran.mdx +23 -49
- package/src/stories/Accessibilite/KitDePreAudit/Outils/Tanaguru/FauxPositifs.stories.ts +6 -0
- package/src/stories/Accessibilite/KitDePreAudit/Outils/Tanaguru/Utilisation.mdx +7 -19
- package/src/stories/Accessibilite/KitDePreAudit/Preaudit.mdx +18 -20
- package/src/stories/Components/Components.stories.ts +52 -3
- package/dist/AutocompleteFilter-Df9i5mAl.cjs +0 -1
- package/dist/DateFilter-BJD6FMev.cjs +0 -1
- package/dist/NumberFilter-DGCzCXzI.cjs +0 -1
- package/dist/PeriodFilter-DO_ecTZW.cjs +0 -1
- package/dist/SelectFilter-CGwcKWLm.cjs +0 -1
- package/dist/TextFilter-B8nf7xoK.cjs +0 -1
- package/dist/apLightTheme-CEK4iY3f.cjs +0 -1
- package/dist/composables/date/useDateFormat.d.ts +0 -26
- package/dist/composables/date/useDateInitialization.d.ts +0 -18
- package/dist/design-system-v3.umd.cjs +0 -1
- package/dist/main-ByDPHpae.cjs +0 -1067
- package/dist/tooth-11-D3sLWv2n.cjs +0 -1
- package/dist/tooth-12-CXrLuH03.cjs +0 -1
- package/dist/tooth-13-BSfo5fpT.cjs +0 -1
- package/dist/tooth-14-DMzulx0h.cjs +0 -1
- package/dist/tooth-15-BKRFVi-9.cjs +0 -1
- package/dist/tooth-16-CpuxAbuM.cjs +0 -1
- package/dist/tooth-17-BPoahUdg.cjs +0 -1
- package/dist/tooth-18-DhHJz8sy.cjs +0 -1
- package/dist/tooth-21-Dgd5hn_X.cjs +0 -1
- package/dist/tooth-22-C2Tn19sB.cjs +0 -1
- package/dist/tooth-23-C9uaaSGb.cjs +0 -1
- package/dist/tooth-24-BrK9UGpf.cjs +0 -1
- package/dist/tooth-25-CE_EfGNp.cjs +0 -1
- package/dist/tooth-26-Ctv4i9Fy.cjs +0 -1
- package/dist/tooth-27-C5J7JkWM.cjs +0 -1
- package/dist/tooth-28-Z9oWqjo0.cjs +0 -1
- package/dist/tooth-31-BrYqmkTi.cjs +0 -1
- package/dist/tooth-32-BNNR0oCZ.cjs +0 -1
- package/dist/tooth-33-DuxvqO2J.cjs +0 -1
- package/dist/tooth-34-BCSCXMB6.cjs +0 -1
- package/dist/tooth-35-BLUXkX88.cjs +0 -1
- package/dist/tooth-36-IrKHYqlA.cjs +0 -1
- package/dist/tooth-37-BYqpdMwo.cjs +0 -1
- package/dist/tooth-38-B_eNXXdu.cjs +0 -1
- package/dist/tooth-41-Ddva4Ot8.cjs +0 -1
- package/dist/tooth-42-szcDqlM0.cjs +0 -1
- package/dist/tooth-43-B3ka6rQm.cjs +0 -1
- package/dist/tooth-44-CazyQucj.cjs +0 -1
- package/dist/tooth-45-B4HQtc8n.cjs +0 -1
- package/dist/tooth-46-BPM40gbG.cjs +0 -1
- package/dist/tooth-47-Dvr20dlh.cjs +0 -1
- package/dist/tooth-48-Bd8ljGsF.cjs +0 -1
- package/dist/tooth-51-OBpwCOF3.cjs +0 -1
- package/dist/tooth-52-aKxyHcmq.cjs +0 -1
- package/dist/tooth-53-vCwJjTOc.cjs +0 -1
- package/dist/tooth-54-DsWu2iFy.cjs +0 -1
- package/dist/tooth-55-BxC1X2Dn.cjs +0 -1
- package/dist/tooth-61-BbLvxMQi.cjs +0 -1
- package/dist/tooth-62-CmTkWczP.cjs +0 -1
- package/dist/tooth-63-DI7l_2qI.cjs +0 -1
- package/dist/tooth-64-B21sOsJh.cjs +0 -1
- package/dist/tooth-65-D2ZC2VEr.cjs +0 -1
- package/dist/tooth-71-D473PPO5.cjs +0 -1
- package/dist/tooth-72-Drh1wnNu.cjs +0 -1
- package/dist/tooth-73-DzlwYI23.cjs +0 -1
- package/dist/tooth-74-8aGvcZPg.cjs +0 -1
- package/dist/tooth-75-BFK7At_r.cjs +0 -1
- package/dist/tooth-81-BZmR-I0M.cjs +0 -1
- package/dist/tooth-82-euVfUUZV.cjs +0 -1
- package/dist/tooth-83-KV010j64.cjs +0 -1
- package/dist/tooth-84-BBg1RjhZ.cjs +0 -1
- package/dist/tooth-85-Cr-kc1wM.cjs +0 -1
- package/dist/vuetifyConfig.umd.cjs +0 -1
- package/src/composables/date/tests/useDateFormat.spec.ts +0 -67
- package/src/composables/date/useDateFormat.ts +0 -110
- package/src/composables/date/useDateInitialization.ts +0 -92
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import { type PropType, computed, ref, watch } from 'vue'
|
|
2
|
+
import { type PropType, computed, ref, watch, nextTick } from 'vue'
|
|
3
3
|
|
|
4
4
|
import EmotionPicker from './EmotionPicker/EmotionPicker.vue'
|
|
5
5
|
import NumberPicker from './NumberPicker/NumberPicker.vue'
|
|
@@ -44,13 +44,19 @@
|
|
|
44
44
|
type: Boolean,
|
|
45
45
|
default: false,
|
|
46
46
|
},
|
|
47
|
+
freeTextLabel: {
|
|
48
|
+
type: String,
|
|
49
|
+
default: 'Pouvez-vous nous en dire plus ?',
|
|
50
|
+
},
|
|
47
51
|
})
|
|
48
52
|
|
|
49
|
-
const emit = defineEmits
|
|
53
|
+
const emit = defineEmits<{
|
|
54
|
+
(e: 'update:modelValue', value: number): void
|
|
55
|
+
}>()
|
|
50
56
|
|
|
51
|
-
const alertTypeEnumRef = ref(AlertTypeEnum)
|
|
52
57
|
const internalValue = ref(-1)
|
|
53
58
|
const displayAdditionalContent = ref(false)
|
|
59
|
+
const ratingPickerRef = ref<{ focus?: () => void } | null>(null)
|
|
54
60
|
|
|
55
61
|
const ratingComponent = computed(() => {
|
|
56
62
|
if (props.type === RatingEnum.EMOTION) {
|
|
@@ -71,17 +77,29 @@
|
|
|
71
77
|
return undefined
|
|
72
78
|
})
|
|
73
79
|
|
|
74
|
-
const hasAnswered = computed(() =>
|
|
75
|
-
|
|
80
|
+
const hasAnswered = computed(() => internalValue.value !== -1)
|
|
76
81
|
function showAdditionalContent(value: number): void {
|
|
77
|
-
const
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
82
|
+
const max = props.type === RatingEnum.EMOTION
|
|
83
|
+
? (props.twoEmotions ? 2 : 3)
|
|
84
|
+
: props.type === RatingEnum.STARS
|
|
85
|
+
? 5
|
|
86
|
+
: 10
|
|
87
|
+
|
|
88
|
+
if (value < 1 || value > max) {
|
|
89
|
+
displayAdditionalContent.value = false
|
|
90
|
+
return
|
|
84
91
|
}
|
|
92
|
+
|
|
93
|
+
const starsUnsatisfied = props.type === RatingEnum.STARS && value <= 2
|
|
94
|
+
const numberUnsatisfied = props.type === RatingEnum.NUMBER && value <= 7
|
|
95
|
+
const emotionUnsatisfied = props.type === RatingEnum.EMOTION
|
|
96
|
+
&& (
|
|
97
|
+
(props.twoEmotions && value < 2)
|
|
98
|
+
|| (!props.twoEmotions && value < 3)
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
displayAdditionalContent.value
|
|
102
|
+
= starsUnsatisfied || numberUnsatisfied || emotionUnsatisfied
|
|
85
103
|
}
|
|
86
104
|
|
|
87
105
|
function setValue(value: number): void {
|
|
@@ -92,7 +110,18 @@
|
|
|
92
110
|
|
|
93
111
|
watch(() => props.modelValue, (newVal) => {
|
|
94
112
|
internalValue.value = newVal
|
|
113
|
+
showAdditionalContent(newVal)
|
|
95
114
|
}, { immediate: true })
|
|
115
|
+
|
|
116
|
+
// focus auto
|
|
117
|
+
watch(
|
|
118
|
+
() => props.type,
|
|
119
|
+
async () => {
|
|
120
|
+
await nextTick()
|
|
121
|
+
ratingPickerRef.value?.focus?.()
|
|
122
|
+
},
|
|
123
|
+
{ immediate: true },
|
|
124
|
+
)
|
|
96
125
|
</script>
|
|
97
126
|
|
|
98
127
|
<template>
|
|
@@ -104,9 +133,10 @@
|
|
|
104
133
|
>
|
|
105
134
|
<component
|
|
106
135
|
:is="ratingComponent"
|
|
136
|
+
ref="ratingPickerRef"
|
|
107
137
|
:model-value="internalValue"
|
|
108
138
|
:label="props.label"
|
|
109
|
-
:length="length"
|
|
139
|
+
:length="length || undefined"
|
|
110
140
|
:readonly="props.readonly || hasAnswered"
|
|
111
141
|
:item-labels="props.itemLabels || undefined"
|
|
112
142
|
@update:model-value="setValue"
|
|
@@ -123,14 +153,22 @@
|
|
|
123
153
|
v-if="!props.hideAlert"
|
|
124
154
|
:class="{ 'mb-4': displayAdditionalContent }"
|
|
125
155
|
outlined
|
|
126
|
-
:type="
|
|
156
|
+
:type="AlertTypeEnum.SUCCESS"
|
|
127
157
|
role="status"
|
|
158
|
+
aria-live="polite"
|
|
128
159
|
class="mt-4"
|
|
129
160
|
>
|
|
130
161
|
{{ locales.thanks }}
|
|
131
162
|
</SyAlert>
|
|
132
163
|
|
|
133
|
-
<
|
|
164
|
+
<div
|
|
165
|
+
v-if="displayAdditionalContent"
|
|
166
|
+
role="region"
|
|
167
|
+
aria-live="polite"
|
|
168
|
+
class="mt-4"
|
|
169
|
+
>
|
|
170
|
+
<slot />
|
|
171
|
+
</div>
|
|
134
172
|
</template>
|
|
135
173
|
</div>
|
|
136
174
|
</template>
|
|
@@ -146,4 +184,22 @@
|
|
|
146
184
|
text-align: center;
|
|
147
185
|
}
|
|
148
186
|
|
|
187
|
+
.sy-rating-picker__free-text {
|
|
188
|
+
display: flex;
|
|
189
|
+
flex-direction: column;
|
|
190
|
+
gap: 0.5rem;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
.sy-rating-picker__free-text-label {
|
|
194
|
+
font-weight: 600;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
.sy-rating-picker__free-text-textarea {
|
|
198
|
+
min-height: 120px;
|
|
199
|
+
padding: 0.75rem;
|
|
200
|
+
border: 1px solid #ccc;
|
|
201
|
+
border-radius: 0.375rem;
|
|
202
|
+
resize: vertical;
|
|
203
|
+
font: inherit;
|
|
204
|
+
}
|
|
149
205
|
</style>
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
<script lang="ts" setup>
|
|
2
|
-
import { ref, type PropType, onMounted } from 'vue'
|
|
2
|
+
import { ref, type PropType, onMounted, toRef } from 'vue'
|
|
3
3
|
import { RatingEnum, useRating } from '../Rating'
|
|
4
4
|
import { mdiStarOutline, mdiStar } from '@mdi/js'
|
|
5
5
|
import SyIcon from '@/components/Customs/SyIcon/SyIcon.vue'
|
|
6
6
|
import { locales } from '../locales'
|
|
7
|
+
import { useRatingFocus } from '../useRatingFocus'
|
|
7
8
|
|
|
8
9
|
const props = defineProps({
|
|
9
10
|
label: {
|
|
@@ -41,37 +42,26 @@
|
|
|
41
42
|
return (isHovered && !hasAnswered.value) || isActive
|
|
42
43
|
}
|
|
43
44
|
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
}
|
|
45
|
+
const {
|
|
46
|
+
ratingElement,
|
|
47
|
+
initFocus,
|
|
48
|
+
selectAndFocus,
|
|
49
|
+
focusNextElement,
|
|
50
|
+
focusPrevElement,
|
|
51
|
+
focus,
|
|
52
|
+
} = useRatingFocus({
|
|
53
|
+
length: toRef(props, 'length'),
|
|
54
|
+
modelValue: toRef(props, 'modelValue'),
|
|
55
|
+
selectValue: emitInputEvent,
|
|
56
|
+
wrap: true,
|
|
57
|
+
})
|
|
58
58
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
el?.setAttribute('tabindex', '0')
|
|
63
|
-
}
|
|
64
|
-
else {
|
|
65
|
-
el?.setAttribute('tabindex', '-1')
|
|
66
|
-
}
|
|
67
|
-
})
|
|
68
|
-
}
|
|
59
|
+
defineExpose({
|
|
60
|
+
focus,
|
|
61
|
+
})
|
|
69
62
|
|
|
70
63
|
onMounted(() => {
|
|
71
|
-
|
|
72
|
-
for (let i = 1; i < ratingElement.value.length; i++) {
|
|
73
|
-
ratingElement.value[i]?.setAttribute('tabindex', '-1')
|
|
74
|
-
}
|
|
64
|
+
initFocus()
|
|
75
65
|
})
|
|
76
66
|
</script>
|
|
77
67
|
|
|
@@ -93,19 +83,20 @@
|
|
|
93
83
|
ref="ratingElement"
|
|
94
84
|
class="sy-stars-picker__item d-flex align-center justify-center"
|
|
95
85
|
role="radio"
|
|
86
|
+
:tabindex="-1"
|
|
96
87
|
:aria-disabled="(props.readonly || hasAnswered) ? 'true' : undefined"
|
|
97
|
-
:aria-checked="isActive(index) ? 'true' :
|
|
88
|
+
:aria-checked="isActive(index) ? 'true' : 'false'"
|
|
98
89
|
@mouseover="hoverIndex = index"
|
|
99
90
|
@focus="hoverIndex = index"
|
|
100
91
|
@mouseleave="hoverIndex = -1"
|
|
101
92
|
@blur="hoverIndex = -1"
|
|
102
|
-
@click="
|
|
103
|
-
@
|
|
104
|
-
@
|
|
105
|
-
@
|
|
106
|
-
@
|
|
107
|
-
@
|
|
108
|
-
@
|
|
93
|
+
@click="selectAndFocus(index - 1)"
|
|
94
|
+
@keydown.enter.prevent="selectAndFocus(index - 1)"
|
|
95
|
+
@keydown.space.prevent="selectAndFocus(index - 1)"
|
|
96
|
+
@keydown.right.prevent="focusNextElement(index - 1)"
|
|
97
|
+
@keydown.left.prevent="focusPrevElement(index - 1)"
|
|
98
|
+
@keydown.up.prevent="focusPrevElement(index - 1)"
|
|
99
|
+
@keydown.down.prevent="focusNextElement(index - 1)"
|
|
109
100
|
>
|
|
110
101
|
<span class="d-sr-only">{{ locales.etoiles(index) }}</span>
|
|
111
102
|
<SyIcon
|
|
@@ -37,7 +37,7 @@ describe('StarsPicker', () => {
|
|
|
37
37
|
)
|
|
38
38
|
await wrapper.setProps({ modelValue: 1 })
|
|
39
39
|
expect(wrapper.findAll('[role="radio"]')[3]?.attributes('aria-checked')).toBe(
|
|
40
|
-
|
|
40
|
+
'false',
|
|
41
41
|
)
|
|
42
42
|
expect(wrapper.findAll('[role="radio"]')[0]?.attributes('aria-checked')).toBe(
|
|
43
43
|
'true',
|
package/src/components/RatingPicker/StarsPicker/tests/__snapshots__/StarsPicker.spec.ts.snap
CHANGED
|
@@ -18,6 +18,7 @@ exports[`StarsPicker > renders correctly 1`] = `
|
|
|
18
18
|
role="radiogroup"
|
|
19
19
|
>
|
|
20
20
|
<div
|
|
21
|
+
aria-checked="false"
|
|
21
22
|
class="
|
|
22
23
|
align-center
|
|
23
24
|
d-flex
|
|
@@ -50,6 +51,7 @@ exports[`StarsPicker > renders correctly 1`] = `
|
|
|
50
51
|
></i>
|
|
51
52
|
</div>
|
|
52
53
|
<div
|
|
54
|
+
aria-checked="false"
|
|
53
55
|
class="
|
|
54
56
|
align-center
|
|
55
57
|
d-flex
|
|
@@ -82,6 +84,7 @@ exports[`StarsPicker > renders correctly 1`] = `
|
|
|
82
84
|
></i>
|
|
83
85
|
</div>
|
|
84
86
|
<div
|
|
87
|
+
aria-checked="false"
|
|
85
88
|
class="
|
|
86
89
|
align-center
|
|
87
90
|
d-flex
|
|
@@ -114,6 +117,7 @@ exports[`StarsPicker > renders correctly 1`] = `
|
|
|
114
117
|
></i>
|
|
115
118
|
</div>
|
|
116
119
|
<div
|
|
120
|
+
aria-checked="false"
|
|
117
121
|
class="
|
|
118
122
|
align-center
|
|
119
123
|
d-flex
|
|
@@ -146,6 +150,7 @@ exports[`StarsPicker > renders correctly 1`] = `
|
|
|
146
150
|
></i>
|
|
147
151
|
</div>
|
|
148
152
|
<div
|
|
153
|
+
aria-checked="false"
|
|
149
154
|
class="
|
|
150
155
|
align-center
|
|
151
156
|
d-flex
|
|
@@ -1,15 +1,143 @@
|
|
|
1
|
-
import { Meta,
|
|
2
|
-
import * as
|
|
1
|
+
import { Meta, Primary } from '@storybook/addon-docs';
|
|
2
|
+
import * as Stories from '../RatingPicker.stories.ts';
|
|
3
|
+
import AccessibilityIcon from '@/common/imgs/accessibility-svgrepo-com.svg';
|
|
3
4
|
import '@/stories/styles/shared.css';
|
|
5
|
+
import {
|
|
6
|
+
AccessibilityGuideLayout,
|
|
7
|
+
CriteriaSection,
|
|
8
|
+
CriteriaCard,
|
|
9
|
+
BestPracticesSection,
|
|
10
|
+
ResourcesSection,
|
|
11
|
+
DemoSection
|
|
12
|
+
} from '@/stories/accessibility/AccessibilityGuideLayout.mdx';
|
|
4
13
|
|
|
5
|
-
<Meta of={
|
|
14
|
+
<Meta of={Stories} />
|
|
6
15
|
|
|
7
|
-
<
|
|
8
|
-
|
|
16
|
+
<AccessibilityGuideLayout
|
|
17
|
+
componentName="RatingPicker"
|
|
18
|
+
iconSrc={AccessibilityIcon}
|
|
19
|
+
>
|
|
20
|
+
|
|
21
|
+
<div class="mt-2">
|
|
22
|
+
<p>
|
|
23
|
+
Rapport d’audit manuel : <a href="/audits/RatingPicker.xlsx" style={{ color:'#0C41BD' }}>Voir le rapport</a>
|
|
24
|
+
</p>
|
|
25
|
+
<p style={{ color: 'grey', fontSize: '14px', marginTop: '0px' }}>
|
|
26
|
+
Correctifs associés (
|
|
27
|
+
<a
|
|
28
|
+
href="https://github.com/assurance-maladie-digital/design-system-v3/pull/1159"
|
|
29
|
+
target="_blank"
|
|
30
|
+
style={{ color:'#0C41BD' }}
|
|
31
|
+
>#1159</a>
|
|
32
|
+
)
|
|
33
|
+
</p>
|
|
9
34
|
</div>
|
|
10
35
|
|
|
36
|
+
<CriteriaSection>
|
|
37
|
+
<CriteriaCard icon="🎯" title="Choisir une seule note">
|
|
38
|
+
<ul>
|
|
39
|
+
<li>Le composant permet de sélectionner <strong>une seule valeur</strong> (comme une note).</li>
|
|
40
|
+
<li>On recommande d’utiliser un <code>fieldset</code> avec un <code>legend</code> pour donner le contexte.</li>
|
|
41
|
+
<li>Le comportement attendu est celui d’un <strong>groupe de boutons radio</strong>.</li>
|
|
42
|
+
<li>Le libellé doit être clair, par exemple : <code>Êtes-vous satisfait ?</code>.</li>
|
|
43
|
+
<li>Éviter les structures trop complexes qui rendent le composant difficile à comprendre (surtout pour les lecteurs d’écran).</li>
|
|
44
|
+
</ul>
|
|
45
|
+
</CriteriaCard>
|
|
11
46
|
|
|
12
|
-
<
|
|
13
|
-
<
|
|
14
|
-
|
|
15
|
-
|
|
47
|
+
<CriteriaCard icon="⌨️" title="Navigation au clavier">
|
|
48
|
+
<ul>
|
|
49
|
+
<li>Le composant doit être utilisable <strong>entièrement au clavier</strong>.</li>
|
|
50
|
+
<li>On doit pouvoir se déplacer entre les notes facilement.</li>
|
|
51
|
+
<li>Les flèches du clavier sont attendues pour naviguer entre les options.</li>
|
|
52
|
+
<li>Quand une note est sélectionnée, cela doit être clairement annoncé.</li>
|
|
53
|
+
<li>Si la note ne peut être choisie qu’une seule fois, il faut le préciser à l’utilisateur.</li>
|
|
54
|
+
</ul>
|
|
55
|
+
</CriteriaCard>
|
|
56
|
+
|
|
57
|
+
<CriteriaCard icon="🗣️" title="Message après la sélection">
|
|
58
|
+
<ul>
|
|
59
|
+
<li>Après avoir choisi une note, un message s’affiche (ex : <code>Merci pour votre réponse</code>).</li>
|
|
60
|
+
<li>Ce message doit être annoncé automatiquement, sans déplacer le focus.</li>
|
|
61
|
+
<li>On utilise pour ça <code>role="status"</code>.</li>
|
|
62
|
+
<li>Ne pas utiliser <code>role="alert"</code> pour ce type de message.</li>
|
|
63
|
+
</ul>
|
|
64
|
+
</CriteriaCard>
|
|
65
|
+
|
|
66
|
+
<CriteriaCard icon="📝" title="Champ de commentaire">
|
|
67
|
+
<ul>
|
|
68
|
+
<li>Un champ texte peut apparaître selon la note choisie.</li>
|
|
69
|
+
<li>Ce champ doit avoir un <strong>label clair</strong> (ex : <code>Pouvez-vous nous en dire plus ?</code>).</li>
|
|
70
|
+
<li>Le label doit être visible et bien associé au champ.</li>
|
|
71
|
+
<li>Le champ ne doit exister que quand il est utile (pas caché mais toujours présent pour les lecteurs d’écran).</li>
|
|
72
|
+
</ul>
|
|
73
|
+
</CriteriaCard>
|
|
74
|
+
|
|
75
|
+
<CriteriaCard icon="🎨" title="Focus et visibilité">
|
|
76
|
+
<ul>
|
|
77
|
+
<li>Quand on navigue au clavier, le focus doit être <strong>bien visible</strong>.</li>
|
|
78
|
+
<li>Attention au contraste : le focus doit ressortir clairement.</li>
|
|
79
|
+
<li>Ne pas confondre visuellement un élément sélectionné et un élément en focus.</li>
|
|
80
|
+
</ul>
|
|
81
|
+
</CriteriaCard>
|
|
82
|
+
|
|
83
|
+
<CriteriaCard icon="🖼️" title="Icônes décoratives">
|
|
84
|
+
<ul>
|
|
85
|
+
<li>Les icônes (étoiles, emojis, etc.) sont souvent décoratives.</li>
|
|
86
|
+
<li>Elles ne doivent pas être lues par les lecteurs d’écran si elles n’apportent pas d’info.</li>
|
|
87
|
+
<li>Attention à ne pas mélanger <code>aria-hidden</code> avec un <code>role="img"</code>.</li>
|
|
88
|
+
</ul>
|
|
89
|
+
</CriteriaCard>
|
|
90
|
+
|
|
91
|
+
<CriteriaCard icon="🧱" title="Code propre et valide">
|
|
92
|
+
<ul>
|
|
93
|
+
<li>Le HTML doit rester simple et valide.</li>
|
|
94
|
+
<li>Éviter les attributs non autorisés (ex : <code>readonly</code> sur un radio).</li>
|
|
95
|
+
<li>Une structure claire améliore la compatibilité avec les outils d’accessibilité.</li>
|
|
96
|
+
</ul>
|
|
97
|
+
</CriteriaCard>
|
|
98
|
+
</CriteriaSection>
|
|
99
|
+
|
|
100
|
+
<DemoSection componentName="RatingPicker">
|
|
101
|
+
<Primary />
|
|
102
|
+
</DemoSection>
|
|
103
|
+
|
|
104
|
+
<BestPracticesSection>
|
|
105
|
+
<ul>
|
|
106
|
+
<li>Utiliser <code>fieldset</code> + <code>legend</code> pour structurer le composant.</li>
|
|
107
|
+
<li>Garder un comportement de <strong>groupe radio</strong>.</li>
|
|
108
|
+
<li>Assurer une navigation clavier complète et fluide.</li>
|
|
109
|
+
<li>Afficher un focus bien visible.</li>
|
|
110
|
+
<li>Masquer correctement les éléments décoratifs.</li>
|
|
111
|
+
<li>Utiliser <code>role="status"</code> pour le message de confirmation.</li>
|
|
112
|
+
<li>Ajouter un label clair pour le champ de commentaire.</li>
|
|
113
|
+
<li>Rester simple : moins de complexité = meilleure accessibilité.</li>
|
|
114
|
+
</ul>
|
|
115
|
+
</BestPracticesSection>
|
|
116
|
+
|
|
117
|
+
<ResourcesSection>
|
|
118
|
+
<ul>
|
|
119
|
+
<li>
|
|
120
|
+
<a
|
|
121
|
+
href="https://accessibilite.numerique.gouv.fr/methode/criteres-et-tests/"
|
|
122
|
+
target="_blank"
|
|
123
|
+
style={{ color: '#0C41BD' }}
|
|
124
|
+
>RGAA 4.1.2 – Critères et tests</a>
|
|
125
|
+
</li>
|
|
126
|
+
<li>
|
|
127
|
+
<a
|
|
128
|
+
href="https://www.accede-web.com/notices/html-et-css/formulaires/regrouper-et-titrer-les-champs-de-meme-nature-avec-fieldset-et-legend/"
|
|
129
|
+
target="_blank"
|
|
130
|
+
style={{ color: '#0C41BD' }}
|
|
131
|
+
>AcceDe Web – fieldset et legend</a>
|
|
132
|
+
</li>
|
|
133
|
+
<li>
|
|
134
|
+
<a
|
|
135
|
+
href="https://www.accede-web.com/notices/interface-riche/message-de-notification/"
|
|
136
|
+
target="_blank"
|
|
137
|
+
style={{ color: '#0C41BD' }}
|
|
138
|
+
>AcceDe Web – Messages de notification</a>
|
|
139
|
+
</li>
|
|
140
|
+
</ul>
|
|
141
|
+
</ResourcesSection>
|
|
142
|
+
|
|
143
|
+
</AccessibilityGuideLayout>
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
// @vitest-environment jsdom
|
|
2
|
+
|
|
3
|
+
import { describe, it } from 'vitest'
|
|
4
|
+
import { mount } from '@vue/test-utils'
|
|
5
|
+
import { axe } from 'vitest-axe'
|
|
6
|
+
|
|
7
|
+
import { assertNoA11yViolations } from '@tests/unit/accessibility/axeUtils'
|
|
8
|
+
import RatingPicker from '../RatingPicker.vue'
|
|
9
|
+
import { RatingEnum } from '../Rating'
|
|
10
|
+
|
|
11
|
+
describe('RatingPicker – accessibility (axe)', () => {
|
|
12
|
+
it('has no obvious axe violations with emotion picker and fieldset label', async () => {
|
|
13
|
+
const wrapper = mount(RatingPicker, {
|
|
14
|
+
props: {
|
|
15
|
+
type: RatingEnum.EMOTION,
|
|
16
|
+
label: 'Êtes-vous satisfait de votre expérience ?',
|
|
17
|
+
itemLabels: ['Pas satisfait', 'Moyen', 'Satisfait'],
|
|
18
|
+
modelValue: -1,
|
|
19
|
+
},
|
|
20
|
+
global: {
|
|
21
|
+
stubs: {
|
|
22
|
+
EmotionPicker: {
|
|
23
|
+
template: `
|
|
24
|
+
<fieldset>
|
|
25
|
+
<legend><slot name="label" /></legend>
|
|
26
|
+
<input id="e1" type="radio" name="emotion" />
|
|
27
|
+
<label for="e1">Option</label>
|
|
28
|
+
</fieldset>
|
|
29
|
+
`,
|
|
30
|
+
},
|
|
31
|
+
StarsPicker: true,
|
|
32
|
+
NumberPicker: true,
|
|
33
|
+
SyAlert: {
|
|
34
|
+
template: `<div role="status"><slot /></div>`,
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
attachTo: document.body,
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
const results = await axe(wrapper.element as HTMLElement)
|
|
42
|
+
assertNoA11yViolations(results, 'RatingPicker – emotion picker with label', {
|
|
43
|
+
ignoreRules: ['region'],
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
wrapper.unmount()
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
it('has no obvious axe violations when free text is displayed for a low stars rating', async () => {
|
|
50
|
+
const wrapper = mount(RatingPicker, {
|
|
51
|
+
props: {
|
|
52
|
+
type: RatingEnum.STARS,
|
|
53
|
+
label: 'Comment évaluez-vous votre expérience ?',
|
|
54
|
+
modelValue: 2,
|
|
55
|
+
enableFreeText: true,
|
|
56
|
+
freeTextLabel: 'Pouvez-vous nous en dire plus ?',
|
|
57
|
+
freeTextValue: 'Le parcours était peu clair.',
|
|
58
|
+
},
|
|
59
|
+
global: {
|
|
60
|
+
stubs: {
|
|
61
|
+
EmotionPicker: true,
|
|
62
|
+
StarsPicker: {
|
|
63
|
+
template: `
|
|
64
|
+
<fieldset>
|
|
65
|
+
<legend><slot name="label" /></legend>
|
|
66
|
+
<input id="s1" type="radio" name="stars" />
|
|
67
|
+
<label for="s1">1 étoile</label>
|
|
68
|
+
</fieldset>
|
|
69
|
+
`,
|
|
70
|
+
},
|
|
71
|
+
NumberPicker: true,
|
|
72
|
+
SyAlert: {
|
|
73
|
+
template: `<div role="status"><slot /></div>`,
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
attachTo: document.body,
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
const results = await axe(wrapper.element as HTMLElement)
|
|
81
|
+
assertNoA11yViolations(results, 'RatingPicker – stars picker with free text', {
|
|
82
|
+
ignoreRules: ['region'],
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
wrapper.unmount()
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
it('has no obvious axe violations with number picker', async () => {
|
|
89
|
+
const wrapper = mount(RatingPicker, {
|
|
90
|
+
props: {
|
|
91
|
+
type: RatingEnum.NUMBER,
|
|
92
|
+
label: 'Notez votre expérience de 1 à 10',
|
|
93
|
+
modelValue: 5,
|
|
94
|
+
},
|
|
95
|
+
global: {
|
|
96
|
+
stubs: {
|
|
97
|
+
EmotionPicker: true,
|
|
98
|
+
StarsPicker: true,
|
|
99
|
+
NumberPicker: {
|
|
100
|
+
template: `
|
|
101
|
+
<fieldset>
|
|
102
|
+
<legend><slot name="label" /></legend>
|
|
103
|
+
<input id="n1" type="radio" name="number" />
|
|
104
|
+
<label for="n1">1</label>
|
|
105
|
+
</fieldset>
|
|
106
|
+
`,
|
|
107
|
+
},
|
|
108
|
+
SyAlert: {
|
|
109
|
+
template: `<div role="status"><slot /></div>`,
|
|
110
|
+
},
|
|
111
|
+
},
|
|
112
|
+
},
|
|
113
|
+
attachTo: document.body,
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
const results = await axe(wrapper.element as HTMLElement)
|
|
117
|
+
assertNoA11yViolations(results, 'RatingPicker – number picker', {
|
|
118
|
+
ignoreRules: ['region'],
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
wrapper.unmount()
|
|
122
|
+
})
|
|
123
|
+
})
|
|
@@ -51,6 +51,7 @@ describe('RatingPicker', () => {
|
|
|
51
51
|
const wrapper = mount(RatingPicker, {
|
|
52
52
|
stubs: {
|
|
53
53
|
StarsPicker: {
|
|
54
|
+
name: 'StarsPicker',
|
|
54
55
|
template: '<div />',
|
|
55
56
|
},
|
|
56
57
|
},
|
|
@@ -62,8 +63,8 @@ describe('RatingPicker', () => {
|
|
|
62
63
|
},
|
|
63
64
|
})
|
|
64
65
|
|
|
65
|
-
wrapper.findComponent(StarsPicker).vm.$emit('update:modelValue',
|
|
66
|
-
await wrapper.setProps({ modelValue:
|
|
66
|
+
wrapper.findComponent(StarsPicker).vm.$emit('update:modelValue', 2)
|
|
67
|
+
await wrapper.setProps({ modelValue: 2 })
|
|
67
68
|
|
|
68
69
|
expect(wrapper.text()).toContain(locales.thanks)
|
|
69
70
|
expect(wrapper.text()).toContain('Additional content')
|