@cnamts/synapse 0.0.0-alpha.0 → 0.0.2-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.
Files changed (55) hide show
  1. package/README.md +72 -2
  2. package/dist/design-system-v3.d.ts +234 -2
  3. package/dist/design-system-v3.js +3020 -2142
  4. package/dist/design-system-v3.umd.cjs +2 -2
  5. package/dist/style.css +1 -1
  6. package/package.json +11 -10
  7. package/src/components/CollapsibleList/CollapsibleList.mdx +47 -0
  8. package/src/components/CollapsibleList/CollapsibleList.stories.ts +52 -0
  9. package/src/components/CollapsibleList/CollapsibleList.vue +157 -0
  10. package/src/components/CollapsibleList/tests/CollapsibleList.spec.ts +60 -0
  11. package/src/components/CollapsibleList/types.d.ts +5 -0
  12. package/src/components/Customs/CustomInputSelect/CustomInputSelect.mdx +42 -0
  13. package/src/components/Customs/CustomInputSelect/CustomInputSelect.stories.ts +154 -0
  14. package/src/components/Customs/CustomInputSelect/CustomInputSelect.vue +185 -0
  15. package/src/components/Customs/CustomInputSelect/tests/CustomInputSelect.spec.ts +216 -0
  16. package/src/components/Customs/CustomSelect/CustomSelect.mdx +47 -0
  17. package/src/components/Customs/CustomSelect/CustomSelect.stories.ts +182 -0
  18. package/src/components/Customs/CustomSelect/CustomSelect.vue +188 -0
  19. package/src/components/Customs/CustomSelect/tests/CustomSelect.spec.ts +236 -0
  20. package/src/components/FooterBar/A11yCompliance.ts +9 -0
  21. package/src/components/FooterBar/FooterBar.mdx +115 -0
  22. package/src/components/FooterBar/FooterBar.stories.ts +632 -0
  23. package/src/components/FooterBar/FooterBar.vue +330 -0
  24. package/src/components/FooterBar/config.ts +20 -0
  25. package/src/components/FooterBar/defaultSocialMediaLinks.ts +21 -0
  26. package/src/components/FooterBar/locales.ts +16 -0
  27. package/src/components/FooterBar/tests/FooterBar.spec.ts +167 -0
  28. package/src/components/FooterBar/tests/FooterBarConfig.spec.ts +36 -0
  29. package/src/components/FooterBar/tests/__snapshots__/FooterBar.spec.ts.snap +27 -0
  30. package/src/components/FooterBar/types.d.ts +10 -0
  31. package/src/components/LangBtn/LangBtn.mdx +2 -1
  32. package/src/components/LangBtn/LangBtn.vue +3 -3
  33. package/src/components/Logo/Logo.mdx +26 -0
  34. package/src/components/Logo/Logo.stories.ts +217 -0
  35. package/src/components/Logo/Logo.vue +397 -0
  36. package/src/components/Logo/LogoSize.ts +7 -0
  37. package/src/components/Logo/locales.ts +6 -0
  38. package/src/components/Logo/logoDimensionsMapping.ts +16 -0
  39. package/src/components/Logo/tests/Logo.spec.ts +75 -0
  40. package/src/components/Logo/types.d.ts +8 -0
  41. package/src/components/SocialMediaLinks/DefaultSocialMediaLinks.ts +21 -0
  42. package/src/components/SocialMediaLinks/SocialMediaLinks.mdx +15 -0
  43. package/src/components/SocialMediaLinks/SocialMediaLinks.stories.ts +72 -0
  44. package/src/components/SocialMediaLinks/SocialMediaLinks.vue +92 -0
  45. package/src/components/SocialMediaLinks/locales.ts +3 -0
  46. package/src/components/SocialMediaLinks/tests/DefaultSocialMediaLinks.spec.ts +21 -0
  47. package/src/components/SocialMediaLinks/tests/SocialMediaLinks.spec.ts +89 -0
  48. package/src/components/SocialMediaLinks/tests/__snapshots__/SocialMediaLinks.spec.ts.snap +24 -0
  49. package/src/components/SocialMediaLinks/types.d.ts +5 -0
  50. package/src/components/index.ts +6 -0
  51. package/src/directives/clickOutside.ts +24 -0
  52. package/src/temp/TestDTComponent.vue +6 -10
  53. package/src/temp/gridsTests.vue +0 -4
  54. package/src/utils/propValidator/index.ts +20 -0
  55. package/src/utils/propValidator/tests/propValidator.spec.ts +40 -0
