@cnamts/synapse 1.0.1 → 1.0.2
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/README.md +1 -1
- package/dist/{DateFilter-BmRuzQ9Z.js → DateFilter-YWOTbfeL.js} +1 -1
- package/dist/{NumberFilter-CnIPDHqx.js → NumberFilter-DMmMgALM.js} +1 -1
- package/dist/{PeriodFilter-CZwZ8CnQ.js → PeriodFilter-Bok5BHcn.js} +1 -1
- package/dist/SelectFilter-BKud2WhN.js +136 -0
- package/dist/{TextFilter-DTxZHJwX.js → TextFilter-DvMf2thH.js} +1 -1
- package/dist/components/Accordion/Accordion.d.ts +2 -1
- package/dist/components/Accordion/composables/useAccordionGroupCommunication.d.ts +5 -0
- package/dist/components/Accordion/composables/useAccordionKeyboardNavigation.d.ts +12 -0
- package/dist/components/Accordion/composables/useAccordionState.d.ts +13 -0
- package/dist/components/Customs/SyCheckbox/SyCheckbox.d.ts +85 -0
- package/dist/components/Customs/SyInputSelect/SyInputSelect.d.ts +2 -0
- package/dist/components/Customs/SySelect/SySelect.d.ts +33 -13
- package/dist/components/Customs/SyTextField/SyTextField.d.ts +2 -2
- package/dist/components/DatePicker/ComplexDatePicker/ComplexDatePicker.d.ts +1585 -1452
- package/dist/components/DatePicker/DatePicker/DatePicker.d.ts +16 -2
- package/dist/components/DatePicker/DateTextInput/DateTextInput.d.ts +3 -1
- package/dist/components/DatePicker/composables/index.d.ts +2 -0
- package/dist/components/DatePicker/composables/useAsteriskDisplay.d.ts +14 -0
- package/dist/components/DatePicker/composables/useDateAutoClamp.d.ts +16 -0
- package/dist/components/DatePicker/composables/useDateRangeInput.d.ts +1 -1
- package/dist/components/DatePicker/composables/useDisplayedDateString.d.ts +3 -0
- package/dist/components/DatePicker/composables/useInputBlurHandler.d.ts +1 -0
- package/dist/components/DatePicker/composables/useMonthButtonCustomization.d.ts +5 -2
- package/dist/components/NirField/NirField.d.ts +7 -3
- package/dist/components/NirField/nirValidation.d.ts +1 -1
- package/dist/components/PasswordField/PasswordField.d.ts +2 -0
- package/dist/components/PeriodField/PeriodField.d.ts +52 -8
- package/dist/components/PhoneField/PhoneField.d.ts +2 -2
- package/dist/components/RangeField/RangeField.d.ts +2 -0
- package/dist/components/SearchListField/SearchListField.d.ts +9 -0
- package/dist/components/SyTextArea/SyTextArea.d.ts +2 -0
- package/dist/components/Tables/SyServerTable/SyServerTable.d.ts +14 -9
- package/dist/components/Tables/SyTable/SyTable.d.ts +12 -7
- package/dist/components/Tables/common/SyTablePagination.d.ts +1636 -0
- package/dist/components/Tables/common/TableHeader.d.ts +2 -20
- package/dist/components/Tables/common/filters/SelectFilter.d.ts +5 -5
- package/dist/components/Tables/common/filters/getFilterComponent.d.ts +1 -0
- package/dist/components/Tables/common/filters/locales.d.ts +4 -0
- package/dist/components/Tables/common/filters/logics/date.d.ts +1 -0
- package/dist/components/Tables/common/filters/logics/number.d.ts +1 -0
- package/dist/components/Tables/common/filters/logics/period.d.ts +1 -0
- package/dist/components/Tables/common/filters/logics/select.d.ts +1 -0
- package/dist/components/Tables/common/filters/logics/text.d.ts +1 -0
- package/dist/components/Tables/common/locales.d.ts +21 -0
- package/dist/components/Tables/common/organizeColumns/OrganizeColumns.d.ts +267 -0
- package/dist/components/Tables/common/organizeColumns/sortHeaders.d.ts +2 -0
- package/dist/components/Tables/common/tableFilterUtils.d.ts +1 -0
- package/dist/components/Tables/common/tableStorageUtils.d.ts +41 -1
- package/dist/components/Tables/common/tableUtils.d.ts +42 -5
- package/dist/components/Tables/common/types.d.ts +19 -8
- package/dist/components/Tables/common/usePagination.d.ts +22 -0
- package/dist/components/Tables/common/useTableCheckbox.d.ts +20 -0
- package/dist/components/Tables/common/useTableHeaders.d.ts +76 -0
- package/dist/components/Tables/common/useTableItems.d.ts +24 -0
- package/dist/components/Tables/common/useTableOptions.d.ts +18 -0
- package/dist/components/ToolbarContainer/ToolbarContainer.d.ts +11 -0
- package/dist/components/UserMenuBtn/UserMenuBtn.d.ts +9 -2
- package/dist/components/index.d.ts +8 -6
- package/dist/design-system-v3.js +58 -56
- package/dist/design-system-v3.umd.cjs +22 -22
- package/dist/main-Cx8qG7YR.js +16344 -0
- package/dist/stories/Accessibilite/Vuetify/VuetifyItems.d.ts +14 -2
- package/dist/stories/DesignTokens/StylesTypographiques.stories.new.d.ts +8 -0
- package/dist/stories/DesignTokens/TypographyDisplay.d.ts +28 -0
- package/dist/stories/DesignTokens/vue-shims.d.ts +6 -0
- package/dist/style.css +1 -1
- package/package.json +1 -1
- package/src/common/imgs/accessibility-svgrepo-com.svg +4 -0
- package/src/components/Accordion/Accessibilite/AccessibilityGuide.mdx +249 -0
- package/src/components/Accordion/Accordion.vue +48 -76
- package/src/components/Accordion/composables/__tests__/useAccordionGroupCommunication.spec.ts +146 -0
- package/src/components/Accordion/composables/__tests__/useAccordionKeyboardNavigation.spec.ts +209 -0
- package/src/components/Accordion/composables/__tests__/useAccordionState.spec.ts +144 -0
- package/src/components/Accordion/composables/useAccordionGroupCommunication.ts +52 -0
- package/src/components/Accordion/composables/useAccordionKeyboardNavigation.ts +111 -0
- package/src/components/Accordion/composables/useAccordionState.ts +59 -0
- package/src/components/Accordion/tests/__snapshots__/accordion.spec.ts.snap +3 -0
- package/src/components/Customs/SyCheckbox/Accessibilite.mdx +303 -0
- package/src/components/Customs/SyCheckbox/SyCheckbox.mdx +50 -0
- package/src/components/Customs/SyCheckbox/SyCheckbox.stories.ts +630 -0
- package/src/components/Customs/SyCheckbox/SyCheckbox.vue +326 -0
- package/src/components/Customs/SyCheckbox/tests/SyCheckbox.spec.ts +201 -0
- package/src/components/Customs/SyInputSelect/SyInputSelect.stories.ts +1 -0
- package/src/components/Customs/SyInputSelect/SyInputSelect.vue +8 -1
- package/src/components/Customs/SySelect/SySelect.stories.ts +160 -0
- package/src/components/Customs/SySelect/SySelect.vue +291 -32
- package/src/components/Customs/SySelect/tests/SySelect.spec.ts +230 -0
- package/src/components/Customs/SyTextField/SyTextField.stories.ts +3 -2
- package/src/components/Customs/SyTextField/SyTextField.vue +19 -8
- package/src/components/DatePicker/ComplexDatePicker/ComplexDatePicker.stories.ts +241 -31
- package/src/components/DatePicker/ComplexDatePicker/ComplexDatePicker.vue +305 -57
- package/src/components/DatePicker/ComplexDatePicker/tests/ComplexDatePicker.events.spec.ts +161 -0
- package/src/components/DatePicker/ComplexDatePicker/tests/ComplexDatePicker.spec.ts +4 -2
- package/src/components/DatePicker/DatePicker/DatePicker.stories.ts +259 -137
- package/src/components/DatePicker/DatePicker/DatePicker.vue +153 -25
- package/src/components/DatePicker/DatePicker/tests/DatePicker.events.spec.ts +189 -0
- package/src/components/DatePicker/DatePicker/{DatePicker.spec.ts → tests/DatePicker.spec.ts} +1 -15
- package/src/components/DatePicker/DateTextInput/DateRange.stories.ts +24 -14
- package/src/components/DatePicker/DateTextInput/DateTextInput.events.spec.ts +148 -0
- package/src/components/DatePicker/DateTextInput/DateTextInput.spec.ts +3 -1
- package/src/components/DatePicker/DateTextInput/DateTextInput.vue +200 -5
- package/src/components/DatePicker/DateTextInput/NoCalendar.stories.ts +241 -31
- package/src/components/DatePicker/composables/index.ts +2 -0
- package/src/components/DatePicker/composables/tests/useDateAutoClamp.spec.ts +190 -0
- package/src/components/DatePicker/composables/tests/useInputBlurHandler.spec.ts +182 -4
- package/src/components/DatePicker/composables/tests/useMonthButtonCustomization.spec.ts +105 -80
- package/src/components/DatePicker/composables/useAsteriskDisplay.ts +31 -0
- package/src/components/DatePicker/composables/useDateAutoClamp.ts +136 -0
- package/src/components/DatePicker/composables/useDateRangeInput.ts +21 -18
- package/src/components/DatePicker/composables/useDisplayedDateString.ts +13 -1
- package/src/components/DatePicker/composables/useInputBlurHandler.ts +84 -20
- package/src/components/DatePicker/composables/useMonthButtonCustomization.ts +149 -51
- package/src/components/DiacriticPicker/DiacriticPicker.stories.ts +10 -0
- package/src/components/ErrorPage/Accessibilite.stories.ts +8 -0
- package/src/components/ErrorPage/ErrorPage.vue +12 -6
- package/src/components/ErrorPage/tests/__snapshots__/ErrorPage.spec.ts.snap +4 -4
- package/src/components/NirField/NirField.mdx +22 -9
- package/src/components/NirField/NirField.stories.ts +26 -2
- package/src/components/NirField/NirField.vue +209 -22
- package/src/components/NirField/nirValidation.ts +17 -3
- package/src/components/NirField/tests/NirField.spec.ts +2 -2
- package/src/components/NotFoundPage/Accessibilite.stories.ts +8 -0
- package/src/components/NotFoundPage/NotFoundPage.vue +2 -1
- package/src/components/NotFoundPage/tests/__snapshots__/NotFoundPage.spec.ts.snap +8 -6
- package/src/components/PaginatedTable/PaginatedTable.mdx +2 -0
- package/src/components/PasswordField/PasswordField.stories.ts +4 -0
- package/src/components/PasswordField/PasswordField.vue +3 -0
- package/src/components/PeriodField/PeriodField.vue +2 -0
- package/src/components/PhoneField/PhoneField.stories.ts +15 -15
- package/src/components/PhoneField/PhoneField.vue +1 -1
- package/src/components/RangeField/RangeField.stories.ts +9 -0
- package/src/components/RangeField/RangeField.vue +4 -0
- package/src/components/RangeField/tests/__snapshots__/RangeField.spec.ts.snap +12 -0
- package/src/components/SearchListField/SearchListField.vue +5 -0
- package/src/components/SyTextArea/SyTextArea.vue +3 -0
- package/src/components/SyTextArea/tests/SyTextArea.spec.ts +0 -1
- package/src/components/Tables/SyServerTable/FilterRules.stories.ts +632 -15
- package/src/components/Tables/SyServerTable/SyServerTable.mdx +15 -5
- package/src/components/Tables/SyServerTable/SyServerTable.stories.ts +2844 -1377
- package/src/components/Tables/SyServerTable/SyServerTable.vue +155 -66
- package/src/components/Tables/SyServerTable/tests/SyServerTable.spec.ts +256 -4
- package/src/components/Tables/SyTable/FilterRules.stories.ts +183 -0
- package/src/components/Tables/SyTable/SyTable.mdx +14 -4
- package/src/components/Tables/SyTable/SyTable.stories.ts +1265 -477
- package/src/components/Tables/SyTable/SyTable.vue +152 -72
- package/src/components/Tables/SyTable/tests/SyTable.spec.ts +366 -4
- package/src/components/Tables/common/SyTableFilter.vue +3 -56
- package/src/components/Tables/common/SyTablePagination.vue +375 -0
- package/src/components/Tables/common/TableHeader.vue +10 -26
- package/src/components/Tables/common/filters/SelectFilter.vue +131 -22
- package/src/components/Tables/common/filters/getFilterComponent.ts +54 -0
- package/src/components/Tables/common/filters/locales.ts +4 -0
- package/src/components/Tables/common/filters/logics/date.ts +12 -0
- package/src/components/Tables/common/filters/logics/number.ts +48 -0
- package/src/components/Tables/common/filters/logics/period.ts +25 -0
- package/src/components/Tables/common/filters/logics/select.ts +27 -0
- package/src/components/Tables/common/filters/logics/tests/TextFilterLogic.spec.ts +177 -0
- package/src/components/Tables/common/filters/logics/text.ts +62 -0
- package/src/components/Tables/common/filters/tests/TextFilter.spec.ts +11 -11
- package/src/components/Tables/common/locales.ts +24 -0
- package/src/components/Tables/common/organizeColumns/OrganizeColumns.vue +269 -0
- package/src/components/Tables/common/organizeColumns/sortHeaders.ts +9 -0
- package/src/components/Tables/common/tableFilterUtils.ts +43 -295
- package/src/components/Tables/common/tableStorageUtils.ts +27 -2
- package/src/components/Tables/common/tableStyles.scss +26 -0
- package/src/components/Tables/common/tableUtils.ts +3 -16
- package/src/components/Tables/common/tests/SyTablePagination.spec.ts +170 -0
- package/src/components/Tables/common/tests/filterByRange.spec.ts +215 -0
- package/src/components/Tables/common/tests/tableFilterUtils.spec.ts +0 -14
- package/src/components/Tables/common/tests/tableUtils.spec.ts +7 -51
- package/src/components/Tables/common/types.ts +17 -6
- package/src/components/Tables/common/usePagination.ts +83 -0
- package/src/components/Tables/common/useTableCheckbox.ts +58 -0
- package/src/components/Tables/common/useTableHeaders.ts +88 -0
- package/src/components/Tables/common/useTableItems.ts +87 -0
- package/src/components/Tables/common/useTableOptions.ts +93 -0
- package/src/components/ToolbarContainer/ToolbarContainer.mdx +16 -0
- package/src/components/ToolbarContainer/ToolbarContainer.stories.ts +675 -0
- package/src/components/ToolbarContainer/ToolbarContainer.vue +128 -0
- package/src/components/ToolbarContainer/tests/ToolbarContainer.spec.ts +156 -0
- package/src/components/UserMenuBtn/UserMenuBtn.stories.ts +74 -0
- package/src/components/UserMenuBtn/UserMenuBtn.vue +19 -17
- package/src/components/index.ts +8 -6
- package/src/stories/Accessibilite/Aculturation/AuditDesignSystem.mdx +293 -20
- package/src/stories/Accessibilite/Aculturation/SensibilisationAccessibilite.mdx +448 -54
- package/src/stories/Accessibilite/Audit/RGAA.mdx +231 -23
- package/src/stories/Accessibilite/Avancement/Avancement.mdx +591 -7
- package/src/stories/Accessibilite/Avancement/Avancement.stories.ts +139 -38
- package/src/stories/Accessibilite/Introduction.mdx +258 -18
- package/src/stories/Accessibilite/KitDePreAudit/Echantillonnage.mdx +221 -31
- package/src/stories/Accessibilite/KitDePreAudit/Introduction.mdx +204 -22
- package/src/stories/Accessibilite/KitDePreAudit/Outils/Introduction.mdx +537 -24
- package/src/stories/Accessibilite/KitDePreAudit/Outils/LecteursDEcran.mdx +577 -70
- package/src/stories/Accessibilite/KitDePreAudit/Outils/Tanaguru.mdx +382 -31
- package/src/stories/Accessibilite/KitDePreAudit/Preaudit.mdx +419 -81
- package/src/stories/Accessibilite/Vuetify/Vuetify.mdx +132 -6
- package/src/stories/Accessibilite/Vuetify/Vuetify.stories.ts +370 -146
- package/src/stories/Accessibilite/Vuetify/VuetifyItems.ts +35 -57
- package/src/stories/Demarrer/Accueil.stories.ts +20 -5
- package/src/stories/DesignTokens/StylesTypographiques.mdx +10 -9
- package/src/stories/DesignTokens/StylesTypographiques.stories.new.ts +397 -0
- package/src/stories/DesignTokens/StylesTypographiques.stories.ts +397 -0
- package/src/stories/DesignTokens/TypographyDisplay.vue +155 -0
- package/src/stories/DesignTokens/vue-shims.d.ts +6 -0
- package/src/stories/GuideDuDev/LesBreackingChanges.mdx +0 -2
- package/src/stories/GuideDuDev/MigrationDepuisBridge.mdx +1 -1
- package/src/stories/GuideDuDev/MigrationDepuisVue2.mdx +1 -1
- package/src/stories/GuideDuDev/PortailAgent.mdx +10 -0
- package/src/stories/GuideDuDev/PortailAgent.stories.ts +506 -0
- package/src/stories/GuideDuDev/Theme.mdx +41 -0
- package/dist/SelectFilter-Cj-GW2Cc.js +0 -97
- package/dist/main-WDqeoGM-.js +0 -14788
- package/src/components/PaginatedTable/tests/__snapshots__/PaginatedTable.spec.ts.snap +0 -886
- package/src/components/Tables/SyServerTable/tests/__snapshots__/SyServerTable.spec.ts.snap +0 -521
- package/src/components/Tables/SyTable/tests/__snapshots__/SyTable.spec.ts.snap +0 -521
- package/src/stories/DesignTokens/ThemePA.mdx +0 -35
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
<script lang="ts" setup>
|
|
2
|
+
import { computed, ref, watch, onMounted, nextTick } from 'vue'
|
|
3
|
+
import { useValidation, type ValidationRule } from '@/composables/validation/useValidation'
|
|
4
|
+
|
|
5
|
+
const props = withDefaults(
|
|
6
|
+
defineProps<{
|
|
7
|
+
modelValue?: boolean | null
|
|
8
|
+
indeterminate?: boolean
|
|
9
|
+
label?: string
|
|
10
|
+
color?: string
|
|
11
|
+
disabled?: boolean
|
|
12
|
+
readonly?: boolean
|
|
13
|
+
required?: boolean
|
|
14
|
+
hideDetails?: boolean | 'auto'
|
|
15
|
+
density?: 'default' | 'comfortable' | 'compact'
|
|
16
|
+
errorMessages?: string[] | null
|
|
17
|
+
warningMessages?: string[] | null
|
|
18
|
+
successMessages?: string[] | null
|
|
19
|
+
customRules?: ValidationRule[]
|
|
20
|
+
customWarningRules?: ValidationRule[]
|
|
21
|
+
customSuccessRules?: ValidationRule[]
|
|
22
|
+
showSuccessMessages?: boolean
|
|
23
|
+
isValidateOnBlur?: boolean
|
|
24
|
+
disableErrorHandling?: boolean
|
|
25
|
+
id?: string
|
|
26
|
+
name?: string
|
|
27
|
+
value?: unknown
|
|
28
|
+
trueValue?: unknown
|
|
29
|
+
falseValue?: unknown
|
|
30
|
+
controlsIds?: string[]
|
|
31
|
+
}>(),
|
|
32
|
+
{
|
|
33
|
+
modelValue: false,
|
|
34
|
+
indeterminate: false,
|
|
35
|
+
label: '',
|
|
36
|
+
color: 'primary',
|
|
37
|
+
disabled: false,
|
|
38
|
+
readonly: false,
|
|
39
|
+
required: false,
|
|
40
|
+
hideDetails: false,
|
|
41
|
+
density: 'default',
|
|
42
|
+
errorMessages: null,
|
|
43
|
+
warningMessages: null,
|
|
44
|
+
successMessages: null,
|
|
45
|
+
customRules: () => [],
|
|
46
|
+
customWarningRules: () => [],
|
|
47
|
+
customSuccessRules: () => [],
|
|
48
|
+
showSuccessMessages: true,
|
|
49
|
+
isValidateOnBlur: true,
|
|
50
|
+
disableErrorHandling: false,
|
|
51
|
+
id: undefined,
|
|
52
|
+
name: undefined,
|
|
53
|
+
value: undefined,
|
|
54
|
+
trueValue: () => true,
|
|
55
|
+
falseValue: () => false,
|
|
56
|
+
controlsIds: () => [],
|
|
57
|
+
},
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
const emit = defineEmits(['update:modelValue', 'update:indeterminate', 'change'])
|
|
61
|
+
|
|
62
|
+
const internalIndeterminate = ref(props.indeterminate)
|
|
63
|
+
|
|
64
|
+
const model = computed({
|
|
65
|
+
get() {
|
|
66
|
+
return props.modelValue
|
|
67
|
+
},
|
|
68
|
+
set(value) {
|
|
69
|
+
if (internalIndeterminate.value) {
|
|
70
|
+
internalIndeterminate.value = false
|
|
71
|
+
emit('update:indeterminate', false)
|
|
72
|
+
}
|
|
73
|
+
emit('update:modelValue', value)
|
|
74
|
+
emit('change', value)
|
|
75
|
+
},
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
watch(() => props.indeterminate, (val) => {
|
|
79
|
+
internalIndeterminate.value = val
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
// Initialisation du composable de validation
|
|
83
|
+
const validation = useValidation({
|
|
84
|
+
customRules: props.customRules,
|
|
85
|
+
warningRules: props.customWarningRules,
|
|
86
|
+
successRules: props.customSuccessRules,
|
|
87
|
+
showSuccessMessages: props.showSuccessMessages,
|
|
88
|
+
fieldIdentifier: props.label,
|
|
89
|
+
disableErrorHandling: props.disableErrorHandling,
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
// Synchronisation des messages externes
|
|
93
|
+
watch(() => props.errorMessages, (newVal) => {
|
|
94
|
+
validation.errors.value = newVal || []
|
|
95
|
+
}, { immediate: true })
|
|
96
|
+
|
|
97
|
+
watch(() => props.warningMessages, (newVal) => {
|
|
98
|
+
validation.warnings.value = newVal || []
|
|
99
|
+
}, { immediate: true })
|
|
100
|
+
|
|
101
|
+
watch(() => props.successMessages, (newVal) => {
|
|
102
|
+
validation.successes.value = newVal || []
|
|
103
|
+
}, { immediate: true })
|
|
104
|
+
|
|
105
|
+
// Construction des règles de validation
|
|
106
|
+
const defaultRules = computed<ValidationRule[]>(() => props.required
|
|
107
|
+
? [{
|
|
108
|
+
type: 'required',
|
|
109
|
+
options: {
|
|
110
|
+
message: `Le champ ${props.label || 'ce champ'} est requis.`,
|
|
111
|
+
fieldIdentifier: props.label,
|
|
112
|
+
},
|
|
113
|
+
}]
|
|
114
|
+
: [],
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
const validateField = (value: boolean | null) => {
|
|
118
|
+
// Si en lecture seule ou si la valeur est null et non requise, pas de validation
|
|
119
|
+
if (props.readonly) {
|
|
120
|
+
validation.clearValidation()
|
|
121
|
+
return true
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (value === null && !props.required) {
|
|
125
|
+
validation.clearValidation()
|
|
126
|
+
return true
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Pour les règles personnalisées qui vérifient si la case est cochée
|
|
130
|
+
// Si la valeur est true, on peut déjà savoir que la validation va réussir
|
|
131
|
+
if (value === true && props.customRules.every(rule =>
|
|
132
|
+
rule.type === 'custom',
|
|
133
|
+
)) {
|
|
134
|
+
validation.clearValidation()
|
|
135
|
+
return true
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Validation standard
|
|
139
|
+
const result = validation.validateField(
|
|
140
|
+
value,
|
|
141
|
+
[...defaultRules.value, ...props.customRules],
|
|
142
|
+
props.customWarningRules,
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
return !result.hasError
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const validateOnSubmit = () => {
|
|
149
|
+
return validateField(model.value)
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const checkErrorOnBlur = () => {
|
|
153
|
+
validateField(model.value)
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
watch(model, (newValue) => {
|
|
157
|
+
if (!props.isValidateOnBlur) {
|
|
158
|
+
// Valider le champ et s'assurer que l'état d'erreur est correctement mis à jour
|
|
159
|
+
const isValid = validateField(newValue)
|
|
160
|
+
|
|
161
|
+
// Si la validation réussit, s'assurer que les erreurs sont effacées
|
|
162
|
+
if (isValid && validation.hasError.value) {
|
|
163
|
+
validation.clearValidation()
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
})
|
|
167
|
+
|
|
168
|
+
const hasError = computed(() => validation.hasError.value)
|
|
169
|
+
const hasWarning = computed(() => validation.hasWarning.value)
|
|
170
|
+
const hasSuccess = computed(() => validation.hasSuccess.value)
|
|
171
|
+
|
|
172
|
+
const errors = computed(() => validation.errors.value)
|
|
173
|
+
const warnings = computed(() => validation.warnings.value)
|
|
174
|
+
const successes = computed(() => validation.successes.value)
|
|
175
|
+
|
|
176
|
+
const ariaChecked = computed(() => {
|
|
177
|
+
if (internalIndeterminate.value) return 'mixed'
|
|
178
|
+
return model.value ? 'true' : 'false'
|
|
179
|
+
})
|
|
180
|
+
|
|
181
|
+
// Propriétés ARIA personnalisées pour éviter les conflits
|
|
182
|
+
const ariaAttributes = computed(() => {
|
|
183
|
+
return {
|
|
184
|
+
'aria-checked': ariaChecked.value,
|
|
185
|
+
'aria-controls': props.controlsIds.length > 0 ? props.controlsIds.join(' ') : undefined,
|
|
186
|
+
}
|
|
187
|
+
})
|
|
188
|
+
|
|
189
|
+
// Fonction pour supprimer l'attribut aria-disabled="false" des éléments input
|
|
190
|
+
const removeAriaDisabled = () => {
|
|
191
|
+
nextTick(() => {
|
|
192
|
+
// Sélectionner tous les inputs de type checkbox dans le composant
|
|
193
|
+
const checkboxInputs = document.querySelectorAll('input[type="checkbox"][aria-disabled="false"]')
|
|
194
|
+
|
|
195
|
+
// Supprimer l'attribut aria-disabled="false" de chaque input
|
|
196
|
+
checkboxInputs.forEach((input) => {
|
|
197
|
+
input.removeAttribute('aria-disabled')
|
|
198
|
+
})
|
|
199
|
+
|
|
200
|
+
// Configurer un MutationObserver pour surveiller les changements futurs
|
|
201
|
+
const observer = new MutationObserver((mutations) => {
|
|
202
|
+
mutations.forEach(() => {
|
|
203
|
+
const newCheckboxInputs = document.querySelectorAll('input[type="checkbox"][aria-disabled="false"]')
|
|
204
|
+
newCheckboxInputs.forEach((input) => {
|
|
205
|
+
input.removeAttribute('aria-disabled')
|
|
206
|
+
})
|
|
207
|
+
})
|
|
208
|
+
})
|
|
209
|
+
|
|
210
|
+
// Observer le document pour les changements
|
|
211
|
+
observer.observe(document.body, {
|
|
212
|
+
subtree: true,
|
|
213
|
+
childList: true,
|
|
214
|
+
attributes: true,
|
|
215
|
+
attributeFilter: ['aria-disabled'],
|
|
216
|
+
})
|
|
217
|
+
})
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// Appliquer la correction lors du montage du composant
|
|
221
|
+
onMounted(() => {
|
|
222
|
+
removeAriaDisabled()
|
|
223
|
+
})
|
|
224
|
+
|
|
225
|
+
const toggleMixed = () => {
|
|
226
|
+
if (!props.readonly && !props.disabled) {
|
|
227
|
+
if (internalIndeterminate.value) {
|
|
228
|
+
// Désactiver l'état indéterminé
|
|
229
|
+
internalIndeterminate.value = false
|
|
230
|
+
emit('update:indeterminate', false)
|
|
231
|
+
// Émettre l'événement update:modelValue directement
|
|
232
|
+
emit('update:modelValue', true)
|
|
233
|
+
emit('change', true)
|
|
234
|
+
}
|
|
235
|
+
else if (model.value) {
|
|
236
|
+
// Émettre l'événement update:modelValue directement
|
|
237
|
+
emit('update:modelValue', false)
|
|
238
|
+
emit('change', false)
|
|
239
|
+
}
|
|
240
|
+
else {
|
|
241
|
+
if (props.controlsIds.length > 0) {
|
|
242
|
+
// Activer l'état indéterminé
|
|
243
|
+
internalIndeterminate.value = true
|
|
244
|
+
emit('update:indeterminate', true)
|
|
245
|
+
}
|
|
246
|
+
else {
|
|
247
|
+
// Émettre l'événement update:modelValue directement
|
|
248
|
+
emit('update:modelValue', true)
|
|
249
|
+
emit('change', true)
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
defineExpose({
|
|
256
|
+
validation,
|
|
257
|
+
validateOnSubmit,
|
|
258
|
+
checkErrorOnBlur,
|
|
259
|
+
toggleMixed,
|
|
260
|
+
})
|
|
261
|
+
</script>
|
|
262
|
+
|
|
263
|
+
<template>
|
|
264
|
+
<VCheckbox
|
|
265
|
+
:id="props.id"
|
|
266
|
+
v-model="model"
|
|
267
|
+
:name="props.name"
|
|
268
|
+
:label="props.label"
|
|
269
|
+
:color="props.color"
|
|
270
|
+
:disabled="props.disabled"
|
|
271
|
+
:readonly="props.readonly"
|
|
272
|
+
:hide-details="props.hideDetails"
|
|
273
|
+
:density="props.density"
|
|
274
|
+
:error="hasError"
|
|
275
|
+
:error-messages="errors"
|
|
276
|
+
:messages="hasError ? errors : (hasWarning ? warnings : (hasSuccess && props.showSuccessMessages ? successes : []))"
|
|
277
|
+
:indeterminate="internalIndeterminate"
|
|
278
|
+
:value="props.value"
|
|
279
|
+
:true-value="props.trueValue"
|
|
280
|
+
:false-value="props.falseValue"
|
|
281
|
+
v-bind="ariaAttributes"
|
|
282
|
+
@click="toggleMixed"
|
|
283
|
+
@blur="checkErrorOnBlur"
|
|
284
|
+
>
|
|
285
|
+
<template
|
|
286
|
+
v-if="$slots.label"
|
|
287
|
+
#label
|
|
288
|
+
>
|
|
289
|
+
<slot name="label" />
|
|
290
|
+
</template>
|
|
291
|
+
<template
|
|
292
|
+
v-if="$slots.default"
|
|
293
|
+
#default
|
|
294
|
+
>
|
|
295
|
+
<slot />
|
|
296
|
+
</template>
|
|
297
|
+
</VCheckbox>
|
|
298
|
+
</template>
|
|
299
|
+
|
|
300
|
+
<style scoped>
|
|
301
|
+
:deep(.v-selection-control--dirty .v-selection-control__input) {
|
|
302
|
+
color: v-bind('props.color');
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
:deep(.v-checkbox--indeterminate .v-selection-control__input) {
|
|
306
|
+
color: v-bind('props.color');
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
:deep(.v-checkbox--indeterminate .v-selection-control__input .v-selection-control__input-icon) {
|
|
310
|
+
transform: scale(0.8);
|
|
311
|
+
height: 16px;
|
|
312
|
+
width: 16px;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
:deep(.v-selection-control__input) {
|
|
316
|
+
cursor: pointer;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
:deep(.v-selection-control--disabled .v-selection-control__input) {
|
|
320
|
+
cursor: not-allowed;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
:deep(.v-selection-control--error .v-selection-control__input) {
|
|
324
|
+
color: rgb(var(--v-theme-error));
|
|
325
|
+
}
|
|
326
|
+
</style>
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import { mount } from '@vue/test-utils'
|
|
2
|
+
import { describe, it, expect } from 'vitest'
|
|
3
|
+
import { nextTick } from 'vue'
|
|
4
|
+
import SyCheckbox from '../SyCheckbox.vue'
|
|
5
|
+
import { createVuetify } from 'vuetify'
|
|
6
|
+
import * as components from 'vuetify/components'
|
|
7
|
+
import * as directives from 'vuetify/directives'
|
|
8
|
+
|
|
9
|
+
describe('SyCheckbox', () => {
|
|
10
|
+
// Configuration de Vuetify pour les tests
|
|
11
|
+
const vuetify = createVuetify({
|
|
12
|
+
components,
|
|
13
|
+
directives,
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
const global = {
|
|
17
|
+
plugins: [vuetify],
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
it('should render correctly', () => {
|
|
21
|
+
const wrapper = mount(SyCheckbox, {
|
|
22
|
+
props: {
|
|
23
|
+
label: 'Test checkbox',
|
|
24
|
+
},
|
|
25
|
+
global,
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
expect(wrapper.find('.v-checkbox').exists()).toBe(true)
|
|
29
|
+
expect(wrapper.text()).toContain('Test checkbox')
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
it('should handle v-model correctly', async () => {
|
|
33
|
+
const wrapper = mount(SyCheckbox, {
|
|
34
|
+
props: {
|
|
35
|
+
'modelValue': false,
|
|
36
|
+
'onUpdate:modelValue': e => wrapper.setProps({ modelValue: e }),
|
|
37
|
+
},
|
|
38
|
+
global,
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
await wrapper.find('input[type="checkbox"]').setValue(true)
|
|
42
|
+
expect(wrapper.emitted('update:modelValue')?.[0]).toEqual([true])
|
|
43
|
+
expect(wrapper.emitted('change')?.[0]).toEqual([true])
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
it('should handle indeterminate state correctly', async () => {
|
|
47
|
+
const wrapper = mount(SyCheckbox, {
|
|
48
|
+
props: {
|
|
49
|
+
'modelValue': false,
|
|
50
|
+
'indeterminate': true,
|
|
51
|
+
'onUpdate:modelValue': e => wrapper.setProps({ modelValue: e }),
|
|
52
|
+
'onUpdate:indeterminate': e => wrapper.setProps({ indeterminate: e }),
|
|
53
|
+
},
|
|
54
|
+
global,
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
// Vérifier que l'état indéterminé est actif
|
|
58
|
+
expect(wrapper.props('indeterminate')).toBe(true)
|
|
59
|
+
|
|
60
|
+
// Cliquer sur la case à cocher devrait changer l'état indéterminé à checked
|
|
61
|
+
await wrapper.find('.v-selection-control').trigger('click')
|
|
62
|
+
expect(wrapper.emitted('update:indeterminate')?.[0]).toEqual([false])
|
|
63
|
+
expect(wrapper.emitted('update:modelValue')?.[0]).toEqual([true])
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
it('should toggle between states correctly', async () => {
|
|
67
|
+
// Monter le composant avec des handlers pour les événements
|
|
68
|
+
const wrapper = mount(SyCheckbox, {
|
|
69
|
+
props: {
|
|
70
|
+
modelValue: false,
|
|
71
|
+
controlsIds: ['child-1', 'child-2'],
|
|
72
|
+
},
|
|
73
|
+
global,
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
// État initial: non coché
|
|
77
|
+
expect(wrapper.props('modelValue')).toBe(false)
|
|
78
|
+
|
|
79
|
+
// Premier toggle: passe à indéterminé (car controlsIds est défini)
|
|
80
|
+
await wrapper.vm.toggleMixed()
|
|
81
|
+
await nextTick()
|
|
82
|
+
|
|
83
|
+
// Vérifier que l'événement update:indeterminate a été émis
|
|
84
|
+
const indeterminateEvents = wrapper.emitted('update:indeterminate')
|
|
85
|
+
expect(indeterminateEvents).toBeTruthy()
|
|
86
|
+
expect(indeterminateEvents && indeterminateEvents[0]).toEqual([true])
|
|
87
|
+
|
|
88
|
+
// Simuler la mise à jour des props par le parent
|
|
89
|
+
await wrapper.setProps({
|
|
90
|
+
indeterminate: true,
|
|
91
|
+
modelValue: false,
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
// Deuxième toggle: passe à coché
|
|
95
|
+
await wrapper.vm.toggleMixed()
|
|
96
|
+
await nextTick()
|
|
97
|
+
|
|
98
|
+
// Vérifier que les événements ont été émis
|
|
99
|
+
const updatedIndeterminateEvents = wrapper.emitted('update:indeterminate')
|
|
100
|
+
const modelValueEvents = wrapper.emitted('update:modelValue')
|
|
101
|
+
expect(updatedIndeterminateEvents && updatedIndeterminateEvents[1]).toEqual([false])
|
|
102
|
+
expect(modelValueEvents).toBeTruthy()
|
|
103
|
+
expect(modelValueEvents && modelValueEvents[0]).toEqual([true])
|
|
104
|
+
|
|
105
|
+
// Simuler la mise à jour des props par le parent
|
|
106
|
+
await wrapper.setProps({
|
|
107
|
+
indeterminate: false,
|
|
108
|
+
modelValue: true,
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
// Troisième toggle: passe à non coché
|
|
112
|
+
await wrapper.vm.toggleMixed()
|
|
113
|
+
await nextTick()
|
|
114
|
+
|
|
115
|
+
// Vérifier que l'événement update:modelValue a été émis avec false
|
|
116
|
+
const finalModelValueEvents = wrapper.emitted('update:modelValue')
|
|
117
|
+
expect(finalModelValueEvents && finalModelValueEvents[1]).toEqual([false])
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
it('should handle validation correctly', async () => {
|
|
121
|
+
const wrapper = mount(SyCheckbox, {
|
|
122
|
+
props: {
|
|
123
|
+
'modelValue': false,
|
|
124
|
+
'label': 'Required checkbox',
|
|
125
|
+
'required': true,
|
|
126
|
+
'onUpdate:modelValue': e => wrapper.setProps({ modelValue: e }),
|
|
127
|
+
},
|
|
128
|
+
global,
|
|
129
|
+
})
|
|
130
|
+
|
|
131
|
+
// Simuler un événement blur pour déclencher la validation
|
|
132
|
+
await wrapper.find('.v-checkbox').trigger('blur')
|
|
133
|
+
|
|
134
|
+
// Vérifier que le message d'erreur est affiché
|
|
135
|
+
expect(wrapper.find('.v-messages').exists()).toBe(true)
|
|
136
|
+
expect(wrapper.find('.v-messages').text()).toContain('Required checkbox est requis')
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
it('should handle custom validation rules', async () => {
|
|
140
|
+
// Créer une règle de validation au format attendu par le composant
|
|
141
|
+
const customRule = {
|
|
142
|
+
type: 'custom',
|
|
143
|
+
validator: (value: boolean) => value === true,
|
|
144
|
+
options: {
|
|
145
|
+
message: 'This checkbox must be checked',
|
|
146
|
+
fieldIdentifier: 'Custom checkbox',
|
|
147
|
+
},
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Monter le composant avec la règle de validation personnalisée
|
|
151
|
+
const wrapper = mount(SyCheckbox, {
|
|
152
|
+
props: {
|
|
153
|
+
'modelValue': false,
|
|
154
|
+
'customRules': [customRule],
|
|
155
|
+
'isValidateOnBlur': false, // Valider immédiatement
|
|
156
|
+
'onUpdate:modelValue': e => wrapper.setProps({ modelValue: e }),
|
|
157
|
+
},
|
|
158
|
+
global,
|
|
159
|
+
})
|
|
160
|
+
|
|
161
|
+
// Vérifier l'état initial
|
|
162
|
+
expect(wrapper.props('modelValue')).toBe(false)
|
|
163
|
+
|
|
164
|
+
// Simuler la validation du formulaire
|
|
165
|
+
const isValid = await wrapper.vm.validateOnSubmit()
|
|
166
|
+
await wrapper.vm.$nextTick()
|
|
167
|
+
|
|
168
|
+
// La validation devrait échouer car la case n'est pas cochée
|
|
169
|
+
expect(isValid).toBe(false)
|
|
170
|
+
|
|
171
|
+
// Cocher la case
|
|
172
|
+
await wrapper.setProps({ modelValue: true })
|
|
173
|
+
|
|
174
|
+
// Simuler à nouveau la validation du formulaire
|
|
175
|
+
const isValidAfterCheck = await wrapper.vm.validateOnSubmit()
|
|
176
|
+
await wrapper.vm.$nextTick()
|
|
177
|
+
|
|
178
|
+
// La validation devrait réussir maintenant
|
|
179
|
+
expect(isValidAfterCheck).toBe(true)
|
|
180
|
+
})
|
|
181
|
+
|
|
182
|
+
it('should handle readonly and disabled states', async () => {
|
|
183
|
+
const wrapper = mount(SyCheckbox, {
|
|
184
|
+
props: {
|
|
185
|
+
'modelValue': false,
|
|
186
|
+
'readonly': true,
|
|
187
|
+
'onUpdate:modelValue': e => wrapper.setProps({ modelValue: e }),
|
|
188
|
+
},
|
|
189
|
+
global,
|
|
190
|
+
})
|
|
191
|
+
|
|
192
|
+
// Cliquer sur une case à cocher en lecture seule ne devrait pas changer sa valeur
|
|
193
|
+
await wrapper.find('.v-checkbox').trigger('click')
|
|
194
|
+
expect(wrapper.emitted('update:modelValue')).toBeFalsy()
|
|
195
|
+
|
|
196
|
+
// Tester l'état désactivé
|
|
197
|
+
await wrapper.setProps({ readonly: false, disabled: true })
|
|
198
|
+
await wrapper.find('.v-checkbox').trigger('click')
|
|
199
|
+
expect(wrapper.emitted('update:modelValue')).toBeFalsy()
|
|
200
|
+
})
|
|
201
|
+
})
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
clearable?: boolean
|
|
21
21
|
customRules?: ValidationRule[]
|
|
22
22
|
disableErrorHandling?: boolean
|
|
23
|
+
bgColor?: string
|
|
23
24
|
}>(), {
|
|
24
25
|
|
|
25
26
|
modelValue: null,
|
|
@@ -36,6 +37,7 @@
|
|
|
36
37
|
clearable: false,
|
|
37
38
|
customRules: () => [],
|
|
38
39
|
disableErrorHandling: false,
|
|
40
|
+
bgColor: 'white',
|
|
39
41
|
})
|
|
40
42
|
|
|
41
43
|
const options = useCustomizableOptions(defaultOptions, props)
|
|
@@ -241,7 +243,8 @@
|
|
|
241
243
|
'sy-input-select',
|
|
242
244
|
buttonClass,
|
|
243
245
|
hasError ? 'text-error' : 'text-'+options.menu.color,
|
|
244
|
-
hasError ? 'error--text' : ''
|
|
246
|
+
hasError ? 'error--text' : '',
|
|
247
|
+
bgColor ? 'bg-color' : '',
|
|
245
248
|
]"
|
|
246
249
|
role="menu"
|
|
247
250
|
tabindex="0"
|
|
@@ -323,6 +326,10 @@
|
|
|
323
326
|
max-height: 300px;
|
|
324
327
|
}
|
|
325
328
|
|
|
329
|
+
.bg-color {
|
|
330
|
+
background-color: v-bind(bgColor);
|
|
331
|
+
}
|
|
332
|
+
|
|
326
333
|
.v-list-item:hover {
|
|
327
334
|
background-color: rgb(0 0 0 / 4%);
|
|
328
335
|
}
|