@cnamts/synapse 1.0.26 → 1.0.27
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-BPR-a55G.js → AutocompleteFilter-C9eLKyW8.js} +3 -3
- package/dist/{DateFilter-CknrJWs2.js → DateFilter-y-GLkAkn.js} +8 -8
- package/dist/{NumberFilter-DJ-yNlzv.js → NumberFilter-DN6hIBS7.js} +1 -1
- package/dist/{PeriodFilter-CiB5Oa9Z.js → PeriodFilter-MoUUp9qS.js} +1 -1
- package/dist/{SelectFilter-EiafX97M.js → SelectFilter-bCbrdLmu.js} +1 -1
- package/dist/{TextFilter-BzOmpdxj.js → TextFilter-CvjgEaoM.js} +4 -4
- package/dist/apLightTheme2026-ug4Y23ns.js +611 -0
- package/dist/components/Customs/Selects/SyAutocomplete/SyAutocomplete.d.ts +2369 -353
- package/dist/components/Customs/Selects/SyAutocomplete/composables/useSyAutocompleteValidation.d.ts +18 -0
- package/dist/components/Customs/Selects/SyAutocomplete/utils/ariaManager.d.ts +1 -1
- package/dist/components/Customs/Selects/SyAutocomplete/utils/useKeyboardHandler.d.ts +3 -1
- package/dist/components/Customs/Selects/SySelect/SySelect.d.ts +9 -10
- package/dist/components/Customs/Selects/SySelect/composables/useSySelectKeyboard.d.ts +1 -0
- package/dist/components/Customs/Selects/SySelect/composables/useSySelectValidation.d.ts +15 -0
- package/dist/components/Customs/SyCheckBoxGroup/SyCheckBoxGroup.d.ts +3 -3
- package/dist/components/Customs/SyCheckbox/SyCheckbox.d.ts +3 -3
- package/dist/components/Customs/SyIconButton/SyIconButton.d.ts +18 -0
- package/dist/components/Customs/SyRadioGroup/SyRadioGroup.d.ts +20 -38
- package/dist/components/Customs/SyRadioGroup/composables/useSyRadioGroupValidation.d.ts +50 -0
- package/dist/components/Customs/SyTextField/SyTextField.d.ts +6 -6
- package/dist/components/DatePicker/CalendarMode/DatePicker.d.ts +147 -136
- package/dist/components/DatePicker/ComplexDatePicker/ComplexDatePicker.d.ts +62 -54
- package/dist/components/DatePicker/DateTextInput/DateTextInput.d.ts +27 -24
- package/dist/components/DatePicker/composables/index.d.ts +1 -0
- package/dist/components/DatePicker/composables/useDatePickerValidationBridge.d.ts +51 -0
- package/dist/components/MonthPicker/MonthPicker.d.ts +23 -23
- package/dist/components/MonthPicker/MonthPickerText/MonthPickerInput.d.ts +23 -23
- package/dist/components/NirField/NirField.d.ts +56 -56
- package/dist/components/PasswordField/PasswordField.d.ts +3 -3
- package/dist/components/PeriodField/PeriodField.d.ts +236 -212
- package/dist/components/PhoneField/PhoneField.d.ts +23 -23
- package/dist/components/SyTextArea/SyTextArea.d.ts +25 -15
- package/dist/components/SyTextArea/composables/useSyTextAreaValidation.d.ts +20 -0
- package/dist/components/SyTextArea/locales.d.ts +1 -0
- package/dist/components/Tables/SyServerTable/SyServerTable.d.ts +1 -0
- package/dist/components/Tables/SyTable/SyTable.d.ts +1 -0
- package/dist/components/Tables/common/SyTablePagination.d.ts +25 -25
- package/dist/components/Tables/common/types.d.ts +2 -0
- package/dist/components/index.d.ts +1 -0
- package/dist/composables/unifyValidation/documentationValidationProps.d.ts +160 -160
- package/dist/composables/unifyValidation/useValidation.d.ts +16 -14
- package/dist/design-system-v3.js +81 -80
- package/dist/designTokens/tokens/amelipro/apContextual.d.ts +6 -6
- package/dist/designTokens/tokens/amelipro/apDarkTheme.d.ts +3 -1
- package/dist/designTokens/tokens/amelipro/apLightTheme.d.ts +53 -100
- package/dist/designTokens/tokens/baseContextualTokens.d.ts +0 -6
- package/dist/designTokens/tokens/baseTokens.d.ts +232 -0
- package/dist/designTokens/tokens/cnam/cnamContextual.d.ts +6 -6
- package/dist/designTokens/tokens/cnam/cnamDarkTheme.d.ts +1 -1
- package/dist/designTokens/tokens/cnam/cnamLightTheme.d.ts +57 -101
- package/dist/designTokens/tokens/pa/paContextual.d.ts +0 -6
- package/dist/designTokens/tokens/pa/paDarkTheme.d.ts +1 -1
- package/dist/designTokens/tokens/pa/paLightTheme.d.ts +53 -97
- package/dist/designTokens/tokens/pa/paSemantic.d.ts +1 -0
- package/dist/designTokens/tokens/semanticTokens.d.ts +112 -0
- package/dist/main-CI6Q9nmO.js +39234 -0
- package/dist/synapse.css +1 -1
- package/dist/vuetifyConfig.js +208 -72
- package/package.json +4 -2
- package/src/assets/overrides/_icons.scss +5 -4
- package/src/assets/overrides/_otp.scss +4 -4
- package/src/assets/overrides/_typography.scss +2 -1
- package/src/assets/overrides/_utilities.scss +1 -42
- package/src/components/ChipList/ChipList.vue +30 -18
- package/src/components/ChipList/tests/chipList.spec.ts +4 -4
- package/src/components/CopyBtn/CopyBtn.vue +2 -2
- package/src/components/Customs/Selects/SelectBtnField/SelectBtnField.stories.ts +4 -0
- package/src/components/Customs/Selects/SelectBtnField/SelectBtnField.vue +7 -6
- package/src/components/Customs/Selects/SelectBtnField/tests/SelectBtnField.spec.ts +223 -0
- package/src/components/Customs/Selects/SyAutocomplete/SyAutocomplete.stories.ts +283 -351
- package/src/components/Customs/Selects/SyAutocomplete/SyAutocomplete.vue +182 -218
- package/src/components/Customs/Selects/SyAutocomplete/composables/useSyAutocompleteValidation.ts +101 -0
- package/src/components/Customs/Selects/SyAutocomplete/tests/SyAutocomplete.spec.ts +761 -1
- package/src/components/Customs/Selects/SyAutocomplete/utils/ariaManager.ts +3 -1
- package/src/components/Customs/Selects/SyAutocomplete/utils/useKeyboardHandler.ts +79 -5
- package/src/components/Customs/Selects/SyAutocomplete/validation/Validation.stories.ts +1029 -0
- package/src/components/Customs/Selects/SySelect/SySelect.stories.ts +9 -491
- package/src/components/Customs/Selects/SySelect/SySelect.vue +46 -79
- package/src/components/Customs/Selects/SySelect/composables/useSySelectKeyboard.ts +3 -0
- package/src/components/Customs/Selects/SySelect/composables/useSySelectValidation.ts +64 -0
- package/src/components/Customs/Selects/SySelect/tests/SySelect.spec.ts +196 -0
- package/src/components/Customs/Selects/SySelect/validation/Validation.stories.ts +1026 -0
- package/src/components/Customs/SyCheckBoxGroup/SyCheckBoxGroup.stories.ts +18 -7
- package/src/components/Customs/SyCheckBoxGroup/SyCheckBoxGroup.vue +2 -2
- package/src/components/Customs/SyCheckbox/SyCheckbox.stories.ts +8 -8
- package/src/components/Customs/SyCheckbox/SyCheckbox.vue +8 -8
- package/src/components/Customs/SyCheckbox/tests/SyCheckbox.spec.ts +1 -1
- package/src/components/Customs/SyIcon/accessibilite/Accessibility.mdx +0 -6
- package/src/components/Customs/SyIcon/utils/tests/iconUtils.spec.ts +107 -0
- package/src/components/Customs/SyRadioGroup/SyRadioGroup.mdx +2 -2
- package/src/components/Customs/SyRadioGroup/SyRadioGroup.stories.ts +395 -200
- package/src/components/Customs/SyRadioGroup/SyRadioGroup.vue +82 -127
- package/src/components/Customs/SyRadioGroup/composables/useSyRadioGroupValidation.ts +127 -0
- package/src/components/Customs/SyRadioGroup/tests/SyRadioGroup.a11y.spec.ts +93 -1
- package/src/components/Customs/SyRadioGroup/tests/SyRadioGroup.spec.ts +146 -9
- package/src/components/Customs/SyRadioGroup/tests/SyRadioGroup.visual.cy.ts +165 -0
- package/src/components/Customs/SyRadioGroup/validation/Validation.stories.ts +773 -0
- package/src/components/Customs/SyTabs/config.ts +3 -3
- package/src/components/Customs/SyTabs/tests/SyTabs.spec.ts +265 -0
- package/src/components/Customs/SyTabs/tests/useTabTransition.spec.ts +188 -0
- package/src/components/Customs/SyTextField/SyTextField.stories.ts +10 -29
- package/src/components/Customs/SyTextField/SyTextField.vue +23 -15
- package/src/components/DataList/DataList.stories.ts +1 -1
- package/src/components/DataListItem/tests/DataListItem.spec.ts +3 -1
- package/src/components/DatePicker/CalendarMode/DatePicker.vue +37 -142
- package/src/components/DatePicker/CalendarMode/tests/DatePicker.coverage.spec.ts +156 -0
- package/src/components/DatePicker/CalendarMode/tests/DatePicker.spec.ts +495 -4
- package/src/components/DatePicker/ComplexDatePicker/ComplexDatePicker.vue +47 -66
- package/src/components/DatePicker/ComplexDatePicker/tests/ComplexDatePicker.spec.ts +206 -0
- package/src/components/DatePicker/ComplexDatePicker/tests/bridge-integration.regression.spec.ts +210 -0
- package/src/components/DatePicker/ComplexDatePicker/tests/calendar-navigation.regression.spec.ts +214 -0
- package/src/components/DatePicker/ComplexDatePicker/tests/validation-cross.regression.spec.ts +194 -0
- package/src/components/DatePicker/ComplexDatePicker/tests/validation-success-messages.regression.spec.ts +83 -0
- package/src/components/DatePicker/DateTextInput/DateTextInput.vue +129 -54
- package/src/components/DatePicker/DateTextInput/tests/DateTextInput.spec.ts +320 -0
- package/src/components/DatePicker/composables/index.ts +1 -0
- package/src/components/DatePicker/composables/tests/useCalendarKeyboardNavigation.spec.ts +360 -0
- package/src/components/DatePicker/composables/tests/useDatePickerValidationBridge.spec.ts +129 -0
- package/src/components/DatePicker/composables/useDatePickerValidationBridge.ts +205 -0
- package/src/components/DatePicker/docExamples/BidirectionalComplexValidation.vue +1 -1
- package/src/components/DatePicker/docExamples/DatePickerBidirectionalValidation.vue +1 -1
- package/src/components/DatePicker/tests/exposed-methods.coverage.spec.ts +75 -0
- package/src/components/DialogBox/DialogBox.vue +1 -1
- package/src/components/FileList/UploadItem/UploadItem.vue +4 -4
- package/src/components/FileUpload/FileUpload.vue +2 -2
- package/src/components/FileUpload/FileUploadContent.vue +1 -1
- package/src/components/FilterInline/FilterInline.mdx +2 -2
- package/src/components/FilterSideBar/FilterSideBar.stories.ts +1 -1
- package/src/components/FilterSideBar/FilterSideBar.vue +2 -2
- package/src/components/FooterBar/FooterBar.vue +7 -7
- package/src/components/FranceConnectBtn/FranceConnectBtn.vue +1 -1
- package/src/components/HeaderBar/HeaderBurgerMenu/HeaderMenuItem/HeaderMenuItem.vue +2 -2
- package/src/components/HeaderBar/HeaderBurgerMenu/HeaderSubMenu/HeaderSubMenu.vue +7 -7
- package/src/components/HeaderBar/HeaderMenuBtn/HeaderMenuBtn.vue +2 -2
- package/src/components/HeaderLoading/tests/HeaderLoading.spec.ts +87 -8
- package/src/components/HeaderNavigationBar/HorizontalNavbar/HorizontalNavbar.vue +3 -3
- package/src/components/HeaderNavigationBar/HorizontalNavbar/tests/HorizontalNavbar.spec.ts +589 -0
- package/src/components/HeaderToolbar/tests/HeaderToolBar.spec.ts +153 -1
- package/src/components/HeaderToolbar/tests/useMobileRightMenu.spec.ts +258 -0
- package/src/components/LogoBrandSection/tests/LogoBrandSection.spec.ts +2 -2
- package/src/components/LogoBrandSection/tests/__snapshots__/LogoBrandSection.spec.ts.snap +1 -1
- package/src/components/LunarCalendar/tests/useLunarCalendarRules.spec.ts +184 -0
- package/src/components/MonthPicker/MonthPickerVisual/MonthSelector.vue +3 -3
- package/src/components/MonthPicker/MonthPickerVisual/VisualPickerFooter.vue +1 -1
- package/src/components/MonthPicker/MonthPickerVisual/VisualPickerHeader.vue +2 -2
- package/src/components/MonthPicker/MonthPickerVisual/YearSelector.vue +1 -1
- package/src/components/NirField/NirField.vue +3 -3
- package/src/components/NotificationBar/Notification/Notification.vue +12 -12
- package/src/components/NotificationBar/NotificationBar.stories.ts +8 -8
- package/src/components/PaginatedTable/Pagination.vue +2 -2
- package/src/components/PasswordField/PasswordField.vue +8 -8
- package/src/components/PasswordField/tests/PasswordField.spec.ts +3 -3
- package/src/components/RangeField/RangeSlider/RangeSlider.vue +2 -2
- package/src/components/RangeField/RangeSlider/Tooltip/Tooltip.vue +1 -1
- package/src/components/StatusPage/tests/StatusPage.spec.ts +149 -0
- package/src/components/SubHeader/SubHeader.vue +1 -1
- package/src/components/SyAlert/SyAlert.vue +23 -23
- package/src/components/SyTextArea/SyTextArea.stories.ts +177 -131
- package/src/components/SyTextArea/SyTextArea.vue +235 -83
- package/src/components/SyTextArea/composables/useSyTextAreaValidation.ts +81 -0
- package/src/components/SyTextArea/locales.ts +1 -0
- package/src/components/SyTextArea/tests/SyTextArea.spec.ts +449 -1
- package/src/components/SyTextArea/useDefaultValidationRules.ts +2 -7
- package/src/components/SyTextArea/validation/Validation.stories.ts +856 -0
- package/src/components/TableToolbar/TableToolbar.vue +6 -6
- package/src/components/TableToolbar/accessibilite/Accessibility.mdx +81 -7
- package/src/components/Tables/SyServerTable/SyServerTable.stories.ts +163 -0
- package/src/components/Tables/SyServerTable/SyServerTable.vue +2 -1
- package/src/components/Tables/SyServerTable/tests/SyServerTable.spec.ts +67 -0
- package/src/components/Tables/SyTable/SyTable.stories.ts +94 -0
- package/src/components/Tables/SyTable/SyTable.vue +2 -1
- package/src/components/Tables/SyTable/tests/SyTable.spec.ts +64 -0
- package/src/components/Tables/common/TableHeader.vue +2 -2
- package/src/components/Tables/common/filters/logics/tests/NumberFilterLogic.spec.ts +176 -0
- package/src/components/Tables/common/filters/logics/tests/SelectFilterLogic.spec.ts +111 -0
- package/src/components/Tables/common/tableStyles.scss +6 -6
- package/src/components/Tables/common/types.ts +2 -0
- package/src/components/UploadWorkflow/tests/UploadWorkflow.spec.ts +2 -0
- package/src/components/index.ts +1 -0
- package/src/composables/date/tests/useDateFormatDayjs.spec.ts +31 -0
- package/src/composables/date/tests/useHolidayDay.spec.ts +109 -0
- package/src/composables/rules/tests/useFieldValidation.spec.ts +374 -0
- package/src/composables/tests/useError.spec.ts +30 -0
- package/src/composables/tests/useFormFieldErrorHandling.spec.ts +234 -0
- package/src/composables/unifyValidation/documentationValidationProps.ts +5 -5
- package/src/composables/unifyValidation/tests/documentationValidationProps.spec.ts +177 -0
- package/src/composables/unifyValidation/tests/useCustomValidation.spec.ts +30 -0
- package/src/composables/unifyValidation/tests/useValidation.spec.ts +6 -2
- package/src/composables/unifyValidation/useCustomValidation.ts +19 -9
- package/src/composables/unifyValidation/useValidation.ts +18 -21
- package/src/composables/useFilterable/useFilterable.spec.ts +42 -0
- package/src/composables/useFilterable/useFilterable.ts +11 -7
- package/src/composables/useFormFieldErrorHandling.ts +2 -2
- package/src/composantsVuetify/VBtn/VBtn.mdx +9 -39
- package/src/composantsVuetify/VBtn/v-btn.stories.ts +26 -86
- package/src/designTokens/tokens/amelipro/apContextual.ts +6 -0
- package/src/designTokens/tokens/amelipro/apDarkTheme.ts +2 -2
- package/src/designTokens/tokens/amelipro/apLightTheme.ts +72 -103
- package/src/designTokens/tokens/amelipro/apSemantic.ts +1 -1
- package/src/designTokens/tokens/baseContextualTokens.ts +1 -6
- package/src/designTokens/tokens/baseTokens.ts +232 -0
- package/src/designTokens/tokens/cnam/cnamContextual.ts +6 -0
- package/src/designTokens/tokens/cnam/cnamDarkTheme.ts +2 -2
- package/src/designTokens/tokens/cnam/cnamLightTheme.ts +76 -104
- package/src/designTokens/tokens/pa/paDarkTheme.ts +2 -2
- package/src/designTokens/tokens/pa/paLightTheme.ts +73 -99
- package/src/designTokens/tokens/pa/paSemantic.ts +2 -0
- package/src/designTokens/tokens/semanticTokens.ts +114 -0
- package/src/stories/Components/Components.stories.ts +7 -3
- package/src/stories/DesignTokens/ColorIntegrationExample.vue +2 -3
- package/src/stories/DesignTokens/Colors.mdx +6 -8
- package/src/stories/DesignTokens/colors.stories.ts +244 -1081
- package/src/utils/amelipro/toKebabCase/tests/toKebabCase.spec.ts +52 -0
- package/src/utils/formatNir/tests/formatNir.spec.ts +34 -0
- package/src/utils/tests/insertAt.spec.ts +44 -0
- package/dist/apLightTheme-DS0Uy44H.js +0 -954
- package/dist/components/RatingPicker/tests/RatingPicker.a11y.spect.d.ts +0 -1
- package/dist/main-BsJ9ec3i.js +0 -38954
- package/src/components/BackBtn/tests/__snapshots__/back-btn-custom-bg.snap.png +0 -0
- package/src/components/BackBtn/tests/__snapshots__/back-btn-dark-mode.snap.png +0 -0
- package/src/components/BackBtn/tests/__snapshots__/back-btn-default.snap.png +0 -0
- package/src/components/BackBtn/tests/__snapshots__/back-btn-no-icon.snap.png +0 -0
- package/src/components/DatePicker/CalendarMode/tests/DatePicker.events.spec.ts +0 -178
- package/src/components/DialogBox/tests/__snapshots__/dialog-box-custom-texts.snap.png +0 -0
- package/src/components/DialogBox/tests/__snapshots__/dialog-box-default.snap.png +0 -0
- package/src/components/DialogBox/tests/__snapshots__/dialog-box-no-actions.snap.png +0 -0
- package/src/components/HeaderBar/HeaderBurgerMenu/tests/__snapshots__/header-burger-menu-generated-submenu-open.snap.png +0 -0
- package/src/components/HeaderBar/HeaderBurgerMenu/tests/__snapshots__/header-burger-menu-generated.snap.png +0 -0
- package/src/components/HeaderBar/tests/__snapshots__/header-bar-custom-width.snap.png +0 -0
- package/src/components/HeaderBar/tests/__snapshots__/header-bar-default.snap.png +0 -0
- package/src/components/HeaderBar/tests/__snapshots__/header-bar-no-sticky.snap.png +0 -0
- package/src/components/HeaderBar/tests/__snapshots__/header-bar-with-prepend.snap.png +0 -0
- package/src/components/HeaderBar/tests/__snapshots__/header-bar-with-side.snap.png +0 -0
- package/src/components/HeaderBar/tests/__snapshots__/header-bar-with-subtitle.snap.png +0 -0
- package/src/components/Logo/tests/__snapshots__/logo-avatar.snap.png +0 -0
- package/src/components/Logo/tests/__snapshots__/logo-dark.snap.png +0 -0
- package/src/components/Logo/tests/__snapshots__/logo-default.snap.png +0 -0
- package/src/components/Logo/tests/__snapshots__/logo-no-organism.snap.png +0 -0
- package/src/components/Logo/tests/__snapshots__/logo-no-signature.snap.png +0 -0
- package/src/components/Logo/tests/__snapshots__/logo-risque-pro.snap.png +0 -0
- package/src/components/RangeField/tests/__snapshots__/range-field-custom-bg.snap.png +0 -0
- package/src/components/RangeField/tests/__snapshots__/range-field-custom-range.snap.png +0 -0
- package/src/components/RangeField/tests/__snapshots__/range-field-default.snap.png +0 -0
- package/src/components/RangeField/tests/__snapshots__/range-field-step.snap.png +0 -0
- package/src/components/RangeField/tests/__snapshots__/range-field-with-label.snap.png +0 -0
- package/src/components/SyAlert/tests/__snapshots__/sy-alert-closable.snap.png +0 -0
- package/src/components/SyAlert/tests/__snapshots__/sy-alert-error.snap.png +0 -0
- package/src/components/SyAlert/tests/__snapshots__/sy-alert-info.snap.png +0 -0
- package/src/components/SyAlert/tests/__snapshots__/sy-alert-success.snap.png +0 -0
- package/src/components/SyAlert/tests/__snapshots__/sy-alert-variant-outlined.snap.png +0 -0
- package/src/components/SyAlert/tests/__snapshots__/sy-alert-variant-tonal.snap.png +0 -0
- package/src/components/SyAlert/tests/__snapshots__/sy-alert-warning.snap.png +0 -0
- /package/src/components/RatingPicker/tests/{RatingPicker.a11y.spect.ts → RatingPicker.a11y.spec.ts} +0 -0
|
@@ -7,12 +7,13 @@
|
|
|
7
7
|
useDateInputEditing,
|
|
8
8
|
useDateAutoClamp,
|
|
9
9
|
useDateTextField,
|
|
10
|
+
useDatePickerValidationBridge,
|
|
10
11
|
} from '../composables'
|
|
11
12
|
import { ref, computed, watch, nextTick, onMounted, toRefs } from 'vue'
|
|
12
13
|
import SyTextField from '../../Customs/SyTextField/SyTextField.vue'
|
|
13
14
|
import dayjs from 'dayjs'
|
|
14
15
|
import customParseFormat from 'dayjs/plugin/customParseFormat'
|
|
15
|
-
import {
|
|
16
|
+
import type { ValidationRule, ValidationResult } from '@/composables/validation/useValidation'
|
|
16
17
|
import { useValidatable } from '@/composables/validation/useValidatable'
|
|
17
18
|
import { useDateFormat } from '@/composables/date/useDateFormatDayjs'
|
|
18
19
|
import { DATE_PICKER_MESSAGES } from '../constants/messages'
|
|
@@ -48,8 +49,10 @@
|
|
|
48
49
|
required?: boolean
|
|
49
50
|
showSuccessMessages?: boolean
|
|
50
51
|
title?: string | false
|
|
52
|
+
/** @internal Désactive la validation interne quand utilisé dans un parent avec validation */
|
|
53
|
+
skipInternalValidation?: boolean
|
|
51
54
|
}>(), {
|
|
52
|
-
autoClamp:
|
|
55
|
+
autoClamp: false,
|
|
53
56
|
bgColor: 'white',
|
|
54
57
|
customRules: () => [],
|
|
55
58
|
customWarningRules: () => [],
|
|
@@ -74,6 +77,7 @@
|
|
|
74
77
|
required: false,
|
|
75
78
|
showSuccessMessages: true,
|
|
76
79
|
title: false,
|
|
80
|
+
skipInternalValidation: false,
|
|
77
81
|
})
|
|
78
82
|
|
|
79
83
|
const emit = defineEmits<{
|
|
@@ -98,13 +102,35 @@
|
|
|
98
102
|
|
|
99
103
|
/**
|
|
100
104
|
* =====================
|
|
101
|
-
* Validation setup (
|
|
105
|
+
* Validation setup (using DatePickerValidationBridge)
|
|
102
106
|
* =====================
|
|
103
107
|
*/
|
|
104
|
-
const
|
|
108
|
+
const selectedDates = ref<DateObjectValue>(null)
|
|
109
|
+
const currentRangeIsValid = ref(true)
|
|
110
|
+
const getRangeValidationError = ref('')
|
|
111
|
+
const isUpdatingFromInternal = ref(false)
|
|
112
|
+
|
|
113
|
+
// Quand skipInternalValidation est true, on utilise le readonlyValidation (pas de validation active)
|
|
114
|
+
// pour éviter la double validation avec le parent
|
|
115
|
+
const shouldUseInternalValidation = computed(() => !props.skipInternalValidation && !readonly.value)
|
|
116
|
+
|
|
117
|
+
const bridgeValidation = useDatePickerValidationBridge({
|
|
105
118
|
showSuccessMessages: props.showSuccessMessages,
|
|
106
|
-
fieldIdentifier: props.label || props.placeholder,
|
|
107
119
|
disableErrorHandling: props.disableErrorHandling,
|
|
120
|
+
noCalendar: true,
|
|
121
|
+
required: props.required,
|
|
122
|
+
displayRange: props.displayRange,
|
|
123
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Compatibility with legacy rule format
|
|
124
|
+
customRules: computed(() => shouldUseInternalValidation.value ? props.customRules as any : []),
|
|
125
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Compatibility with legacy rule format
|
|
126
|
+
customWarningRules: computed(() => shouldUseInternalValidation.value ? props.customWarningRules as any : []),
|
|
127
|
+
selectedDates,
|
|
128
|
+
isUpdatingFromInternal,
|
|
129
|
+
currentRangeIsValid,
|
|
130
|
+
getRangeValidationError,
|
|
131
|
+
skipValidationWhenReadonly: true,
|
|
132
|
+
readonly: readonly,
|
|
133
|
+
fieldIdentifier: props.label || props.placeholder || 'Date',
|
|
108
134
|
})
|
|
109
135
|
|
|
110
136
|
const readonlyValidation = {
|
|
@@ -122,39 +148,56 @@
|
|
|
122
148
|
} as ValidationResult),
|
|
123
149
|
}
|
|
124
150
|
|
|
125
|
-
const validationApi = computed(() => (readonly.value ? readonlyValidation : baseValidation))
|
|
126
|
-
|
|
127
151
|
const errors = computed({
|
|
128
|
-
get: () =>
|
|
129
|
-
set: (value) => {
|
|
152
|
+
get: () => readonly.value ? readonlyValidation.errors.value : bridgeValidation.errors.value,
|
|
153
|
+
set: (value) => {
|
|
154
|
+
if (!readonly.value) bridgeValidation.errors.value = value
|
|
155
|
+
},
|
|
130
156
|
})
|
|
131
157
|
const warnings = computed({
|
|
132
|
-
get: () =>
|
|
133
|
-
set: (value) => {
|
|
158
|
+
get: () => readonly.value ? readonlyValidation.warnings.value : bridgeValidation.warnings.value,
|
|
159
|
+
set: (value) => {
|
|
160
|
+
if (!readonly.value) bridgeValidation.warnings.value = value
|
|
161
|
+
},
|
|
134
162
|
})
|
|
135
163
|
const successes = computed({
|
|
136
|
-
get: () =>
|
|
137
|
-
set: (value) => {
|
|
164
|
+
get: () => readonly.value ? readonlyValidation.successes.value : bridgeValidation.successes.value,
|
|
165
|
+
set: (value) => {
|
|
166
|
+
if (!readonly.value) bridgeValidation.successes.value = value
|
|
167
|
+
},
|
|
138
168
|
})
|
|
139
169
|
const hasError = computed(() => {
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- computed/ref dual shape
|
|
143
|
-
return (api as any).hasError?.value ?? api.errors.value.length > 0
|
|
170
|
+
if (readonly.value) return false
|
|
171
|
+
return bridgeValidation.errors.value.length > 0
|
|
144
172
|
})
|
|
145
173
|
|
|
146
|
-
const clearValidation = () =>
|
|
174
|
+
const clearValidation = () => {
|
|
175
|
+
if (readonly.value) {
|
|
176
|
+
readonlyValidation.clearValidation()
|
|
177
|
+
}
|
|
178
|
+
else {
|
|
179
|
+
bridgeValidation.clearValidation()
|
|
180
|
+
}
|
|
181
|
+
}
|
|
147
182
|
|
|
148
183
|
const validateField = async (
|
|
149
184
|
value: unknown,
|
|
150
185
|
rules?: ValidationRule[],
|
|
151
186
|
warningRules?: ValidationRule[],
|
|
152
|
-
): Promise<ValidationResult> =>
|
|
187
|
+
): Promise<ValidationResult> => {
|
|
188
|
+
if (readonly.value) {
|
|
189
|
+
return readonlyValidation.validateField()
|
|
190
|
+
}
|
|
191
|
+
return await bridgeValidation.validateField(value, rules, warningRules)
|
|
192
|
+
}
|
|
153
193
|
|
|
154
194
|
// Agrégation des erreurs internes et externes
|
|
155
195
|
const errorMessages = computed(() => [...errors.value, ...props.externalErrorMessages])
|
|
156
196
|
const warningMessages = warnings
|
|
157
|
-
const displaySuccesses = computed(() =>
|
|
197
|
+
const displaySuccesses = computed(() => {
|
|
198
|
+
if (readonly.value) return []
|
|
199
|
+
return (bridgeValidation.validation as { displaySuccesses?: { value: string[] } }).displaySuccesses?.value ?? []
|
|
200
|
+
})
|
|
158
201
|
const successMessages = displaySuccesses
|
|
159
202
|
|
|
160
203
|
/**
|
|
@@ -176,7 +219,6 @@
|
|
|
176
219
|
* Range input + validations
|
|
177
220
|
* =====================
|
|
178
221
|
*/
|
|
179
|
-
const selectedDates = ref<DateObjectValue>(null)
|
|
180
222
|
const {
|
|
181
223
|
handleRangeInput,
|
|
182
224
|
resetState,
|
|
@@ -187,14 +229,22 @@
|
|
|
187
229
|
handlePaste: handlePasteRange,
|
|
188
230
|
} = useDateRangeInput(displayFormat.value, isRange.value, parseDate, formatDate)
|
|
189
231
|
|
|
190
|
-
|
|
232
|
+
// Note: currentRangeIsValid et getRangeValidationError sont déjà définis pour le Bridge
|
|
233
|
+
// On met juste à jour les refs depuis useDateRangeValidation
|
|
234
|
+
const rangeValidation = useDateRangeValidation(selectedDates, isRange.value)
|
|
235
|
+
watch(() => rangeValidation.currentRangeIsValid.value, (v) => {
|
|
236
|
+
currentRangeIsValid.value = v
|
|
237
|
+
})
|
|
238
|
+
watch(() => rangeValidation.getRangeValidationError.value, (v) => {
|
|
239
|
+
getRangeValidationError.value = v
|
|
240
|
+
})
|
|
191
241
|
|
|
192
242
|
/**
|
|
193
243
|
* =====================
|
|
194
244
|
* Format + manual validation
|
|
195
245
|
* =====================
|
|
196
246
|
*/
|
|
197
|
-
|
|
247
|
+
// isUpdatingFromInternal est déjà déclaré plus haut pour le Bridge
|
|
198
248
|
const isFocused = ref(false)
|
|
199
249
|
const hasInteracted = ref(false)
|
|
200
250
|
const ariaLabel = ref(props.label || props.placeholder || DATE_PICKER_MESSAGES.LABEL_DEFAULT)
|
|
@@ -523,6 +573,19 @@
|
|
|
523
573
|
const nextCursorPosition = nextEditableIndex(dateFormat, editPosition + 1)
|
|
524
574
|
updateDateValue(updatedDateText, nextCursorPosition)
|
|
525
575
|
}
|
|
576
|
+
else if (isEditingLeftDate && separatorIndex !== -1) {
|
|
577
|
+
const rightStartPosition = nextEditableIndex(dateFormat, 0)
|
|
578
|
+
const updatedRightDateText = overwriteAt(rightDateText, rightStartPosition, keyboardEvent.key)
|
|
579
|
+
const nextCursorPosition = nextEditableIndex(dateFormat, rightStartPosition + 1)
|
|
580
|
+
|
|
581
|
+
isOverwriteEditing.value = true
|
|
582
|
+
inputValue.value = `${leftDateText}${rangeSeparator}${updatedRightDateText}`
|
|
583
|
+
requestAnimationFrame(() => {
|
|
584
|
+
const absoluteCursorPosition = separatorIndex + rangeSeparator.length + nextCursorPosition
|
|
585
|
+
inputElement.setSelectionRange(absoluteCursorPosition, absoluteCursorPosition)
|
|
586
|
+
isOverwriteEditing.value = false
|
|
587
|
+
})
|
|
588
|
+
}
|
|
526
589
|
return
|
|
527
590
|
}
|
|
528
591
|
|
|
@@ -659,6 +722,30 @@
|
|
|
659
722
|
else handlePasteSingle(evt)
|
|
660
723
|
}
|
|
661
724
|
|
|
725
|
+
function applyAutoClampOnCurrentInput(syncModel = true): boolean {
|
|
726
|
+
if (!props.autoClamp || !inputValue.value) return false
|
|
727
|
+
|
|
728
|
+
const clamped = clampIfNeeded(inputValue.value)
|
|
729
|
+
if (clamped === inputValue.value) return false
|
|
730
|
+
|
|
731
|
+
inputValue.value = clamped
|
|
732
|
+
if (!syncModel) return true
|
|
733
|
+
|
|
734
|
+
// Sync model après clamp uniquement si la valeur a changé.
|
|
735
|
+
isFormatting.value = true
|
|
736
|
+
if (isRange.value) {
|
|
737
|
+
const [startDate, endDate] = parseRangeInput(inputValue.value)
|
|
738
|
+
if (startDate && endDate) emitModel([toReturnFormat(startDate), toReturnFormat(endDate)])
|
|
739
|
+
else if (startDate) emit('date-selected', toReturnFormat(startDate))
|
|
740
|
+
}
|
|
741
|
+
else {
|
|
742
|
+
const parsedDate = parseDate(inputValue.value, displayFormat.value)
|
|
743
|
+
if (parsedDate) emitModel(returnFormat.value !== displayFormat.value ? toReturnFormat(parsedDate) : formatDate(parsedDate, displayFormat.value))
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
return true
|
|
747
|
+
}
|
|
748
|
+
|
|
662
749
|
async function onFocus() {
|
|
663
750
|
isFocused.value = true
|
|
664
751
|
// Si aucun chiffre n'a été saisi (champ vide ou squelette), bootstrap et place le caret au début
|
|
@@ -684,6 +771,14 @@
|
|
|
684
771
|
return
|
|
685
772
|
}
|
|
686
773
|
|
|
774
|
+
// Le mode overwrite désactive le clamp pendant la frappe pour préserver le curseur.
|
|
775
|
+
// On l'applique donc avant la validation au blur, sinon une date comme 31/04
|
|
776
|
+
// sort en erreur avant d'atteindre la logique d'autoClamp.
|
|
777
|
+
// isFormatting bloque le watcher inputValue pour éviter une double émission du modèle.
|
|
778
|
+
isFormatting.value = true
|
|
779
|
+
applyAutoClampOnCurrentInput(false)
|
|
780
|
+
isFormatting.value = false
|
|
781
|
+
|
|
687
782
|
if (inputValue.value) {
|
|
688
783
|
const formatValidationResult = validateDateFormatForSingleOrRange(inputValue.value)
|
|
689
784
|
const customRulesValidationResult = await safeValidateField(inputValue.value, computed(() => props.customRules).value, computed(() => props.customWarningRules).value)
|
|
@@ -729,26 +824,6 @@
|
|
|
729
824
|
}
|
|
730
825
|
}
|
|
731
826
|
|
|
732
|
-
// autoClamp au blur
|
|
733
|
-
if (props.autoClamp) {
|
|
734
|
-
const clamped = clampIfNeeded(inputValue.value)
|
|
735
|
-
if (clamped !== inputValue.value) {
|
|
736
|
-
inputValue.value = clamped
|
|
737
|
-
|
|
738
|
-
// Sync model après clamp uniquement si la valeur a changé
|
|
739
|
-
isFormatting.value = true
|
|
740
|
-
if (isRange.value) {
|
|
741
|
-
const [startDate, endDate] = parseRangeInput(inputValue.value)
|
|
742
|
-
if (startDate && endDate) emitModel([toReturnFormat(startDate), toReturnFormat(endDate)])
|
|
743
|
-
else if (startDate) emit('date-selected', toReturnFormat(startDate))
|
|
744
|
-
}
|
|
745
|
-
else {
|
|
746
|
-
const parsedDate = parseDate(inputValue.value, displayFormat.value)
|
|
747
|
-
if (parsedDate) emitModel(returnFormat.value !== displayFormat.value ? toReturnFormat(parsedDate) : formatDate(parsedDate, displayFormat.value))
|
|
748
|
-
}
|
|
749
|
-
}
|
|
750
|
-
}
|
|
751
|
-
|
|
752
827
|
runRules(inputValue.value)
|
|
753
828
|
|
|
754
829
|
// Release isFormatting after the current microtask so that
|
|
@@ -906,7 +981,7 @@
|
|
|
906
981
|
})).validateDates()
|
|
907
982
|
}
|
|
908
983
|
finally {
|
|
909
|
-
|
|
984
|
+
queueMicrotask(() => (isUpdatingFromInternal.value = false))
|
|
910
985
|
}
|
|
911
986
|
|
|
912
987
|
if (result.isComplete && result.dates[1]) {
|
|
@@ -924,7 +999,7 @@
|
|
|
924
999
|
|
|
925
1000
|
emit('input', result.formattedValue)
|
|
926
1001
|
if (result.cursorPosition !== undefined && !isHandlingBackspace.value) {
|
|
927
|
-
|
|
1002
|
+
queueMicrotask(() => inputEl?.setSelectionRange(result.cursorPosition!, result.cursorPosition!))
|
|
928
1003
|
}
|
|
929
1004
|
}
|
|
930
1005
|
else {
|
|
@@ -1024,7 +1099,7 @@
|
|
|
1024
1099
|
successes,
|
|
1025
1100
|
})).validateDates()
|
|
1026
1101
|
}
|
|
1027
|
-
finally {
|
|
1102
|
+
finally { queueMicrotask(() => (isUpdatingFromInternal.value = false)) }
|
|
1028
1103
|
inputValue.value = formatRangeForDisplay(sd, ed)
|
|
1029
1104
|
runRules(inputValue.value)
|
|
1030
1105
|
}
|
|
@@ -1154,10 +1229,10 @@
|
|
|
1154
1229
|
}
|
|
1155
1230
|
|
|
1156
1231
|
:deep(.v-field) {
|
|
1157
|
-
color: rgb(var(--v-theme-
|
|
1232
|
+
color: rgb(var(--v-theme-warning)) !important;
|
|
1158
1233
|
|
|
1159
1234
|
.v-field__outline {
|
|
1160
|
-
color: rgb(var(--v-theme-
|
|
1235
|
+
color: rgb(var(--v-theme-warning)) !important;
|
|
1161
1236
|
}
|
|
1162
1237
|
}
|
|
1163
1238
|
|
|
@@ -1165,7 +1240,7 @@
|
|
|
1165
1240
|
opacity: 1 !important;
|
|
1166
1241
|
|
|
1167
1242
|
.v-messages__message {
|
|
1168
|
-
color: rgb(var(--v-theme-
|
|
1243
|
+
color: rgb(var(--v-theme-warning)) !important;
|
|
1169
1244
|
}
|
|
1170
1245
|
}
|
|
1171
1246
|
}
|
|
@@ -1173,11 +1248,11 @@
|
|
|
1173
1248
|
.error-field {
|
|
1174
1249
|
:deep(.v-input__control),
|
|
1175
1250
|
:deep(.v-messages__message) {
|
|
1176
|
-
color: rgb(var(--v-theme-
|
|
1251
|
+
color: rgb(var(--v-theme-error)) !important;
|
|
1177
1252
|
}
|
|
1178
1253
|
|
|
1179
1254
|
.v-field--active & {
|
|
1180
|
-
color: rgb(var(--v-theme-
|
|
1255
|
+
color: rgb(var(--v-theme-error)) !important;
|
|
1181
1256
|
}
|
|
1182
1257
|
}
|
|
1183
1258
|
|
|
@@ -1189,10 +1264,10 @@
|
|
|
1189
1264
|
}
|
|
1190
1265
|
|
|
1191
1266
|
:deep(.v-field) {
|
|
1192
|
-
color: rgb(var(--v-theme-
|
|
1267
|
+
color: rgb(var(--v-theme-onSuccessVariant)) !important;
|
|
1193
1268
|
|
|
1194
1269
|
.v-field__outline {
|
|
1195
|
-
color: rgb(var(--v-theme-
|
|
1270
|
+
color: rgb(var(--v-theme-onSuccessVariant)) !important;
|
|
1196
1271
|
}
|
|
1197
1272
|
}
|
|
1198
1273
|
|
|
@@ -1200,7 +1275,7 @@
|
|
|
1200
1275
|
opacity: 1 !important;
|
|
1201
1276
|
|
|
1202
1277
|
.v-messages__message {
|
|
1203
|
-
color: rgb(var(--v-theme-
|
|
1278
|
+
color: rgb(var(--v-theme-onSuccessVariant)) !important;
|
|
1204
1279
|
}
|
|
1205
1280
|
}
|
|
1206
1281
|
}
|
|
@@ -8,6 +8,13 @@ describe('DateTextInput.clean', () => {
|
|
|
8
8
|
props: { label: 'Date', ...props },
|
|
9
9
|
})
|
|
10
10
|
|
|
11
|
+
const typeDigits = async (input: ReturnType<ReturnType<typeof mountComponent>['find']>, digits: string) => {
|
|
12
|
+
for (const digit of digits) {
|
|
13
|
+
await input.trigger('keydown', { key: digit })
|
|
14
|
+
await flushPromises()
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
11
18
|
it('renders a single-date text field by default', () => {
|
|
12
19
|
const wrapper = mountComponent({
|
|
13
20
|
label: 'Date',
|
|
@@ -32,6 +39,18 @@ describe('DateTextInput.clean', () => {
|
|
|
32
39
|
const emitted = wrapper.emitted('update:model-value')
|
|
33
40
|
expect(emitted).toBeTruthy()
|
|
34
41
|
expect(emitted && emitted[0]?.[0]).toBe('01/01/2025')
|
|
42
|
+
expect(wrapper.emitted('update:modelValue')).toBeFalsy()
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
it('forwards externalErrorMessages to the underlying text field', () => {
|
|
46
|
+
const wrapper = mountComponent({
|
|
47
|
+
label: 'Date',
|
|
48
|
+
format: 'DD/MM/YYYY',
|
|
49
|
+
externalErrorMessages: ['Erreur externe de contrat DateTextInput'],
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
const textField = wrapper.findComponent(SyTextField)
|
|
53
|
+
expect(textField.props('errorMessages')).toEqual(['Erreur externe de contrat DateTextInput'])
|
|
35
54
|
})
|
|
36
55
|
|
|
37
56
|
it('formats modelValue according to dateFormatReturn in single mode', async () => {
|
|
@@ -51,6 +70,103 @@ describe('DateTextInput.clean', () => {
|
|
|
51
70
|
expect(emitted && emitted[0]?.[0]).toBe('2025-01-01')
|
|
52
71
|
})
|
|
53
72
|
|
|
73
|
+
it('auto-clamps invalid day on blur in single mode', async () => {
|
|
74
|
+
const wrapper = mountComponent({
|
|
75
|
+
label: 'Date',
|
|
76
|
+
format: 'DD/MM/YYYY',
|
|
77
|
+
autoClamp: true,
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
const input = wrapper.find('input')
|
|
81
|
+
await input.setValue('31/04/2025')
|
|
82
|
+
await input.trigger('blur')
|
|
83
|
+
await flushPromises()
|
|
84
|
+
|
|
85
|
+
expect(input.element.value).toBe('30/04/2025')
|
|
86
|
+
const emitted = wrapper.emitted('update:model-value')
|
|
87
|
+
expect(emitted).toBeTruthy()
|
|
88
|
+
expect(emitted && emitted[emitted.length - 1]?.[0]).toBe('30/04/2025')
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
it('auto-clamps a masked keyboard input on blur in single mode', async () => {
|
|
92
|
+
const wrapper = mountComponent({
|
|
93
|
+
label: 'Date',
|
|
94
|
+
format: 'DD/MM/YYYY',
|
|
95
|
+
autoClamp: true,
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
const input = wrapper.find('input')
|
|
99
|
+
await typeDigits(input, '31042025')
|
|
100
|
+
await input.trigger('blur')
|
|
101
|
+
await flushPromises()
|
|
102
|
+
|
|
103
|
+
expect(input.element.value).toBe('30/04/2025')
|
|
104
|
+
const emitted = wrapper.emitted('update:model-value')
|
|
105
|
+
expect(emitted).toBeTruthy()
|
|
106
|
+
expect(emitted && emitted[emitted.length - 1]?.[0]).toBe('30/04/2025')
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
it.each([
|
|
110
|
+
['DD-MM-YYYY', '31042025', '30-04-2025'],
|
|
111
|
+
['YYYY.MM.DD', '20250431', '2025.04.30'],
|
|
112
|
+
])('auto-clamps masked keyboard input with %s format', async (format, digits, expected) => {
|
|
113
|
+
const wrapper = mountComponent({
|
|
114
|
+
label: 'Date',
|
|
115
|
+
format,
|
|
116
|
+
autoClamp: true,
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
const input = wrapper.find('input')
|
|
120
|
+
await typeDigits(input, digits)
|
|
121
|
+
await input.trigger('blur')
|
|
122
|
+
await flushPromises()
|
|
123
|
+
|
|
124
|
+
expect(input.element.value).toBe(expected)
|
|
125
|
+
const emitted = wrapper.emitted('update:model-value')
|
|
126
|
+
expect(emitted).toBeTruthy()
|
|
127
|
+
expect(emitted && emitted[emitted.length - 1]?.[0]).toBe(expected)
|
|
128
|
+
})
|
|
129
|
+
|
|
130
|
+
it('auto-clamps invalid days on blur in range mode', async () => {
|
|
131
|
+
const wrapper = mountComponent({
|
|
132
|
+
label: 'Plage de dates',
|
|
133
|
+
format: 'DD/MM/YYYY',
|
|
134
|
+
displayRange: true,
|
|
135
|
+
autoClamp: true,
|
|
136
|
+
})
|
|
137
|
+
|
|
138
|
+
const input = wrapper.find('input')
|
|
139
|
+
await input.setValue('31/04/2025 - 29/02/2025')
|
|
140
|
+
await input.trigger('blur')
|
|
141
|
+
await flushPromises()
|
|
142
|
+
|
|
143
|
+
expect(input.element.value).toBe('30/04/2025 - 28/02/2025')
|
|
144
|
+
const emitted = wrapper.emitted('update:model-value')
|
|
145
|
+
expect(emitted).toBeTruthy()
|
|
146
|
+
const last = emitted && emitted[emitted.length - 1]?.[0]
|
|
147
|
+
expect(last).toEqual(['30/04/2025', '28/02/2025'])
|
|
148
|
+
})
|
|
149
|
+
|
|
150
|
+
it('auto-clamps a masked keyboard input on blur in range mode', async () => {
|
|
151
|
+
const wrapper = mountComponent({
|
|
152
|
+
label: 'Plage de dates',
|
|
153
|
+
format: 'DD/MM/YYYY',
|
|
154
|
+
displayRange: true,
|
|
155
|
+
autoClamp: true,
|
|
156
|
+
})
|
|
157
|
+
|
|
158
|
+
const input = wrapper.find('input')
|
|
159
|
+
await typeDigits(input, '2902202531042025')
|
|
160
|
+
await input.trigger('blur')
|
|
161
|
+
await flushPromises()
|
|
162
|
+
|
|
163
|
+
expect(input.element.value).toBe('28/02/2025 - 30/04/2025')
|
|
164
|
+
const emitted = wrapper.emitted('update:model-value')
|
|
165
|
+
expect(emitted).toBeTruthy()
|
|
166
|
+
const last = emitted && emitted[emitted.length - 1]?.[0]
|
|
167
|
+
expect(last).toEqual(['28/02/2025', '30/04/2025'])
|
|
168
|
+
})
|
|
169
|
+
|
|
54
170
|
it('validates on submit for required single date', async () => {
|
|
55
171
|
const wrapper = mountComponent({
|
|
56
172
|
label: 'Date',
|
|
@@ -378,6 +494,210 @@ describe('DateTextInput.clean', () => {
|
|
|
378
494
|
expect((input.element as HTMLInputElement).value).toBe('01/01/2025')
|
|
379
495
|
})
|
|
380
496
|
|
|
497
|
+
it('keyboard Backspace efface le chiffre précédent en mode single (overwrite)', async () => {
|
|
498
|
+
const wrapper = mountComponent({ format: 'DD/MM/YYYY' })
|
|
499
|
+
const input = wrapper.find('input')
|
|
500
|
+
await typeDigits(input, '01')
|
|
501
|
+
await input.trigger('keydown', { key: 'Backspace' })
|
|
502
|
+
await flushPromises()
|
|
503
|
+
// Le masque de saisie doit avoir remplacé le dernier chiffre par '_'
|
|
504
|
+
expect(input.element.value).toContain('_')
|
|
505
|
+
})
|
|
506
|
+
|
|
507
|
+
it('keyboard digit avec sélection remplace la sélection en mode single', async () => {
|
|
508
|
+
const wrapper = mountComponent({ format: 'DD/MM/YYYY' })
|
|
509
|
+
const input = wrapper.find('input')
|
|
510
|
+
await typeDigits(input, '01012025')
|
|
511
|
+
await flushPromises()
|
|
512
|
+
// Simuler une sélection puis un chiffre
|
|
513
|
+
input.element.setSelectionRange(0, 2)
|
|
514
|
+
await input.trigger('keydown', { key: '2' })
|
|
515
|
+
await flushPromises()
|
|
516
|
+
expect(input.element.value).toBeTruthy()
|
|
517
|
+
})
|
|
518
|
+
|
|
519
|
+
it('keyboard Backspace avec sélection efface la sélection en mode single', async () => {
|
|
520
|
+
const wrapper = mountComponent({ format: 'DD/MM/YYYY' })
|
|
521
|
+
const input = wrapper.find('input')
|
|
522
|
+
await typeDigits(input, '01012025')
|
|
523
|
+
await flushPromises()
|
|
524
|
+
input.element.setSelectionRange(0, 2)
|
|
525
|
+
await input.trigger('keydown', { key: 'Backspace' })
|
|
526
|
+
await flushPromises()
|
|
527
|
+
expect(input.element.value).toContain('_')
|
|
528
|
+
})
|
|
529
|
+
|
|
530
|
+
it('readonly : validateField retourne un résultat sans erreur', async () => {
|
|
531
|
+
const wrapper = mountComponent({
|
|
532
|
+
format: 'DD/MM/YYYY',
|
|
533
|
+
readonly: true,
|
|
534
|
+
})
|
|
535
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
536
|
+
const result = await (wrapper.vm as any).validateField('01/01/2025')
|
|
537
|
+
expect(result).toBeDefined()
|
|
538
|
+
expect(result.hasError).toBe(false)
|
|
539
|
+
})
|
|
540
|
+
|
|
541
|
+
it('watcher modelValue met à jour inputValue pour une plage de dates', async () => {
|
|
542
|
+
const wrapper = mountComponent({
|
|
543
|
+
format: 'DD/MM/YYYY',
|
|
544
|
+
displayRange: true,
|
|
545
|
+
modelValue: ['01/01/2025', '10/01/2025'],
|
|
546
|
+
})
|
|
547
|
+
await flushPromises()
|
|
548
|
+
const input = wrapper.find('input')
|
|
549
|
+
expect(input.element.value).toContain('01/01/2025')
|
|
550
|
+
expect(input.element.value).toContain('10/01/2025')
|
|
551
|
+
})
|
|
552
|
+
|
|
553
|
+
it('watcher modelValue met à jour inputValue pour une date unique', async () => {
|
|
554
|
+
const wrapper = mountComponent({
|
|
555
|
+
format: 'DD/MM/YYYY',
|
|
556
|
+
modelValue: '15/06/2025',
|
|
557
|
+
})
|
|
558
|
+
await flushPromises()
|
|
559
|
+
const input = wrapper.find('input')
|
|
560
|
+
expect(input.element.value).toBe('15/06/2025')
|
|
561
|
+
})
|
|
562
|
+
|
|
563
|
+
it('watcher modelValue efface inputValue si la nouvelle valeur est vide', async () => {
|
|
564
|
+
const wrapper = mountComponent({
|
|
565
|
+
format: 'DD/MM/YYYY',
|
|
566
|
+
modelValue: '15/06/2025',
|
|
567
|
+
})
|
|
568
|
+
await flushPromises()
|
|
569
|
+
await wrapper.setProps({ modelValue: '' })
|
|
570
|
+
await flushPromises()
|
|
571
|
+
const input = wrapper.find('input')
|
|
572
|
+
expect(input.element.value).toBe('')
|
|
573
|
+
})
|
|
574
|
+
|
|
575
|
+
it('keyboard Backspace en mode range efface un chiffre', async () => {
|
|
576
|
+
const wrapper = mountComponent({ format: 'DD/MM/YYYY', displayRange: true })
|
|
577
|
+
const input = wrapper.find('input')
|
|
578
|
+
await typeDigits(input, '01012025')
|
|
579
|
+
await input.trigger('keydown', { key: 'Backspace' })
|
|
580
|
+
await flushPromises()
|
|
581
|
+
expect(input.element.value).toContain('_')
|
|
582
|
+
})
|
|
583
|
+
|
|
584
|
+
it('keyboard digit en mode range saisit un chiffre', async () => {
|
|
585
|
+
const wrapper = mountComponent({ format: 'DD/MM/YYYY', displayRange: true })
|
|
586
|
+
const input = wrapper.find('input')
|
|
587
|
+
await typeDigits(input, '01012025')
|
|
588
|
+
await flushPromises()
|
|
589
|
+
expect(input.element.value).toContain('2025')
|
|
590
|
+
})
|
|
591
|
+
|
|
592
|
+
it('watcher modelValue initialise correctement une plage avec dateFormatReturn différent', async () => {
|
|
593
|
+
const wrapper = mountComponent({
|
|
594
|
+
format: 'DD/MM/YYYY',
|
|
595
|
+
dateFormatReturn: 'YYYY-MM-DD',
|
|
596
|
+
displayRange: true,
|
|
597
|
+
modelValue: ['2025-01-01', '2025-01-10'],
|
|
598
|
+
})
|
|
599
|
+
await flushPromises()
|
|
600
|
+
const input = wrapper.find('input')
|
|
601
|
+
expect(input.element.value).toContain('01/01/2025')
|
|
602
|
+
expect(input.element.value).toContain('10/01/2025')
|
|
603
|
+
})
|
|
604
|
+
|
|
605
|
+
it('watcher modelValue initialise une plage avec un seul élément sans crash', async () => {
|
|
606
|
+
const wrapper = mountComponent({
|
|
607
|
+
format: 'DD/MM/YYYY',
|
|
608
|
+
displayRange: true,
|
|
609
|
+
modelValue: ['01/01/2025'],
|
|
610
|
+
})
|
|
611
|
+
// Ne doit pas crasher ; la valeur peut être partiellement affichée
|
|
612
|
+
await flushPromises()
|
|
613
|
+
expect(wrapper.find('input').exists()).toBe(true)
|
|
614
|
+
})
|
|
615
|
+
|
|
616
|
+
it('autoClamp sync model en mode range (735-746)', async () => {
|
|
617
|
+
const wrapper = mountComponent({
|
|
618
|
+
format: 'DD/MM/YYYY',
|
|
619
|
+
displayRange: true,
|
|
620
|
+
autoClamp: true,
|
|
621
|
+
})
|
|
622
|
+
const input = wrapper.find('input')
|
|
623
|
+
await input.setValue('31/04/2025 - 29/02/2025')
|
|
624
|
+
await input.trigger('blur')
|
|
625
|
+
await flushPromises()
|
|
626
|
+
// Les dates doivent être clampées et le modèle émis
|
|
627
|
+
const emitted = wrapper.emitted('update:model-value')
|
|
628
|
+
expect(emitted).toBeTruthy()
|
|
629
|
+
})
|
|
630
|
+
|
|
631
|
+
it('isOverwriteEditing : émission correcte après saisie overwrite en mode range', async () => {
|
|
632
|
+
const wrapper = mountComponent({ format: 'DD/MM/YYYY', displayRange: true })
|
|
633
|
+
const input = wrapper.find('input')
|
|
634
|
+
await typeDigits(input, '0101202510012025')
|
|
635
|
+
await input.trigger('blur')
|
|
636
|
+
await flushPromises()
|
|
637
|
+
const emitted = wrapper.emitted('update:model-value')
|
|
638
|
+
expect(emitted).toBeTruthy()
|
|
639
|
+
})
|
|
640
|
+
|
|
641
|
+
it('autoClamp range blur : toutes les émissions tableau ont la valeur clampée (pas de double émission avec l\'ancienne valeur)', async () => {
|
|
642
|
+
const wrapper = mountComponent({
|
|
643
|
+
label: 'Plage de dates',
|
|
644
|
+
format: 'DD/MM/YYYY',
|
|
645
|
+
displayRange: true,
|
|
646
|
+
autoClamp: true,
|
|
647
|
+
})
|
|
648
|
+
|
|
649
|
+
const input = wrapper.find('input')
|
|
650
|
+
await input.setValue('31/04/2025 - 29/02/2025')
|
|
651
|
+
await input.trigger('blur')
|
|
652
|
+
await flushPromises()
|
|
653
|
+
|
|
654
|
+
const emitted = wrapper.emitted('update:model-value')
|
|
655
|
+
expect(emitted).toBeTruthy()
|
|
656
|
+
const rangeEmissions = emitted!.filter(e => Array.isArray(e[0]))
|
|
657
|
+
expect(rangeEmissions.length).toBeGreaterThanOrEqual(1)
|
|
658
|
+
// Toutes les émissions tableau doivent avoir la valeur clampée — aucune ne doit contenir l'ancienne valeur non-clampée
|
|
659
|
+
for (const emission of rangeEmissions) {
|
|
660
|
+
expect(emission[0]).toEqual(['30/04/2025', '28/02/2025'])
|
|
661
|
+
}
|
|
662
|
+
})
|
|
663
|
+
|
|
664
|
+
it('autoClamp range : le modèle émis pendant la frappe est un tableau même si une seule date est clampée', async () => {
|
|
665
|
+
const wrapper = mountComponent({
|
|
666
|
+
label: 'Plage de dates',
|
|
667
|
+
format: 'DD/MM/YYYY',
|
|
668
|
+
displayRange: true,
|
|
669
|
+
autoClamp: true,
|
|
670
|
+
})
|
|
671
|
+
|
|
672
|
+
const input = wrapper.find('input')
|
|
673
|
+
await typeDigits(input, '3104202501012025')
|
|
674
|
+
await flushPromises()
|
|
675
|
+
|
|
676
|
+
const emitted = wrapper.emitted('update:model-value')
|
|
677
|
+
if (emitted && emitted.length > 0) {
|
|
678
|
+
const last = emitted[emitted.length - 1]?.[0]
|
|
679
|
+
if (last !== null) {
|
|
680
|
+
expect(Array.isArray(last)).toBe(true)
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
})
|
|
684
|
+
|
|
685
|
+
it('autoClamp range blur : séparateur sans espaces est passé tel quel sans crash', async () => {
|
|
686
|
+
const wrapper = mountComponent({
|
|
687
|
+
label: 'Plage de dates',
|
|
688
|
+
format: 'DD/MM/YYYY',
|
|
689
|
+
displayRange: true,
|
|
690
|
+
autoClamp: true,
|
|
691
|
+
})
|
|
692
|
+
|
|
693
|
+
const input = wrapper.find('input')
|
|
694
|
+
await input.setValue('31/04/2025-29/02/2025')
|
|
695
|
+
await input.trigger('blur')
|
|
696
|
+
await flushPromises()
|
|
697
|
+
|
|
698
|
+
expect(wrapper.find('input').exists()).toBe(true)
|
|
699
|
+
})
|
|
700
|
+
|
|
381
701
|
it('restores range from modelValue when disabled and input is cleared', async () => {
|
|
382
702
|
const wrapper = mountComponent({
|
|
383
703
|
label: 'Plage de dates',
|