@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,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é&nbsp;: non conforme</routerlink>
23
+ </li>
24
+ <!--v-if-->
25
+ </ul>
26
+ </footer>"
27
+ `;
@@ -0,0 +1,10 @@
1
+ import { RawLocation } from 'vue-router'
2
+
3
+ export interface LinkItem {
4
+ hidden?: unknown
5
+ text: string
6
+ to?: RawLocation
7
+ href?: string
8
+ ariaLabel?: string
9
+ openInNewTab?: boolean
10
+ }
@@ -1,4 +1,5 @@
1
- import { Controls, Canvas, Meta, Source } from '@storybook/blocks';import * as LangBtnStories from './LangBtn.stories';
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
- @import '../../assets/tokens';
156
+ @use '@/assets/tokens.scss';
157
157
 
158
158
  .v-list-item:hover {
159
- background-color: rgba($colors-overlay, 0.005)
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($colors-overlay, 0.5);
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
+ `} />