@cnamts/synapse 1.0.18 → 1.0.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{DateFilter-C0qj2Xp4.js → DateFilter-CeVuSfJ9.js} +1 -1
- package/dist/{NumberFilter-DLlqjkwg.js → NumberFilter-C8PAu_sw.js} +1 -1
- package/dist/{PeriodFilter-BQCGbydv.js → PeriodFilter-UMUaxx3d.js} +1 -1
- package/dist/{SelectFilter-CPnShk0h.js → SelectFilter-CqZl8CYt.js} +1 -1
- package/dist/{TextFilter-6N05mkcs.js → TextFilter-D_RhhNOh.js} +1 -1
- package/dist/components/Amelipro/AmeliproAutoCompleteField/AmeliproAutoCompleteField.d.ts +2 -2
- package/dist/components/Amelipro/AmeliproCaptcha/AmeliproCaptcha.d.ts +1 -1
- package/dist/components/Amelipro/AmeliproCard/AmeliproCard.d.ts +1 -1
- package/dist/components/Amelipro/AmeliproClickableTile/AmeliproClickableTile.d.ts +1 -1
- package/dist/components/Amelipro/AmeliproFilters/types.d.ts +6 -0
- package/dist/components/Captcha/Captcha.d.ts +1 -1
- package/dist/components/{Customs/Selects/SyBtnSelect/SyBtnSelect.d.ts → SyBtnMenu/SyBtnMenu.d.ts} +2 -17
- package/dist/components/UserMenuBtn/UserMenuBtn.d.ts +2 -2
- package/dist/components/index.d.ts +21 -21
- package/dist/design-system-v3.js +10 -10
- package/dist/design-system-v3.umd.cjs +16 -16
- package/dist/{main-CY1bof-3.js → main-B39UVv5p.js} +9018 -8993
- package/dist/style.css +1 -1
- package/package.json +2 -2
- package/src/components/Accordion/Accessibilite/AccessibilityGuide.mdx +1 -1
- package/src/components/Amelipro/AmeliproFilters/AmeliproFilters.mdx +14 -0
- package/src/components/Amelipro/AmeliproFilters/AmeliproFilters.stories.ts +106 -0
- package/src/components/Amelipro/AmeliproFilters/AmeliproFilters.vue +218 -0
- package/src/components/Amelipro/AmeliproFilters/__tests__/AmeliproFilters.spec.ts +180 -0
- package/src/components/Amelipro/AmeliproFilters/types.d.ts +6 -0
- package/src/components/Customs/Selects/SelectOverview.mdx +0 -68
- package/src/components/DatePicker/CalendarMode/tests/DatePicker.spec.ts +19 -6
- package/src/components/{Customs/Selects/SyBtnSelect/SyBtnSelect.mdx → SyBtnMenu/SyBtnMenu.mdx} +8 -9
- package/src/components/{Customs/Selects/SyBtnSelect/SyBtnSelect.stories.ts → SyBtnMenu/SyBtnMenu.stories.ts} +80 -78
- package/src/components/{Customs/Selects/SyBtnSelect/SyBtnSelect.vue → SyBtnMenu/SyBtnMenu.vue} +84 -24
- package/src/components/SyBtnMenu/accessibilite/AccessibilityGuide.mdx +77 -0
- package/src/components/{Customs/Selects/SyBtnSelect/tests/SyBtnSelect.spec.ts → SyBtnMenu/tests/SyBtnMenu.spec.ts} +38 -11
- package/src/components/UserMenuBtn/UserMenuBtn.vue +3 -3
- package/src/components/UserMenuBtn/tests/UserMenuBtn.spec.ts +3 -3
- package/src/components/index.ts +21 -21
- package/src/composables/date/tests/useDateFormatDayjs.spec.ts +19 -0
- package/src/composables/date/useDateFormatDayjs.ts +4 -4
- package/src/stories/Accessibilite/Avancement/Avancement.stories.ts +1 -1
- package/src/stories/Accessibilite/Vuetify/VuetifyItems.ts +0 -4
- package/src/stories/Components/Components.stories.ts +2 -2
- package/src/stories/GuideDuDev/PortailAgent.stories.ts +0 -1
- package/src/components/Customs/Selects/SyBtnSelect/Accessibilite.mdx +0 -13
- package/src/components/Customs/Selects/SyBtnSelect/Accessibilite.stories.ts +0 -25
package/src/components/{Customs/Selects/SyBtnSelect/SyBtnSelect.vue → SyBtnMenu/SyBtnMenu.vue}
RENAMED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<script lang="ts" setup>
|
|
2
|
-
import { ref, watch, computed, onMounted, useSlots, type PropType } from 'vue'
|
|
2
|
+
import { ref, watch, computed, onMounted, useSlots, type PropType, nextTick } from 'vue'
|
|
3
3
|
import { useDisplay } from 'vuetify'
|
|
4
|
+
import slugify from 'slugify'
|
|
4
5
|
|
|
5
6
|
type Item = string | Record<string, unknown>
|
|
6
7
|
|
|
@@ -15,7 +16,7 @@
|
|
|
15
16
|
},
|
|
16
17
|
label: {
|
|
17
18
|
type: String,
|
|
18
|
-
default: '
|
|
19
|
+
default: 'Menu utilisateur',
|
|
19
20
|
},
|
|
20
21
|
required: {
|
|
21
22
|
type: Boolean,
|
|
@@ -69,10 +70,6 @@
|
|
|
69
70
|
|
|
70
71
|
const isOpen = ref(false)
|
|
71
72
|
const selectedItem = ref<Record<string, unknown> | string | null>(props.modelValue as Record<string, unknown> | string | null)
|
|
72
|
-
const toggleMenu = () => {
|
|
73
|
-
isOpen.value = !isOpen.value
|
|
74
|
-
}
|
|
75
|
-
|
|
76
73
|
const buttonRef = ref<HTMLElement | null>(null)
|
|
77
74
|
const buttonWidth = ref('')
|
|
78
75
|
|
|
@@ -101,7 +98,7 @@
|
|
|
101
98
|
if (props.hideIcon) {
|
|
102
99
|
return 'pa-1 pa-sm-2'
|
|
103
100
|
}
|
|
104
|
-
return isMobileVersion.value ? 'pa-
|
|
101
|
+
return isMobileVersion.value ? 'pa-1' : 'pa-1 pa-sm-3'
|
|
105
102
|
})
|
|
106
103
|
|
|
107
104
|
const hasListContent = computed(() => {
|
|
@@ -118,6 +115,27 @@
|
|
|
118
115
|
|
|
119
116
|
const generatedId = ref(`custom-btn-select-${Math.random().toString(36).substring(7)}`)
|
|
120
117
|
|
|
118
|
+
function getSelectedValue() {
|
|
119
|
+
if (!selectedItem.value) return undefined
|
|
120
|
+
if (typeof selectedItem.value === 'string') return selectedItem.value
|
|
121
|
+
return selectedItem.value[props.textKey] as string
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const menu = ref<InstanceType<typeof import('vuetify/components').VList> | null>(null)
|
|
125
|
+
|
|
126
|
+
watch(isOpen, async (newVal) => {
|
|
127
|
+
if (newVal) {
|
|
128
|
+
await nextTick()
|
|
129
|
+
if (menu.value?.$el) {
|
|
130
|
+
menu.value.$el.querySelector('[role="menuitem"]')?.focus()
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
else {
|
|
134
|
+
await nextTick()
|
|
135
|
+
document.getElementById(generatedId.value)!.focus()
|
|
136
|
+
}
|
|
137
|
+
})
|
|
138
|
+
|
|
121
139
|
defineExpose({
|
|
122
140
|
isOpen,
|
|
123
141
|
formattedItems,
|
|
@@ -131,7 +149,9 @@
|
|
|
131
149
|
class="sy-user-menu-btn-ctn d-inline-block"
|
|
132
150
|
>
|
|
133
151
|
<VMenu
|
|
134
|
-
:id="
|
|
152
|
+
:id="$props.menuId"
|
|
153
|
+
v-model="isOpen"
|
|
154
|
+
class="sy-user-menu"
|
|
135
155
|
:disabled="!hasListContent"
|
|
136
156
|
location="bottom end"
|
|
137
157
|
transition="fade-transition"
|
|
@@ -152,17 +172,16 @@
|
|
|
152
172
|
...menuProps,
|
|
153
173
|
...props.options['btn'],
|
|
154
174
|
}"
|
|
155
|
-
@click="toggleMenu"
|
|
156
175
|
>
|
|
157
|
-
<
|
|
176
|
+
<span
|
|
158
177
|
:class="['text-'+props?.options['btn']?.color]"
|
|
159
|
-
class="d-flex align-center"
|
|
178
|
+
class="d-flex align-center ga-2"
|
|
160
179
|
>
|
|
161
180
|
<slot name="prepend-icon" />
|
|
162
181
|
<span class="d-sr-only">{{ props.label }}</span>
|
|
163
182
|
<span
|
|
164
183
|
v-if="!isMobileVersion && !iconOnly"
|
|
165
|
-
class="d-flex flex-column align-end py-1
|
|
184
|
+
class="d-flex flex-column align-end py-1"
|
|
166
185
|
>
|
|
167
186
|
<span
|
|
168
187
|
:class="`text-${props?.options['btn']?.textColor}`"
|
|
@@ -172,7 +191,7 @@
|
|
|
172
191
|
</span>
|
|
173
192
|
<span
|
|
174
193
|
:class="`text-${props?.options['btn']?.textColor}`"
|
|
175
|
-
class="text-grey text-darken-2
|
|
194
|
+
class="subtitle text-grey text-darken-2 font-weight-regular"
|
|
176
195
|
>
|
|
177
196
|
{{ props.secondaryInfo }}
|
|
178
197
|
</span>
|
|
@@ -185,27 +204,41 @@
|
|
|
185
204
|
{{ props.primaryInfo }}
|
|
186
205
|
</span>
|
|
187
206
|
<slot name="append-icon" />
|
|
188
|
-
</
|
|
207
|
+
</span>
|
|
189
208
|
</VBtn>
|
|
190
209
|
</template>
|
|
191
210
|
<slot name="content">
|
|
192
211
|
<VList
|
|
193
212
|
v-if="hasListContent"
|
|
213
|
+
ref="menu"
|
|
214
|
+
tag="ul"
|
|
215
|
+
role="menu"
|
|
194
216
|
v-bind="props.options['list']"
|
|
217
|
+
:aria-labelledby="generatedId"
|
|
218
|
+
:aria-activedescendant="getSelectedValue() ? `item-${slugify(getSelectedValue()!)}` : undefined"
|
|
195
219
|
>
|
|
196
|
-
<
|
|
220
|
+
<li
|
|
197
221
|
v-for="(item, index) in formattedItems"
|
|
222
|
+
:id="`item-${slugify(item[props.textKey] as string)}`"
|
|
198
223
|
:key="index"
|
|
199
|
-
:class="`text-${props?.options['list']?.textColor}`"
|
|
200
|
-
v-bind="props.options['list']"
|
|
201
|
-
:href="item.link"
|
|
202
|
-
:to="item.to"
|
|
203
|
-
@click="selectItem(item)"
|
|
204
224
|
>
|
|
205
|
-
<
|
|
206
|
-
{
|
|
207
|
-
|
|
208
|
-
|
|
225
|
+
<VListItem
|
|
226
|
+
:class="`text-${props?.options['list']?.textColor}`"
|
|
227
|
+
v-bind="props.options['list']"
|
|
228
|
+
:href="item.link"
|
|
229
|
+
:to="item.to"
|
|
230
|
+
:tabindex="0"
|
|
231
|
+
role="menuitem"
|
|
232
|
+
:aria-current="selectedItem === item ? 'page' : undefined"
|
|
233
|
+
@click="selectItem(item)"
|
|
234
|
+
>
|
|
235
|
+
<VListItemTitle
|
|
236
|
+
class="item-title"
|
|
237
|
+
>
|
|
238
|
+
{{ item[props.textKey] }}
|
|
239
|
+
</VListItemTitle>
|
|
240
|
+
</VListItem>
|
|
241
|
+
</li>
|
|
209
242
|
<slot />
|
|
210
243
|
<slot name="footer-list-item" />
|
|
211
244
|
</VList>
|
|
@@ -247,9 +280,36 @@
|
|
|
247
280
|
&:focus {
|
|
248
281
|
background: rgba(tokens.$blue-base, 0.08) !important;
|
|
249
282
|
}
|
|
283
|
+
|
|
284
|
+
.subtitle {
|
|
285
|
+
font-size: 0.875rem;
|
|
286
|
+
line-height: 1.5;
|
|
287
|
+
}
|
|
250
288
|
}
|
|
251
289
|
|
|
252
290
|
:deep(.sy-user-menu-btn:focus > .v-btn__overlay) {
|
|
253
291
|
opacity: 0 !important;
|
|
254
292
|
}
|
|
293
|
+
|
|
294
|
+
.v-btn:focus-visible {
|
|
295
|
+
outline: 2px solid rgb(var(--v-theme-primary));
|
|
296
|
+
outline-offset: 2px;
|
|
297
|
+
|
|
298
|
+
:deep(.v-btn__overlay) {
|
|
299
|
+
opacity: 0;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
&::after {
|
|
303
|
+
opacity: 0;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
:global(.sy-user-menu .v-list-item:focus) {
|
|
308
|
+
outline: 2px solid rgb(var(--v-theme-primary));
|
|
309
|
+
outline-offset: -2px;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
.item-title {
|
|
313
|
+
white-space: wrap;
|
|
314
|
+
}
|
|
255
315
|
</style>
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { Meta, Primary } from '@storybook/blocks';
|
|
2
|
+
import * as SyBtnMenuStories from '../SyBtnMenu.stories.ts';
|
|
3
|
+
import AccessibilityIcon from '@/common/imgs/accessibility-svgrepo-com.svg';
|
|
4
|
+
import {
|
|
5
|
+
AccessibilityGuideLayout,
|
|
6
|
+
CriteriaSection,
|
|
7
|
+
CriteriaCard,
|
|
8
|
+
DemoSection,
|
|
9
|
+
BestPracticesSection,
|
|
10
|
+
ResourcesSection,
|
|
11
|
+
} from '@/stories/accessibility/AccessibilityGuideLayout.mdx';
|
|
12
|
+
|
|
13
|
+
<Meta of={SyBtnMenuStories} />
|
|
14
|
+
|
|
15
|
+
<AccessibilityGuideLayout
|
|
16
|
+
componentName="SyBtnMenu"
|
|
17
|
+
iconSrc={AccessibilityIcon}
|
|
18
|
+
apgHref="https://www.w3.org/WAI/ARIA/apg/patterns/menu-button/"
|
|
19
|
+
>
|
|
20
|
+
<CriteriaSection>
|
|
21
|
+
<CriteriaCard icon="🔍" title="Structure sémantique">
|
|
22
|
+
<ul>
|
|
23
|
+
<li><strong>Rôles ARIA appropriés</strong> : <code>role="menu"</code> pour la liste (<code><ul></code>), <code>role="menuitem"</code> pour chaque option (<code><li></code>)</li>
|
|
24
|
+
<li><strong>Attributs ARIA</strong> : <code>aria-haspopup</code>, <code>aria-expanded</code>, <code>aria-controls</code> sur le bouton, <code>aria-activedescendant</code> sur le menu</li>
|
|
25
|
+
<li><strong>Liens sémantiques</strong> : <code>aria-labelledby</code> pour relier le menu au bouton</li>
|
|
26
|
+
</ul>
|
|
27
|
+
</CriteriaCard>
|
|
28
|
+
|
|
29
|
+
<CriteriaCard icon="⌨️" title="Navigation clavier complète">
|
|
30
|
+
<ul>
|
|
31
|
+
<li><strong>Tabulation</strong> : Accès au bouton puis navigation dans le menu</li>
|
|
32
|
+
<li><strong>Touches fléchées</strong> : Navigation verticale dans les options</li>
|
|
33
|
+
<li><strong>Entrée/Espace</strong> : Activation d'une option</li>
|
|
34
|
+
<li><strong>Échappement</strong> : Fermeture du menu et retour au bouton</li>
|
|
35
|
+
</ul>
|
|
36
|
+
</CriteriaCard>
|
|
37
|
+
|
|
38
|
+
<CriteriaCard icon="📱" title="États et retours d'information">
|
|
39
|
+
<ul>
|
|
40
|
+
<li><strong>État d'ouverture</strong> : <code>aria-expanded</code> reflète l'état du menu</li>
|
|
41
|
+
<li><strong>Focus visible</strong> : Indication claire de l'élément focalisé</li>
|
|
42
|
+
<li><strong>Retour visuel</strong> : Styles pour l'état actif/sélectionné</li>
|
|
43
|
+
</ul>
|
|
44
|
+
</CriteriaCard>
|
|
45
|
+
|
|
46
|
+
<CriteriaCard icon="🎨" title="Personnalisation accessible">
|
|
47
|
+
<ul>
|
|
48
|
+
<li><strong>Options de personnalisation</strong> : Couleurs, tailles, et icônes adaptables</li>
|
|
49
|
+
<li><strong>Compatibilité mobile</strong> : Utilisation adaptée sur petits écrans</li>
|
|
50
|
+
</ul>
|
|
51
|
+
</CriteriaCard>
|
|
52
|
+
</CriteriaSection>
|
|
53
|
+
|
|
54
|
+
<DemoSection componentName="SyBtnMenu">
|
|
55
|
+
<Primary />
|
|
56
|
+
</DemoSection>
|
|
57
|
+
|
|
58
|
+
<BestPracticesSection>
|
|
59
|
+
<ul>
|
|
60
|
+
<li>Utilisez un libellé explicite pour le bouton d'ouverture du menu</li>
|
|
61
|
+
<li>Utiliser des libellés explicites pour les options du menu</li>
|
|
62
|
+
<li>Évitez de surcharger le menu avec trop d'options</li>
|
|
63
|
+
</ul>
|
|
64
|
+
</BestPracticesSection>
|
|
65
|
+
|
|
66
|
+
<ResourcesSection>
|
|
67
|
+
<ul>
|
|
68
|
+
<li><a href="https://www.w3.org/WAI/ARIA/apg/patterns/menu-button/" target="_blank" rel="noopener noreferrer">WAI-ARIA Authoring Practices: Menu Button</a></li>
|
|
69
|
+
<li><a href="https://inclusive-components.design/menus-menu-buttons/" target="_blank" rel="noopener noreferrer">Inclusive Components: Menus & Menu Buttons</a></li>
|
|
70
|
+
</ul>
|
|
71
|
+
</ResourcesSection>
|
|
72
|
+
|
|
73
|
+
<div class="mt-8">
|
|
74
|
+
<p>Rapport d’audit manuel : <a href="/audits/SyBtnMenu.xlsx" style={{ color:'#0C41BD' }}>Voir le rapport</a></p>
|
|
75
|
+
<p style={{ color: 'grey', fontSize: '14px', marginTop: '0px' }}>Correctifs associés (<a href="https://github.com/assurance-maladie-digital/design-system-v3/issues/918" target="_blank" style={{color:'#0C41BD'}}>issue #918</a>)</p>
|
|
76
|
+
</div>
|
|
77
|
+
</AccessibilityGuideLayout>
|
|
@@ -1,32 +1,38 @@
|
|
|
1
1
|
import { describe, it, expect } from 'vitest'
|
|
2
2
|
import { mount } from '@vue/test-utils'
|
|
3
|
-
import
|
|
3
|
+
import SyBtnMenu from '../SyBtnMenu.vue'
|
|
4
4
|
|
|
5
|
-
describe('
|
|
5
|
+
describe('SyBtnMenu', () => {
|
|
6
6
|
it('renders the component with default props', () => {
|
|
7
|
-
const wrapper = mount(
|
|
7
|
+
const wrapper = mount(SyBtnMenu, {
|
|
8
8
|
props: {
|
|
9
9
|
primaryInfo: 'John Doe',
|
|
10
10
|
},
|
|
11
|
+
attachTo: document.body,
|
|
11
12
|
})
|
|
12
13
|
|
|
13
14
|
expect(wrapper.exists()).toBe(true)
|
|
14
15
|
expect(wrapper.text()).toContain('John Doe')
|
|
16
|
+
|
|
17
|
+
wrapper.unmount()
|
|
15
18
|
})
|
|
16
19
|
|
|
17
20
|
it('shows secondaryInfo if provided', () => {
|
|
18
|
-
const wrapper = mount(
|
|
21
|
+
const wrapper = mount(SyBtnMenu, {
|
|
19
22
|
props: {
|
|
20
23
|
primaryInfo: 'John Doe',
|
|
21
24
|
secondaryInfo: 'Additional Info',
|
|
22
25
|
},
|
|
26
|
+
attachTo: document.body,
|
|
23
27
|
})
|
|
24
28
|
|
|
25
29
|
expect(wrapper.text()).toContain('Additional Info')
|
|
30
|
+
|
|
31
|
+
wrapper.unmount()
|
|
26
32
|
})
|
|
27
33
|
|
|
28
34
|
it('emits "update:modelValue" when an item is selected', async () => {
|
|
29
|
-
const wrapper = mount(
|
|
35
|
+
const wrapper = mount(SyBtnMenu, {
|
|
30
36
|
props: {
|
|
31
37
|
modelValue: null,
|
|
32
38
|
menuItems: [
|
|
@@ -35,6 +41,7 @@ describe('SyBtnSelect', () => {
|
|
|
35
41
|
],
|
|
36
42
|
primaryInfo: 'John Doe',
|
|
37
43
|
},
|
|
44
|
+
attachTo: document.body,
|
|
38
45
|
})
|
|
39
46
|
|
|
40
47
|
const button = wrapper.find('.sy-user-menu-btn')
|
|
@@ -50,13 +57,16 @@ describe('SyBtnSelect', () => {
|
|
|
50
57
|
expect(wrapper.emitted('update:modelValue')![0]).toEqual([
|
|
51
58
|
{ text: 'Option 1', value: 'option1' },
|
|
52
59
|
])
|
|
60
|
+
|
|
61
|
+
wrapper.unmount()
|
|
53
62
|
})
|
|
54
63
|
|
|
55
64
|
it('toggles the menu open and closed', async () => {
|
|
56
|
-
const wrapper = mount(
|
|
65
|
+
const wrapper = mount(SyBtnMenu, {
|
|
57
66
|
props: {
|
|
58
67
|
primaryInfo: 'John Doe',
|
|
59
68
|
},
|
|
69
|
+
attachTo: document.body,
|
|
60
70
|
})
|
|
61
71
|
|
|
62
72
|
const button = wrapper.find('.sy-user-menu-btn')
|
|
@@ -67,14 +77,17 @@ describe('SyBtnSelect', () => {
|
|
|
67
77
|
|
|
68
78
|
await button.trigger('click')
|
|
69
79
|
expect(wrapper.vm.isOpen).toBe(false)
|
|
80
|
+
|
|
81
|
+
wrapper.unmount()
|
|
70
82
|
})
|
|
71
83
|
|
|
72
84
|
it('formats menu items correctly', () => {
|
|
73
|
-
const wrapper = mount(
|
|
85
|
+
const wrapper = mount(SyBtnMenu, {
|
|
74
86
|
props: {
|
|
75
87
|
primaryInfo: 'John Doe',
|
|
76
88
|
menuItems: ['Option 1', 'Option 2'],
|
|
77
89
|
},
|
|
90
|
+
attachTo: document.body,
|
|
78
91
|
})
|
|
79
92
|
|
|
80
93
|
const formattedItems = wrapper.vm.formattedItems
|
|
@@ -82,59 +95,73 @@ describe('SyBtnSelect', () => {
|
|
|
82
95
|
{ text: 'Option 1', value: 'Option 1' },
|
|
83
96
|
{ text: 'Option 2', value: 'Option 2' },
|
|
84
97
|
])
|
|
98
|
+
|
|
99
|
+
wrapper.unmount()
|
|
85
100
|
})
|
|
86
101
|
|
|
87
102
|
it('updates selectedItem when modelValue changes', async () => {
|
|
88
|
-
const wrapper = mount(
|
|
103
|
+
const wrapper = mount(SyBtnMenu, {
|
|
89
104
|
props: {
|
|
90
105
|
modelValue: 'initial-value',
|
|
91
106
|
primaryInfo: 'John Doe',
|
|
92
107
|
},
|
|
108
|
+
attachTo: document.body,
|
|
93
109
|
})
|
|
94
110
|
|
|
95
111
|
expect(wrapper.vm.selectedItem).toBe('initial-value')
|
|
96
112
|
|
|
97
113
|
await wrapper.setProps({ modelValue: 'new-value' })
|
|
98
114
|
expect(wrapper.vm.selectedItem).toBe('new-value')
|
|
115
|
+
|
|
116
|
+
wrapper.unmount()
|
|
99
117
|
})
|
|
100
118
|
|
|
101
119
|
it('renders the primaryInfo in a span when isMobileVersion and hideIcon are true', async () => {
|
|
102
|
-
const wrapper = mount(
|
|
120
|
+
const wrapper = mount(SyBtnMenu, {
|
|
103
121
|
props: {
|
|
104
122
|
primaryInfo: 'John Doe',
|
|
105
123
|
isMobileView: true,
|
|
106
124
|
hideIcon: true,
|
|
107
125
|
},
|
|
126
|
+
attachTo: document.body,
|
|
108
127
|
})
|
|
109
128
|
|
|
110
129
|
const span = wrapper.find('span.font-weight-bold.text-caption')
|
|
111
130
|
|
|
112
131
|
expect(span.text()).toBe('John Doe')
|
|
132
|
+
|
|
133
|
+
wrapper.unmount()
|
|
113
134
|
})
|
|
114
135
|
|
|
115
136
|
it('does not render the span if isMobileVersion is false', () => {
|
|
116
|
-
const wrapper = mount(
|
|
137
|
+
const wrapper = mount(SyBtnMenu, {
|
|
117
138
|
props: {
|
|
118
139
|
primaryInfo: 'John Doe',
|
|
119
140
|
isMobileView: false,
|
|
120
141
|
hideIcon: true,
|
|
121
142
|
},
|
|
143
|
+
attachTo: document.body,
|
|
122
144
|
})
|
|
123
145
|
|
|
124
146
|
const span = wrapper.find('span.font-weight-bold.text-sm-caption')
|
|
125
147
|
expect(span.exists()).toBe(false)
|
|
148
|
+
|
|
149
|
+
wrapper.unmount()
|
|
126
150
|
})
|
|
127
151
|
|
|
128
152
|
it('does not render the span if hideIcon is false', () => {
|
|
129
|
-
const wrapper = mount(
|
|
153
|
+
const wrapper = mount(SyBtnMenu, {
|
|
130
154
|
props: {
|
|
131
155
|
primaryInfo: 'John Doe',
|
|
132
156
|
isMobileView: true,
|
|
133
157
|
hideIcon: false,
|
|
134
158
|
},
|
|
159
|
+
attachTo: document.body,
|
|
135
160
|
})
|
|
136
161
|
|
|
137
162
|
const span = wrapper.find('span.font-weight-bold.text-sm-caption')
|
|
138
163
|
expect(span.exists()).toBe(false)
|
|
164
|
+
|
|
165
|
+
wrapper.unmount()
|
|
139
166
|
})
|
|
140
167
|
})
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<script lang="ts" setup>
|
|
2
2
|
import { computed } from 'vue'
|
|
3
3
|
import type { RouteLocationRaw } from 'vue-router'
|
|
4
|
-
import
|
|
4
|
+
import SyBtnMenu from '@/components/SyBtnMenu/SyBtnMenu.vue'
|
|
5
5
|
import { useDisplay } from 'vuetify'
|
|
6
6
|
import { mdiAccount, mdiLoginVariant } from '@mdi/js'
|
|
7
7
|
import useCustomizableOptions, { type CustomizableOptions } from '@/composables/useCustomizableOptions'
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
</script>
|
|
44
44
|
|
|
45
45
|
<template>
|
|
46
|
-
<
|
|
46
|
+
<SyBtnMenu
|
|
47
47
|
v-model="modelValue"
|
|
48
48
|
:hide-icon="hideUserIcon"
|
|
49
49
|
:icon-only="isMobileView"
|
|
@@ -85,7 +85,7 @@
|
|
|
85
85
|
</VListItem>
|
|
86
86
|
</slot>
|
|
87
87
|
</template>
|
|
88
|
-
</
|
|
88
|
+
</SyBtnMenu>
|
|
89
89
|
</template>
|
|
90
90
|
|
|
91
91
|
<style scoped lang="scss">
|
|
@@ -91,9 +91,9 @@ describe('UserMenuBtn', () => {
|
|
|
91
91
|
},
|
|
92
92
|
})
|
|
93
93
|
|
|
94
|
-
const
|
|
95
|
-
// Utiliser le format camelCase pour l'événement, comme déclaré dans le composant
|
|
96
|
-
await
|
|
94
|
+
const SyBtnMenu = wrapper.findComponent({ name: 'SyBtnMenu' })
|
|
95
|
+
// Utiliser le format camelCase pour l'événement, comme déclaré dans le composant SyBtnMenu
|
|
96
|
+
await SyBtnMenu.vm.$emit('update:modelValue', 'test-value')
|
|
97
97
|
|
|
98
98
|
expect(wrapper.emitted('update:modelValue')).toBeTruthy()
|
|
99
99
|
expect(wrapper.emitted('update:modelValue')![0]).toEqual(['test-value'])
|
package/src/components/index.ts
CHANGED
|
@@ -4,12 +4,12 @@
|
|
|
4
4
|
export { default as FooterBar } from './FooterBar/FooterBar.vue'
|
|
5
5
|
export { default as HeaderBar } from './HeaderBar/HeaderBar.vue'
|
|
6
6
|
export { default as HeaderBurgerMenu } from './HeaderBar/HeaderBurgerMenu/HeaderBurgerMenu.vue'
|
|
7
|
-
export { default as HeaderLoading } from './HeaderLoading/HeaderLoading.vue'
|
|
8
|
-
export { default as HeaderLogo } from './HeaderBar/HeaderLogo/HeaderLogo.vue'
|
|
9
|
-
export { default as HeaderMenuBtn } from './HeaderBar/HeaderMenuBtn/HeaderMenuBtn.vue'
|
|
10
7
|
export { default as HeaderMenuItem } from './HeaderBar/HeaderBurgerMenu/HeaderMenuItem/HeaderMenuItem.vue'
|
|
11
8
|
export { default as HeaderMenuSection } from './HeaderBar/HeaderBurgerMenu/HeaderMenuSection/HeaderMenuSection.vue'
|
|
12
9
|
export { default as HeaderSubMenu } from './HeaderBar/HeaderBurgerMenu/HeaderSubMenu/HeaderSubMenu.vue'
|
|
10
|
+
export { default as HeaderLogo } from './HeaderBar/HeaderLogo/HeaderLogo.vue'
|
|
11
|
+
export { default as HeaderMenuBtn } from './HeaderBar/HeaderMenuBtn/HeaderMenuBtn.vue'
|
|
12
|
+
export { default as HeaderLoading } from './HeaderLoading/HeaderLoading.vue'
|
|
13
13
|
export { default as HeaderToolbar } from './HeaderToolbar/HeaderToolbar.vue'
|
|
14
14
|
export { default as SubHeader } from './SubHeader/SubHeader.vue'
|
|
15
15
|
|
|
@@ -23,12 +23,13 @@ export { default as ToolbarContainer } from './ToolbarContainer/ToolbarContainer
|
|
|
23
23
|
// Navigation
|
|
24
24
|
// ===========================
|
|
25
25
|
export { default as ContextualMenu } from './ContextualMenu/ContextualMenu.vue'
|
|
26
|
+
export { default as SyPagination } from './Customs/SyPagination/SyPagination.vue'
|
|
27
|
+
export { default as SyTabs } from './Customs/SyTabs/SyTabs.vue'
|
|
26
28
|
export { default as ExternalLinks } from './ExternalLinks/ExternalLinks.vue'
|
|
27
29
|
export { default as HeaderNavigationBar } from './HeaderNavigationBar/HeaderNavigationBar.vue'
|
|
28
30
|
export { default as SkipLink } from './SkipLink/SkipLink.vue'
|
|
29
31
|
export { default as SocialMediaLinks } from './SocialMediaLinks/SocialMediaLinks.vue'
|
|
30
|
-
export { default as
|
|
31
|
-
export { default as SyTabs } from './Customs/SyTabs/SyTabs.vue'
|
|
32
|
+
export { default as SyBtnMenu } from './SyBtnMenu/SyBtnMenu.vue'
|
|
32
33
|
|
|
33
34
|
// ===========================
|
|
34
35
|
// Boutons
|
|
@@ -45,6 +46,14 @@ export { default as UserMenuBtn } from './UserMenuBtn/UserMenuBtn.vue'
|
|
|
45
46
|
// Formulaires
|
|
46
47
|
// ===========================
|
|
47
48
|
export { default as DatePicker } from '@/components/DatePicker/CalendarMode/DatePicker.vue'
|
|
49
|
+
export { default as Captcha } from './Captcha/Captcha.vue'
|
|
50
|
+
export { default as SelectBtnField } from './Customs/Selects/SelectBtnField/SelectBtnField.vue'
|
|
51
|
+
export { default as SyInputSelect } from './Customs/Selects/SyInputSelect/SyInputSelect.vue'
|
|
52
|
+
export { default as SySelect } from './Customs/Selects/SySelect/SySelect.vue'
|
|
53
|
+
export { default as SyCheckbox } from './Customs/SyCheckbox/SyCheckbox.vue'
|
|
54
|
+
export { default as SyForm } from './Customs/SyForm/SyForm.vue'
|
|
55
|
+
export { default as SyRadioGroup } from './Customs/SyRadioGroup/SyRadioGroup.vue'
|
|
56
|
+
export { default as SyTextField } from './Customs/SyTextField/SyTextField.vue'
|
|
48
57
|
export { default as DiacriticPicker } from './DiacriticPicker/DiacriticPicker.vue'
|
|
49
58
|
export { default as FileUpload } from './FileUpload/FileUpload.vue'
|
|
50
59
|
export { default as LunarCalendar } from './LunarCalendar/LunarCalendar.vue'
|
|
@@ -56,17 +65,8 @@ export { indicatifs } from './PhoneField/indicatifs'
|
|
|
56
65
|
export { default as PhoneField } from './PhoneField/PhoneField.vue'
|
|
57
66
|
export { default as RangeField } from './RangeField/RangeField.vue'
|
|
58
67
|
export { default as SearchListField } from './SearchListField/SearchListField.vue'
|
|
59
|
-
export { default as SelectBtnField } from './Customs/Selects/SelectBtnField/SelectBtnField.vue'
|
|
60
|
-
export { default as SyBtnSelect } from './Customs/Selects/SyBtnSelect/SyBtnSelect.vue'
|
|
61
|
-
export { default as SyCheckbox } from './Customs/SyCheckbox/SyCheckbox.vue'
|
|
62
|
-
export { default as SyInputSelect } from './Customs/Selects/SyInputSelect/SyInputSelect.vue'
|
|
63
|
-
export { default as SySelect } from './Customs/Selects/SySelect/SySelect.vue'
|
|
64
68
|
export { default as SyTextArea } from './SyTextArea/SyTextArea.vue'
|
|
65
|
-
export { default as SyTextField } from './Customs/SyTextField/SyTextField.vue'
|
|
66
69
|
export { default as UploadWorkflow } from './UploadWorkflow/UploadWorkflow.vue'
|
|
67
|
-
export { default as Captcha } from './Captcha/Captcha.vue'
|
|
68
|
-
export { default as SyForm } from './Customs/SyForm/SyForm.vue'
|
|
69
|
-
export { default as SyRadioGroup } from './Customs/SyRadioGroup/SyRadioGroup.vue'
|
|
70
70
|
|
|
71
71
|
// ===========================
|
|
72
72
|
// Tableaux
|
|
@@ -89,6 +89,7 @@ export { default as Accordion } from './Accordion/Accordion.vue'
|
|
|
89
89
|
export { default as ChipList } from './ChipList/ChipList.vue'
|
|
90
90
|
export * from './ChipList/types'
|
|
91
91
|
export { default as CollapsibleList } from './CollapsibleList/CollapsibleList.vue'
|
|
92
|
+
export { default as SyIcon } from './Customs/SyIcon/SyIcon.vue'
|
|
92
93
|
export { default as DataList } from './DataList/DataList.vue'
|
|
93
94
|
export { default as DataListGroup } from './DataListGroup/DataListGroup.vue'
|
|
94
95
|
export { default as DataListItem } from './DataListItem/DataListItem.vue'
|
|
@@ -96,7 +97,6 @@ export { default as FileList } from './FileList/FileList.vue'
|
|
|
96
97
|
export { default as FilePreview } from './FilePreview/FilePreview.vue'
|
|
97
98
|
export { default as Logo } from './Logo/Logo.vue'
|
|
98
99
|
export { default as LogoBrandSection } from './LogoBrandSection/LogoBrandSection.vue'
|
|
99
|
-
export { default as SyIcon } from './Customs/SyIcon/SyIcon.vue'
|
|
100
100
|
|
|
101
101
|
// ===========================
|
|
102
102
|
// Feedback
|
|
@@ -122,8 +122,8 @@ export { default as NotFoundPage } from './NotFoundPage/NotFoundPage.vue'
|
|
|
122
122
|
// Amelipro
|
|
123
123
|
// ===========================
|
|
124
124
|
export { default as AmeliproAccordion } from './Amelipro/AmeliproAccordion/AmeliproAccordion.vue'
|
|
125
|
-
export { default as AmeliproAccordionGroup } from './Amelipro/AmeliproAccordionGroup/AmeliproAccordionGroup.vue'
|
|
126
125
|
export { default as AmeliproAccordionFrieze } from './Amelipro/AmeliproAccordionFrieze/AmeliproAccordionFrieze.vue'
|
|
126
|
+
export { default as AmeliproAccordionGroup } from './Amelipro/AmeliproAccordionGroup/AmeliproAccordionGroup.vue'
|
|
127
127
|
export { default as AmeliproAccordionList } from './Amelipro/AmeliproAccordionList/AmeliproAccordionList.vue'
|
|
128
128
|
export { default as AmeliproAccordionResult } from './Amelipro/AmeliproAccordionResult/AmeliproAccordionResult.vue'
|
|
129
129
|
export { default as AmeliproAccordionResultList } from './Amelipro/AmeliproAccordionResultList/AmeliproAccordionResultList.vue'
|
|
@@ -131,10 +131,10 @@ export { default as AmeliproAutoCompleteField } from './Amelipro/AmeliproAutoCom
|
|
|
131
131
|
export { default as AmeliproBadge } from './Amelipro/AmeliproBadge/AmeliproBadge.vue'
|
|
132
132
|
export { default as AmeliproBreadcrumb } from './Amelipro/AmeliproBreadcrumb/AmeliproBreadcrumb.vue'
|
|
133
133
|
export { default as AmeliproBtn } from './Amelipro/AmeliproBtn/AmeliproBtn.vue'
|
|
134
|
-
export { default as AmeliproCard } from './Amelipro/AmeliproCard/AmeliproCard.vue'
|
|
135
134
|
export { default as AmeliproCallback } from './Amelipro/AmeliproCallback/AmeliproCallback.vue'
|
|
136
|
-
export { default as AmeliproCarousel } from './Amelipro/AmeliproCarousel/AmeliproCarousel.vue'
|
|
137
135
|
export { default as AmeliproCaptcha } from './Amelipro/AmeliproCaptcha/AmeliproCaptcha.vue'
|
|
136
|
+
export { default as AmeliproCard } from './Amelipro/AmeliproCard/AmeliproCard.vue'
|
|
137
|
+
export { default as AmeliproCarousel } from './Amelipro/AmeliproCarousel/AmeliproCarousel.vue'
|
|
138
138
|
export { default as AmeliproCheckbox } from './Amelipro/AmeliproCheckbox/AmeliproCheckbox.vue'
|
|
139
139
|
export { default as AmeliproCheckboxGroup } from './Amelipro/AmeliproCheckboxGroup/AmeliproCheckboxGroup.vue'
|
|
140
140
|
export { default as AmeliproChips } from './Amelipro/AmeliproChips/AmeliproChips.vue'
|
|
@@ -179,8 +179,8 @@ export { default as AmeliproTextField } from './Amelipro/AmeliproTextField/Ameli
|
|
|
179
179
|
export { default as AmeliproTileBtn } from './Amelipro/AmeliproTileBtn/AmeliproTileBtn.vue'
|
|
180
180
|
export { default as AmeliproTooltips } from './Amelipro/AmeliproTooltips/AmeliproTooltips.vue'
|
|
181
181
|
export { default as AmeliproTransmission } from './Amelipro/AmeliproTransmission/AmeliproTransmission.vue'
|
|
182
|
-
export { default as StructureMenu } from './Amelipro/StructureMenu/StructureMenu.vue'
|
|
183
182
|
export { default as ServiceMenu } from './Amelipro/ServiceMenu/ServiceMenu.vue'
|
|
183
|
+
export { default as StructureMenu } from './Amelipro/StructureMenu/StructureMenu.vue'
|
|
184
184
|
export { default as UserInformationSummary } from './Amelipro/UserInformationSummary/UserInformationSummary.vue'
|
|
185
185
|
export { default as UserMenu } from './Amelipro/UserMenu/UserMenu.vue'
|
|
186
186
|
|
|
@@ -189,10 +189,10 @@ export { default as UserMenu } from './Amelipro/UserMenu/UserMenu.vue'
|
|
|
189
189
|
// ===========================
|
|
190
190
|
export { useDateFormat } from '../composables/date/useDateFormatDayjs'
|
|
191
191
|
export { useFieldValidation } from '../composables/rules/useFieldValidation'
|
|
192
|
-
export { useNotificationService } from '../services/NotificationService'
|
|
193
|
-
export { useValidation } from '../composables/validation/useValidation'
|
|
194
192
|
export type { RuleOptions } from '../composables/rules/useFieldValidation'
|
|
195
193
|
export { useValidatable } from '../composables/validation/useValidatable'
|
|
194
|
+
export { useValidation } from '../composables/validation/useValidation'
|
|
195
|
+
export { useNotificationService } from '../services/NotificationService'
|
|
196
196
|
|
|
197
197
|
// ===========================
|
|
198
198
|
// Directives
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest'
|
|
2
|
+
import { formatDate, parseDate } from '../useDateFormatDayjs'
|
|
3
|
+
|
|
4
|
+
describe('useDateFormatDayjs', () => {
|
|
5
|
+
describe('parseDate', () => {
|
|
6
|
+
it('keeps the same calendar day when formatting back in a negative timezone (UTC-4)', () => {
|
|
7
|
+
const previousTz = process.env.TZ
|
|
8
|
+
process.env.TZ = 'America/Guadeloupe'
|
|
9
|
+
try {
|
|
10
|
+
const parsed = parseDate('2023-01-15', 'YYYY-MM-DD')
|
|
11
|
+
expect(parsed).toBeInstanceOf(Date)
|
|
12
|
+
expect(formatDate(parsed, 'YYYY-MM-DD')).toBe('2023-01-15')
|
|
13
|
+
}
|
|
14
|
+
finally {
|
|
15
|
+
process.env.TZ = previousTz
|
|
16
|
+
}
|
|
17
|
+
})
|
|
18
|
+
})
|
|
19
|
+
})
|
|
@@ -49,10 +49,10 @@ export const parseDate = (dateString: string | Date | null | undefined, format:
|
|
|
49
49
|
return null
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
-
// Extraire les composants de la date pour créer une date
|
|
53
|
-
//
|
|
54
|
-
//
|
|
55
|
-
return dayjs
|
|
52
|
+
// Extraire les composants de la date pour créer une date à minuit en timezone locale.
|
|
53
|
+
// On évite ainsi les décalages jour-1/jour+1 quand on formate la Date en local,
|
|
54
|
+
// notamment dans les fuseaux négatifs (ex: UTC-4).
|
|
55
|
+
return dayjs()
|
|
56
56
|
.year(parsedDate.year())
|
|
57
57
|
.month(parsedDate.month())
|
|
58
58
|
.date(parsedDate.date())
|
|
@@ -43,7 +43,7 @@ const ITEMS: AuditItem[] = [
|
|
|
43
43
|
{ composant: 'PasswordField', status: STATUS.AUDITED },
|
|
44
44
|
{ composant: 'PhoneField', status: STATUS.AUDITED },
|
|
45
45
|
{ composant: 'UserMenuBtn', status: STATUS.NOT_AUDITED },
|
|
46
|
-
{ composant: '
|
|
46
|
+
{ composant: 'SyBtnMenu', status: STATUS.AUDITED },
|
|
47
47
|
{ composant: 'NirField', status: STATUS.IN_PROGRESS },
|
|
48
48
|
{ composant: 'PeriodField', status: STATUS.NOT_AUDITED },
|
|
49
49
|
{ composant: 'RangeField', status: STATUS.NOT_AUDITED },
|
|
@@ -87,10 +87,6 @@ export const VuetifyItems = [
|
|
|
87
87
|
name: 'SyInputSelect',
|
|
88
88
|
href: '/?path=/docs/composants-formulaires-selects-syinputselect--docs',
|
|
89
89
|
},
|
|
90
|
-
{
|
|
91
|
-
name: 'SyBtnSelect',
|
|
92
|
-
href: '/?path=/docs/composants-formulaires-selects-sybtnselect--docs',
|
|
93
|
-
},
|
|
94
90
|
],
|
|
95
91
|
},
|
|
96
92
|
{
|