@cnamts/synapse 0.0.0-alpha.0

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 (130) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +2 -0
  3. package/dist/design-system-v3.d.ts +246 -0
  4. package/dist/design-system-v3.js +5425 -0
  5. package/dist/design-system-v3.umd.cjs +2 -0
  6. package/dist/style.css +1 -0
  7. package/package.json +104 -0
  8. package/src/assets/tokens.scss +500 -0
  9. package/src/components/Alert/Alert.mdx +36 -0
  10. package/src/components/Alert/Alert.stories.ts +115 -0
  11. package/src/components/Alert/Alert.vue +248 -0
  12. package/src/components/Alert/locales.ts +3 -0
  13. package/src/components/Alert/tests/Alert.spec.ts +105 -0
  14. package/src/components/Alert/tests/__snapshots__/Alert.spec.ts.snap +15 -0
  15. package/src/components/BackBtn/BackBtn.mdx +26 -0
  16. package/src/components/BackBtn/BackBtn.stories.ts +138 -0
  17. package/src/components/BackBtn/BackBtn.vue +60 -0
  18. package/src/components/BackBtn/locales.ts +3 -0
  19. package/src/components/BackBtn/tests/BackBtn.spec.ts +103 -0
  20. package/src/components/BackBtn/tests/__snapshots__/BackBtn.spec.ts.snap +9 -0
  21. package/src/components/BackToTopBtn/BackToTopBtn.mdx +52 -0
  22. package/src/components/BackToTopBtn/BackToTopBtn.stories.ts +188 -0
  23. package/src/components/BackToTopBtn/BackToTopBtn.vue +137 -0
  24. package/src/components/BackToTopBtn/config.ts +12 -0
  25. package/src/components/BackToTopBtn/locales.ts +3 -0
  26. package/src/components/BackToTopBtn/tests/BackToTopBtn.spec.ts +173 -0
  27. package/src/components/BackToTopBtn/tests/__snapshots__/BackToTopBtn.spec.ts.snap +17 -0
  28. package/src/components/Beta/beta.mdx +5 -0
  29. package/src/components/CopyBtn/CopyBtn.mdx +38 -0
  30. package/src/components/CopyBtn/CopyBtn.stories.ts +209 -0
  31. package/src/components/CopyBtn/CopyBtn.vue +103 -0
  32. package/src/components/CopyBtn/config.ts +17 -0
  33. package/src/components/CopyBtn/locales.ts +3 -0
  34. package/src/components/CopyBtn/tests/CopyBtn.spec.ts +99 -0
  35. package/src/components/CopyBtn/tests/__snapshots__/CopyBtn.spec.ts.snap +7 -0
  36. package/src/components/Deprecated/deprecated.mdx +5 -0
  37. package/src/components/DownloadBtn/DownloadBtn.mdx +94 -0
  38. package/src/components/DownloadBtn/DownloadBtn.stories.ts +211 -0
  39. package/src/components/DownloadBtn/DownloadBtn.vue +113 -0
  40. package/src/components/DownloadBtn/config.ts +13 -0
  41. package/src/components/DownloadBtn/tests/DownloadBtn.spec.ts +82 -0
  42. package/src/components/DownloadBtn/tests/__snapshots__/DownloadBtn.spec.ts.snap +17 -0
  43. package/src/components/DownloadBtn/tests/data/filePromise.ts +53 -0
  44. package/src/components/DownloadBtn/tests/data/test.json +0 -0
  45. package/src/components/FranceConnectBtn/FranceConnectBtn.mdx +34 -0
  46. package/src/components/FranceConnectBtn/FranceConnectBtn.stories.ts +92 -0
  47. package/src/components/FranceConnectBtn/FranceConnectBtn.vue +154 -0
  48. package/src/components/FranceConnectBtn/locales.ts +6 -0
  49. package/src/components/FranceConnectBtn/tests/FranceConnectBtn.spec.ts +62 -0
  50. package/src/components/FranceConnectBtn/tests/__snapshots__/FranceConnectBtn.spec.ts.snap +36 -0
  51. package/src/components/LangBtn/LangBtn.mdx +37 -0
  52. package/src/components/LangBtn/LangBtn.stories.ts +147 -0
  53. package/src/components/LangBtn/LangBtn.vue +167 -0
  54. package/src/components/LangBtn/config.ts +17 -0
  55. package/src/components/LangBtn/locales.ts +3 -0
  56. package/src/components/LangBtn/tests/Config.spec.ts +24 -0
  57. package/src/components/LangBtn/tests/LangBtn.spec.ts +283 -0
  58. package/src/components/LangBtn/tests/__snapshots__/LangBtn.spec.ts.snap +11 -0
  59. package/src/components/LangBtn/types.d.ts +7 -0
  60. package/src/components/NotificationBar/NotificationBar.mdx +94 -0
  61. package/src/components/NotificationBar/NotificationBar.stories.ts +366 -0
  62. package/src/components/NotificationBar/NotificationBar.vue +296 -0
  63. package/src/components/NotificationBar/options.ts +15 -0
  64. package/src/components/NotificationBar/tests/NotificationBar.spec.ts +332 -0
  65. package/src/components/NotificationBar/tests/__snapshots__/NotificationBar.spec.ts.snap +7 -0
  66. package/src/components/NotificationBar/types.ts +7 -0
  67. package/src/components/PageContainer/PageContainer.mdx +29 -0
  68. package/src/components/PageContainer/PageContainer.stories.ts +115 -0
  69. package/src/components/PageContainer/PageContainer.vue +68 -0
  70. package/src/components/PageContainer/tests/PageContainer.spec.ts +56 -0
  71. package/src/components/PageContainer/tests/__snapshots__/PageContainer.spec.ts.snap +7 -0
  72. package/src/components/SkipLink/SkipLink.mdx +55 -0
  73. package/src/components/SkipLink/SkipLink.stories.ts +70 -0
  74. package/src/components/SkipLink/SkipLink.vue +79 -0
  75. package/src/components/SkipLink/locales.ts +3 -0
  76. package/src/components/SkipLink/tests/__snapshots__/skipLink.spec.ts.snap +3 -0
  77. package/src/components/SkipLink/tests/skipLink.spec.ts +46 -0
  78. package/src/components/index.ts +8 -0
  79. package/src/composables/useCustomizableOptions.ts +23 -0
  80. package/src/designTokens/bootstrapColors.md +66 -0
  81. package/src/designTokens/cnamColors.md +193 -0
  82. package/src/designTokens/index.ts +15 -0
  83. package/src/designTokens/tokens/bootstrap/bootstrapColors.ts +158 -0
  84. package/src/designTokens/tokens/bootstrap/bootstrapLightTheme.ts +22 -0
  85. package/src/designTokens/tokens/cnam/cnamColors.ts +171 -0
  86. package/src/designTokens/tokens/cnam/cnamContextual.ts +58 -0
  87. package/src/designTokens/tokens/cnam/cnamLightTheme.ts +90 -0
  88. package/src/designTokens/tokens/cnam/cnamSemantic.ts +87 -0
  89. package/src/designTokens/tokens/json/contextual-tokens.json +156 -0
  90. package/src/designTokens/tokens/json/primitives.json +209 -0
  91. package/src/designTokens/tokens/json/semantic.json +120 -0
  92. package/src/designTokens/utils/convertGaps.ts +11 -0
  93. package/src/designTokens/utils/convertSemanticsToken.ts +32 -0
  94. package/src/designTokens/utils/createFlattenTheme.ts +19 -0
  95. package/src/designTokens/utils/index.ts +4 -0
  96. package/src/main.ts +2 -0
  97. package/src/services/NotificationService.ts +27 -0
  98. package/src/stories/Fondamentaux/Accessibilite/Accessibilite.mdx +52 -0
  99. package/src/stories/Fondamentaux/Accessibilite/Accessibilite.stories.ts +36 -0
  100. package/src/stories/Fondamentaux/Accessibilite/AccessibiliteItems.ts +706 -0
  101. package/src/stories/Fondamentaux/Accessibilite/constants/ExpertiseLevelEnum.ts +5 -0
  102. package/src/stories/Fondamentaux/Accessibilite/constants/RGAALevelEnum.ts +4 -0
  103. package/src/stories/Fondamentaux/EcoConception/EcoConception.mdx +24 -0
  104. package/src/stories/Fondamentaux/EcoConception/Econception.stories.ts +30 -0
  105. package/src/stories/Fondamentaux/EcoConception/ecoDesignItems.ts +55 -0
  106. package/src/stories/GuideDuDev/CommentContribuer.mdx +22 -0
  107. package/src/stories/GuideDuDev/components.stories.ts +23 -0
  108. package/src/stories/GuideDuDev/moduleDeNotification.mdx +182 -0
  109. package/src/stories/GuideDuDev/vuetifyOptions.mdx +72 -0
  110. package/src/stories/Guidelines/Colors.mdx +220 -0
  111. package/src/stories/Guidelines/CustomisationEtThemes.mdx +3 -0
  112. package/src/stories/Guidelines/Introduction.mdx +35 -0
  113. package/src/stories/Guidelines/Typo.mdx +53 -0
  114. package/src/stories/Home/Accueil.mdx +7 -0
  115. package/src/stories/Home/PolitiqueDeConfidentialite.mdx +4 -0
  116. package/src/stories/Home/synapse.webp +0 -0
  117. package/src/temp/TestA11y.vue +14 -0
  118. package/src/temp/TestComponent.vue +37 -0
  119. package/src/temp/TestDTComponent.vue +93 -0
  120. package/src/temp/customizableOptions.vue +18 -0
  121. package/src/temp/gridsTests.vue +54 -0
  122. package/src/temp/options.json +5 -0
  123. package/src/types/vuetifyTypes.ts +3 -0
  124. package/src/utils/convertToUnit/index.ts +16 -0
  125. package/src/utils/convertToUnit/test/convertToUnit.spec.ts +32 -0
  126. package/src/utils/functions/copyToClipboard/index.ts +38 -0
  127. package/src/utils/functions/copyToClipboard/tests/copyToClipboard.spec.ts +104 -0
  128. package/src/utils/functions/downloadFile/index.ts +37 -0
  129. package/src/utils/functions/downloadFile/tests/downloadFile.spec.ts +69 -0
  130. package/src/utils/functions/downloadFile/types.ts +1 -0