@@ -0,0 +1,182 @@
1
+ import type { Meta, StoryObj } from '@storybook/vue3'
2
+ import CustomSelect from '@/components/Customs/CustomSelect/CustomSelect.vue'
3
+ import { VBtn, VMenu, VList, VListItem, VListItemTitle } from 'vuetify/components'
4
+ import { ref } from 'vue'
5
+ import Alert from '../../Alert/Alert.vue'
6
+
7
+ const meta = {
8
+ title: 'Components/CustomSelect',
9
+ component: CustomSelect,
10
+ parameters: {
11
+ layout: 'fullscreen',
12
+ controls: { exclude: ['selectedValue'] },
13
+ },
14
+ argTypes: {
15
+ selectedValue: { control: 'text' },
16
+ items: { control: 'object' },
17
+ errorMessages: { control: 'object' },
18
+ required: { control: 'boolean' },
19
+ },
20
+ } as Meta<typeof CustomSelect>
21
+
22
+ export default meta
23
+
24
+ type Story = StoryObj<typeof meta>
25
+ export const Default: Story = {
26
+ args: {
27
+ items: [
28
+ { text: 'Option 1', value: '1' },
29
+ { text: 'Option 2', value: '2' },
30
+ ],
31
+ },
32
+ render: (args) => {
33
+ return {
34
+ components: { CustomSelect, VBtn, VMenu, VList, VListItem, VListItemTitle },
35
+ setup() {
36
+ return { args }
37
+ },
38
+ template: `
39
+ <div class="d-flex flex-wrap align-center pa-4">
40
+ <CustomSelect
41
+ v-bind="args"
42
+ />
43
+ </div>
44
+ <br/><br/><br/><br/>
45
+ `,
46
+ }
47
+ },
48
+ }
49
+
50
+ export const Outlined: Story = {
51
+ args: {
52
+ items: [
53
+ { text: 'Option 1', value: '1' },
54
+ { text: 'Option 2', value: '2' },
55
+ ],
56
+ },
57
+ render: (args) => {
58
+ return {
59
+ components: { CustomSelect, VBtn, VMenu, VList, VListItem, VListItemTitle },
60
+ setup() {
61
+ return { args }
62
+ },
63
+ template: `
64
+ <div class="d-flex flex-wrap align-center pa-4" style="z-index: 99999">
65
+ <CustomSelect
66
+ v-bind="args"
67
+ outlined
68
+ />
69
+ </div>
70
+ `,
71
+ }
72
+ },
73
+ }
74
+
75
+ export const Required: Story = {
76
+ args: {
77
+ items: [
78
+ { text: 'Option 1', value: '1' },
79
+ { text: 'Option 2', value: '2' },
80
+ ],
81
+ },
82
+ render: (args) => {
83
+ return {
84
+ components: { CustomSelect },
85
+ setup() {
86
+ return { args }
87
+ },
88
+ template: `
89
+ <div class="d-flex flex-wrap align-center pa-4">
90
+ <CustomSelect
91
+ v-bind="args"
92
+ required
93
+ />
94
+ </div>
95
+ `,
96
+ }
97
+ },
98
+ }
99
+
100
+ export const withCustomError: Story = {
101
+ args: {
102
+ items: [
103
+ { text: 'Option 1', value: '1' },
104
+ { text: 'Option 2', value: '2' },
105
+ ],
106
+ },
107
+ render: (args) => {
108
+ return {
109
+ components: { CustomSelect, VBtn, VMenu, VList, VListItem, VListItemTitle },
110
+ setup() {
111
+ const errorMessages = ref([])
112
+ const triggerError = () => {
113
+ // @ts-expect-error test error message
114
+ errorMessages.value = ['This is a test error message']
115
+ }
116
+ return { args, errorMessages, triggerError }
117
+ },
118
+ template: `
119
+ <div class="d-flex flex-wrap align-center pa-4">
120
+ <CustomSelect
121
+ v-bind="args"
122
+ :error-messages="errorMessages"
123
+ />
124
+ </div>
125
+ <div class="d-flex flex-wrap align-center px-4">
126
+ <VBtn @click="triggerError">
127
+ Trigger Error
128
+ </VBtn>
129
+ </div>
130
+ `,
131
+ }
132
+ },
133
+ }
134
+
135
+ export const withCustomKey: Story = {
136
+ args: {
137
+ items: [
138
+ { customKey: 'Choix 1', value: '1' },
139
+ { customKey: 'Choix 2', value: '2' },
140
+ ],
141
+ },
142
+ render: (args) => {
143
+ return {
144
+ components: { CustomSelect, VBtn, VMenu, VList, VListItem, VListItemTitle },
145
+ setup() {
146
+ return { args }
147
+ },
148
+ template: `
149
+ <div class="d-flex flex-wrap align-center pa-4">
150
+ <CustomSelect
151
+ v-bind="args"
152
+ outlined
153
+ text-key="customKey"
154
+ />
155
+ </div>
156
+ `,
157
+ }
158
+ },
159
+ }
160
+
161
+ export const Info: Story = {
162
+ render: (args) => {
163
+ return {
164
+ components: { Alert },
165
+ setup() {
166
+ return { args }
167
+ },
168
+ template: `
169
+ <Alert v-model="args.modelValue" :type="args.type" :variant="tonal" :closable="false">
170
+ <template #default>
171
+ <b>Format des items :</b>
172
+ <ul>
173
+ <li>- Si les items passés en props sont des objets, le composant les utilisera directement.</li>
174
+ <li>- Si les items sont un tableau de string, le composant les utilisera directement.</li>
175
+ </ul>
176
+ </template>
177
+ </Alert>
178
+ `,
179
+ }
180
+ },
181
+ tags: ['!dev'],
182
+ }
@@ -0,0 +1,188 @@
1
+ <script setup lang="ts">
2
+ import { mdiMenuDown } from '@mdi/js'
3
+ import { ref, watch, computed, type PropType } from 'vue'
4
+ import { VIcon, VTextField, VList, VListItem, VListItemTitle } from 'vuetify/components'
5
+
6
+ const props = defineProps({
7
+ modelValue: {
8
+ type: [Object, String],
9
+ default: null,
10
+ },
11
+ items: {
12
+ type: Array,
13
+ default: () => [],
14
+ },
15
+ label: {
16
+ type: String,
17
+ default: 'Sélectionnez une option',
18
+ },
19
+ errorMessages: {
20
+ type: [String, Array] as PropType<string | readonly string[]>,
21
+ default: () => [],
22
+ },
23
+ required: {
24
+ type: Boolean,
25
+ default: false,
26
+ },
27
+ menuId: {
28
+ type: String,
29
+ default: 'custom-select-menu',
30
+ },
31
+ outlined: {
32
+ type: Boolean,
33
+ default: false,
34
+ },
35
+ textKey: {
36
+ type: String,
37
+ default: 'text',
38
+ },
39
+ valueKey: {
40
+ type: String,
41
+ default: 'value',
42
+ },
43
+ })
44
+
45
+ const emit = defineEmits(['update:modelValue'])
46
+
47
+ const isOpen = ref(false)
48
+ const selectedItem = ref<Record<string, unknown > | string | null>(props.modelValue)
49
+
50
+ const toggleMenu = () => {
51
+ isOpen.value = !isOpen.value
52
+ }
53
+ const closeList = () => {
54
+ isOpen.value = false
55
+ }
56
+ const inputId = ref(`custom-select-${Math.random().toString(36).substring(7)}`)
57
+
58
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- This is a generic type
59
+ const selectItem = (item: any) => {
60
+ selectedItem.value = item
61
+ emit('update:modelValue', item)
62
+ isOpen.value = false
63
+ }
64
+
65
+ const getItemText = (item: unknown) => {
66
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- This is a generic type
67
+ return (item as Record<string, any>)[props.textKey]
68
+ }
69
+
70
+ const selectedItemText = computed(() => {
71
+ if (selectedItem.value && typeof selectedItem.value === 'object') {
72
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- This is a generic type
73
+ return (selectedItem.value as Record<string, any>)[props.textKey]
74
+ }
75
+ return props.label
76
+ })
77
+
78
+ const formattedItems = computed(() => {
79
+ return props.items.map((item) => {
80
+ if (typeof item === 'string') {
81
+ return { [props.textKey]: item, [props.valueKey]: item }
82
+ }
83
+ return item
84
+ })
85
+ })
86
+
87
+ const isRequired = computed(() => {
88
+ return (props.required || props.errorMessages.length > 0) && !selectedItem.value
89
+ })
90
+
91
+ watch(() => props.modelValue, (newValue) => {
92
+ selectedItem.value = newValue
93
+ })
94
+ </script>
95
+
96
+ <template>
97
+ <div class="d-block w-100">
98
+ <VTextField
99
+ :id="inputId"
100
+ ref="input"
101
+ v-model="selectedItemText"
102
+ v-click-outside="closeList"
103
+ title="Sélectionnez une option"
104
+ color="primary"
105
+ tabindex="0"
106
+ readonly
107
+ :label="selectedItem ? label : ''"
108
+ :aria-label="selectedItem ? label : 'Sélectionnez une option'"
109
+ :error-messages="errorMessages"
110
+ :variant="outlined ? 'outlined' : 'underlined'"
111
+ :rules="isRequired ? ['Le champ est requis.'] : []"
112
+ class="custom-select"
113
+ @click="toggleMenu"
114
+ @keydown.enter.prevent="toggleMenu"
115
+ @keydown.space.prevent="toggleMenu"
116
+ >
117
+ <VIcon>{{ mdiMenuDown }}</VIcon>
118
+ </VTextField>
119
+ <VList
120
+ v-if="isOpen"
121
+ class="v-list"
122
+ :style="`min-width: ${$refs.input?.$el.offsetWidth}px`"
123
+ @keydown.esc.prevent="isOpen = false"
124
+ >
125
+ <VListItem
126
+ v-for="(item, index) in formattedItems"
127
+ :key="index"
128
+ :ref="'options-' + index"
129
+ role="option"
130
+ class="v-list-item"
131
+ :aria-selected="selectedItem === item"
132
+ :tabindex="index + 1"
133
+ @click="selectItem(item)"
134
+ >
135
+ <VListItemTitle>
136
+ {{ getItemText(item) }}
137
+ </VListItemTitle>
138
+ </VListItem>
139
+ </VList>
140
+ </div>
141
+ </template>
142
+
143
+ <style scoped lang="scss">
144
+ @use '@/assets/tokens.scss';
145
+
146
+ .custom-select {
147
+ display: flex;
148
+ flex-direction: column;
149
+ }
150
+
151
+ .v-field {
152
+ position: relative;
153
+ }
154
+ .v-field--focused {
155
+ .v-icon {
156
+ transform: rotateX(180deg) translateY(50%);
157
+ }
158
+ }
159
+
160
+ .v-list {
161
+ position: absolute;
162
+ left: inherit !important;
163
+ margin-top: -22px;
164
+ background-color: white;
165
+ max-height: 300px;
166
+ padding: 0;
167
+ box-shadow: 0 2px 5px rgba(0, 0, 0, 0.12), 0 2px 10px rgba(0, 0, 0, 0.08);
168
+ border-radius: 4px;
169
+ overflow-y: auto;
170
+ z-index: 2;
171
+ }
172
+
173
+ .v-list-item:hover {
174
+ background-color: rgba(0, 0, 0, 0.04);
175
+ }
176
+
177
+ .v-list-item[aria-selected='true'] {
178
+ background-color: rgba(0, 0, 0, 0.08);
179
+ }
180
+
181
+ .v-icon {
182
+ position: absolute;
183
+ right: 10px;
184
+ top: 50%;
185
+ transform: translateY(-50%);
186
+ color: tokens.$grey-darken-20;
187
+ }
188
+ </style>
@@ -0,0 +1,236 @@
1
+ import { mount } from '@vue/test-utils'
2
+ import { expect, describe, it } from 'vitest'
3
+ import CustomSelect from '../CustomSelect.vue'
4
+ import { vuetify } from '@tests/unit/setup'
5
+
6
+ describe('customSelect.vue', () => {
7
+ it('renders the component with default props', () => {
8
+ const wrapper = mount(CustomSelect, {
9
+ global: {
10
+ plugins: [vuetify],
11
+ },
12
+ })
13
+ expect(wrapper.exists()).toBe(true)
14
+ expect(wrapper.find('.custom-select').text()).toBe('')
15
+ })
16
+
17
+ it('toggles the menu when clicked', async () => {
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 () => {
46
+ const items = [{ text: 'Option 1', value: '1' }, { text: 'Option 2', value: '2' }]
47
+ const wrapper = mount(CustomSelect, {
48
+ props: { items },
49
+ global: {
50
+ plugins: [vuetify],
51
+ },
52
+ })
53
+ await wrapper.find('.custom-select').trigger('click')
54
+ const firstItem = wrapper.findAll('.v-list-item').at(0)
55
+ if (firstItem) {
56
+ await firstItem.trigger('click')
57
+ }
58
+ expect(wrapper.find('.v-list').exists()).toBe(false)
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
+ await wrapper.find('.custom-select').trigger('click')
70
+ const firstItem = wrapper.findAll('.v-list-item').at(0)
71
+ if (firstItem) {
72
+ await firstItem.trigger('click')
73
+ }
74
+ expect(wrapper.find('input').element.value).toBe('Option 1')
75
+ })
76
+
77
+ it('closes the menu on escape key press', async () => {
78
+ const items = [{ text: 'Option 1', value: '1' }, { text: 'Option 2', value: '2' }]
79
+ const wrapper = mount(CustomSelect, {
80
+ props: { items },
81
+ global: {
82
+ plugins: [vuetify],
83
+ },
84
+ })
85
+ await wrapper.find('.custom-select').trigger('click')
86
+ await wrapper.find('.v-list').trigger('keydown.esc')
87
+ expect(wrapper.find('.v-list').exists()).toBe(false)
88
+ })
89
+
90
+ it('renders error messages when provided', () => {
91
+ const errorMessages = ['Error 1']
92
+ const wrapper = mount(CustomSelect, {
93
+ props: { errorMessages },
94
+ global: {
95
+ plugins: [vuetify],
96
+ },
97
+ })
98
+ expect(wrapper.find('.v-messages__message').text()).toContain('Error 1')
99
+ })
100
+
101
+ it('does not render error messages when not provided', () => {
102
+ const wrapper = mount(CustomSelect, {
103
+ global: {
104
+ plugins: [vuetify],
105
+ },
106
+ })
107
+ expect(wrapper.find('.v-messages__message').exists()).toBe(false)
108
+ })
109
+
110
+ it('does not render the label when not provided', () => {
111
+ const wrapper = mount(CustomSelect, {
112
+ global: {
113
+ plugins: [vuetify],
114
+ },
115
+ })
116
+ expect(wrapper.find('.v-label').text()).toBe('')
117
+ })
118
+
119
+ it('returns the correct item text using getItemText', () => {
120
+ const wrapper = mount(CustomSelect, {
121
+ props: { textKey: 'text' },
122
+ global: {
123
+ plugins: [vuetify],
124
+ },
125
+ })
126
+ const item = { text: 'Option 1', value: '1' }
127
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- This is a generic type
128
+ const instance = wrapper.vm as any
129
+ expect(instance.getItemText(item)).toBe('Option 1')
130
+ })
131
+
132
+ it('returns default text when selectedItem is null', () => {
133
+ const wrapper = mount(CustomSelect, {
134
+ global: {
135
+ plugins: [vuetify],
136
+ },
137
+ })
138
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- This is a generic type
139
+ const instance = wrapper.vm as any
140
+ expect(instance.selectedItemText).toBe('Sélectionnez une option')
141
+ })
142
+
143
+ it('returns the correct text when selectedItem is an object', async () => {
144
+ const wrapper = mount(CustomSelect, {
145
+ props: { modelValue: { text: 'Option 1', value: '1' }, textKey: 'text' },
146
+ global: {
147
+ plugins: [vuetify],
148
+ },
149
+ })
150
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- This is a generic type
151
+ const instance = wrapper.vm as any
152
+ await wrapper.setProps({ modelValue: { text: 'Option 1', value: '1' } })
153
+ expect(instance.selectedItemText).toBe('Option 1')
154
+ })
155
+
156
+ it('formats items correctly', () => {
157
+ const items = ['Option 1', 'Option 2']
158
+ const wrapper = mount(CustomSelect, {
159
+ props: { items, textKey: 'text', valueKey: 'value' },
160
+ global: {
161
+ plugins: [vuetify],
162
+ },
163
+ })
164
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- This is a generic type
165
+ const formattedItems = (wrapper.vm as any).formattedItems
166
+ expect(formattedItems).toEqual([
167
+ { text: 'Option 1', value: 'Option 1' },
168
+ { text: 'Option 2', value: 'Option 2' },
169
+ ])
170
+ })
171
+
172
+ it('applies the correct button class when outlined is true', () => {
173
+ const wrapper = mount(CustomSelect, {
174
+ props: { outlined: true },
175
+ global: {
176
+ plugins: [vuetify],
177
+ },
178
+ })
179
+ expect(wrapper.find('.v-field--variant-outlined').exists()).toBe(true)
180
+ })
181
+
182
+ it('does not apply the outlined button class when outlined is false', () => {
183
+ const wrapper = mount(CustomSelect, {
184
+ props: { outlined: false },
185
+ global: {
186
+ plugins: [vuetify],
187
+ },
188
+ })
189
+ expect(wrapper.find('.custom-select').classes()).not.toContain('v-btn--variant-outlined')
190
+ })
191
+
192
+ it('updates selectedItem when v-model changes', async () => {
193
+ const wrapper = mount(CustomSelect, {
194
+ props: { modelValue: { text: 'Option 1', value: '1' }, textKey: 'text' },
195
+ global: {
196
+ plugins: [vuetify],
197
+ },
198
+ })
199
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- This is a generic type
200
+ const instance = wrapper.vm as any
201
+ expect(instance.selectedItem).toEqual({ text: 'Option 1', value: '1' })
202
+
203
+ await wrapper.setProps({ modelValue: { text: 'Option 2', value: '2' } })
204
+ expect(instance.selectedItem).toEqual({ text: 'Option 2', value: '2' })
205
+ })
206
+
207
+ it('emits update:modelValue when selectedItem changes', async () => {
208
+ const wrapper = mount(CustomSelect, {
209
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- This is a generic type
210
+ props: { modelValue: null as any, textKey: 'text' },
211
+ global: {
212
+ plugins: [vuetify],
213
+ },
214
+ })
215
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- This is a generic type
216
+ const instance = wrapper.vm as any
217
+ instance.selectItem({ text: 'Option 1', value: '1' })
218
+ await wrapper.vm.$nextTick()
219
+ expect(wrapper.emitted()['update:modelValue'][0]).toEqual([{ text: 'Option 1', value: '1' }])
220
+ })
221
+
222
+ it('closes the menu when v-click-outside directive is called', async () => {
223
+ const wrapper = mount(CustomSelect, {
224
+ global: {
225
+ plugins: [vuetify],
226
+ },
227
+ })
228
+ await wrapper.find('.custom-select').trigger('click')
229
+ expect(wrapper.find('.v-list').exists()).toBe(true)
230
+ await wrapper.find('.custom-select').trigger('mouseleave')
231
+ await wrapper.find('.custom-select').trigger('click')
232
+ await wrapper.vm.$nextTick()
233
+
234
+ expect(wrapper.find('.v-list').exists()).toBe(false)
235
+ })
236
+ })
@@ -0,0 +1,9 @@
1
+ export type A11yComplianceType = 'non-compliant' | 'partially-compliant' | 'fully-compliant'
2
+
3
+ export const A11yComplianceEnum: Record<A11yComplianceType, A11yComplianceType> = {
4
+ 'non-compliant': 'non-compliant',
5
+ 'partially-compliant': 'partially-compliant',
6
+ 'fully-compliant': 'fully-compliant',
7
+ }
8
+
9
+ export const A11Y_COMPLIANCE_ENUM_VALUES = Object.values(A11yComplianceEnum)