@cnamts/synapse 0.0.4-alpha → 0.0.5-alpha
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/design-system-v3.d.ts +932 -224
- package/dist/design-system-v3.js +3374 -1502
- package/dist/design-system-v3.umd.cjs +6 -6
- package/dist/style.css +1 -1
- package/package.json +2 -1
- package/src/assets/tokens.scss +0 -1
- package/src/components/Alert/Accessibilite.mdx +14 -0
- package/src/components/Alert/Accessibilite.stories.ts +166 -0
- package/src/components/Alert/AccessibiliteItems.ts +152 -0
- package/src/components/Alert/constants/ExpertiseLevelEnum.ts +4 -0
- package/src/components/BackBtn/Accessibilite.mdx +14 -0
- package/src/components/BackBtn/Accessibilite.stories.ts +166 -0
- package/src/components/BackBtn/AccessibiliteItems.ts +132 -0
- package/src/components/BackBtn/constants/ExpertiseLevelEnum.ts +4 -0
- package/src/components/BackToTopBtn/Accessibilite.mdx +14 -0
- package/src/components/BackToTopBtn/Accessibilite.stories.ts +166 -0
- package/src/components/BackToTopBtn/AccessibiliteItems.ts +82 -0
- package/src/components/BackToTopBtn/constants/ExpertiseLevelEnum.ts +4 -0
- package/src/components/CollapsibleList/Accessibilite.mdx +14 -0
- package/src/components/CollapsibleList/Accessibilite.stories.ts +166 -0
- package/src/components/CollapsibleList/AccessibiliteItems.ts +129 -0
- package/src/components/CollapsibleList/constants/ExpertiseLevelEnum.ts +4 -0
- package/src/components/CopyBtn/Accessibilite.mdx +14 -0
- package/src/components/CopyBtn/Accessibilite.stories.ts +166 -0
- package/src/components/CopyBtn/AccessibiliteItems.ts +135 -0
- package/src/components/CopyBtn/config.ts +2 -0
- package/src/components/CopyBtn/constants/ExpertiseLevelEnum.ts +4 -0
- package/src/components/Customs/SyBtnSelect/SyBtnSelect.mdx +44 -0
- package/src/components/Customs/SyBtnSelect/SyBtnSelect.stories.ts +606 -0
- package/src/components/Customs/SyBtnSelect/SyBtnSelect.vue +246 -0
- package/src/components/Customs/SyBtnSelect/tests/SyBtnSelect.spec.ts +168 -0
- package/src/components/Customs/SyInputSelect/SyInputSelect.mdx +40 -0
- package/src/components/Customs/{CustomInputSelect/CustomInputSelect.stories.ts → SyInputSelect/SyInputSelect.stories.ts} +29 -29
- package/src/components/Customs/{CustomInputSelect/CustomInputSelect.vue → SyInputSelect/SyInputSelect.vue} +10 -2
- package/src/components/Customs/SyInputSelect/tests/SyInputSelect.spec.ts +140 -0
- package/src/components/Customs/{CustomSelect/CustomSelect.mdx → SySelect/SySelect.mdx} +8 -8
- package/src/components/Customs/{CustomSelect/CustomSelect.stories.ts → SySelect/SySelect.stories.ts} +25 -25
- package/src/components/Customs/{CustomSelect/CustomSelect.vue → SySelect/SySelect.vue} +9 -5
- package/src/components/Customs/{CustomInputSelect/tests/CustomInputSelect.spec.ts → SySelect/tests/SySelect.spec.ts} +48 -71
- package/src/components/Customs/SyTextField/SyTextField.mdx +44 -0
- package/src/components/Customs/{CustomTextField/CustomTextField.stories.ts → SyTextField/SyTextField.stories.ts} +34 -34
- package/src/components/Customs/{CustomTextField/tests/CustomTextField.spec.ts → SyTextField/tests/SyTextField.spec.ts} +3 -3
- package/src/components/Customs/{CustomTextField/tests/__snapshots__/CustomTextField.spec.ts.snap → SyTextField/tests/__snapshots__/SyTextField.spec.ts.snap} +3 -5
- package/src/components/DialogBox/DialogBox.mdx +14 -0
- package/src/components/DialogBox/DialogBox.stories.ts +798 -0
- package/src/components/DialogBox/DialogBox.vue +181 -0
- package/src/components/DialogBox/config.ts +25 -0
- package/src/components/DialogBox/locales.ts +5 -0
- package/src/components/DialogBox/tests/DialogBox.spec.ts +329 -0
- package/src/components/DialogBox/tests/__snapshots__/DialogBox.spec.ts.snap +46 -0
- package/src/components/ErrorPage/ErrorPage.mdx +21 -0
- package/src/components/ErrorPage/ErrorPage.stories.ts +133 -0
- package/src/components/ErrorPage/ErrorPage.vue +93 -0
- package/src/components/ErrorPage/locales.ts +5 -0
- package/src/components/ErrorPage/tests/ErrorPage.spec.ts +40 -0
- package/src/components/ErrorPage/tests/__snapshots__/ErrorPage.spec.ts.snap +78 -0
- package/src/components/FooterBar/FooterBar.stories.ts +556 -8
- package/src/components/FooterBar/config.ts +2 -3
- package/src/components/FooterBar/tests/FooterBar.spec.ts +1 -1
- package/src/components/FooterBar/tests/FooterBarConfig.spec.ts +1 -1
- package/src/components/FooterBar/tests/__snapshots__/FooterBar.spec.ts.snap +2 -2
- package/src/components/FranceConnectBtn/Accessibilite.mdx +14 -0
- package/src/components/FranceConnectBtn/Accessibilite.stories.ts +194 -0
- package/src/components/FranceConnectBtn/AccessibiliteItems.ts +199 -0
- package/src/components/FranceConnectBtn/constants/ExpertiseLevelEnum.ts +4 -0
- package/src/components/HeaderBar/HeaderBar.stories.ts +60 -2
- package/src/components/HeaderNavigationBar/HeaderNavigationBar.mdx +6 -6
- package/src/components/HeaderNavigationBar/tests/HeaderNavigationBar.spec.ts +1 -1
- package/src/components/HeaderToolbar/HeaderToolbar.vue +2 -2
- package/src/components/HeaderToolbar/tests/HeaderToolbar.spec.ts +36 -2
- package/src/components/LangBtn/Accessibilite.mdx +14 -0
- package/src/components/LangBtn/Accessibilite.stories.ts +166 -0
- package/src/components/LangBtn/AccessibiliteItems.ts +132 -0
- package/src/components/LangBtn/constants/ExpertiseLevelEnum.ts +4 -0
- package/src/components/LangBtn/tests/LangBtn.spec.ts +1 -1
- package/src/components/LangBtn/tests/__snapshots__/LangBtn.spec.ts.snap +1 -1
- package/src/components/MaintenancePage/MaintenancePage.mdx +11 -0
- package/src/components/MaintenancePage/MaintenancePage.stories.ts +41 -0
- package/src/components/MaintenancePage/MaintenancePage.vue +25 -0
- package/src/components/MaintenancePage/assets/maintenance.svg +1 -0
- package/src/components/MaintenancePage/index.ts +3 -0
- package/src/components/MaintenancePage/locales.ts +5 -0
- package/src/components/MaintenancePage/tests/MaintenancePage.spec.ts +12 -0
- package/src/components/MaintenancePage/tests/__snapshots__/MaintenancePage.spec.ts.snap +3 -0
- package/src/components/NotFoundPage/NotFoundPage.mdx +19 -0
- package/src/components/NotFoundPage/NotFoundPage.stories.ts +76 -0
- package/src/components/NotFoundPage/NotFoundPage.vue +52 -0
- package/src/components/NotFoundPage/assets/not-found.svg +1 -0
- package/src/components/NotFoundPage/locales.ts +6 -0
- package/src/components/NotFoundPage/tests/NotFoundPage.spec.ts +38 -0
- package/src/components/NotFoundPage/tests/__snapshots__/NotFoundPage.spec.ts.snap +76 -0
- package/src/components/NotificationBar/Accessibilite.mdx +14 -0
- package/src/components/NotificationBar/Accessibilite.stories.ts +166 -0
- package/src/components/NotificationBar/AccessibiliteItems.ts +174 -0
- package/src/components/NotificationBar/constants/ExpertiseLevelEnum.ts +4 -0
- package/src/components/NotificationBar/options.ts +1 -0
- package/src/components/PageContainer/Accessibilite.mdx +14 -0
- package/src/components/PageContainer/Accessibilite.stories.ts +166 -0
- package/src/components/PageContainer/AccessibiliteItems.ts +52 -0
- package/src/components/PageContainer/constants/ExpertiseLevelEnum.ts +4 -0
- package/src/components/PhoneField/PhoneField.vue +5 -5
- package/src/components/PhoneField/tests/PhoneField.spec.ts +3 -3
- package/src/components/SkipLink/Accessibilite.mdx +14 -0
- package/src/components/SkipLink/Accessibilite.stories.ts +167 -0
- package/src/components/SkipLink/AccessibiliteItems.ts +77 -0
- package/src/components/SkipLink/constants/ExpertiseLevelEnum.ts +4 -0
- package/src/components/SocialMediaLinks/SocialMediaLinks.mdx +17 -13
- package/src/components/SocialMediaLinks/SocialMediaLinks.stories.ts +2 -1
- package/src/components/SocialMediaLinks/tests/SocialMediaLinks.spec.ts +1 -1
- package/src/components/SocialMediaLinks/tests/__snapshots__/SocialMediaLinks.spec.ts.snap +2 -2
- package/src/components/SubHeader/SubHeader.vue +1 -1
- package/src/components/UserMenuBtn/UserMenuBtn.mdx +35 -0
- package/src/components/UserMenuBtn/UserMenuBtn.stories.ts +438 -0
- package/src/components/UserMenuBtn/UserMenuBtn.vue +105 -0
- package/src/components/UserMenuBtn/config.ts +24 -0
- package/src/components/UserMenuBtn/tests/UserMenuBtn.spec.ts +125 -0
- package/src/components/index.ts +12 -3
- package/src/composables/index.ts +8 -0
- package/src/composables/rules/tests/useFieldValidation.spec.ts +82 -0
- package/src/composables/rules/useFieldValidation.ts +53 -0
- package/src/designTokens/index.ts +2 -0
- package/src/designTokens/tokens/cnam/cnamDarkTheme.ts +5 -0
- package/src/designTokens/tokens/cnam/cnamLightTheme.ts +1 -0
- package/src/main.ts +2 -0
- package/src/stories/Fondamentaux/Arrondis.mdx +24 -0
- package/src/stories/{Guidelines → Fondamentaux}/Colors.mdx +1 -1
- package/src/stories/Fondamentaux/Conteneurs.mdx +7 -0
- package/src/stories/Fondamentaux/CustomisationEtThemes.mdx +7 -0
- package/src/stories/Fondamentaux/Elevations.mdx +14 -0
- package/src/stories/Fondamentaux/Espacements.mdx +29 -0
- package/src/stories/{Guidelines → Fondamentaux}/Introduction.mdx +1 -1
- package/src/stories/Fondamentaux/StylesTypographiques.mdx +33 -0
- package/src/stories/Fondamentaux/Typographie.mdx +58 -0
- package/src/stories/GuideDuDev/CommentUtiliserLesRules.mdx +132 -0
- package/src/stories/{Fondamentaux → Guidelines}/Accessibilite/Accessibilite.stories.ts +1 -1
- package/src/stories/{Fondamentaux → Guidelines}/EcoConception/Econception.stories.ts +2 -2
- package/src/components/Customs/CustomInputSelect/CustomInputSelect.mdx +0 -40
- package/src/components/Customs/CustomSelect/tests/CustomSelect.spec.ts +0 -236
- package/src/components/Customs/CustomTextField/CustomTextField.mdx +0 -44
- package/src/stories/Guidelines/CustomisationEtThemes.mdx +0 -3
- package/src/stories/Guidelines/Typo.mdx +0 -53
- /package/src/components/Customs/{CustomInputSelect → SyInputSelect}/config.ts +0 -0
- /package/src/components/Customs/{CustomTextField/CustomTextField.vue → SyTextField/SyTextField.vue} +0 -0
- /package/src/components/Customs/{CustomTextField → SyTextField}/types.d.ts +0 -0
- /package/src/stories/{Fondamentaux → Guidelines}/Accessibilite/Accessibilite.mdx +0 -0
- /package/src/stories/{Fondamentaux → Guidelines}/Accessibilite/AccessibiliteItems.ts +0 -0
- /package/src/stories/{Fondamentaux → Guidelines}/Accessibilite/constants/ExpertiseLevelEnum.ts +0 -0
- /package/src/stories/{Fondamentaux → Guidelines}/Accessibilite/constants/RGAALevelEnum.ts +0 -0
- /package/src/stories/{Fondamentaux → Guidelines}/EcoConception/EcoConception.mdx +0 -0
- /package/src/stories/{Fondamentaux → Guidelines}/EcoConception/ecoDesignItems.ts +0 -0
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
isOpen.value = false
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
const inputId = ref(`
|
|
46
|
+
const inputId = ref(`sy-input-select-${Math.random().toString(36).substring(7)}`)
|
|
47
47
|
|
|
48
48
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- This is a generic type
|
|
49
49
|
const selectItem = (item: any) => {
|
|
@@ -113,6 +113,14 @@
|
|
|
113
113
|
localErrorMessages.value = []
|
|
114
114
|
return true
|
|
115
115
|
}
|
|
116
|
+
|
|
117
|
+
defineExpose({
|
|
118
|
+
isOpen,
|
|
119
|
+
closeList,
|
|
120
|
+
selectItem,
|
|
121
|
+
selectedItem,
|
|
122
|
+
getItemText,
|
|
123
|
+
})
|
|
116
124
|
</script>
|
|
117
125
|
|
|
118
126
|
<template>
|
|
@@ -128,7 +136,7 @@
|
|
|
128
136
|
<div
|
|
129
137
|
ref="menu"
|
|
130
138
|
v-click-outside="closeList"
|
|
131
|
-
:class="['
|
|
139
|
+
:class="['sy-input-select', buttonClass, 'text-'+options.menu.color]"
|
|
132
140
|
role="menu"
|
|
133
141
|
tabindex="0"
|
|
134
142
|
@click="toggleMenu"
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { mount } from '@vue/test-utils'
|
|
2
|
+
import { expect, describe, it } from 'vitest'
|
|
3
|
+
import SyInputSelect from '../SyInputSelect.vue'
|
|
4
|
+
import { vuetify } from '@tests/unit/setup'
|
|
5
|
+
|
|
6
|
+
describe('SyInputSelect', () => {
|
|
7
|
+
it('renders the component with default props', () => {
|
|
8
|
+
const wrapper = mount(SyInputSelect, {
|
|
9
|
+
global: {
|
|
10
|
+
plugins: [vuetify],
|
|
11
|
+
},
|
|
12
|
+
})
|
|
13
|
+
expect(wrapper.exists()).toBe(true)
|
|
14
|
+
expect(wrapper.find('.sy-input-select').text()).toBe('Sélectionnez une option')
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
it('displays the selected item text', async () => {
|
|
18
|
+
const items = [{ text: 'Option 1', value: '1' }, { text: 'Option 2', value: '2' }]
|
|
19
|
+
const wrapper = mount(SyInputSelect, {
|
|
20
|
+
props: { items, modelValue: { text: 'Option 1', value: '1' } },
|
|
21
|
+
global: {
|
|
22
|
+
plugins: [vuetify],
|
|
23
|
+
},
|
|
24
|
+
})
|
|
25
|
+
expect(wrapper.find('.sy-input-select').text()).toContain('Option 1')
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
it('does not render error messages when not provided', () => {
|
|
29
|
+
const wrapper = mount(SyInputSelect, {
|
|
30
|
+
global: {
|
|
31
|
+
plugins: [vuetify],
|
|
32
|
+
},
|
|
33
|
+
})
|
|
34
|
+
expect(wrapper.find('.v-messages__message').exists()).toBe(false)
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
it('does not render the label when not provided', () => {
|
|
38
|
+
const wrapper = mount(SyInputSelect, {
|
|
39
|
+
global: {
|
|
40
|
+
plugins: [vuetify],
|
|
41
|
+
},
|
|
42
|
+
})
|
|
43
|
+
expect(wrapper.find('label').exists()).toBe(false)
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
it('formats items correctly', () => {
|
|
47
|
+
const items = ['Option 1', 'Option 2']
|
|
48
|
+
const wrapper = mount(SyInputSelect, {
|
|
49
|
+
props: { items, textKey: 'text', valueKey: 'value' },
|
|
50
|
+
global: {
|
|
51
|
+
plugins: [vuetify],
|
|
52
|
+
},
|
|
53
|
+
})
|
|
54
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- This is a generic type
|
|
55
|
+
const formattedItems = (wrapper.vm as any).formattedItems
|
|
56
|
+
expect(formattedItems).toEqual([
|
|
57
|
+
{ text: 'Option 1', value: 'Option 1' },
|
|
58
|
+
{ text: 'Option 2', value: 'Option 2' },
|
|
59
|
+
])
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
it('applies the correct button class when outlined is true', () => {
|
|
63
|
+
const wrapper = mount(SyInputSelect, {
|
|
64
|
+
props: { outlined: true },
|
|
65
|
+
global: {
|
|
66
|
+
plugins: [vuetify],
|
|
67
|
+
},
|
|
68
|
+
})
|
|
69
|
+
expect(wrapper.find('.sy-input-select').classes()).toContain('v-btn--variant-outlined')
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
it('toggles the menu when the button is clicked', async () => {
|
|
73
|
+
const wrapper = mount(SyInputSelect, {
|
|
74
|
+
global: {
|
|
75
|
+
plugins: [vuetify],
|
|
76
|
+
},
|
|
77
|
+
})
|
|
78
|
+
const button = wrapper.find('.sy-input-select')
|
|
79
|
+
await button.trigger('click')
|
|
80
|
+
expect(wrapper.vm.isOpen).toBe(true)
|
|
81
|
+
await button.trigger('click')
|
|
82
|
+
expect(wrapper.vm.isOpen).toBe(false)
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
it('use closeList method', async () => {
|
|
86
|
+
const wrapper = mount(SyInputSelect, {
|
|
87
|
+
global: {
|
|
88
|
+
plugins: [vuetify],
|
|
89
|
+
},
|
|
90
|
+
})
|
|
91
|
+
await wrapper.vm.closeList()
|
|
92
|
+
expect(wrapper.vm.isOpen).toBe(false)
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
it('selectItem method', async () => {
|
|
96
|
+
const wrapper = mount(SyInputSelect, {
|
|
97
|
+
global: {
|
|
98
|
+
plugins: [vuetify],
|
|
99
|
+
},
|
|
100
|
+
})
|
|
101
|
+
await wrapper.vm.selectItem({ text: 'Option 1', value: '1' })
|
|
102
|
+
expect(wrapper.vm.isOpen).toBe(false)
|
|
103
|
+
expect(wrapper.vm.selectedItem).toEqual({ text: 'Option 1', value: '1' })
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
it('getItemText method', async () => {
|
|
107
|
+
const wrapper = mount(SyInputSelect, {
|
|
108
|
+
global: {
|
|
109
|
+
plugins: [vuetify],
|
|
110
|
+
},
|
|
111
|
+
})
|
|
112
|
+
const item = { text: 'Option 1', value: '1' }
|
|
113
|
+
const text = wrapper.vm.getItemText(item)
|
|
114
|
+
expect(text).toBe('Option 1')
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
it('watch modelValue', async () => {
|
|
118
|
+
const wrapper = mount(SyInputSelect, {
|
|
119
|
+
props: { modelValue: { text: 'Option 1', value: '1' } },
|
|
120
|
+
global: {
|
|
121
|
+
plugins: [vuetify],
|
|
122
|
+
},
|
|
123
|
+
})
|
|
124
|
+
expect(wrapper.vm.selectedItem).toEqual({ text: 'Option 1', value: '1' })
|
|
125
|
+
await wrapper.setProps({ modelValue: { text: 'Option 2', value: '2' } })
|
|
126
|
+
expect(wrapper.vm.selectedItem).toEqual({ text: 'Option 2', value: '2' })
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
it('watch errorMessages', async () => {
|
|
130
|
+
const wrapper = mount(SyInputSelect, {
|
|
131
|
+
props: { errorMessages: ['Error message'] },
|
|
132
|
+
global: {
|
|
133
|
+
plugins: [vuetify],
|
|
134
|
+
},
|
|
135
|
+
})
|
|
136
|
+
expect(wrapper.find('.v-messages__message').exists()).toBe(true)
|
|
137
|
+
await wrapper.setProps({ errorMessages: [] })
|
|
138
|
+
expect(wrapper.find('.v-messages__message').exists()).toBe(false)
|
|
139
|
+
})
|
|
140
|
+
})
|
|
@@ -1,26 +1,26 @@
|
|
|
1
1
|
import { Canvas, Meta, Controls, Story, Source } from '@storybook/blocks';
|
|
2
|
-
import * as
|
|
2
|
+
import * as SySelectStories from "./SySelect.stories";
|
|
3
3
|
|
|
4
|
-
<Meta of={
|
|
4
|
+
<Meta of={SySelectStories} />
|
|
5
5
|
|
|
6
|
-
#
|
|
6
|
+
# SySelect
|
|
7
7
|
|
|
8
|
-
Le composant `
|
|
8
|
+
Le composant `SySelect` est utilisé pour proposer une alternative au `v-select` de Vuetify qui ne respecte pas les règles d'accessibilité RGAA.
|
|
9
9
|
|
|
10
|
-
<Canvas of={
|
|
10
|
+
<Canvas of={SySelectStories.Default} />
|
|
11
11
|
|
|
12
|
-
<Story of={
|
|
12
|
+
<Story of={SySelectStories.Info} />
|
|
13
13
|
|
|
14
14
|
# API
|
|
15
15
|
|
|
16
|
-
<Controls of={
|
|
16
|
+
<Controls of={SySelectStories.Default} />
|
|
17
17
|
|
|
18
18
|
# Exemple d'utilisation
|
|
19
19
|
|
|
20
20
|
<Source dark code={`
|
|
21
21
|
<script setup lang="ts">
|
|
22
22
|
import { ref } from 'vue'
|
|
23
|
-
import
|
|
23
|
+
import SySelect from '@cnamts/synapse'
|
|
24
24
|
|
|
25
25
|
const selectedValue = ref(undefined)
|
|
26
26
|
|
package/src/components/Customs/{CustomSelect/CustomSelect.stories.ts → SySelect/SySelect.stories.ts}
RENAMED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import type { Meta, StoryObj } from '@storybook/vue3'
|
|
2
|
-
import
|
|
2
|
+
import SySelect from '@/components/Customs/SySelect/SySelect.vue'
|
|
3
3
|
import { VBtn, VMenu, VList, VListItem, VListItemTitle } from 'vuetify/components'
|
|
4
4
|
import { ref } from 'vue'
|
|
5
5
|
import Alert from '../../Alert/Alert.vue'
|
|
6
6
|
|
|
7
7
|
const meta = {
|
|
8
|
-
title: 'Composants/Formulaires/
|
|
9
|
-
component:
|
|
8
|
+
title: 'Composants/Formulaires/SySelect',
|
|
9
|
+
component: SySelect,
|
|
10
10
|
parameters: {
|
|
11
11
|
layout: 'fullscreen',
|
|
12
|
-
controls: { exclude: ['selectedValue'] },
|
|
12
|
+
controls: { exclude: ['selectedValue', 'isOpen', 'closeList'] },
|
|
13
13
|
},
|
|
14
14
|
argTypes: {
|
|
15
15
|
selectedValue: { control: 'text' },
|
|
@@ -17,7 +17,7 @@ const meta = {
|
|
|
17
17
|
errorMessages: { control: 'object' },
|
|
18
18
|
required: { control: 'boolean' },
|
|
19
19
|
},
|
|
20
|
-
} as Meta<typeof
|
|
20
|
+
} as Meta<typeof SySelect>
|
|
21
21
|
|
|
22
22
|
export default meta
|
|
23
23
|
|
|
@@ -29,7 +29,7 @@ export const Default: Story = {
|
|
|
29
29
|
name: 'Template',
|
|
30
30
|
code: `
|
|
31
31
|
<template>
|
|
32
|
-
<
|
|
32
|
+
<SySelect
|
|
33
33
|
v-model="value"
|
|
34
34
|
:items="items"
|
|
35
35
|
/>
|
|
@@ -40,7 +40,7 @@ export const Default: Story = {
|
|
|
40
40
|
name: 'Script',
|
|
41
41
|
code: `
|
|
42
42
|
<script setup lang="ts">
|
|
43
|
-
import
|
|
43
|
+
import SySelect from '@cnamts/SySelect'
|
|
44
44
|
|
|
45
45
|
const items = [
|
|
46
46
|
{ text: 'Option 1', value: '1' },
|
|
@@ -59,13 +59,13 @@ export const Default: Story = {
|
|
|
59
59
|
},
|
|
60
60
|
render: (args) => {
|
|
61
61
|
return {
|
|
62
|
-
components: {
|
|
62
|
+
components: { SySelect, VBtn, VMenu, VList, VListItem, VListItemTitle },
|
|
63
63
|
setup() {
|
|
64
64
|
return { args }
|
|
65
65
|
},
|
|
66
66
|
template: `
|
|
67
67
|
<div class="d-flex flex-wrap align-center pa-4">
|
|
68
|
-
<
|
|
68
|
+
<SySelect
|
|
69
69
|
v-bind="args"
|
|
70
70
|
/>
|
|
71
71
|
</div>
|
|
@@ -82,7 +82,7 @@ export const Outlined: Story = {
|
|
|
82
82
|
name: 'Template',
|
|
83
83
|
code: `
|
|
84
84
|
<template>
|
|
85
|
-
<
|
|
85
|
+
<SySelect
|
|
86
86
|
v-model="value"
|
|
87
87
|
:items="items"
|
|
88
88
|
outlined
|
|
@@ -94,7 +94,7 @@ export const Outlined: Story = {
|
|
|
94
94
|
name: 'Script',
|
|
95
95
|
code: `
|
|
96
96
|
<script setup lang="ts">
|
|
97
|
-
import
|
|
97
|
+
import SySelect from '@cnamts/SySelect'
|
|
98
98
|
|
|
99
99
|
const items = [
|
|
100
100
|
{ text: 'Option 1', value: '1' },
|
|
@@ -113,13 +113,13 @@ export const Outlined: Story = {
|
|
|
113
113
|
},
|
|
114
114
|
render: (args) => {
|
|
115
115
|
return {
|
|
116
|
-
components: {
|
|
116
|
+
components: { SySelect, VBtn, VMenu, VList, VListItem, VListItemTitle },
|
|
117
117
|
setup() {
|
|
118
118
|
return { args }
|
|
119
119
|
},
|
|
120
120
|
template: `
|
|
121
121
|
<div class="d-flex flex-wrap align-center pa-4" style="z-index: 99999">
|
|
122
|
-
<
|
|
122
|
+
<SySelect
|
|
123
123
|
v-bind="args"
|
|
124
124
|
outlined
|
|
125
125
|
/>
|
|
@@ -136,7 +136,7 @@ export const Required: Story = {
|
|
|
136
136
|
name: 'Template',
|
|
137
137
|
code: `
|
|
138
138
|
<template>
|
|
139
|
-
<
|
|
139
|
+
<SySelect
|
|
140
140
|
v-model="value"
|
|
141
141
|
:items="items"
|
|
142
142
|
required
|
|
@@ -148,7 +148,7 @@ export const Required: Story = {
|
|
|
148
148
|
name: 'Script',
|
|
149
149
|
code: `
|
|
150
150
|
<script setup lang="ts">
|
|
151
|
-
import
|
|
151
|
+
import SySelect from '@cnamts/SySelect'
|
|
152
152
|
|
|
153
153
|
const items = [
|
|
154
154
|
{ text: 'Option 1', value: '1' },
|
|
@@ -168,13 +168,13 @@ export const Required: Story = {
|
|
|
168
168
|
},
|
|
169
169
|
render: (args) => {
|
|
170
170
|
return {
|
|
171
|
-
components: {
|
|
171
|
+
components: { SySelect },
|
|
172
172
|
setup() {
|
|
173
173
|
return { args }
|
|
174
174
|
},
|
|
175
175
|
template: `
|
|
176
176
|
<div class="d-flex flex-wrap align-center pa-4">
|
|
177
|
-
<
|
|
177
|
+
<SySelect
|
|
178
178
|
v-bind="args"
|
|
179
179
|
:required="args.required"
|
|
180
180
|
/>
|
|
@@ -191,7 +191,7 @@ export const withCustomError: Story = {
|
|
|
191
191
|
name: 'Template',
|
|
192
192
|
code: `
|
|
193
193
|
<template>
|
|
194
|
-
<
|
|
194
|
+
<SySelect
|
|
195
195
|
v-model="value"
|
|
196
196
|
:items="items"
|
|
197
197
|
:error-messages="errorMessages"
|
|
@@ -206,7 +206,7 @@ export const withCustomError: Story = {
|
|
|
206
206
|
name: 'Script',
|
|
207
207
|
code: `
|
|
208
208
|
<script setup lang="ts">
|
|
209
|
-
import
|
|
209
|
+
import SySelect from '@cnamts/SySelect'
|
|
210
210
|
import { ref } from 'vue'
|
|
211
211
|
|
|
212
212
|
const items = [
|
|
@@ -232,7 +232,7 @@ export const withCustomError: Story = {
|
|
|
232
232
|
},
|
|
233
233
|
render: (args) => {
|
|
234
234
|
return {
|
|
235
|
-
components: {
|
|
235
|
+
components: { SySelect, VBtn, VMenu, VList, VListItem, VListItemTitle },
|
|
236
236
|
setup() {
|
|
237
237
|
const errorMessages = ref([])
|
|
238
238
|
const triggerError = () => {
|
|
@@ -243,7 +243,7 @@ export const withCustomError: Story = {
|
|
|
243
243
|
},
|
|
244
244
|
template: `
|
|
245
245
|
<div class="d-flex flex-wrap align-center pa-4">
|
|
246
|
-
<
|
|
246
|
+
<SySelect
|
|
247
247
|
v-bind="args"
|
|
248
248
|
:error-messages="errorMessages"
|
|
249
249
|
/>
|
|
@@ -265,7 +265,7 @@ export const withCustomKey: Story = {
|
|
|
265
265
|
name: 'Template',
|
|
266
266
|
code: `
|
|
267
267
|
<template>
|
|
268
|
-
<
|
|
268
|
+
<SySelect
|
|
269
269
|
v-model="value"
|
|
270
270
|
:items="items"
|
|
271
271
|
text-key="customKey"
|
|
@@ -278,7 +278,7 @@ export const withCustomKey: Story = {
|
|
|
278
278
|
name: 'Script',
|
|
279
279
|
code: `
|
|
280
280
|
<script setup lang="ts">
|
|
281
|
-
import
|
|
281
|
+
import SySelect from '@cnamts/SySelect'
|
|
282
282
|
|
|
283
283
|
const items = [
|
|
284
284
|
{ customKey: 'Choix 1', value: '1' },
|
|
@@ -297,13 +297,13 @@ export const withCustomKey: Story = {
|
|
|
297
297
|
},
|
|
298
298
|
render: (args) => {
|
|
299
299
|
return {
|
|
300
|
-
components: {
|
|
300
|
+
components: { SySelect, VBtn, VMenu, VList, VListItem, VListItemTitle },
|
|
301
301
|
setup() {
|
|
302
302
|
return { args }
|
|
303
303
|
},
|
|
304
304
|
template: `
|
|
305
305
|
<div class="d-flex flex-wrap align-center pa-4">
|
|
306
|
-
<
|
|
306
|
+
<SySelect
|
|
307
307
|
v-bind="args"
|
|
308
308
|
outlined
|
|
309
309
|
text-key="customKey"
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
},
|
|
27
27
|
menuId: {
|
|
28
28
|
type: String,
|
|
29
|
-
default: '
|
|
29
|
+
default: 'sy-select-menu',
|
|
30
30
|
},
|
|
31
31
|
outlined: {
|
|
32
32
|
type: Boolean,
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
const emit = defineEmits(['update:modelValue'])
|
|
46
46
|
|
|
47
47
|
const isOpen = ref(false)
|
|
48
|
-
const selectedItem = ref<Record<string, unknown > | string | null>(props.modelValue)
|
|
48
|
+
const selectedItem = ref<Record<string, unknown > | string | null | undefined>(props.modelValue)
|
|
49
49
|
const hasError = ref(false)
|
|
50
50
|
|
|
51
51
|
const toggleMenu = () => {
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
const closeList = () => {
|
|
55
55
|
isOpen.value = false
|
|
56
56
|
}
|
|
57
|
-
const inputId = ref(`
|
|
57
|
+
const inputId = ref(`sy-select-${Math.random().toString(36).substring(7)}`)
|
|
58
58
|
|
|
59
59
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- This is a generic type
|
|
60
60
|
const selectItem = (item: any) => {
|
|
@@ -97,6 +97,10 @@
|
|
|
97
97
|
hasError.value = !newValue && !selectedItem.value && isRequired.value
|
|
98
98
|
})
|
|
99
99
|
|
|
100
|
+
defineExpose({
|
|
101
|
+
isOpen,
|
|
102
|
+
closeList,
|
|
103
|
+
})
|
|
100
104
|
</script>
|
|
101
105
|
|
|
102
106
|
<template>
|
|
@@ -115,7 +119,7 @@
|
|
|
115
119
|
:error-messages="errorMessages"
|
|
116
120
|
:variant="outlined ? 'outlined' : 'underlined'"
|
|
117
121
|
:rules="isRequired ? ['Le champ est requis.'] : []"
|
|
118
|
-
class="
|
|
122
|
+
class="sy-select"
|
|
119
123
|
@click="toggleMenu"
|
|
120
124
|
@keydown.enter.prevent="toggleMenu"
|
|
121
125
|
@keydown.space.prevent="toggleMenu"
|
|
@@ -159,7 +163,7 @@
|
|
|
159
163
|
<style scoped lang="scss">
|
|
160
164
|
@use '@/assets/tokens.scss';
|
|
161
165
|
|
|
162
|
-
.
|
|
166
|
+
.sy-select {
|
|
163
167
|
display: flex;
|
|
164
168
|
flex-direction: column;
|
|
165
169
|
min-width: 225px;
|
|
@@ -1,90 +1,51 @@
|
|
|
1
1
|
import { mount } from '@vue/test-utils'
|
|
2
2
|
import { expect, describe, it } from 'vitest'
|
|
3
|
-
import
|
|
3
|
+
import SySelect from '../SySelect.vue'
|
|
4
4
|
import { vuetify } from '@tests/unit/setup'
|
|
5
5
|
|
|
6
|
-
describe('
|
|
6
|
+
describe('SySelect.vue', () => {
|
|
7
7
|
it('renders the component with default props', () => {
|
|
8
|
-
const wrapper = mount(
|
|
8
|
+
const wrapper = mount(SySelect, {
|
|
9
9
|
global: {
|
|
10
10
|
plugins: [vuetify],
|
|
11
11
|
},
|
|
12
12
|
})
|
|
13
13
|
expect(wrapper.exists()).toBe(true)
|
|
14
|
-
expect(wrapper.find('.
|
|
14
|
+
expect(wrapper.find('.sy-select').text()).toBe('')
|
|
15
15
|
})
|
|
16
16
|
|
|
17
|
-
it('
|
|
18
|
-
const wrapper = mount(CustomSelect, {
|
|
19
|
-
global: {
|
|
20
|
-
plugins: [vuetify],
|
|
21
|
-
},
|
|
22
|
-
})
|
|
23
|
-
await wrapper.find('.custom-select').trigger('click')
|
|
24
|
-
expect(wrapper.find('.v-list').exists()).toBe(true)
|
|
25
|
-
await wrapper.find('.custom-select').trigger('click')
|
|
26
|
-
expect(wrapper.find('.v-list').exists()).toBe(false)
|
|
27
|
-
})
|
|
28
|
-
|
|
29
|
-
it('selects an item when clicked', async () => {
|
|
30
|
-
const items = [{ text: 'Option 1', value: '1' }, { text: 'Option 2', value: '2' }]
|
|
31
|
-
const wrapper = mount(CustomSelect, {
|
|
32
|
-
props: { items },
|
|
33
|
-
global: {
|
|
34
|
-
plugins: [vuetify],
|
|
35
|
-
},
|
|
36
|
-
})
|
|
37
|
-
await wrapper.find('.custom-select').trigger('click')
|
|
38
|
-
const firstItem = wrapper.findAll('.v-list-item').at(0)
|
|
39
|
-
if (firstItem) {
|
|
40
|
-
await firstItem.trigger('click')
|
|
41
|
-
}
|
|
42
|
-
expect(wrapper.emitted()['update:modelValue'][0]).toEqual([{ text: 'Option 1', value: '1' }])
|
|
43
|
-
})
|
|
44
|
-
|
|
45
|
-
it('closes the menu when an item is selected', async () => {
|
|
17
|
+
it('displays the selected item text', async () => {
|
|
46
18
|
const items = [{ text: 'Option 1', value: '1' }, { text: 'Option 2', value: '2' }]
|
|
47
|
-
const wrapper = mount(
|
|
48
|
-
props: { items },
|
|
19
|
+
const wrapper = mount(SySelect, {
|
|
20
|
+
props: { items, modelValue: { text: 'Option 1', value: '1' } },
|
|
49
21
|
global: {
|
|
50
22
|
plugins: [vuetify],
|
|
51
23
|
},
|
|
52
24
|
})
|
|
53
|
-
await wrapper.find('.
|
|
25
|
+
await wrapper.find('.sy-select').trigger('click')
|
|
54
26
|
const firstItem = wrapper.findAll('.v-list-item').at(0)
|
|
55
27
|
if (firstItem) {
|
|
56
28
|
await firstItem.trigger('click')
|
|
57
29
|
}
|
|
58
|
-
expect(wrapper.find('
|
|
59
|
-
})
|
|
60
|
-
|
|
61
|
-
it('displays the selected item text', async () => {
|
|
62
|
-
const items = [{ text: 'Option 1', value: '1' }, { text: 'Option 2', value: '2' }]
|
|
63
|
-
const wrapper = mount(CustomSelect, {
|
|
64
|
-
props: { items, modelValue: { text: 'Option 1', value: '1' } },
|
|
65
|
-
global: {
|
|
66
|
-
plugins: [vuetify],
|
|
67
|
-
},
|
|
68
|
-
})
|
|
69
|
-
expect(wrapper.find('.custom-select').text()).toContain('Option 1')
|
|
30
|
+
expect(wrapper.find('input').element.value).toBe('Option 1')
|
|
70
31
|
})
|
|
71
32
|
|
|
72
33
|
it('closes the menu on escape key press', async () => {
|
|
73
34
|
const items = [{ text: 'Option 1', value: '1' }, { text: 'Option 2', value: '2' }]
|
|
74
|
-
const wrapper = mount(
|
|
35
|
+
const wrapper = mount(SySelect, {
|
|
75
36
|
props: { items },
|
|
76
37
|
global: {
|
|
77
38
|
plugins: [vuetify],
|
|
78
39
|
},
|
|
79
40
|
})
|
|
80
|
-
await wrapper.find('.
|
|
41
|
+
await wrapper.find('.sy-select').trigger('click')
|
|
81
42
|
await wrapper.find('.v-list').trigger('keydown.esc')
|
|
82
43
|
expect(wrapper.find('.v-list').exists()).toBe(false)
|
|
83
44
|
})
|
|
84
45
|
|
|
85
46
|
it('renders error messages when provided', () => {
|
|
86
47
|
const errorMessages = ['Error 1']
|
|
87
|
-
const wrapper = mount(
|
|
48
|
+
const wrapper = mount(SySelect, {
|
|
88
49
|
props: { errorMessages },
|
|
89
50
|
global: {
|
|
90
51
|
plugins: [vuetify],
|
|
@@ -94,7 +55,7 @@ describe('CustomInputSelect.vue', () => {
|
|
|
94
55
|
})
|
|
95
56
|
|
|
96
57
|
it('does not render error messages when not provided', () => {
|
|
97
|
-
const wrapper = mount(
|
|
58
|
+
const wrapper = mount(SySelect, {
|
|
98
59
|
global: {
|
|
99
60
|
plugins: [vuetify],
|
|
100
61
|
},
|
|
@@ -102,17 +63,8 @@ describe('CustomInputSelect.vue', () => {
|
|
|
102
63
|
expect(wrapper.find('.v-messages__message').exists()).toBe(false)
|
|
103
64
|
})
|
|
104
65
|
|
|
105
|
-
it('does not render the label when not provided', () => {
|
|
106
|
-
const wrapper = mount(CustomSelect, {
|
|
107
|
-
global: {
|
|
108
|
-
plugins: [vuetify],
|
|
109
|
-
},
|
|
110
|
-
})
|
|
111
|
-
expect(wrapper.find('label').exists()).toBe(false)
|
|
112
|
-
})
|
|
113
|
-
|
|
114
66
|
it('returns the correct item text using getItemText', () => {
|
|
115
|
-
const wrapper = mount(
|
|
67
|
+
const wrapper = mount(SySelect, {
|
|
116
68
|
props: { textKey: 'text' },
|
|
117
69
|
global: {
|
|
118
70
|
plugins: [vuetify],
|
|
@@ -125,7 +77,7 @@ describe('CustomInputSelect.vue', () => {
|
|
|
125
77
|
})
|
|
126
78
|
|
|
127
79
|
it('returns default text when selectedItem is null', () => {
|
|
128
|
-
const wrapper = mount(
|
|
80
|
+
const wrapper = mount(SySelect, {
|
|
129
81
|
global: {
|
|
130
82
|
plugins: [vuetify],
|
|
131
83
|
},
|
|
@@ -136,7 +88,7 @@ describe('CustomInputSelect.vue', () => {
|
|
|
136
88
|
})
|
|
137
89
|
|
|
138
90
|
it('returns the correct text when selectedItem is an object', async () => {
|
|
139
|
-
const wrapper = mount(
|
|
91
|
+
const wrapper = mount(SySelect, {
|
|
140
92
|
props: { modelValue: { text: 'Option 1', value: '1' }, textKey: 'text' },
|
|
141
93
|
global: {
|
|
142
94
|
plugins: [vuetify],
|
|
@@ -150,7 +102,7 @@ describe('CustomInputSelect.vue', () => {
|
|
|
150
102
|
|
|
151
103
|
it('formats items correctly', () => {
|
|
152
104
|
const items = ['Option 1', 'Option 2']
|
|
153
|
-
const wrapper = mount(
|
|
105
|
+
const wrapper = mount(SySelect, {
|
|
154
106
|
props: { items, textKey: 'text', valueKey: 'value' },
|
|
155
107
|
global: {
|
|
156
108
|
plugins: [vuetify],
|
|
@@ -165,27 +117,27 @@ describe('CustomInputSelect.vue', () => {
|
|
|
165
117
|
})
|
|
166
118
|
|
|
167
119
|
it('applies the correct button class when outlined is true', () => {
|
|
168
|
-
const wrapper = mount(
|
|
120
|
+
const wrapper = mount(SySelect, {
|
|
169
121
|
props: { outlined: true },
|
|
170
122
|
global: {
|
|
171
123
|
plugins: [vuetify],
|
|
172
124
|
},
|
|
173
125
|
})
|
|
174
|
-
expect(wrapper.find('.
|
|
126
|
+
expect(wrapper.find('.v-field--variant-outlined').exists()).toBe(true)
|
|
175
127
|
})
|
|
176
128
|
|
|
177
129
|
it('does not apply the outlined button class when outlined is false', () => {
|
|
178
|
-
const wrapper = mount(
|
|
130
|
+
const wrapper = mount(SySelect, {
|
|
179
131
|
props: { outlined: false },
|
|
180
132
|
global: {
|
|
181
133
|
plugins: [vuetify],
|
|
182
134
|
},
|
|
183
135
|
})
|
|
184
|
-
expect(wrapper.find('.
|
|
136
|
+
expect(wrapper.find('.sy-select').classes()).not.toContain('v-btn--variant-outlined')
|
|
185
137
|
})
|
|
186
138
|
|
|
187
139
|
it('updates selectedItem when v-model changes', async () => {
|
|
188
|
-
const wrapper = mount(
|
|
140
|
+
const wrapper = mount(SySelect, {
|
|
189
141
|
props: { modelValue: { text: 'Option 1', value: '1' }, textKey: 'text' },
|
|
190
142
|
global: {
|
|
191
143
|
plugins: [vuetify],
|
|
@@ -200,7 +152,7 @@ describe('CustomInputSelect.vue', () => {
|
|
|
200
152
|
})
|
|
201
153
|
|
|
202
154
|
it('emits update:modelValue when selectedItem changes', async () => {
|
|
203
|
-
const wrapper = mount(
|
|
155
|
+
const wrapper = mount(SySelect, {
|
|
204
156
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- This is a generic type
|
|
205
157
|
props: { modelValue: null as any, textKey: 'text' },
|
|
206
158
|
global: {
|
|
@@ -213,4 +165,29 @@ describe('CustomInputSelect.vue', () => {
|
|
|
213
165
|
await wrapper.vm.$nextTick()
|
|
214
166
|
expect(wrapper.emitted()['update:modelValue'][0]).toEqual([{ text: 'Option 1', value: '1' }])
|
|
215
167
|
})
|
|
168
|
+
|
|
169
|
+
it('closes the menu when v-click-outside directive is called', async () => {
|
|
170
|
+
const wrapper = mount(SySelect, {
|
|
171
|
+
global: {
|
|
172
|
+
plugins: [vuetify],
|
|
173
|
+
},
|
|
174
|
+
})
|
|
175
|
+
await wrapper.find('.sy-select').trigger('click')
|
|
176
|
+
expect(wrapper.find('.v-list').exists()).toBe(true)
|
|
177
|
+
await wrapper.find('.sy-select').trigger('mouseleave')
|
|
178
|
+
await wrapper.find('.sy-select').trigger('click')
|
|
179
|
+
await wrapper.vm.$nextTick()
|
|
180
|
+
|
|
181
|
+
expect(wrapper.find('.v-list').exists()).toBe(false)
|
|
182
|
+
})
|
|
183
|
+
|
|
184
|
+
it('use closeList method', async () => {
|
|
185
|
+
const wrapper = mount(SySelect, {
|
|
186
|
+
global: {
|
|
187
|
+
plugins: [vuetify],
|
|
188
|
+
},
|
|
189
|
+
})
|
|
190
|
+
await wrapper.vm.closeList()
|
|
191
|
+
expect(wrapper.vm.isOpen).toBe(false)
|
|
192
|
+
})
|
|
216
193
|
})
|