@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.
- package/README.md +72 -2
- package/dist/design-system-v3.d.ts +234 -2
- package/dist/design-system-v3.js +3020 -2142
- package/dist/design-system-v3.umd.cjs +2 -2
- package/dist/style.css +1 -1
- package/package.json +11 -10
- package/src/components/CollapsibleList/CollapsibleList.mdx +47 -0
- package/src/components/CollapsibleList/CollapsibleList.stories.ts +52 -0
- package/src/components/CollapsibleList/CollapsibleList.vue +157 -0
- package/src/components/CollapsibleList/tests/CollapsibleList.spec.ts +60 -0
- package/src/components/CollapsibleList/types.d.ts +5 -0
- package/src/components/Customs/CustomInputSelect/CustomInputSelect.mdx +42 -0
- package/src/components/Customs/CustomInputSelect/CustomInputSelect.stories.ts +154 -0
- package/src/components/Customs/CustomInputSelect/CustomInputSelect.vue +185 -0
- package/src/components/Customs/CustomInputSelect/tests/CustomInputSelect.spec.ts +216 -0
- package/src/components/Customs/CustomSelect/CustomSelect.mdx +47 -0
- package/src/components/Customs/CustomSelect/CustomSelect.stories.ts +182 -0
- package/src/components/Customs/CustomSelect/CustomSelect.vue +188 -0
- package/src/components/Customs/CustomSelect/tests/CustomSelect.spec.ts +236 -0
- package/src/components/FooterBar/A11yCompliance.ts +9 -0
- package/src/components/FooterBar/FooterBar.mdx +115 -0
- package/src/components/FooterBar/FooterBar.stories.ts +632 -0
- package/src/components/FooterBar/FooterBar.vue +330 -0
- package/src/components/FooterBar/config.ts +20 -0
- package/src/components/FooterBar/defaultSocialMediaLinks.ts +21 -0
- package/src/components/FooterBar/locales.ts +16 -0
- package/src/components/FooterBar/tests/FooterBar.spec.ts +167 -0
- package/src/components/FooterBar/tests/FooterBarConfig.spec.ts +36 -0
- package/src/components/FooterBar/tests/__snapshots__/FooterBar.spec.ts.snap +27 -0
- package/src/components/FooterBar/types.d.ts +10 -0
- package/src/components/LangBtn/LangBtn.mdx +2 -1
- package/src/components/LangBtn/LangBtn.vue +3 -3
- package/src/components/Logo/Logo.mdx +26 -0
- package/src/components/Logo/Logo.stories.ts +217 -0
- package/src/components/Logo/Logo.vue +397 -0
- package/src/components/Logo/LogoSize.ts +7 -0
- package/src/components/Logo/locales.ts +6 -0
- package/src/components/Logo/logoDimensionsMapping.ts +16 -0
- package/src/components/Logo/tests/Logo.spec.ts +75 -0
- package/src/components/Logo/types.d.ts +8 -0
- package/src/components/SocialMediaLinks/DefaultSocialMediaLinks.ts +21 -0
- package/src/components/SocialMediaLinks/SocialMediaLinks.mdx +15 -0
- package/src/components/SocialMediaLinks/SocialMediaLinks.stories.ts +72 -0
- package/src/components/SocialMediaLinks/SocialMediaLinks.vue +92 -0
- package/src/components/SocialMediaLinks/locales.ts +3 -0
- package/src/components/SocialMediaLinks/tests/DefaultSocialMediaLinks.spec.ts +21 -0
- package/src/components/SocialMediaLinks/tests/SocialMediaLinks.spec.ts +89 -0
- package/src/components/SocialMediaLinks/tests/__snapshots__/SocialMediaLinks.spec.ts.snap +24 -0
- package/src/components/SocialMediaLinks/types.d.ts +5 -0
- package/src/components/index.ts +6 -0
- package/src/directives/clickOutside.ts +24 -0
- package/src/temp/TestDTComponent.vue +6 -10
- package/src/temp/gridsTests.vue +0 -4
- package/src/utils/propValidator/index.ts +20 -0
- package/src/utils/propValidator/tests/propValidator.spec.ts +40 -0
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { computed, useSlots } from 'vue'
|
|
3
|
+
import { type RouteLocationRaw } from 'vue-router'
|
|
4
|
+
|
|
5
|
+
import Logo from '@/components/Logo/Logo.vue'
|
|
6
|
+
import { LogoSize } from '@/components/Logo/LogoSize'
|
|
7
|
+
import SocialMediaLinks from '@/components/SocialMediaLinks/SocialMediaLinks.vue'
|
|
8
|
+
import type { SocialMediaLink } from '@/components/SocialMediaLinks/types'
|
|
9
|
+
import { A11yComplianceEnum } from './A11yCompliance'
|
|
10
|
+
import { defaultSocialMediaLinks } from './defaultSocialMediaLinks'
|
|
11
|
+
import type { LinkItem } from './types'
|
|
12
|
+
|
|
13
|
+
import { mdiArrowUp } from '@mdi/js'
|
|
14
|
+
import { useDisplay } from 'vuetify'
|
|
15
|
+
import { config } from './config'
|
|
16
|
+
import { locales } from './locales'
|
|
17
|
+
|
|
18
|
+
import useCustomizableOptions, { type CustomizableOptions } from '@/composables/useCustomizableOptions'
|
|
19
|
+
|
|
20
|
+
const props = withDefaults(defineProps<CustomizableOptions & {
|
|
21
|
+
a11yCompliance?: string
|
|
22
|
+
linkItems?: LinkItem[] | null
|
|
23
|
+
items?: LinkItem[] | null
|
|
24
|
+
sitemapRoute?: RouteLocationRaw
|
|
25
|
+
cguRoute?: RouteLocationRaw
|
|
26
|
+
cookiesRoute?: RouteLocationRaw
|
|
27
|
+
legalNoticeRoute?: RouteLocationRaw
|
|
28
|
+
a11yStatementRoute?: RouteLocationRaw
|
|
29
|
+
hideSitemapLink?: boolean
|
|
30
|
+
hideCguLink?: boolean
|
|
31
|
+
hideCookiesLink?: boolean
|
|
32
|
+
hideLegalNoticeLink?: boolean
|
|
33
|
+
hideA11yLink?: boolean
|
|
34
|
+
version?: string | undefined
|
|
35
|
+
hideLogo?: boolean
|
|
36
|
+
hideSocialMediaLinks?: boolean
|
|
37
|
+
socialMediaLinks?: SocialMediaLink[]
|
|
38
|
+
light?: boolean
|
|
39
|
+
}>(), {
|
|
40
|
+
a11yCompliance: 'non-compliant',
|
|
41
|
+
linkItems: null,
|
|
42
|
+
items: null,
|
|
43
|
+
sitemapRoute: () => ({ name: 'sitemap' }),
|
|
44
|
+
cguRoute: () => ({ name: 'cgu' }),
|
|
45
|
+
cookiesRoute: () => ({ name: 'cookies' }),
|
|
46
|
+
legalNoticeRoute: () => ({ name: 'legalNotice' }),
|
|
47
|
+
a11yStatementRoute: () => ({ name: 'a11yStatement' }),
|
|
48
|
+
hideSitemapLink: false,
|
|
49
|
+
hideCguLink: false,
|
|
50
|
+
hideCookiesLink: false,
|
|
51
|
+
hideLegalNoticeLink: false,
|
|
52
|
+
hideA11yLink: false,
|
|
53
|
+
version: undefined,
|
|
54
|
+
hideLogo: false,
|
|
55
|
+
hideSocialMediaLinks: false,
|
|
56
|
+
socialMediaLinks: () => defaultSocialMediaLinks as SocialMediaLink[],
|
|
57
|
+
light: false,
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
const arrowTopIcon = mdiArrowUp
|
|
61
|
+
const logoSizeEnum = LogoSize
|
|
62
|
+
const slots = useSlots()
|
|
63
|
+
const display = useDisplay()
|
|
64
|
+
const options = useCustomizableOptions(config, props)
|
|
65
|
+
|
|
66
|
+
const getLinkComponent = (item: LinkItem): string => {
|
|
67
|
+
return item.href ? 'a' : 'RouterLink'
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const scrollToTop = () => {
|
|
71
|
+
window.scrollTo({
|
|
72
|
+
top: 0,
|
|
73
|
+
behavior: 'smooth',
|
|
74
|
+
})
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const A11yComplianceLabel = computed(() => {
|
|
78
|
+
const complianceLabel = locales[props.a11yCompliance as keyof typeof A11yComplianceEnum]
|
|
79
|
+
return typeof complianceLabel === 'string' ? locales.a11yLabel(complianceLabel) : ''
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
const extendedMode = computed(() => {
|
|
83
|
+
return Boolean(slots.default)
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
const logoSize = computed(() => {
|
|
87
|
+
return display.smAndDown.value
|
|
88
|
+
? logoSizeEnum.SMALL
|
|
89
|
+
: logoSizeEnum.NORMAL
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
const footerLinksMapping = computed(() => {
|
|
93
|
+
if (props.linkItems) {
|
|
94
|
+
return props.linkItems as LinkItem[]
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const linksMapping: LinkItem[] = [
|
|
98
|
+
{
|
|
99
|
+
text: locales.sitemapLabel,
|
|
100
|
+
to: props.sitemapRoute,
|
|
101
|
+
hidden: props.hideSitemapLink,
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
text: locales.cguLabel,
|
|
105
|
+
to: props.cguRoute,
|
|
106
|
+
hidden: props.hideCguLink,
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
text: locales.cookiesLabel,
|
|
110
|
+
to: props.cookiesRoute,
|
|
111
|
+
hidden: props.hideCookiesLink,
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
text: locales.legalNoticeLabel,
|
|
115
|
+
to: props.legalNoticeRoute,
|
|
116
|
+
hidden: props.hideLegalNoticeLink,
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
text: A11yComplianceLabel.value,
|
|
120
|
+
to: props.a11yStatementRoute,
|
|
121
|
+
hidden: props.hideA11yLink,
|
|
122
|
+
},
|
|
123
|
+
] as LinkItem[]
|
|
124
|
+
|
|
125
|
+
return linksMapping.filter(item => !item.hidden)
|
|
126
|
+
})
|
|
127
|
+
|
|
128
|
+
defineExpose({
|
|
129
|
+
logoSize,
|
|
130
|
+
})
|
|
131
|
+
</script>
|
|
132
|
+
|
|
133
|
+
<template>
|
|
134
|
+
<VFooter
|
|
135
|
+
v-bind="{
|
|
136
|
+
...options.footer,
|
|
137
|
+
...$attrs,
|
|
138
|
+
}"
|
|
139
|
+
:color="props.light ? 'white' : options.footer.color"
|
|
140
|
+
class="vd-footer-bar flex-column align-stretch pa-3 w-100"
|
|
141
|
+
:class="{
|
|
142
|
+
'py-4 py-sm-7 px-4 px-md-14': extendedMode,
|
|
143
|
+
'v-theme--light': props.light,
|
|
144
|
+
'v-theme--dark': !props.light,
|
|
145
|
+
}"
|
|
146
|
+
role="contentinfo"
|
|
147
|
+
>
|
|
148
|
+
<div
|
|
149
|
+
v-if="extendedMode"
|
|
150
|
+
class="d-flex align-start align-sm-center mb-6"
|
|
151
|
+
>
|
|
152
|
+
<div class="d-flex flex-grow-1 flex-column flex-sm-row">
|
|
153
|
+
<slot name="logo">
|
|
154
|
+
<Logo
|
|
155
|
+
v-if="!props.hideLogo"
|
|
156
|
+
:size="logoSize"
|
|
157
|
+
:class="{ 'mb-2 mb-sm-0': !props.hideSocialMediaLinks }"
|
|
158
|
+
class="logo"
|
|
159
|
+
/>
|
|
160
|
+
</slot>
|
|
161
|
+
|
|
162
|
+
<VSpacer v-bind="options.spacer" />
|
|
163
|
+
|
|
164
|
+
<SocialMediaLinks
|
|
165
|
+
v-if="!props.hideSocialMediaLinks"
|
|
166
|
+
:links="props.socialMediaLinks"
|
|
167
|
+
class="mr-8 social"
|
|
168
|
+
/>
|
|
169
|
+
</div>
|
|
170
|
+
|
|
171
|
+
<VBtn
|
|
172
|
+
id="scroll-btn"
|
|
173
|
+
v-bind="options.goTopBtn"
|
|
174
|
+
:aria-label="locales.goTopBtnLabel"
|
|
175
|
+
@click="scrollToTop"
|
|
176
|
+
>
|
|
177
|
+
<VIcon
|
|
178
|
+
v-bind="options.goTopBtnIcon"
|
|
179
|
+
class="scroll"
|
|
180
|
+
>
|
|
181
|
+
{{ arrowTopIcon }}
|
|
182
|
+
</VIcon>
|
|
183
|
+
</VBtn>
|
|
184
|
+
</div>
|
|
185
|
+
|
|
186
|
+
<VDivider
|
|
187
|
+
v-if="extendedMode"
|
|
188
|
+
v-bind="options.divider"
|
|
189
|
+
class="mb-3"
|
|
190
|
+
/>
|
|
191
|
+
|
|
192
|
+
<slot />
|
|
193
|
+
|
|
194
|
+
<VDivider
|
|
195
|
+
v-if="extendedMode"
|
|
196
|
+
v-bind="options.divider"
|
|
197
|
+
class="mt-3 mb-6"
|
|
198
|
+
/>
|
|
199
|
+
|
|
200
|
+
<ul
|
|
201
|
+
:class="{ 'py-2 py-sm-0': !extendedMode }"
|
|
202
|
+
class="vd-footer-bar-links text-sm-center d-flex flex-column flex-sm-row flex-wrap align-start justify-center max-width-none mx-n3 my-n3"
|
|
203
|
+
>
|
|
204
|
+
<slot name="prepend" />
|
|
205
|
+
|
|
206
|
+
<li
|
|
207
|
+
v-for="(item, index) in footerLinksMapping"
|
|
208
|
+
:key="index"
|
|
209
|
+
>
|
|
210
|
+
<component
|
|
211
|
+
:is="getLinkComponent(item)"
|
|
212
|
+
:href="item.href"
|
|
213
|
+
:to="item.to"
|
|
214
|
+
:aria-label="item.ariaLabel"
|
|
215
|
+
:target="item.openInNewTab ? '_blank' : undefined"
|
|
216
|
+
:tabindex="index"
|
|
217
|
+
:rel="item.openInNewTab ? 'noopener noreferrer' : undefined"
|
|
218
|
+
class="my-3 mx-4"
|
|
219
|
+
>
|
|
220
|
+
{{ item.text }}
|
|
221
|
+
</component>
|
|
222
|
+
</li>
|
|
223
|
+
|
|
224
|
+
<li
|
|
225
|
+
v-if="props.version"
|
|
226
|
+
class="text-primary my-3 mx-4"
|
|
227
|
+
>
|
|
228
|
+
{{ locales.versionLabel }} {{ props.version }}
|
|
229
|
+
</li>
|
|
230
|
+
|
|
231
|
+
<slot name="append" />
|
|
232
|
+
</ul>
|
|
233
|
+
</VFooter>
|
|
234
|
+
</template>
|
|
235
|
+
|
|
236
|
+
<style lang="scss" scoped>
|
|
237
|
+
@use '@/assets/tokens.scss';
|
|
238
|
+
$white: #fff;
|
|
239
|
+
|
|
240
|
+
a {
|
|
241
|
+
cursor: pointer;
|
|
242
|
+
}
|
|
243
|
+
.v-btn--icon {
|
|
244
|
+
border: 0;
|
|
245
|
+
}
|
|
246
|
+
// Fix footer bar height in SK
|
|
247
|
+
.v-footer {
|
|
248
|
+
flex-grow: 0 !important;
|
|
249
|
+
justify-content: center;
|
|
250
|
+
}
|
|
251
|
+
.vd-footer-bar :deep() {
|
|
252
|
+
.vd-footer-bar-links a {
|
|
253
|
+
color: rgba(0, 0, 0, .87);
|
|
254
|
+
}
|
|
255
|
+
p,
|
|
256
|
+
.text--primary {
|
|
257
|
+
color: rgba(0, 0, 0, .87);
|
|
258
|
+
}
|
|
259
|
+
.text--secondary {
|
|
260
|
+
color: rgba(0, 0, 0, .6);
|
|
261
|
+
}
|
|
262
|
+
.social {
|
|
263
|
+
.text--primary {
|
|
264
|
+
color: tokens.$primary-base;
|
|
265
|
+
}
|
|
266
|
+
a.v-btn:hover {
|
|
267
|
+
background: rgba(0, 0, 0, 0.05);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
button.v-btn:hover {
|
|
271
|
+
background: rgba(0, 0, 0, 0.05);
|
|
272
|
+
}
|
|
273
|
+
a.text--primary {
|
|
274
|
+
color: tokens.$primary-base;
|
|
275
|
+
}
|
|
276
|
+
.v-divider {
|
|
277
|
+
border-color: rgba(tokens.$parma-darken-60, 1);
|
|
278
|
+
}
|
|
279
|
+
svg.logo {
|
|
280
|
+
fill: tokens.$primary-base;
|
|
281
|
+
}
|
|
282
|
+
.scroll {
|
|
283
|
+
color: tokens.$primary-base !important;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
// Use deep selector to style user content as well
|
|
287
|
+
.vd-footer-bar.v-theme--dark :deep() {
|
|
288
|
+
.vd-footer-bar-links a {
|
|
289
|
+
color: $white;
|
|
290
|
+
}
|
|
291
|
+
p,
|
|
292
|
+
.text--primary {
|
|
293
|
+
color: rgba($white, 0.87);
|
|
294
|
+
}
|
|
295
|
+
.text--secondary {
|
|
296
|
+
color: rgba($white, 0.6);
|
|
297
|
+
}
|
|
298
|
+
a.text--primary {
|
|
299
|
+
color: $white;
|
|
300
|
+
}
|
|
301
|
+
.v-divider {
|
|
302
|
+
border-color: rgba($white, 1);
|
|
303
|
+
}
|
|
304
|
+
svg {
|
|
305
|
+
fill: $white;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
.vd-footer-bar-links :deep() {
|
|
309
|
+
li {
|
|
310
|
+
list-style: none;
|
|
311
|
+
display: flex;
|
|
312
|
+
}
|
|
313
|
+
a {
|
|
314
|
+
transition: 0.15s;
|
|
315
|
+
text-decoration: none;
|
|
316
|
+
padding-top: 1px; // Add top padding to account for bottom border
|
|
317
|
+
border-bottom: 1px solid transparent;
|
|
318
|
+
&:hover,
|
|
319
|
+
&:focus {
|
|
320
|
+
border-color: currentColor;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
p {
|
|
324
|
+
padding: 1px 0;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
.v-theme--dark button.v-btn:hover :deep() {
|
|
328
|
+
background: rgba(white, 0.1);
|
|
329
|
+
}
|
|
330
|
+
</style>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { VariantType, DensityType } from '@/types/vuetifyTypes'
|
|
2
|
+
import { cnamColorsTokens } from '@/designTokens'
|
|
3
|
+
|
|
4
|
+
export const config = {
|
|
5
|
+
footer: {
|
|
6
|
+
elevation: 3,
|
|
7
|
+
color: cnamColorsTokens.parma.darken60,
|
|
8
|
+
height: 'auto',
|
|
9
|
+
},
|
|
10
|
+
goTopBtn: {
|
|
11
|
+
elevation: 0,
|
|
12
|
+
density: 'compact' as DensityType,
|
|
13
|
+
icon: 'true',
|
|
14
|
+
variant: 'text' as VariantType,
|
|
15
|
+
color: cnamColorsTokens.parma.darken60,
|
|
16
|
+
},
|
|
17
|
+
goTopBtnIcon: {
|
|
18
|
+
color: 'white',
|
|
19
|
+
},
|
|
20
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { mdiTwitter, mdiLinkedin, mdiFacebook } from '@mdi/js'
|
|
2
|
+
|
|
3
|
+
import type { SocialMediaLink } from '@/components/SocialMediaLinks/types'
|
|
4
|
+
|
|
5
|
+
export const defaultSocialMediaLinks: SocialMediaLink[] = [
|
|
6
|
+
{
|
|
7
|
+
icon: mdiLinkedin,
|
|
8
|
+
name: 'LinkedIn',
|
|
9
|
+
href: 'https://www.linkedin.com/company/assurance-maladie/',
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
icon: mdiFacebook,
|
|
13
|
+
name: 'Facebook',
|
|
14
|
+
href: 'https://www.facebook.com/AssurMaladie/',
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
icon: mdiTwitter,
|
|
18
|
+
name: 'Twitter',
|
|
19
|
+
href: 'https://twitter.com/Assur_Maladie',
|
|
20
|
+
},
|
|
21
|
+
]
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { A11yComplianceEnum } from './A11yCompliance'
|
|
2
|
+
|
|
3
|
+
export const locales = {
|
|
4
|
+
goTopBtnLabel: 'Retour en haut de la page',
|
|
5
|
+
sitemapLabel: 'Plan du site',
|
|
6
|
+
cguLabel: 'Conditions générales d’utilisation',
|
|
7
|
+
cookiesLabel: 'Gestion des cookies',
|
|
8
|
+
legalNoticeLabel: 'Mentions légales',
|
|
9
|
+
versionLabel: 'Version',
|
|
10
|
+
followUs: 'Suivez-nous',
|
|
11
|
+
[A11yComplianceEnum['non-compliant']]: 'non conforme',
|
|
12
|
+
[A11yComplianceEnum['partially-compliant']]: 'partiellement conforme',
|
|
13
|
+
[A11yComplianceEnum['fully-compliant']]: 'totalement conforme',
|
|
14
|
+
a11yLabel: (complianceLabel: string): string =>
|
|
15
|
+
`Accessibilité\xa0: ${complianceLabel}`,
|
|
16
|
+
}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { mount } from '@vue/test-utils'
|
|
2
|
+
import { describe, it, expect, vi } from 'vitest'
|
|
3
|
+
import FooterBar from '@/components/FooterBar/FooterBar.vue'
|
|
4
|
+
import { locales } from '@/components/FooterBar/locales'
|
|
5
|
+
import { A11yComplianceEnum } from '@/components/FooterBar/A11yCompliance'
|
|
6
|
+
import { vuetify } from '@tests/unit/setup'
|
|
7
|
+
import { LogoSize } from '@/components/Logo/LogoSize'
|
|
8
|
+
import { nextTick } from 'vue'
|
|
9
|
+
|
|
10
|
+
describe('FooterBar.vue', () => {
|
|
11
|
+
const getComponentType = (item: { href: unknown }) => {
|
|
12
|
+
return item.href ? 'a' : 'RouterLink'
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const testFunction = (complianceLabel: unknown) => {
|
|
16
|
+
return typeof complianceLabel === 'string' ? locales.a11yLabel(complianceLabel) : ''
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
it('renders correctly', async () => {
|
|
20
|
+
const wrapper = mount(FooterBar, { global: { plugins: [vuetify] } })
|
|
21
|
+
expect(FooterBar).toBeTruthy()
|
|
22
|
+
expect(wrapper.html()).toMatchSnapshot()
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
it('renders default props correctly', () => {
|
|
26
|
+
const wrapper = mount(FooterBar, { global: { plugins: [vuetify] } })
|
|
27
|
+
expect(wrapper.props().a11yCompliance).toBe('non-compliant')
|
|
28
|
+
expect(wrapper.props().linkItems).toBeNull()
|
|
29
|
+
expect(wrapper.props().sitemapRoute).toEqual({ name: 'sitemap' })
|
|
30
|
+
expect(wrapper.props().hideSitemapLink).toBe(false)
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
it('renders custom props correctly', () => {
|
|
34
|
+
const customProps = {
|
|
35
|
+
a11yCompliance: A11yComplianceEnum['fully-compliant'],
|
|
36
|
+
linkItems: [{ text: 'Custom Link', to: '/custom' }],
|
|
37
|
+
hideSitemapLink: true,
|
|
38
|
+
}
|
|
39
|
+
const wrapper = mount(FooterBar, { props: customProps, global: { plugins: [vuetify] } })
|
|
40
|
+
expect(wrapper.props().a11yCompliance).toBe(A11yComplianceEnum['fully-compliant'])
|
|
41
|
+
expect(wrapper.props().linkItems).toEqual(customProps.linkItems)
|
|
42
|
+
expect(wrapper.props().hideSitemapLink).toBe(true)
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
it('renders footer links correctly', () => {
|
|
46
|
+
const wrapper = mount(FooterBar, { global: { plugins: [vuetify] } })
|
|
47
|
+
const links = wrapper.findAll('.vd-footer-bar-links li')
|
|
48
|
+
expect(links.length).toBeGreaterThan(0)
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
it('hides links based on props', () => {
|
|
52
|
+
const wrapper = mount(FooterBar, {
|
|
53
|
+
props: {
|
|
54
|
+
hideSitemapLink: true,
|
|
55
|
+
hideCguLink: true,
|
|
56
|
+
hideCookiesLink: true,
|
|
57
|
+
hideLegalNoticeLink: true,
|
|
58
|
+
hideA11yLink: true,
|
|
59
|
+
},
|
|
60
|
+
global: { plugins: [vuetify] },
|
|
61
|
+
})
|
|
62
|
+
const links = wrapper.findAll('.vd-footer-bar-links li')
|
|
63
|
+
expect(links.length).toBe(0)
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
it('renders version if provided', () => {
|
|
67
|
+
const version = '1.0.0'
|
|
68
|
+
const wrapper = mount(FooterBar, { props: { version }, global: { plugins: [vuetify] } })
|
|
69
|
+
expect(wrapper.text()).toContain(`${locales.versionLabel} ${version}`)
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
it('computes logoSize correctly for desktop screens', () => {
|
|
73
|
+
const wrapper = mount(FooterBar, {
|
|
74
|
+
global: {
|
|
75
|
+
plugins: [vuetify],
|
|
76
|
+
},
|
|
77
|
+
})
|
|
78
|
+
expect(wrapper.vm.$.exposed?.logoSize.value).toBe(LogoSize.NORMAL)
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
it('computes logoSize correctly for small screens', async () => {
|
|
82
|
+
const wrapper = mount(FooterBar, {
|
|
83
|
+
global: {
|
|
84
|
+
plugins: [vuetify],
|
|
85
|
+
},
|
|
86
|
+
})
|
|
87
|
+
Object.defineProperty(window, 'innerWidth', {
|
|
88
|
+
writable: true,
|
|
89
|
+
configurable: true,
|
|
90
|
+
value: 400,
|
|
91
|
+
})
|
|
92
|
+
window.dispatchEvent(new Event('resize'))
|
|
93
|
+
await nextTick()
|
|
94
|
+
expect(wrapper.vm.$.exposed?.logoSize.value).toBe(LogoSize.SMALL)
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
it('renders the scroll to top button and triggers scrollToTop', async () => {
|
|
98
|
+
// Passer un slot ou forcer une condition pour activer le mode étendu
|
|
99
|
+
const wrapper = mount(FooterBar, {
|
|
100
|
+
global: { plugins: [vuetify] },
|
|
101
|
+
slots: {
|
|
102
|
+
default: '<div>Extended mode content</div>', // Slot pour forcer le mode étendu
|
|
103
|
+
},
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
// Vérifier si le bouton est bien présent dans le DOM
|
|
107
|
+
const button = wrapper.find('#scroll-btn')
|
|
108
|
+
expect(button.exists()).toBe(true)
|
|
109
|
+
|
|
110
|
+
// Simuler le clic si le bouton existe
|
|
111
|
+
const scrollToSpy = vi.spyOn(window, 'scrollTo')
|
|
112
|
+
await button.trigger('click')
|
|
113
|
+
|
|
114
|
+
expect(scrollToSpy).toHaveBeenCalledWith({ top: 0, behavior: 'smooth' })
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
it('returns "a" when href is defined', () => {
|
|
118
|
+
const item = { href: 'https://example.com' }
|
|
119
|
+
expect(getComponentType(item)).toBe('a')
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
it('returns "RouterLink" when href is undefined', () => {
|
|
123
|
+
const item = { href: undefined }
|
|
124
|
+
expect(getComponentType(item)).toBe('RouterLink')
|
|
125
|
+
})
|
|
126
|
+
|
|
127
|
+
it('returns "RouterLink" when href is null', () => {
|
|
128
|
+
const item = { href: null }
|
|
129
|
+
expect(getComponentType(item)).toBe('RouterLink')
|
|
130
|
+
})
|
|
131
|
+
|
|
132
|
+
it('returns "RouterLink" when href is null', () => {
|
|
133
|
+
const item = { href: null }
|
|
134
|
+
expect(getComponentType(item)).toBe('RouterLink')
|
|
135
|
+
})
|
|
136
|
+
|
|
137
|
+
it('sets target attribute correctly based on openInNewTab', () => {
|
|
138
|
+
const linkItems = [
|
|
139
|
+
{ text: 'Link 1', href: 'https://example.com', openInNewTab: true },
|
|
140
|
+
{ text: 'Link 2', href: 'https://example.com', openInNewTab: false },
|
|
141
|
+
]
|
|
142
|
+
const wrapper = mount(FooterBar, {
|
|
143
|
+
props: { linkItems },
|
|
144
|
+
global: { plugins: [vuetify] },
|
|
145
|
+
})
|
|
146
|
+
|
|
147
|
+
const links = wrapper.findAll('.vd-footer-bar-links a')
|
|
148
|
+
expect(links[0].attributes('target')).toBe('_blank')
|
|
149
|
+
expect(links[1].attributes('target')).toBeUndefined()
|
|
150
|
+
})
|
|
151
|
+
|
|
152
|
+
it('returns locales.a11yLabel when complianceLabel is a string', () => {
|
|
153
|
+
const complianceLabel = 'compliant'
|
|
154
|
+
const a11yLabelMock = vi.fn().mockReturnValue('Accessibility Label')
|
|
155
|
+
locales.a11yLabel = a11yLabelMock
|
|
156
|
+
|
|
157
|
+
const result = testFunction(complianceLabel)
|
|
158
|
+
expect(a11yLabelMock).toHaveBeenCalledWith(complianceLabel)
|
|
159
|
+
expect(result).toBe('Accessibility Label')
|
|
160
|
+
})
|
|
161
|
+
|
|
162
|
+
it('returns an empty string when complianceLabel is not a string', () => {
|
|
163
|
+
const complianceLabel = null
|
|
164
|
+
const result = testFunction(complianceLabel)
|
|
165
|
+
expect(result).toBe('')
|
|
166
|
+
})
|
|
167
|
+
})
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { config } from '../config'
|
|
2
|
+
import { describe, it, expect } from 'vitest'
|
|
3
|
+
|
|
4
|
+
describe('FooterBar config', () => {
|
|
5
|
+
it('should have correct footer elevation', () => {
|
|
6
|
+
expect(config.footer.elevation).toBe(3)
|
|
7
|
+
})
|
|
8
|
+
|
|
9
|
+
it('should have correct footer color', () => {
|
|
10
|
+
expect(config.footer.color).toBe('#2f384d')
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
it('should have correct footer minHeight', () => {
|
|
14
|
+
expect(config.footer.height).toBe('auto')
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
it('should have correct goTopBtn density', () => {
|
|
18
|
+
expect(config.goTopBtn.density).toBe('compact')
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
it('should have correct goTopBtn icon', () => {
|
|
22
|
+
expect(config.goTopBtn.icon).toBe('true')
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
it('should have correct goTopBtn variant', () => {
|
|
26
|
+
expect(config.goTopBtn.variant).toBe('text')
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
it('should have correct goTopBtn elevation', () => {
|
|
30
|
+
expect(config.goTopBtn.elevation).toBe(0)
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
it('should have correct goTopBtnIcon color', () => {
|
|
34
|
+
expect(config.goTopBtnIcon.color).toBe('white')
|
|
35
|
+
})
|
|
36
|
+
})
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
|
2
|
+
|
|
3
|
+
exports[`FooterBar.vue > renders correctly 1`] = `
|
|
4
|
+
"<footer data-v-a13c4e60="" class="v-footer v-theme--light elevation-3 vd-footer-bar flex-column align-stretch pa-3 w-100 v-theme--dark" style="background-color: #2f384d; color: #fff; caret-color: #fff; height: auto;" role="contentinfo">
|
|
5
|
+
<!--v-if-->
|
|
6
|
+
<!--v-if-->
|
|
7
|
+
<!--v-if-->
|
|
8
|
+
<ul data-v-a13c4e60="" class="py-2 py-sm-0 vd-footer-bar-links text-sm-center d-flex flex-column flex-sm-row flex-wrap align-start justify-center max-width-none mx-n3 my-n3">
|
|
9
|
+
<li data-v-a13c4e60="">
|
|
10
|
+
<routerlink data-v-a13c4e60="" to="[object Object]" tabindex="0" class="my-3 mx-4">Plan du site</routerlink>
|
|
11
|
+
</li>
|
|
12
|
+
<li data-v-a13c4e60="">
|
|
13
|
+
<routerlink data-v-a13c4e60="" to="[object Object]" tabindex="1" class="my-3 mx-4">Conditions générales d’utilisation</routerlink>
|
|
14
|
+
</li>
|
|
15
|
+
<li data-v-a13c4e60="">
|
|
16
|
+
<routerlink data-v-a13c4e60="" to="[object Object]" tabindex="2" class="my-3 mx-4">Gestion des cookies</routerlink>
|
|
17
|
+
</li>
|
|
18
|
+
<li data-v-a13c4e60="">
|
|
19
|
+
<routerlink data-v-a13c4e60="" to="[object Object]" tabindex="3" class="my-3 mx-4">Mentions légales</routerlink>
|
|
20
|
+
</li>
|
|
21
|
+
<li data-v-a13c4e60="">
|
|
22
|
+
<routerlink data-v-a13c4e60="" to="[object Object]" tabindex="4" class="my-3 mx-4">Accessibilité : non conforme</routerlink>
|
|
23
|
+
</li>
|
|
24
|
+
<!--v-if-->
|
|
25
|
+
</ul>
|
|
26
|
+
</footer>"
|
|
27
|
+
`;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { Controls, Canvas, Meta, Source } from '@storybook/blocks';
|
|
1
|
+
import { Controls, Canvas, Meta, Source } from '@storybook/blocks';
|
|
2
|
+
import * as LangBtnStories from './LangBtn.stories';
|
|
2
3
|
|
|
3
4
|
<Meta of={LangBtnStories} />
|
|
4
5
|
|
|
@@ -153,15 +153,15 @@
|
|
|
153
153
|
</div>
|
|
154
154
|
</template>
|
|
155
155
|
<style lang="scss" scoped>
|
|
156
|
-
@
|
|
156
|
+
@use '@/assets/tokens.scss';
|
|
157
157
|
|
|
158
158
|
.v-list-item:hover {
|
|
159
|
-
background-color: rgba(
|
|
159
|
+
background-color: rgba(tokens.$colors-overlay, 0.005)
|
|
160
160
|
}
|
|
161
161
|
|
|
162
162
|
.vd-lang-btn {
|
|
163
163
|
font-weight: 700;
|
|
164
|
-
--hoverColor: rgba(
|
|
164
|
+
--hoverColor: rgba(tokens.$colors-overlay, 0.5);
|
|
165
165
|
text-transform: capitalize;
|
|
166
166
|
}
|
|
167
167
|
</style>
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { Canvas, Meta, Controls, Source } from '@storybook/blocks';
|
|
2
|
+
import * as LogoStories from './Logo.stories.ts';
|
|
3
|
+
import Logo from './Logo.vue';
|
|
4
|
+
|
|
5
|
+
<Meta title="Components/Logo" component={Logo} />
|
|
6
|
+
|
|
7
|
+
# Logo
|
|
8
|
+
|
|
9
|
+
Le composant `Logo` est utilisé pour afficher le logo de l'application avec différentes options de personnalisation.
|
|
10
|
+
|
|
11
|
+
<Canvas of={LogoStories.Default} />
|
|
12
|
+
|
|
13
|
+
# API
|
|
14
|
+
|
|
15
|
+
<Controls of={LogoStories.Default} />
|
|
16
|
+
|
|
17
|
+
## Utilisation
|
|
18
|
+
|
|
19
|
+
<Source dark code={`
|
|
20
|
+
<script setup lang='ts'>
|
|
21
|
+
import Logo from '@/components/Logo/Logo.vue'
|
|
22
|
+
</script>
|
|
23
|
+
<template>
|
|
24
|
+
<Logo :hideSignature="false" :hideOrganism="false" :risquePro="false" ariaLabel="" :avatar="false" :dark="false" />
|
|
25
|
+
</template>
|
|
26
|
+
`} />
|