@cnamts/synapse 1.0.22 → 1.0.23
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/{DateFilter-B5n-ZkLi.js → DateFilter-Dc-gSGwk.js} +1 -1
- package/dist/{NumberFilter-CtiZ9uj8.js → NumberFilter-vP38Wp6j.js} +1 -1
- package/dist/{PeriodFilter-DzqiMb-b.js → PeriodFilter-Ba1uYUnT.js} +1 -1
- package/dist/{SelectFilter-BOYlF7rX.js → SelectFilter-BioGT6Nn.js} +1 -1
- package/dist/{TextFilter-BOFRNfcX.js → TextFilter-B84dpnoq.js} +1 -1
- package/dist/components/Accordion/Accordion.d.ts +13 -2
- package/dist/components/Accordion/composables/useAccordionState.d.ts +2 -1
- package/dist/components/Amelipro/AmeliproAutoCompleteField/AmeliproAutoCompleteField.d.ts +7 -7
- package/dist/components/Amelipro/AmeliproCheckbox/AmeliproCheckbox.d.ts +1 -1
- package/dist/components/Amelipro/AmeliproCustomSelector/AmeliproCustomSelector.d.ts +1 -1
- package/dist/components/Amelipro/AmeliproPostalAddressField/AmeliproPostalAddressField.d.ts +1 -1
- package/dist/components/Amelipro/AmeliproSelect/AmeliproSelect.d.ts +7 -7
- package/dist/components/Amelipro/AmeliproTabs/AmeliproTabs.d.ts +16 -16
- package/dist/components/Amelipro/AmeliproTextArea/AmeliproTextArea.d.ts +1 -1
- package/dist/components/Amelipro/AmeliproTextField/AmeliproTextField.d.ts +1 -1
- package/dist/components/Customs/Selects/SyAutocomplete/SyAutocomplete.d.ts +22 -1
- package/dist/components/Customs/Selects/SyAutocomplete/locales.d.ts +5 -0
- package/dist/components/Customs/Selects/SyInputSelect/SyInputSelect.d.ts +1 -1
- package/dist/components/Customs/Selects/SySelect/SySelect.d.ts +1 -1
- package/dist/components/Customs/Selects/SySelect/locales.d.ts +1 -0
- package/dist/components/Customs/SyCheckBoxGroup/SyCheckBoxGroup.d.ts +1 -1
- package/dist/components/Customs/SyCheckbox/SyCheckbox.d.ts +1 -1
- package/dist/components/Customs/SyRadioGroup/SyRadioGroup.d.ts +1 -1
- package/dist/components/Customs/SyTextField/SyTextField.d.ts +5 -2
- package/dist/components/DatePicker/CalendarMode/DatePicker.d.ts +13 -9
- package/dist/components/DatePicker/ComplexDatePicker/ComplexDatePicker.d.ts +7 -5
- package/dist/components/DatePicker/DateTextInput/DateTextInput.d.ts +2 -1
- package/dist/components/ErrorPage/ErrorPage.d.ts +3 -1
- package/dist/components/FileList/UploadItem/UploadItem.d.ts +6 -0
- package/dist/components/FileList/UploadItem/locales.d.ts +1 -4
- package/dist/components/FileUpload/FileUploadContent.d.ts +2 -0
- package/dist/components/FileUpload/validateFiles.d.ts +2 -1
- package/dist/components/HeaderBar/HeaderBar.d.ts +2 -1
- package/dist/components/HeaderBar/HeaderLogo/HeaderLogo.d.ts +2 -1
- package/dist/components/HeaderNavigationBar/HeaderNavigationBar.d.ts +2 -1
- package/dist/components/MonthPicker/MonthPicker.d.ts +1939 -0
- package/dist/components/MonthPicker/MonthPickerText/MonthPickerInput.d.ts +1899 -0
- package/dist/components/MonthPicker/MonthPickerText/useTextField.d.ts +21 -0
- package/dist/components/MonthPicker/MonthPickerVisual/MonthPickerVisual.d.ts +21 -0
- package/dist/components/MonthPicker/MonthPickerVisual/MonthPickerVisualProps.d.ts +12 -0
- package/dist/components/MonthPicker/MonthPickerVisual/MonthSelector.d.ts +11 -0
- package/dist/components/MonthPicker/MonthPickerVisual/VisualPickerFooter.d.ts +6 -0
- package/dist/components/MonthPicker/MonthPickerVisual/VisualPickerHeader.d.ts +14 -0
- package/dist/components/MonthPicker/MonthPickerVisual/YearSelector.d.ts +14 -0
- package/dist/components/MonthPicker/MonthPickerVisual/useMonthGrid.d.ts +9 -0
- package/dist/components/MonthPicker/MonthPickerVisual/useYearGrid.d.ts +8 -0
- package/dist/components/MonthPicker/MonthPickerVisual/utils.d.ts +8 -0
- package/dist/components/MonthPicker/locales.d.ts +12 -0
- package/dist/components/MonthPicker/useMonthPickerValidation.d.ts +25 -0
- package/dist/components/NirField/NirField.d.ts +3 -1
- package/dist/components/NotificationBar/Notification/Notification.d.ts +3 -0
- package/dist/components/PasswordField/PasswordField.d.ts +1 -1
- package/dist/components/PeriodField/PeriodField.d.ts +29 -21
- package/dist/components/PhoneField/PhoneField.d.ts +2 -1
- package/dist/components/SyBtnMenu/SyBtnMenu.d.ts +1 -1
- package/dist/components/SyHeading/SyHeading.a11y.test.d.ts +1 -0
- package/dist/components/SyHeading/SyHeading.d.ts +4 -2
- package/dist/components/SyHeading/SyHeading.test.d.ts +1 -0
- package/dist/components/SyTextArea/SyTextArea.d.ts +1 -1
- 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 +6 -6
- package/dist/components/index.d.ts +1 -0
- package/dist/design-system-v3.js +102 -99
- package/dist/design-system-v3.umd.cjs +126 -126
- package/dist/designTokens/tokens/cnam/cnamContextual.d.ts +5 -0
- package/dist/{main-CEl4J8_T.js → main-aLKwdMi1.js} +11167 -10522
- package/dist/main.d.ts +1 -0
- package/dist/style.css +1 -1
- package/package.json +10 -4
- package/src/assets/apTokens.scss +2 -2
- package/src/assets/overrides/_btns.scss +8 -0
- package/src/assets/overrides/_forms.scss +9 -0
- package/src/assets/overrides/_icons.scss +38 -9
- package/src/assets/overrides/_tables.scss +19 -0
- package/src/components/Accordion/Accordion.mdx +23 -9
- package/src/components/Accordion/Accordion.stories.ts +153 -3
- package/src/components/Accordion/Accordion.vue +7 -6
- package/src/components/Accordion/composables/__tests__/useAccordionState.spec.ts +40 -12
- package/src/components/Accordion/composables/useAccordionState.ts +3 -4
- package/src/components/Accordion/tests/accordion.spec.ts +131 -19
- package/src/components/Amelipro/AmeliproPagination/AmeliproPagination.mdx +3 -1
- package/src/components/Amelipro/AmeliproPagination/AmeliproPagination.stories.ts +8 -0
- package/src/components/BackBtn/accessibilite/Accessibility.mdx +62 -10
- package/src/components/BackToTopBtn/BackToTopBtn.stories.ts +9 -3
- package/src/components/BackToTopBtn/accessibilite/Accessibility.mdx +86 -6
- package/src/components/Captcha/tests/Captcha.spec.ts +0 -29
- package/src/components/Captcha/tests/__snapshots__/Captcha.spec.ts.snap +2 -110
- package/src/components/Customs/Selects/SelectBtnField/accessibilite/Accessibility.mdx +133 -10
- package/src/components/Customs/Selects/SyAutocomplete/SyAutocomplete.stories.ts +379 -93
- package/src/components/Customs/Selects/SyAutocomplete/SyAutocomplete.vue +144 -83
- package/src/components/Customs/Selects/SyAutocomplete/accessibilite/Accessibilite.stories.ts +40 -1
- package/src/components/Customs/Selects/SyAutocomplete/accessibilite/Accessibility.mdx +7 -1
- package/src/components/Customs/Selects/SyAutocomplete/locales.ts +5 -0
- package/src/components/Customs/Selects/SyAutocomplete/tests/SyAutocomplete.a11y.spec.ts +96 -0
- package/src/components/Customs/Selects/SyAutocomplete/tests/SyAutocomplete.spec.ts +234 -9
- package/src/components/Customs/Selects/SyAutocomplete/utils/ariaManager.ts +13 -3
- package/src/components/Customs/Selects/SyAutocomplete/utils/useSelectionLogic.ts +9 -10
- package/src/components/Customs/Selects/SySelect/SySelect.vue +46 -3
- package/src/components/Customs/Selects/SySelect/locales.ts +1 -0
- package/src/components/Customs/SyIcon/SyIcon.vue +1 -1
- package/src/components/Customs/SyIcon/tests/SyIcon.a11y.spec.ts +20 -0
- package/src/components/Customs/SyIconButton/SyIconButton.mdx +46 -0
- package/src/components/Customs/SyIconButton/SyIconButton.stories.ts +184 -0
- package/src/components/Customs/SyIconButton/SyIconButton.vue +38 -0
- package/src/components/Customs/SyIconButton/accessibilite/Accessibility.mdx +64 -0
- package/src/components/Customs/SyIconButton/tests/SyIconButton.a11y.spec.ts +87 -0
- package/src/components/Customs/SyIconButton/tests/SyIconButton.spec.ts +152 -0
- package/src/components/Customs/SyIconButton/tests/__snapshots__/SyIconButton.spec.ts.snap +61 -0
- package/src/components/Customs/SyPagination/SyPagination.vue +5 -5
- package/src/components/Customs/SyTextField/SyTextField.vue +20 -2
- package/src/components/Customs/SyTextField/accessibilite/Accessibility.mdx +67 -9
- package/src/components/Customs/SyTextField/tests/SyTextField.a11y.spec.ts +15 -0
- package/src/components/Customs/SyTextField/tests/SyTextField.spec.ts +36 -0
- package/src/components/DataList/accessibilite/Accessibility.mdx +79 -11
- package/src/components/DataListGroup/accessibilite/Accessibility.mdx +80 -11
- package/src/components/DownloadBtn/tests/DownloadBtn.a11y.spec.ts +25 -0
- package/src/components/ErrorPage/ErrorPage.stories.ts +113 -19
- package/src/components/ErrorPage/ErrorPage.vue +17 -2
- package/src/components/ErrorPage/tests/ErrorPage.a11y.spec.ts +17 -0
- package/src/components/ErrorPage/tests/ErrorPage.spec.ts +21 -1
- package/src/components/ErrorPage/tests/__snapshots__/ErrorPage.spec.ts.snap +0 -1
- package/src/components/ExternalLinks/tests/ExternalLinks.a11y.spec.ts +23 -0
- package/src/components/FileList/FileList.stories.ts +51 -1
- package/src/components/FileList/UploadItem/UploadItem.vue +13 -6
- package/src/components/FileList/UploadItem/locales.ts +3 -12
- package/src/components/FileList/accessibilite/Accessibility.mdx +3 -0
- package/src/components/FileUpload/FileUpload.vue +2 -1
- package/src/components/FileUpload/FileUploadContent.vue +2 -1
- package/src/components/FileUpload/tests/FileUpload.spec.ts +47 -0
- package/src/components/FileUpload/validateFiles.ts +5 -2
- package/src/components/FranceConnectBtn/accessibilite/Accessibility.mdx +62 -9
- package/src/components/HeaderBar/HeaderBar.vue +2 -1
- package/src/components/HeaderBar/HeaderLogo/HeaderLogo.vue +2 -1
- package/src/components/HeaderNavigationBar/HeaderNavigationBar.vue +2 -1
- package/src/components/LunarCalendar/accessibilite/Accessibility.mdx +74 -8
- package/src/components/LunarCalendar/tests/LunarCalendar.a11y.spec.ts +163 -0
- package/src/components/MaintenancePage/MaintenancePage.vue +1 -1
- package/src/components/MaintenancePage/tests/MaintenancePage.spec.ts +4 -5
- package/src/components/MaintenancePage/tests/__snapshots__/MaintenancePage.spec.ts.snap +0 -1
- package/src/components/MonthPicker/MonthPicker.mdx +35 -0
- package/src/components/MonthPicker/MonthPicker.stories.ts +527 -0
- package/src/components/MonthPicker/MonthPicker.vue +79 -0
- package/src/components/MonthPicker/MonthPickerText/MonthPickerInput.vue +89 -0
- package/src/components/MonthPicker/MonthPickerText/useTextField.ts +27 -0
- package/src/components/MonthPicker/MonthPickerVisual/MonthPickerVisual.vue +154 -0
- package/src/components/MonthPicker/MonthPickerVisual/MonthPickerVisualProps.ts +13 -0
- package/src/components/MonthPicker/MonthPickerVisual/MonthSelector.vue +137 -0
- package/src/components/MonthPicker/MonthPickerVisual/VisualPickerFooter.vue +60 -0
- package/src/components/MonthPicker/MonthPickerVisual/VisualPickerHeader.vue +149 -0
- package/src/components/MonthPicker/MonthPickerVisual/YearSelector.vue +143 -0
- package/src/components/MonthPicker/MonthPickerVisual/useMonthGrid.ts +45 -0
- package/src/components/MonthPicker/MonthPickerVisual/useYearGrid.ts +45 -0
- package/src/components/MonthPicker/MonthPickerVisual/utils.ts +17 -0
- package/src/components/MonthPicker/accessibilite/Accessibility.mdx +59 -0
- package/src/components/MonthPicker/locales.ts +12 -0
- package/src/components/MonthPicker/tests/MonthPicker.a11y.spec.ts +71 -0
- package/src/components/MonthPicker/tests/MonthPicker.spec.ts +1248 -0
- package/src/components/MonthPicker/tests/__snapshots__/MonthPicker.spec.ts.snap +2545 -0
- package/src/components/MonthPicker/useMonthPickerValidation.ts +30 -0
- package/src/components/NirField/NirField.mdx +1 -2
- package/src/components/NirField/NirField.stories.ts +66 -6
- package/src/components/NotFoundPage/tests/NotFoundPage.spec.ts +2 -3
- package/src/components/NotFoundPage/tests/__snapshots__/NotFoundPage.spec.ts.snap +22 -14
- package/src/components/NotificationBar/Notification/Notification.vue +3 -1
- package/src/components/NotificationBar/NotificationBar.stories.ts +154 -0
- package/src/components/NotificationBar/tests/NotificationBar.a11y.spec.ts +26 -0
- package/src/components/NotificationBar/tests/NotificationBar.spec.ts +60 -0
- package/src/components/RangeField/accessibilite/Accessibility.mdx +79 -11
- package/src/components/SkipLink/tests/SkipLink.a11y.spec.ts +23 -0
- package/src/components/StatusPage/StatusPage.stories.ts +118 -0
- package/src/components/StatusPage/StatusPage.vue +5 -3
- package/src/components/StatusPage/tests/StatusPage.a11y.spec.ts +22 -0
- package/src/components/StatusPage/tests/StatusPage.spec.ts +22 -0
- package/src/components/StatusPage/tests/__snapshots__/StatusPage.spec.ts.snap +22 -14
- package/src/components/SubHeader/tests/SubHeader.a11y.spec.ts +20 -0
- package/src/components/SyAlert/SyAlert.vue +1 -0
- package/src/components/SyAlert/accessibilite/Accessibility.mdx +79 -9
- package/src/components/SyAlert/tests/SyAlert.a11y.spec.ts +23 -0
- package/src/components/SyHeading/SyHeading.a11y.test.ts +149 -0
- package/src/components/SyHeading/SyHeading.test.ts +115 -0
- package/src/components/SyHeading/SyHeading.vue +5 -3
- package/src/components/SyTextArea/accessibilite/Accessibility.mdx +80 -8
- package/src/components/SyTextArea/tests/SyTextArea.a11y.spec.ts +151 -0
- package/src/components/ToolbarContainer/tests/ToolbarContainer.a11y.spec.ts +126 -0
- package/src/components/UploadWorkflow/tests/__snapshots__/UploadWorkflow.spec.ts.snap +2 -2
- package/src/components/index.ts +1 -0
- package/src/composables/useFormFieldErrorHandling.ts +11 -2
- package/src/designTokens/tokens/cnam/cnamContextual.ts +6 -1
- package/src/main.ts +2 -0
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
import { useItemUtils } from './utils/useItemUtils'
|
|
13
13
|
import { useSelectionLogic } from './utils/useSelectionLogic'
|
|
14
14
|
import { useSyAutocompleteKeyboard } from './utils/useKeyboardHandler'
|
|
15
|
+
import { locales } from './locales'
|
|
15
16
|
|
|
16
17
|
const props = defineProps({
|
|
17
18
|
bgColor: {
|
|
@@ -74,6 +75,10 @@
|
|
|
74
75
|
type: Boolean,
|
|
75
76
|
default: false,
|
|
76
77
|
},
|
|
78
|
+
hideDetails: {
|
|
79
|
+
type: Boolean,
|
|
80
|
+
default: false,
|
|
81
|
+
},
|
|
77
82
|
hideNoData: {
|
|
78
83
|
type: Boolean,
|
|
79
84
|
default: false,
|
|
@@ -108,7 +113,7 @@
|
|
|
108
113
|
},
|
|
109
114
|
noDataText: {
|
|
110
115
|
type: String,
|
|
111
|
-
default:
|
|
116
|
+
default: locales.noData,
|
|
112
117
|
},
|
|
113
118
|
placeholder: {
|
|
114
119
|
type: String,
|
|
@@ -138,6 +143,10 @@
|
|
|
138
143
|
type: Array as PropType<string[] | null>,
|
|
139
144
|
default: null,
|
|
140
145
|
},
|
|
146
|
+
selectionText: {
|
|
147
|
+
type: Function as PropType<(selected: SelectArray) => string>,
|
|
148
|
+
default: undefined,
|
|
149
|
+
},
|
|
141
150
|
textKey: {
|
|
142
151
|
type: String,
|
|
143
152
|
default: 'text',
|
|
@@ -152,14 +161,13 @@
|
|
|
152
161
|
},
|
|
153
162
|
})
|
|
154
163
|
|
|
155
|
-
const emit = defineEmits(['update:modelValue'])
|
|
164
|
+
const emit = defineEmits(['update:modelValue', 'search'])
|
|
156
165
|
|
|
157
166
|
const isOpen = ref(false)
|
|
158
167
|
const search = ref('')
|
|
159
168
|
const selected = ref<SelectValue | SelectArray>(props.modelValue as SelectValue | SelectArray)
|
|
160
169
|
const hasInteracted = ref(false)
|
|
161
170
|
const suppressNextInput = ref(false)
|
|
162
|
-
let suppressOpenOnSearch = false
|
|
163
171
|
type SyTextFieldInstance = InstanceType<typeof SyTextField> & { $refs?: { input?: HTMLInputElement } }
|
|
164
172
|
const textFieldRef = ref<SyTextFieldInstance | null>(null)
|
|
165
173
|
const randomId = Math.random().toString(36).slice(2)
|
|
@@ -202,7 +210,7 @@
|
|
|
202
210
|
const normalizedSearch = computed(() => (search.value || '').toLowerCase())
|
|
203
211
|
|
|
204
212
|
const filteredItems = computed(() => {
|
|
205
|
-
if (!props.filter) return formattedItems.value
|
|
213
|
+
if (!props.filter || props.loading) return formattedItems.value
|
|
206
214
|
return formattedItems.value.filter((item) => {
|
|
207
215
|
const text = String(item[props.plainTextKey || props.textKey] ?? item[props.textKey] ?? '').toLowerCase()
|
|
208
216
|
return text.includes(normalizedSearch.value)
|
|
@@ -215,7 +223,6 @@
|
|
|
215
223
|
|
|
216
224
|
const selectItem = (item: ItemType | string | number | null | undefined) => {
|
|
217
225
|
markInteracted()
|
|
218
|
-
suppressOpenOnSearch = true
|
|
219
226
|
updateValue(item ?? null)
|
|
220
227
|
if (props.multiple) {
|
|
221
228
|
suppressNextInput.value = true
|
|
@@ -225,19 +232,15 @@
|
|
|
225
232
|
}
|
|
226
233
|
|
|
227
234
|
watch(() => props.modelValue, (val) => {
|
|
228
|
-
selected.value = val
|
|
229
|
-
|
|
235
|
+
selected.value = props.multiple && (val === null || val === undefined)
|
|
236
|
+
? []
|
|
237
|
+
: val as SelectValue | SelectArray
|
|
230
238
|
syncSearchFromValue()
|
|
231
239
|
}, { immediate: true })
|
|
232
240
|
|
|
233
241
|
let debounceHandle: ReturnType<typeof setTimeout> | null = null
|
|
234
242
|
|
|
235
243
|
watch(search, () => {
|
|
236
|
-
if (suppressOpenOnSearch) {
|
|
237
|
-
suppressOpenOnSearch = false
|
|
238
|
-
return
|
|
239
|
-
}
|
|
240
|
-
|
|
241
244
|
if (!isOpen.value) {
|
|
242
245
|
isOpen.value = true
|
|
243
246
|
}
|
|
@@ -261,13 +264,15 @@
|
|
|
261
264
|
})
|
|
262
265
|
|
|
263
266
|
const hasChips = computed(() => props.multiple && props.chips && Array.isArray(selected.value) && selected.value.length > 0)
|
|
267
|
+
const hasSelectionTextDisplay = computed(() => !!props.selectionText && Array.isArray(selected.value) && (selected.value as SelectArray).length > 0)
|
|
268
|
+
const hasMultipleSelections = computed(() => props.multiple && !props.chips && !props.selectionText && Array.isArray(selected.value) && (selected.value as SelectArray).length > 0)
|
|
269
|
+
const hasInlineSelections = computed(() => hasChips.value || hasMultipleSelections.value)
|
|
264
270
|
|
|
265
271
|
const displayValue = computed(() => {
|
|
266
272
|
if (props.multiple && !props.chips) {
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
return prefix + search.value
|
|
273
|
+
if (props.selectionText || hasMultipleSelections.value) {
|
|
274
|
+
return search.value
|
|
275
|
+
}
|
|
271
276
|
}
|
|
272
277
|
return search.value
|
|
273
278
|
})
|
|
@@ -328,13 +333,35 @@
|
|
|
328
333
|
return (textFieldRef.value?.$el as HTMLElement | undefined)?.querySelector('.v-field') ?? undefined
|
|
329
334
|
})
|
|
330
335
|
|
|
336
|
+
const shouldDisableErrorHandling = computed(() => props.disableErrorHandling)
|
|
337
|
+
|
|
331
338
|
const externalErrors = computed(() => props.errorMessages || [])
|
|
332
|
-
const displayErrors = computed(() =>
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
339
|
+
const displayErrors = computed(() => {
|
|
340
|
+
if (shouldDisableErrorHandling.value) return []
|
|
341
|
+
return externalErrors.value.length > 0
|
|
342
|
+
? externalErrors.value
|
|
343
|
+
: (hasInteracted.value ? errorHandling.errors.value : [])
|
|
344
|
+
})
|
|
345
|
+
const displayWarnings = computed(() => {
|
|
346
|
+
if (shouldDisableErrorHandling.value) return []
|
|
347
|
+
return hasInteracted.value ? errorHandling.warnings.value : []
|
|
348
|
+
})
|
|
349
|
+
const displaySuccesses = computed(() => {
|
|
350
|
+
if (shouldDisableErrorHandling.value) return []
|
|
351
|
+
return hasInteracted.value ? errorHandling.successes.value : []
|
|
352
|
+
})
|
|
353
|
+
const displayHasError = computed(() => {
|
|
354
|
+
if (shouldDisableErrorHandling.value) return false
|
|
355
|
+
return externalErrors.value.length > 0 || (hasInteracted.value && errorHandling.hasError.value)
|
|
356
|
+
})
|
|
357
|
+
const displayHasWarning = computed(() => {
|
|
358
|
+
if (shouldDisableErrorHandling.value) return false
|
|
359
|
+
return hasInteracted.value && errorHandling.hasWarning.value
|
|
360
|
+
})
|
|
361
|
+
const displayHasSuccess = computed(() => {
|
|
362
|
+
if (shouldDisableErrorHandling.value) return false
|
|
363
|
+
return hasInteracted.value && errorHandling.hasSuccess.value
|
|
364
|
+
})
|
|
338
365
|
|
|
339
366
|
const validateOnSubmit = () => {
|
|
340
367
|
markInteracted()
|
|
@@ -363,36 +390,17 @@
|
|
|
363
390
|
const inputValue = getInputValue(value)
|
|
364
391
|
if (inputValue === null) return
|
|
365
392
|
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
getChipLabel(item as ItemType | string | number),
|
|
370
|
-
)
|
|
371
|
-
|
|
372
|
-
const parts = inputValue.split(',').map(p => p.trim())
|
|
373
|
-
let matched = 0
|
|
374
|
-
for (let i = 0; i < Math.min(parts.length, labels.length); i++) {
|
|
375
|
-
if (parts[i] === labels[i]) {
|
|
376
|
-
matched++
|
|
377
|
-
}
|
|
378
|
-
else {
|
|
379
|
-
break
|
|
380
|
-
}
|
|
381
|
-
}
|
|
393
|
+
// Ignore outside emissions (e.g. SyTextField.checkErrorOnBlur re-emitting
|
|
394
|
+
// the current value after a programmatic update): no actual user input occurred.
|
|
395
|
+
if (inputValue === search.value) return
|
|
382
396
|
|
|
383
|
-
|
|
384
|
-
const kept = selectedItems.slice(0, matched)
|
|
385
|
-
selected.value = kept
|
|
386
|
-
emit('update:modelValue', kept)
|
|
387
|
-
}
|
|
397
|
+
search.value = inputValue
|
|
388
398
|
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
}
|
|
392
|
-
else {
|
|
393
|
-
search.value = inputValue
|
|
399
|
+
if (!inputValue && !props.multiple && selected.value != null) {
|
|
400
|
+
updateValue(null)
|
|
394
401
|
}
|
|
395
402
|
|
|
403
|
+
emit('search', inputValue)
|
|
396
404
|
openAndFocus()
|
|
397
405
|
}
|
|
398
406
|
|
|
@@ -405,6 +413,7 @@
|
|
|
405
413
|
const getOptionId = (index: number) => `${optionIdPrefixed.value}-${index}`
|
|
406
414
|
|
|
407
415
|
const resultsLiveText = computed(() => {
|
|
416
|
+
if (!hasInteracted.value) return
|
|
408
417
|
if (props.loading) return 'Chargement des résultats'
|
|
409
418
|
const count = filteredItems.value.length
|
|
410
419
|
if (!props.filter) return ''
|
|
@@ -424,7 +433,6 @@
|
|
|
424
433
|
|
|
425
434
|
watch(isOpen, (open) => {
|
|
426
435
|
if (!open && props.multiple) {
|
|
427
|
-
suppressOpenOnSearch = true
|
|
428
436
|
search.value = ''
|
|
429
437
|
}
|
|
430
438
|
})
|
|
@@ -440,7 +448,10 @@
|
|
|
440
448
|
</script>
|
|
441
449
|
|
|
442
450
|
<template>
|
|
443
|
-
<div
|
|
451
|
+
<div
|
|
452
|
+
class="sy-autocomplete"
|
|
453
|
+
:class="{ 'sy-autocomplete--has-selection-text': hasSelectionTextDisplay }"
|
|
454
|
+
>
|
|
444
455
|
<VMenu
|
|
445
456
|
v-model="isOpen"
|
|
446
457
|
transition="slide-y-transition"
|
|
@@ -459,12 +470,13 @@
|
|
|
459
470
|
ref="textFieldRef"
|
|
460
471
|
:model-value="displayValue"
|
|
461
472
|
:label="hasChips ? '' : label"
|
|
462
|
-
:placeholder="
|
|
473
|
+
:placeholder="hasInlineSelections || hasSelectionTextDisplay ? '' : placeholder"
|
|
474
|
+
:is-active="hasInlineSelections || hasSelectionTextDisplay"
|
|
463
475
|
:readonly="readonly"
|
|
464
476
|
:bg-color="bgColor"
|
|
465
477
|
:density="density"
|
|
466
478
|
:autocomplete="'off'"
|
|
467
|
-
:class="{ 'sy-autocomplete--clearable': clearable }"
|
|
479
|
+
:class="{ 'sy-autocomplete--clearable': clearable, 'sy-autocomplete__field--has-chips': hasInlineSelections }"
|
|
468
480
|
:error-messages="displayErrors"
|
|
469
481
|
:warning-messages="displayWarnings"
|
|
470
482
|
:success-messages="displaySuccesses"
|
|
@@ -473,7 +485,10 @@
|
|
|
473
485
|
:has-success="displayHasSuccess"
|
|
474
486
|
:required="required"
|
|
475
487
|
:display-asterisk="required && displayAsterisk"
|
|
476
|
-
:
|
|
488
|
+
:disable-error-handling="disableErrorHandling"
|
|
489
|
+
:loading="loading"
|
|
490
|
+
:are-details-hidden="hideDetails"
|
|
491
|
+
:aria-label="hasInlineSelections ? label : undefined"
|
|
477
492
|
@click="openAndFocus"
|
|
478
493
|
@update:model-value="handleInput"
|
|
479
494
|
@blur="checkErrorOnBlur"
|
|
@@ -481,10 +496,10 @@
|
|
|
481
496
|
>
|
|
482
497
|
<template #append-inner>
|
|
483
498
|
<button
|
|
484
|
-
v-if="clearable && hasSelectionToClear
|
|
499
|
+
v-if="clearable && hasSelectionToClear"
|
|
485
500
|
type="button"
|
|
486
501
|
class="sy-autocomplete__clear-button"
|
|
487
|
-
:aria-label="
|
|
502
|
+
:aria-label="locales.clearSelection"
|
|
488
503
|
@click.stop.prevent="selectItem(null)"
|
|
489
504
|
>
|
|
490
505
|
<SyIcon
|
|
@@ -499,23 +514,35 @@
|
|
|
499
514
|
decorative
|
|
500
515
|
/>
|
|
501
516
|
</template>
|
|
517
|
+
<template v-if="hasChips">
|
|
518
|
+
<VChip
|
|
519
|
+
v-for="(item, index) in selected as SelectArray"
|
|
520
|
+
:key="getChipKey(item, index)"
|
|
521
|
+
size="small"
|
|
522
|
+
class="sy-autocomplete__chip"
|
|
523
|
+
closable
|
|
524
|
+
:close-label="locales.removeChip(getChipLabel(item as ItemType))"
|
|
525
|
+
@click:close="() => selectItem(item as ItemType)"
|
|
526
|
+
>
|
|
527
|
+
{{ getChipLabel(item as ItemType) }}
|
|
528
|
+
</VChip>
|
|
529
|
+
</template>
|
|
530
|
+
<template v-else-if="hasMultipleSelections">
|
|
531
|
+
<span
|
|
532
|
+
v-for="(item, index) in selected as SelectArray"
|
|
533
|
+
:key="getChipKey(item, index)"
|
|
534
|
+
class="sy-autocomplete__label"
|
|
535
|
+
>
|
|
536
|
+
{{ getChipLabel(item as ItemType) }}
|
|
537
|
+
</span>
|
|
538
|
+
</template>
|
|
502
539
|
<template
|
|
503
|
-
v-if="
|
|
540
|
+
v-if="hasSelectionTextDisplay"
|
|
504
541
|
#prepend-inner
|
|
505
542
|
>
|
|
506
|
-
<
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
:key="getChipKey(item, index)"
|
|
510
|
-
size="small"
|
|
511
|
-
class="ma-1"
|
|
512
|
-
closable
|
|
513
|
-
:close-label="`Supprimer ${getChipLabel(item as ItemType)}`"
|
|
514
|
-
@click:close="() => selectItem(item as ItemType)"
|
|
515
|
-
>
|
|
516
|
-
{{ getChipLabel(item as ItemType) }}
|
|
517
|
-
</VChip>
|
|
518
|
-
</div>
|
|
543
|
+
<span class="sy-autocomplete__selection-text">
|
|
544
|
+
{{ selectionText!(selected as SelectArray) }}
|
|
545
|
+
</span>
|
|
519
546
|
</template>
|
|
520
547
|
</SyTextField>
|
|
521
548
|
</template>
|
|
@@ -524,21 +551,14 @@
|
|
|
524
551
|
:id="uniqueMenuId"
|
|
525
552
|
ref="listRef"
|
|
526
553
|
role="listbox"
|
|
527
|
-
:aria-
|
|
528
|
-
:aria-labelledby="`${uniqueMenuId}-input`"
|
|
554
|
+
:aria-labelledby="`${uniqueMenuId}-input-label`"
|
|
529
555
|
:aria-multiselectable="multiple ? 'true' : undefined"
|
|
530
556
|
:style="{ minWidth: `${textFieldRef?.$el?.offsetWidth || 0}px` }"
|
|
531
557
|
tag="ul"
|
|
532
558
|
tabindex="-1"
|
|
533
559
|
@click.stop
|
|
534
560
|
>
|
|
535
|
-
<template v-if="loading">
|
|
536
|
-
<VListItem
|
|
537
|
-
title="Chargement..."
|
|
538
|
-
tag="li"
|
|
539
|
-
/>
|
|
540
|
-
</template>
|
|
541
|
-
<template v-else-if="filteredItems.length === 0 && !hideNoData">
|
|
561
|
+
<template v-if="filteredItems.length === 0 && !hideNoData && !loading">
|
|
542
562
|
<VListItem
|
|
543
563
|
:title="noDataText"
|
|
544
564
|
disabled
|
|
@@ -622,11 +642,52 @@
|
|
|
622
642
|
color: rgb(0 0 0 / 54%);
|
|
623
643
|
}
|
|
624
644
|
|
|
625
|
-
.sy-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
645
|
+
.sy-autocomplete__chip {
|
|
646
|
+
margin: 2px;
|
|
647
|
+
align-self: center;
|
|
648
|
+
flex-shrink: 0;
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
/* Style spécifique pour les chips */
|
|
652
|
+
:deep(.sy-autocomplete__chip .v-chip__close .v-icon__svg) {
|
|
653
|
+
fill: inherit !important;
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
.sy-autocomplete__label {
|
|
657
|
+
align-self: center;
|
|
658
|
+
white-space: nowrap;
|
|
659
|
+
flex-shrink: 0;
|
|
660
|
+
font-size: inherit;
|
|
661
|
+
|
|
662
|
+
&:not(:last-of-type)::after {
|
|
663
|
+
content: ',';
|
|
664
|
+
margin-right: 4px;
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
:deep(.sy-autocomplete__field--has-chips .v-field) {
|
|
669
|
+
height: auto;
|
|
670
|
+
min-height: var(--v-input-control-height, 56px);
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
:deep(.sy-autocomplete__field--has-chips .v-field__input) {
|
|
674
|
+
flex-wrap: wrap;
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
:deep(.sy-autocomplete__field--has-chips .v-field__input input) {
|
|
678
|
+
flex: 1 1 auto;
|
|
679
|
+
min-width: 64px;
|
|
680
|
+
align-self: center;
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
.sy-autocomplete__selection-text {
|
|
684
|
+
padding: 0 4px;
|
|
685
|
+
white-space: nowrap;
|
|
686
|
+
font-size: inherit;
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
.sy-autocomplete--has-selection-text :deep(input) {
|
|
690
|
+
caret-color: transparent !important;
|
|
630
691
|
}
|
|
631
692
|
|
|
632
693
|
.v-list-item.active,
|
package/src/components/Customs/Selects/SyAutocomplete/accessibilite/Accessibilite.stories.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import type { StoryObj } from '@storybook/vue3'
|
|
2
|
-
import { mdiKeyboard } from '@mdi/js'
|
|
2
|
+
import { mdiKeyboard, mdiLoading } from '@mdi/js'
|
|
3
3
|
|
|
4
4
|
const keyboardIcon = mdiKeyboard
|
|
5
|
+
const loadingIcon = mdiLoading
|
|
5
6
|
|
|
6
7
|
export default {
|
|
7
8
|
title: 'Composants/Formulaires/Selects/SyAutocomplete/Accessibility',
|
|
@@ -68,3 +69,41 @@ export const ComboboxKeyboardNavigation: StoryObj = {
|
|
|
68
69
|
}
|
|
69
70
|
},
|
|
70
71
|
}
|
|
72
|
+
|
|
73
|
+
export const LoadingAccessibility: StoryObj = {
|
|
74
|
+
tags: ['!dev'],
|
|
75
|
+
render: () => {
|
|
76
|
+
return {
|
|
77
|
+
setup() {
|
|
78
|
+
return { loadingIcon }
|
|
79
|
+
},
|
|
80
|
+
template: `
|
|
81
|
+
<div>
|
|
82
|
+
<p>Lorsque la prop <code>loading</code> est à <code>true</code>, le composant affiche une barre de progression au bas du champ.</p>
|
|
83
|
+
<v-table density="compact" style="margin-top: 16px;">
|
|
84
|
+
<thead>
|
|
85
|
+
<tr>
|
|
86
|
+
<th>Comportement</th>
|
|
87
|
+
<th>Détail</th>
|
|
88
|
+
</tr>
|
|
89
|
+
</thead>
|
|
90
|
+
<tbody>
|
|
91
|
+
<tr>
|
|
92
|
+
<td>Barre de progression accessible</td>
|
|
93
|
+
<td>La barre porte un <code>aria-label</code> nommant le champ (<em>« Chargement de [label] »</em>) afin que les lecteurs d’écran annoncent son état.</td>
|
|
94
|
+
</tr>
|
|
95
|
+
<tr>
|
|
96
|
+
<td>Message « Aucune option » masqué</td>
|
|
97
|
+
<td>Le message d’absence de résultats n’est pas affiché pendant le chargement, pour éviter de signaler à tort que la liste est vide.</td>
|
|
98
|
+
</tr>
|
|
99
|
+
<tr>
|
|
100
|
+
<td>Critère RGAA 4.1 / WCAG 4.1.2</td>
|
|
101
|
+
<td>Le rôle <code>progressbar</code> et son nom accessible satisfont le critère « Nom, rôle, valeur » pour les composants d’interface.</td>
|
|
102
|
+
</tr>
|
|
103
|
+
</tbody>
|
|
104
|
+
</v-table>
|
|
105
|
+
</div>
|
|
106
|
+
`,
|
|
107
|
+
}
|
|
108
|
+
},
|
|
109
|
+
}
|
|
@@ -73,6 +73,11 @@ import '@/stories/styles/shared.css';
|
|
|
73
73
|
<Story of={AccessStories.ComboboxKeyboardNavigation} />
|
|
74
74
|
</div>
|
|
75
75
|
|
|
76
|
+
<div className="loading-section">
|
|
77
|
+
<h2>État de chargement</h2>
|
|
78
|
+
<Story of={AccessStories.LoadingAccessibility} />
|
|
79
|
+
</div>
|
|
80
|
+
|
|
76
81
|
<div className="implementation-section">
|
|
77
82
|
<h2>Spécificités d'implémentation</h2>
|
|
78
83
|
<p>
|
|
@@ -172,7 +177,8 @@ import '@/stories/styles/shared.css';
|
|
|
172
177
|
line-height: 1.5;
|
|
173
178
|
}
|
|
174
179
|
|
|
175
|
-
.keyboard-section
|
|
180
|
+
.keyboard-section,
|
|
181
|
+
.loading-section {
|
|
176
182
|
background-color: #f0f7ff;
|
|
177
183
|
padding: 20px;
|
|
178
184
|
border-radius: 8px;
|
|
@@ -69,4 +69,100 @@ describe('SyAutocomplete – accessibility (axe)', () => {
|
|
|
69
69
|
ignoreRules: ['region'],
|
|
70
70
|
})
|
|
71
71
|
})
|
|
72
|
+
|
|
73
|
+
it('has no obvious axe violations for selectionText with no selection', async () => {
|
|
74
|
+
const wrapper = mount(SyAutocomplete, {
|
|
75
|
+
props: {
|
|
76
|
+
modelValue: [],
|
|
77
|
+
items,
|
|
78
|
+
label: 'Colonnes affichées',
|
|
79
|
+
textKey: 'text',
|
|
80
|
+
valueKey: 'value',
|
|
81
|
+
multiple: true,
|
|
82
|
+
selectionText: (selected: unknown[]) => `${selected.length} colonnes sélectionnées`,
|
|
83
|
+
},
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
const results = await axe(wrapper.element as HTMLElement)
|
|
87
|
+
assertNoA11yViolations(results, 'SyAutocomplete – selectionText empty', {
|
|
88
|
+
ignoreRules: ['region'],
|
|
89
|
+
})
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
it('has no obvious axe violations when loading is true', async () => {
|
|
93
|
+
const wrapper = mount(SyAutocomplete, {
|
|
94
|
+
props: {
|
|
95
|
+
modelValue: null,
|
|
96
|
+
items,
|
|
97
|
+
label: 'Choisir une option',
|
|
98
|
+
textKey: 'text',
|
|
99
|
+
valueKey: 'value',
|
|
100
|
+
loading: true,
|
|
101
|
+
},
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
const results = await axe(wrapper.element as HTMLElement)
|
|
105
|
+
assertNoA11yViolations(results, 'SyAutocomplete – loading state', {
|
|
106
|
+
ignoreRules: ['region'],
|
|
107
|
+
})
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
it('has no obvious axe violations when loading is true with no label (chips mode)', async () => {
|
|
111
|
+
const wrapper = mount(SyAutocomplete, {
|
|
112
|
+
props: {
|
|
113
|
+
modelValue: ['1'],
|
|
114
|
+
items,
|
|
115
|
+
label: 'Options',
|
|
116
|
+
textKey: 'text',
|
|
117
|
+
valueKey: 'value',
|
|
118
|
+
multiple: true,
|
|
119
|
+
chips: true,
|
|
120
|
+
loading: true,
|
|
121
|
+
},
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
const results = await axe(wrapper.element as HTMLElement)
|
|
125
|
+
assertNoA11yViolations(results, 'SyAutocomplete – loading + chips', {
|
|
126
|
+
ignoreRules: ['region'],
|
|
127
|
+
})
|
|
128
|
+
})
|
|
129
|
+
|
|
130
|
+
it('has no obvious axe violations for selectionText with selection', async () => {
|
|
131
|
+
const wrapper = mount(SyAutocomplete, {
|
|
132
|
+
props: {
|
|
133
|
+
modelValue: [items[0]!.value, items[1]!.value],
|
|
134
|
+
items,
|
|
135
|
+
label: 'Colonnes affichées',
|
|
136
|
+
textKey: 'text',
|
|
137
|
+
valueKey: 'value',
|
|
138
|
+
multiple: true,
|
|
139
|
+
selectionText: (selected: unknown[]) => `${selected.length} colonnes sélectionnées`,
|
|
140
|
+
},
|
|
141
|
+
})
|
|
142
|
+
|
|
143
|
+
const results = await axe(wrapper.element as HTMLElement)
|
|
144
|
+
assertNoA11yViolations(results, 'SyAutocomplete – selectionText with selection', {
|
|
145
|
+
ignoreRules: ['region'],
|
|
146
|
+
})
|
|
147
|
+
})
|
|
148
|
+
|
|
149
|
+
it('has no obvious axe violations when hideDetails is true', async () => {
|
|
150
|
+
const wrapper = mount(SyAutocomplete, {
|
|
151
|
+
props: {
|
|
152
|
+
modelValue: '1',
|
|
153
|
+
items,
|
|
154
|
+
label: 'Filtrer par option',
|
|
155
|
+
textKey: 'text',
|
|
156
|
+
valueKey: 'value',
|
|
157
|
+
hideDetails: true,
|
|
158
|
+
hasSuccess: true,
|
|
159
|
+
successMessages: ['Sélection valide'],
|
|
160
|
+
},
|
|
161
|
+
})
|
|
162
|
+
|
|
163
|
+
const results = await axe(wrapper.element as HTMLElement)
|
|
164
|
+
assertNoA11yViolations(results, 'SyAutocomplete – hideDetails', {
|
|
165
|
+
ignoreRules: ['region'],
|
|
166
|
+
})
|
|
167
|
+
})
|
|
72
168
|
})
|