@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.
- package/LICENSE +21 -0
- package/README.md +2 -0
- package/dist/design-system-v3.d.ts +246 -0
- package/dist/design-system-v3.js +5425 -0
- package/dist/design-system-v3.umd.cjs +2 -0
- package/dist/style.css +1 -0
- package/package.json +104 -0
- package/src/assets/tokens.scss +500 -0
- package/src/components/Alert/Alert.mdx +36 -0
- package/src/components/Alert/Alert.stories.ts +115 -0
- package/src/components/Alert/Alert.vue +248 -0
- package/src/components/Alert/locales.ts +3 -0
- package/src/components/Alert/tests/Alert.spec.ts +105 -0
- package/src/components/Alert/tests/__snapshots__/Alert.spec.ts.snap +15 -0
- package/src/components/BackBtn/BackBtn.mdx +26 -0
- package/src/components/BackBtn/BackBtn.stories.ts +138 -0
- package/src/components/BackBtn/BackBtn.vue +60 -0
- package/src/components/BackBtn/locales.ts +3 -0
- package/src/components/BackBtn/tests/BackBtn.spec.ts +103 -0
- package/src/components/BackBtn/tests/__snapshots__/BackBtn.spec.ts.snap +9 -0
- package/src/components/BackToTopBtn/BackToTopBtn.mdx +52 -0
- package/src/components/BackToTopBtn/BackToTopBtn.stories.ts +188 -0
- package/src/components/BackToTopBtn/BackToTopBtn.vue +137 -0
- package/src/components/BackToTopBtn/config.ts +12 -0
- package/src/components/BackToTopBtn/locales.ts +3 -0
- package/src/components/BackToTopBtn/tests/BackToTopBtn.spec.ts +173 -0
- package/src/components/BackToTopBtn/tests/__snapshots__/BackToTopBtn.spec.ts.snap +17 -0
- package/src/components/Beta/beta.mdx +5 -0
- package/src/components/CopyBtn/CopyBtn.mdx +38 -0
- package/src/components/CopyBtn/CopyBtn.stories.ts +209 -0
- package/src/components/CopyBtn/CopyBtn.vue +103 -0
- package/src/components/CopyBtn/config.ts +17 -0
- package/src/components/CopyBtn/locales.ts +3 -0
- package/src/components/CopyBtn/tests/CopyBtn.spec.ts +99 -0
- package/src/components/CopyBtn/tests/__snapshots__/CopyBtn.spec.ts.snap +7 -0
- package/src/components/Deprecated/deprecated.mdx +5 -0
- package/src/components/DownloadBtn/DownloadBtn.mdx +94 -0
- package/src/components/DownloadBtn/DownloadBtn.stories.ts +211 -0
- package/src/components/DownloadBtn/DownloadBtn.vue +113 -0
- package/src/components/DownloadBtn/config.ts +13 -0
- package/src/components/DownloadBtn/tests/DownloadBtn.spec.ts +82 -0
- package/src/components/DownloadBtn/tests/__snapshots__/DownloadBtn.spec.ts.snap +17 -0
- package/src/components/DownloadBtn/tests/data/filePromise.ts +53 -0
- package/src/components/DownloadBtn/tests/data/test.json +0 -0
- package/src/components/FranceConnectBtn/FranceConnectBtn.mdx +34 -0
- package/src/components/FranceConnectBtn/FranceConnectBtn.stories.ts +92 -0
- package/src/components/FranceConnectBtn/FranceConnectBtn.vue +154 -0
- package/src/components/FranceConnectBtn/locales.ts +6 -0
- package/src/components/FranceConnectBtn/tests/FranceConnectBtn.spec.ts +62 -0
- package/src/components/FranceConnectBtn/tests/__snapshots__/FranceConnectBtn.spec.ts.snap +36 -0
- package/src/components/LangBtn/LangBtn.mdx +37 -0
- package/src/components/LangBtn/LangBtn.stories.ts +147 -0
- package/src/components/LangBtn/LangBtn.vue +167 -0
- package/src/components/LangBtn/config.ts +17 -0
- package/src/components/LangBtn/locales.ts +3 -0
- package/src/components/LangBtn/tests/Config.spec.ts +24 -0
- package/src/components/LangBtn/tests/LangBtn.spec.ts +283 -0
- package/src/components/LangBtn/tests/__snapshots__/LangBtn.spec.ts.snap +11 -0
- package/src/components/LangBtn/types.d.ts +7 -0
- package/src/components/NotificationBar/NotificationBar.mdx +94 -0
- package/src/components/NotificationBar/NotificationBar.stories.ts +366 -0
- package/src/components/NotificationBar/NotificationBar.vue +296 -0
- package/src/components/NotificationBar/options.ts +15 -0
- package/src/components/NotificationBar/tests/NotificationBar.spec.ts +332 -0
- package/src/components/NotificationBar/tests/__snapshots__/NotificationBar.spec.ts.snap +7 -0
- package/src/components/NotificationBar/types.ts +7 -0
- package/src/components/PageContainer/PageContainer.mdx +29 -0
- package/src/components/PageContainer/PageContainer.stories.ts +115 -0
- package/src/components/PageContainer/PageContainer.vue +68 -0
- package/src/components/PageContainer/tests/PageContainer.spec.ts +56 -0
- package/src/components/PageContainer/tests/__snapshots__/PageContainer.spec.ts.snap +7 -0
- package/src/components/SkipLink/SkipLink.mdx +55 -0
- package/src/components/SkipLink/SkipLink.stories.ts +70 -0
- package/src/components/SkipLink/SkipLink.vue +79 -0
- package/src/components/SkipLink/locales.ts +3 -0
- package/src/components/SkipLink/tests/__snapshots__/skipLink.spec.ts.snap +3 -0
- package/src/components/SkipLink/tests/skipLink.spec.ts +46 -0
- package/src/components/index.ts +8 -0
- package/src/composables/useCustomizableOptions.ts +23 -0
- package/src/designTokens/bootstrapColors.md +66 -0
- package/src/designTokens/cnamColors.md +193 -0
- package/src/designTokens/index.ts +15 -0
- package/src/designTokens/tokens/bootstrap/bootstrapColors.ts +158 -0
- package/src/designTokens/tokens/bootstrap/bootstrapLightTheme.ts +22 -0
- package/src/designTokens/tokens/cnam/cnamColors.ts +171 -0
- package/src/designTokens/tokens/cnam/cnamContextual.ts +58 -0
- package/src/designTokens/tokens/cnam/cnamLightTheme.ts +90 -0
- package/src/designTokens/tokens/cnam/cnamSemantic.ts +87 -0
- package/src/designTokens/tokens/json/contextual-tokens.json +156 -0
- package/src/designTokens/tokens/json/primitives.json +209 -0
- package/src/designTokens/tokens/json/semantic.json +120 -0
- package/src/designTokens/utils/convertGaps.ts +11 -0
- package/src/designTokens/utils/convertSemanticsToken.ts +32 -0
- package/src/designTokens/utils/createFlattenTheme.ts +19 -0
- package/src/designTokens/utils/index.ts +4 -0
- package/src/main.ts +2 -0
- package/src/services/NotificationService.ts +27 -0
- package/src/stories/Fondamentaux/Accessibilite/Accessibilite.mdx +52 -0
- package/src/stories/Fondamentaux/Accessibilite/Accessibilite.stories.ts +36 -0
- package/src/stories/Fondamentaux/Accessibilite/AccessibiliteItems.ts +706 -0
- package/src/stories/Fondamentaux/Accessibilite/constants/ExpertiseLevelEnum.ts +5 -0
- package/src/stories/Fondamentaux/Accessibilite/constants/RGAALevelEnum.ts +4 -0
- package/src/stories/Fondamentaux/EcoConception/EcoConception.mdx +24 -0
- package/src/stories/Fondamentaux/EcoConception/Econception.stories.ts +30 -0
- package/src/stories/Fondamentaux/EcoConception/ecoDesignItems.ts +55 -0
- package/src/stories/GuideDuDev/CommentContribuer.mdx +22 -0
- package/src/stories/GuideDuDev/components.stories.ts +23 -0
- package/src/stories/GuideDuDev/moduleDeNotification.mdx +182 -0
- package/src/stories/GuideDuDev/vuetifyOptions.mdx +72 -0
- package/src/stories/Guidelines/Colors.mdx +220 -0
- package/src/stories/Guidelines/CustomisationEtThemes.mdx +3 -0
- package/src/stories/Guidelines/Introduction.mdx +35 -0
- package/src/stories/Guidelines/Typo.mdx +53 -0
- package/src/stories/Home/Accueil.mdx +7 -0
- package/src/stories/Home/PolitiqueDeConfidentialite.mdx +4 -0
- package/src/stories/Home/synapse.webp +0 -0
- package/src/temp/TestA11y.vue +14 -0
- package/src/temp/TestComponent.vue +37 -0
- package/src/temp/TestDTComponent.vue +93 -0
- package/src/temp/customizableOptions.vue +18 -0
- package/src/temp/gridsTests.vue +54 -0
- package/src/temp/options.json +5 -0
- package/src/types/vuetifyTypes.ts +3 -0
- package/src/utils/convertToUnit/index.ts +16 -0
- package/src/utils/convertToUnit/test/convertToUnit.spec.ts +32 -0
- package/src/utils/functions/copyToClipboard/index.ts +38 -0
- package/src/utils/functions/copyToClipboard/tests/copyToClipboard.spec.ts +104 -0
- package/src/utils/functions/downloadFile/index.ts +37 -0
- package/src/utils/functions/downloadFile/tests/downloadFile.spec.ts +69 -0
- 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,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,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
|
+
`} />
|