@@ -0,0 +1,52 @@
1
+ import {Controls, Canvas, Meta, Source} from '@storybook/blocks';
2
+ import * as BackToTopBtnStories from './BackToTopBtn.stories';
3
+
4
+ <Meta of={BackToTopBtnStories} />
5
+
6
+
7
+ # BackToTopBtn
8
+
9
+ Le composant `BackToTopBtn` est utilisé pour afficher un bouton permettant à l’utilisateur de remonter en haut de la page.
10
+
11
+ Le boutton apparaît lorsque l'utilisateur scroll en dessous d'une certaine hauteur de la page. Cette hauteur est définie par la prop `threshold`, elle est de 120px par defaut.
12
+
13
+ <Canvas of={BackToTopBtnStories.Default} />
14
+
15
+ ## API
16
+
17
+ <Controls of={BackToTopBtnStories.Default} />
18
+
19
+ ## Context d'utilisation
20
+
21
+ Ce composant peut être utilisé à la racine de la page ou bien dans un composant avant son propre context de scroll. Dans ce dernier cas, il conviens de passer la prop `target` un id unique de l'élément scrollable.
22
+
23
+ ### Exemple d'utilisation
24
+
25
+ <Source dark code={`
26
+ <script setup lang="ts">
27
+ import BackToTopBtn from '@/components/BackToTopBtn/BackToTopBtn.vue'
28
+ import { VCard, VSheet } from 'vuetify/components'
29
+ </script>
30
+
31
+ <template>
32
+ <VCard
33
+ id="target"
34
+ width="100%"
35
+ max-height="200px"
36
+ class="overflow-y-auto"
37
+ style="scroll-behavior: smooth"
38
+ >
39
+ <VSheet
40
+ height="600px"
41
+ class="d-flex flex-column align-center"
42
+ >
43
+ <p class="pa-2">
44
+ Haut de la section.
45
+ </p>
46
+ </VSheet>
47
+ <BackToTopBtn target="target">
48
+ Retour en haut de la page
49
+ </BackToTopBtn>
50
+ </VCard>
51
+ </template>
52
+ `} />
@@ -0,0 +1,188 @@
1
+ import type { Meta, StoryObj } from '@storybook/vue3'
2
+
3
+ import BackToTopBtn from './BackToTopBtn.vue'
4
+ import { VCard, VSheet } from 'vuetify/components'
5
+
6
+ const meta = {
7
+ title: 'Components/BackToTopBtn',
8
+ component: BackToTopBtn,
9
+ parameters: {
10
+ layout: 'fullscreen',
11
+ },
12
+ argTypes: {
13
+ threshold: {
14
+ control: { type: 'range', max: 2000 },
15
+ },
16
+ nudgeRight: {
17
+ control: { type: 'range' },
18
+ default: 16,
19
+ },
20
+ nudgeBottom: {
21
+ control: { type: 'range' },
22
+ default: 16,
23
+ },
24
+ target: {
25
+ control: { type: 'text' },
26
+ },
27
+ default: {
28
+ control: { type: 'text' },
29
+ },
30
+ icon: {
31
+ control: { type: 'text' },
32
+ },
33
+ },
34
+ args: {
35
+ threshold: 120,
36
+ nudgeRight: 16,
37
+ nudgeBottom: 16,
38
+ target: 'target',
39
+ default: 'Retour en haut',
40
+ },
41
+ } satisfies Meta<typeof BackToTopBtn>
42
+
43
+ export default meta
44
+
45
+ type Story = StoryObj<typeof meta>
46
+
47
+ export const Default: Story = {
48
+ args: {
49
+ target: 'target',
50
+ vuetifyOptions: {
51
+ btn: {
52
+ variant: 'outlined',
53
+ color: 'primary',
54
+ class: 'text-wrap px-0 px-md-4',
55
+ },
56
+ icon: {
57
+ color: 'primary',
58
+ size: 'small',
59
+ class: 'ml-0 ml-md-1',
60
+ },
61
+ },
62
+ },
63
+ render: (args) => {
64
+ return {
65
+ components: {
66
+ BackToTopBtn,
67
+ VCard,
68
+ VSheet,
69
+ },
70
+ setup() {
71
+ return { args }
72
+ },
73
+ template: `
74
+ <VCard
75
+ id="target"
76
+ width="100%"
77
+ max-height="200px"
78
+ class="overflow-y-auto"
79
+ style="scroll-behavior: smooth"
80
+ >
81
+ <VSheet
82
+ height="600px"
83
+ class="d-flex flex-column align-center"
84
+ >
85
+ <p class="pa-2">
86
+ Haut de la section.
87
+ </p>
88
+ </VSheet>
89
+ <BackToTopBtn v-bind="args" :vuetify-options="args.vuetifyOptions">
90
+ {{args.default}}
91
+ </BackToTopBtn>
92
+ </VCard>
93
+ `,
94
+ }
95
+ },
96
+ play: async ({ canvasElement }) => {
97
+ await new Promise((resolve: (v: unknown) => void) => setTimeout(resolve, 1000))
98
+ const container = canvasElement.querySelector('#target')
99
+ container?.scrollTo(0, 1000)
100
+ },
101
+ }
102
+
103
+ export const Customization: Story = {
104
+ args: {
105
+ target: 'btn-customization',
106
+ vuetifyOptions: {
107
+ btn: {
108
+ variant: 'elevated',
109
+ color: 'primary',
110
+ rounded: true,
111
+ },
112
+ icon: {
113
+ color: 'white',
114
+ },
115
+ },
116
+ },
117
+ render: (args) => {
118
+ return {
119
+ components: {
120
+ BackToTopBtn,
121
+ },
122
+ setup() {
123
+ return { args }
124
+ },
125
+ template: `
126
+ <VCard
127
+ id="btn-customization"
128
+ width="100%"
129
+ max-height="200px"
130
+ class="overflow-y-auto"
131
+ style="scroll-behavior: smooth"
132
+ >
133
+ <VSheet
134
+ height="600px"
135
+ class="d-flex flex-column align-center"
136
+ >
137
+ <p class="pa-2">
138
+ Haut de la section.
139
+ </p>
140
+ </VSheet>
141
+ <BackToTopBtn v-bind="args" :vuetify-options="args.vuetifyOptions">
142
+ {{args.default}}
143
+ </BackToTopBtn>
144
+ </VCard>
145
+ `,
146
+ }
147
+ },
148
+ play: async ({ canvasElement }) => {
149
+ await new Promise((resolve: (v: unknown) => void) => setTimeout(resolve, 1000))
150
+ const container = canvasElement.querySelector('#btn-customization')
151
+ container?.scrollTo(0, 1000)
152
+ },
153
+ }
154
+
155
+ export const PillBtn: Story = {
156
+ args: {
157
+ target: 'pill-btn',
158
+ vuetifyOptions: {
159
+ btn: {
160
+ variant: 'outlined',
161
+ color: 'medium-emphasis',
162
+ minWidth: 92,
163
+ rounded: true,
164
+ },
165
+ },
166
+ },
167
+ render: (args) => {
168
+ return {
169
+ components: {
170
+ BackToTopBtn,
171
+ },
172
+ setup() {
173
+ return { args }
174
+ },
175
+ template: `
176
+ <v-btn
177
+ color="primary"
178
+ variant="outlined"
179
+ size="small"
180
+ rounded
181
+ @click="() => { window.location.href = '' }"
182
+ >
183
+ VuetifyOptions
184
+ </v-btn>
185
+ `,
186
+ }
187
+ },
188
+ }
@@ -0,0 +1,137 @@
1
+ <script setup lang="ts">
2
+ import { mdiArrowUp } from '@mdi/js'
3
+ import { computed, onMounted, onUnmounted, ref, watch } from 'vue'
4
+
5
+ import useCustomizableOptions, { type CustomizableOptions } from '@/composables/useCustomizableOptions'
6
+ import { convertToUnit } from '@/utils/convertToUnit'
7
+ import { useDisplay } from 'vuetify'
8
+
9
+ import { locales } from './locales'
10
+ import { config } from './config'
11
+
12
+ import { type VBtn } from 'vuetify/components'
13
+
14
+ type VBtnProps = InstanceType<typeof VBtn>['$props']
15
+
16
+ export interface Props {
17
+ threshold?: number
18
+ nudgeRight?: string | number
19
+ nudgeBottom?: string | number
20
+ target?: string
21
+ }
22
+
23
+ const props = withDefaults(defineProps<Props & CustomizableOptions>(), {
24
+ threshold: 120,
25
+ nudgeRight: '16px',
26
+ nudgeBottom: '16px',
27
+ target: undefined,
28
+ })
29
+
30
+ const options = useCustomizableOptions(config, props)
31
+
32
+ const showBtn = ref(false)
33
+ const isMobile = ref(false)
34
+
35
+ const targetSelector = computed(() => {
36
+ return props.target ? `#${props.target}` : null
37
+ })
38
+
39
+ const btnStyle = computed(() => {
40
+ const right = convertToUnit(props.nudgeRight) || '0'
41
+ const bottom = convertToUnit(props.nudgeBottom) || '0'
42
+
43
+ return {
44
+ bottom,
45
+ marginRight: right,
46
+ }
47
+ })
48
+
49
+ const minWidth = computed(() => (isMobile.value ? '36px' : undefined))
50
+
51
+ const labelClasses = computed(() => ({
52
+ 'd-sr-only': isMobile.value,
53
+ }))
54
+
55
+ // Watch for changes in display
56
+ const display = useDisplay()
57
+ watch(
58
+ () => display.smAndDown.value,
59
+ (newVal) => {
60
+ isMobile.value = newVal
61
+ },
62
+ { immediate: true },
63
+ )
64
+
65
+ // Methods
66
+ const onScroll = (e: Event) => {
67
+ const target = e.currentTarget as HTMLElement | Window
68
+
69
+ if (target instanceof Window) {
70
+ showBtn.value = window.scrollY > props.threshold
71
+ }
72
+ else {
73
+ showBtn.value = (target as HTMLElement).scrollTop > props.threshold
74
+ }
75
+ }
76
+
77
+ const scrollToTop = () => {
78
+ if (!props.target) {
79
+ window.scrollTo(0, 0)
80
+ }
81
+ else {
82
+ const target = document.getElementById(props.target) || window
83
+ target.scrollTo(0, 0)
84
+ }
85
+ }
86
+
87
+ // Handling scroll events
88
+ const target = computed(() => props.target ? document.getElementById(props.target) : window)
89
+ onMounted(() => {
90
+ target.value?.addEventListener('scroll', onScroll)
91
+ })
92
+ onUnmounted(() => {
93
+ target.value?.removeEventListener('scroll', onScroll)
94
+ })
95
+
96
+ </script>
97
+
98
+ <template>
99
+ <VFadeTransition>
100
+ <VBtn
101
+ v-show="showBtn"
102
+ v-scroll:[targetSelector]="onScroll"
103
+ v-bind="({
104
+ ...options.btn,
105
+ ...$attrs,
106
+ } as VBtnProps)"
107
+ :style="btnStyle"
108
+ :min-width="minWidth"
109
+ class="vd-back-to-top-btn"
110
+ @click="scrollToTop"
111
+ >
112
+ <span :class="labelClasses">
113
+ <slot>
114
+ {{ locales.label }}
115
+ </slot>
116
+ </span>
117
+
118
+ <slot name="icon">
119
+ <VIcon v-bind="options.icon">
120
+ {{ mdiArrowUp }}
121
+ </VIcon>
122
+ </slot>
123
+ </VBtn>
124
+ </VFadeTransition>
125
+ </template>
126
+
127
+ <style lang="scss" scoped>
128
+ .vd-back-to-top-btn {
129
+ position: sticky;
130
+ z-index: 999;
131
+ opacity: 1;
132
+ float: right;
133
+ }
134
+ .v-btn--variant-outlined {
135
+ background: white;
136
+ }
137
+ </style>
@@ -0,0 +1,12 @@
1
+ export const config = {
2
+ btn: {
3
+ variant: 'outlined',
4
+ color: 'primary',
5
+ class: 'text-wrap px-0 px-md-4',
6
+ },
7
+ icon: {
8
+ color: 'primary',
9
+ size: 'small',
10
+ class: 'ml-0 ml-md-1',
11
+ },
12
+ }
@@ -0,0 +1,3 @@
1
+ export const locales = {
2
+ label: 'Retour en haut',
3
+ }
@@ -0,0 +1,173 @@
1
+ import { describe, it, expect, vi, afterEach } from 'vitest'
2
+ import { mount } from '@vue/test-utils'
3
+
4
+ import BackToTopBtn from '../BackToTopBtn.vue'
5
+ import { vuetify } from '@tests/unit/setup'
6
+
7
+ describe('BackToTopBtn', () => {
8
+ afterEach(() => {
9
+ vi.restoreAllMocks()
10
+ document.body.innerHTML = ''
11
+ })
12
+
13
+ it('renders correctly', () => {
14
+ const wrapper = mount(BackToTopBtn, {
15
+ global: {
16
+ plugins: [vuetify],
17
+ },
18
+ })
19
+
20
+ expect(wrapper.html()).toMatchSnapshot()
21
+ })
22
+
23
+ it('renders correctly when nudgeBottom and nudgeRight are set to invalid values', () => {
24
+ const wrapper = mount(BackToTopBtn, {
25
+ global: {
26
+ plugins: [vuetify],
27
+ },
28
+ props: {
29
+ nudgeBottom: '',
30
+ nudgeRight: '',
31
+ },
32
+ })
33
+
34
+ expect(wrapper.html()).toMatchSnapshot()
35
+ })
36
+
37
+ it('shows the button when the user scrolls down', async () => {
38
+ const wrapper = mount(BackToTopBtn, {
39
+ global: {
40
+ plugins: [vuetify],
41
+ },
42
+ attachTo: document.body,
43
+ })
44
+
45
+ const buttonElement = wrapper.find('.vd-back-to-top-btn')
46
+ expect(buttonElement.isVisible()).toBeFalsy()
47
+
48
+ vi.spyOn(window, 'scrollY', 'get').mockReturnValue(500)
49
+ window.dispatchEvent(new CustomEvent('scroll'))
50
+ await wrapper.vm.$nextTick()
51
+
52
+ expect(buttonElement.isVisible()).toBeTruthy()
53
+ })
54
+
55
+ it('shows the button when the user scrolls down and the target is a custom element', async () => {
56
+ document.body.innerHTML = '<div id="test"></div>'
57
+ const divContent = document.getElementById('test') as HTMLDivElement
58
+
59
+ const wrapper = mount(BackToTopBtn, {
60
+ global: {
61
+ plugins: [vuetify],
62
+ },
63
+ attachTo: divContent,
64
+ props: {
65
+ target: 'test',
66
+ },
67
+ })
68
+
69
+ const buttonElement = wrapper.find('.vd-back-to-top-btn')
70
+ expect(buttonElement.isVisible()).toBeFalsy()
71
+
72
+ vi.spyOn(divContent, 'scrollTop', 'get').mockReturnValue(500)
73
+ divContent.dispatchEvent(new CustomEvent('scroll'))
74
+
75
+ await wrapper.vm.$nextTick()
76
+ expect(buttonElement.isVisible()).toBeTruthy()
77
+ })
78
+
79
+ it('do not show the button when the user scrolls up', async () => {
80
+ const wrapper = mount(BackToTopBtn, {
81
+ global: {
82
+ plugins: [vuetify],
83
+ },
84
+ attachTo: document.body,
85
+ })
86
+
87
+ const buttonElement = wrapper.find('.vd-back-to-top-btn')
88
+ expect(buttonElement.isVisible()).toBeFalsy()
89
+
90
+ vi.spyOn(window, 'scrollY', 'get').mockReturnValue(500)
91
+ window.dispatchEvent(new CustomEvent('scroll'))
92
+ await wrapper.vm.$nextTick()
93
+ expect(buttonElement.isVisible()).toBeTruthy()
94
+
95
+ vi.spyOn(window, 'scrollY', 'get').mockReturnValue(100)
96
+ window.dispatchEvent(new CustomEvent('scroll'))
97
+ await wrapper.vm.$nextTick()
98
+
99
+ expect(buttonElement.isVisible()).toBeFalsy()
100
+ })
101
+
102
+ it('scrolls to the top when the button is clicked', async () => {
103
+ const wrapper = mount(BackToTopBtn, {
104
+ global: {
105
+ plugins: [vuetify],
106
+ },
107
+ })
108
+
109
+ const scrollToSpy = vi
110
+ .spyOn(window, 'scrollTo')
111
+ .mockImplementation(() => {})
112
+
113
+ await wrapper.find('.v-btn').trigger('click')
114
+
115
+ expect(scrollToSpy).toHaveBeenCalledWith(0, 0)
116
+ })
117
+
118
+ it('scrolls to the top when the button is clicked and the target is a custom element', async () => {
119
+ const wrapper = mount(BackToTopBtn, {
120
+ global: {
121
+ plugins: [vuetify],
122
+ },
123
+ propsData: {
124
+ target: 'test',
125
+ },
126
+ })
127
+
128
+ const scrollToSpy = vi.fn()
129
+
130
+ vi.spyOn(document, 'getElementById').mockReturnValue({
131
+ scrollTo: scrollToSpy,
132
+ } as unknown as HTMLElement)
133
+
134
+ await wrapper.find('.v-btn').trigger('click')
135
+
136
+ expect(scrollToSpy).toHaveBeenCalledWith(0, 0)
137
+ })
138
+
139
+ it('scrolls to the top when the button is clicked and the target is a custom element that does not exist', async () => {
140
+ const wrapper = mount(BackToTopBtn, {
141
+ global: {
142
+ plugins: [vuetify],
143
+ },
144
+ propsData: {
145
+ target: 'test',
146
+ },
147
+ })
148
+
149
+ const scrollToSpy = vi
150
+ .spyOn(window, 'scrollTo')
151
+ .mockImplementation(() => {})
152
+
153
+ vi.spyOn(document, 'getElementById').mockReturnValue(null)
154
+
155
+ await wrapper.find('.v-btn').trigger('click')
156
+
157
+ expect(scrollToSpy).toHaveBeenCalledWith(0, 0)
158
+ })
159
+
160
+ it('remove the scroll event listener when the component is destroyed', async () => {
161
+ const wrapper = mount(BackToTopBtn, {
162
+ global: {
163
+ plugins: [vuetify],
164
+ },
165
+ })
166
+
167
+ const removeEventListenerSpy = vi.spyOn(window, 'removeEventListener')
168
+
169
+ wrapper.unmount()
170
+
171
+ expect(removeEventListenerSpy).toHaveBeenCalledWith('scroll', expect.any(Function))
172
+ })
173
+ })
@@ -0,0 +1,17 @@
1
+ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2
+
3
+ exports[`BackToTopBtn > renders correctly 1`] = `
4
+ "<transition-stub data-v-8ea81d33="" name="fade-transition" appear="false" persisted="false" css="true"><button data-v-8ea81d33="" type="button" class="v-btn v-theme--light text-primary v-btn--density-default v-btn--size-default v-btn--variant-outlined text-wrap px-0 px-md-4 vd-back-to-top-btn" style="bottom: 16px; margin-right: 16px; display: none;"><span class="v-btn__overlay"></span><span class="v-btn__underlay"></span>
5
+ <!----><span class="v-btn__content" data-no-activator=""><span data-v-8ea81d33="" class="">Retour en haut</span><i data-v-8ea81d33="" class="M13,20H11V8L5.5,13.5L4.08,12.08L12,4.16L19.92,12.08L18.5,13.5L13,8V20Z mdi v-icon notranslate v-theme--light v-icon--size-small text-primary ml-0 ml-md-1" aria-hidden="true"></i></span>
6
+ <!---->
7
+ <!---->
8
+ </button></transition-stub>"
9
+ `;
10
+
11
+ exports[`BackToTopBtn > renders correctly when nudgeBottom and nudgeRight are set to invalid values 1`] = `
12
+ "<transition-stub data-v-8ea81d33="" name="fade-transition" appear="false" persisted="false" css="true"><button data-v-8ea81d33="" type="button" class="v-btn v-theme--light text-primary v-btn--density-default v-btn--size-default v-btn--variant-outlined text-wrap px-0 px-md-4 vd-back-to-top-btn" style="bottom: 0px; margin-right: 0px; display: none;"><span class="v-btn__overlay"></span><span class="v-btn__underlay"></span>
13
+ <!----><span class="v-btn__content" data-no-activator=""><span data-v-8ea81d33="" class="">Retour en haut</span><i data-v-8ea81d33="" class="M13,20H11V8L5.5,13.5L4.08,12.08L12,4.16L19.92,12.08L18.5,13.5L13,8V20Z mdi v-icon notranslate v-theme--light v-icon--size-small text-primary ml-0 ml-md-1" aria-hidden="true"></i></span>
14
+ <!---->
15
+ <!---->
16
+ </button></transition-stub>"
17
+ `;
@@ -0,0 +1,5 @@
1
+ import { Meta } from '@storybook/addon-docs';
2
+
3
+ <Meta title="Components/Beta" />
4
+
5
+ Folder with Beta components
@@ -0,0 +1,38 @@
1
+ import {Controls, Canvas, Meta, Source} from '@storybook/blocks';
2
+
3
+ import * as CopyBtnStories from './CopyBtn.stories';
4
+
5
+ <Meta of={CopyBtnStories} />
6
+
7
+ # CopyBtn
8
+
9
+ Le composant `CopyBtn` est utilisé pour afficher un bouton permettant à l’utilisateur de copier du texte.
10
+
11
+ <Canvas of={CopyBtnStories.Default} />
12
+
13
+ # API
14
+
15
+ <Controls of={CopyBtnStories.Default} />
16
+
17
+ # Exemple d'utilisation
18
+
19
+ <Source dark code={`
20
+ <script setup lang="ts">
21
+ import CopyBtn from '@/components/CopyBtn/CopyBtn.vue'
22
+ </script>
23
+
24
+ <template>
25
+ <main>
26
+ <div class="d-flex flex-wrap align-center pa-4">
27
+ <p class="mb-0 mr-1">
28
+ Patient n°<b>1970756541</b>
29
+ </p>
30
+
31
+ <CopyBtn
32
+ label="Copier le numéro de patient"
33
+ text-to-copy="1970756541"
34
+ />
35
+ </div>
36
+ </main>
37
+ </template>
38
+ `} />