@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,332 @@
1
+ import { describe, it, expect, beforeEach } from 'vitest'
2
+ import { vi } from 'vitest'
3
+ import { mount, shallowMount } from '@vue/test-utils'
4
+ import NotificationBar from '../NotificationBar.vue'
5
+ import { type Notification } from '@/components/NotificationBar/types'
6
+ import { useNotificationService } from '@/services/NotificationService'
7
+ import { nextTick, type Ref, ref } from 'vue'
8
+ import { vuetify } from '@tests/unit/setup'
9
+
10
+ vi.mock('@/services/NotificationService')
11
+ describe('NotificationBar.vue', () => {
12
+ interface NotificationServiceMock {
13
+ notificationQueue: Ref<Notification[]>
14
+ addNotification: (notification: Notification) => void
15
+ removeNotification: (id: string) => void
16
+ }
17
+
18
+ let notificationServiceMock: NotificationServiceMock
19
+ beforeEach(() => {
20
+ notificationServiceMock = {
21
+ notificationQueue: ref([]),
22
+ addNotification: vi.fn(),
23
+ removeNotification: vi.fn(),
24
+ };
25
+ // @ts-expect-error on vi.Mock
26
+ (useNotificationService as vi.Mock).mockReturnValue(notificationServiceMock)
27
+ })
28
+
29
+ it('should render notification bar', async () => {
30
+ const notification: Notification = {
31
+ id: '1',
32
+ message: 'Test message',
33
+ type: 'info',
34
+ timeout: -1,
35
+ icon: null,
36
+ }
37
+ const wrapper = shallowMount(NotificationBar, {
38
+ props: { notification },
39
+ global: {
40
+ plugins: [vuetify],
41
+ },
42
+ })
43
+ wrapper.vm.openNotification(notification)
44
+
45
+ await nextTick()
46
+ expect(wrapper.exists()).toBe(true)
47
+ expect(wrapper.html()).toMatchSnapshot()
48
+ })
49
+
50
+ it('should display the correct color based on notification type', async () => {
51
+ const notification: Notification = {
52
+ id: '1',
53
+ message: 'Test message',
54
+ type: 'success',
55
+ timeout: -1,
56
+ icon: null,
57
+ }
58
+ const wrapper = mount(NotificationBar, {
59
+ props: { notification },
60
+ global: {
61
+ plugins: [vuetify],
62
+ },
63
+ })
64
+ wrapper.vm.openNotification(notification)
65
+
66
+ await nextTick()
67
+ expect(wrapper.vm.color).toBe('success')
68
+ })
69
+
70
+ it('should close notification when handleClearNotification is called', async () => {
71
+ const notification: Notification = {
72
+ id: '1',
73
+ message: 'Test message',
74
+ type: 'info',
75
+ timeout: -1,
76
+ icon: null,
77
+ }
78
+ const wrapper = mount(NotificationBar, {
79
+ props: { notification },
80
+ global: {
81
+ plugins: [vuetify],
82
+ },
83
+ })
84
+ wrapper.vm.openNotification(notification)
85
+ wrapper.vm.handleClearNotification()
86
+
87
+ await nextTick()
88
+ expect(wrapper.vm.isNotificationVisible).toBe(false)
89
+ expect(notificationServiceMock.removeNotification).toHaveBeenCalledWith(notification.id)
90
+ })
91
+
92
+ it('should show the next notification in the queue', async () => {
93
+ const notification1: Notification = {
94
+ id: '1',
95
+ message: 'Test message 1',
96
+ type: 'info',
97
+ timeout: -1,
98
+ icon: null,
99
+ }
100
+ const notification2: Notification = {
101
+ id: '2',
102
+ message: 'Test message 2',
103
+ type: 'success',
104
+ timeout: -1,
105
+ icon: null,
106
+ }
107
+ notificationServiceMock.notificationQueue.value = [notification1, notification2]
108
+
109
+ const wrapper = mount(NotificationBar, {
110
+ props: { notification: notification1 },
111
+ global: {
112
+ plugins: [vuetify],
113
+ },
114
+ })
115
+
116
+ wrapper.vm.showNextNotification()
117
+
118
+ await nextTick()
119
+ expect(wrapper.vm.currentNotification).toEqual(notification1)
120
+ expect(wrapper.vm.isNotificationVisible).toBe(true)
121
+ })
122
+
123
+ it('should show notification based on screen size', async () => {
124
+ const notification: Notification = {
125
+ id: '1',
126
+ message: 'Test message',
127
+ type: 'info',
128
+ timeout: -1,
129
+ icon: null,
130
+ }
131
+ const wrapper = mount(NotificationBar, {
132
+ props: { notification },
133
+ global: {
134
+ plugins: [vuetify],
135
+ },
136
+ })
137
+ wrapper.vm.openNotification(notification)
138
+
139
+ await nextTick()
140
+ expect(wrapper.vm.isMobileVersion).toBe(false)
141
+ expect(wrapper.vm.isVertical).toBe(false)
142
+ })
143
+
144
+ it('should compute hasActionSlot correctly', async () => {
145
+ const notification: Notification = {
146
+ id: '1',
147
+ message: 'Test message',
148
+ type: 'info',
149
+ timeout: -1,
150
+ icon: null,
151
+ }
152
+ const wrapper = mount(NotificationBar, {
153
+ props: { notification },
154
+ global: {
155
+ plugins: [vuetify],
156
+ },
157
+ slots: {
158
+ action: '<div>Action Slot</div>',
159
+ },
160
+ })
161
+ wrapper.vm.openNotification(notification)
162
+
163
+ await nextTick()
164
+ expect(wrapper.vm.hasActionSlot).toBe(true)
165
+ })
166
+
167
+ it('should compute isMobileVersion correctly', async () => {
168
+ const notification: Notification = {
169
+ id: '1',
170
+ message: 'Test message',
171
+ type: 'info',
172
+ timeout: -1,
173
+ icon: null,
174
+ }
175
+ const wrapper = mount(NotificationBar, {
176
+ props: { notification },
177
+ global: {
178
+ plugins: [vuetify],
179
+ },
180
+ })
181
+ wrapper.vm.openNotification(notification)
182
+
183
+ await nextTick()
184
+ expect(wrapper.vm.isMobileVersion).toBe(false)
185
+ })
186
+
187
+ it('should compute hasLongContent correctly', async () => {
188
+ const notification: Notification = {
189
+ id: '1',
190
+ message: 'Test message'.repeat(10),
191
+ type: 'info',
192
+ timeout: -1,
193
+ icon: null,
194
+ }
195
+ const wrapper = mount(NotificationBar, {
196
+ props: { notification },
197
+ global: {
198
+ plugins: [vuetify],
199
+ },
200
+ })
201
+ wrapper.vm.openNotification(notification)
202
+
203
+ await nextTick()
204
+ expect(wrapper.vm.hasLongContent).toBe(true)
205
+ })
206
+
207
+ it('should clear queue', () => {
208
+ const isNotificationVisible = ref(true)
209
+ const emit = vi.fn()
210
+ const removeNotification = vi.fn()
211
+ const currentNotification = ref({ id: 1 })
212
+
213
+ const clearQueue = () => {
214
+ isNotificationVisible.value = false
215
+ emit('clear-notification')
216
+ removeNotification(currentNotification.value.id)
217
+ }
218
+ clearQueue()
219
+
220
+ expect(isNotificationVisible.value).toBe(false)
221
+ expect(emit).toHaveBeenCalledWith('clear-notification')
222
+ expect(removeNotification).toHaveBeenCalledWith(1)
223
+ })
224
+
225
+ it('should reset queue', () => {
226
+ const notificationQueue = ref([{ id: 1 }, { id: 2 }])
227
+ const emit = vi.fn()
228
+ const removeNotification = vi.fn()
229
+ const currentNotification = ref({ id: 1 })
230
+
231
+ const resetQueue = () => {
232
+ notificationQueue.value = []
233
+ emit('clear-notification')
234
+ removeNotification(currentNotification.value.id)
235
+ }
236
+ resetQueue()
237
+
238
+ expect(notificationQueue.value).toEqual([])
239
+ })
240
+
241
+ it('should call clearQueue when resetQueue is called', () => {
242
+ const isNotificationVisible = ref(true)
243
+ const emit = vi.fn()
244
+ const removeNotification = vi.fn()
245
+ const currentNotification = ref({ id: 1 })
246
+
247
+ const clearQueue = () => {
248
+ isNotificationVisible.value = false
249
+ emit('clear-notification')
250
+ removeNotification(currentNotification.value.id)
251
+ }
252
+
253
+ const resetQueue = () => {
254
+ clearQueue()
255
+ }
256
+
257
+ resetQueue()
258
+
259
+ expect(isNotificationVisible.value).toBe(false)
260
+ expect(emit).toHaveBeenCalledWith('clear-notification')
261
+ expect(removeNotification).toHaveBeenCalledWith(1)
262
+ })
263
+
264
+ it('should reset all queue on resetQueue', () => {
265
+ const notificationQueue = ref([{ id: 1 }, { id: 2 }])
266
+ const emit = vi.fn()
267
+ const removeNotification = vi.fn()
268
+ const currentNotification = ref({ id: 1 })
269
+
270
+ const clearQueue = () => {
271
+ notificationQueue.value = []
272
+ emit('clear-notification')
273
+ removeNotification(currentNotification.value.id)
274
+ }
275
+
276
+ const resetQueue = () => {
277
+ clearQueue()
278
+ }
279
+
280
+ resetQueue()
281
+
282
+ expect(notificationQueue.value).toEqual([])
283
+ })
284
+
285
+ it('should handle empty notification queue', async () => {
286
+ notificationServiceMock.notificationQueue.value = []
287
+
288
+ const wrapper = mount(NotificationBar, {
289
+ global: {
290
+ plugins: [vuetify],
291
+ },
292
+ })
293
+
294
+ wrapper.vm.showNextNotification()
295
+
296
+ await nextTick()
297
+ expect(wrapper.vm.isNotificationVisible).toBe(false)
298
+ })
299
+
300
+ it('should handle clearQueue when no notification is visible', () => {
301
+ const isNotificationVisible = ref(false)
302
+ const emit = vi.fn()
303
+ const removeNotification = vi.fn()
304
+ const currentNotification = ref({ id: 1 })
305
+
306
+ const clearQueue = () => {
307
+ isNotificationVisible.value = false
308
+ emit('clear-notification')
309
+ removeNotification(currentNotification.value.id)
310
+ }
311
+ clearQueue()
312
+
313
+ expect(isNotificationVisible.value).toBe(false)
314
+ expect(emit).toHaveBeenCalledWith('clear-notification')
315
+ expect(removeNotification).toHaveBeenCalledWith(1)
316
+ })
317
+
318
+ it('should handle empty notification queue correctly', async () => {
319
+ notificationServiceMock.notificationQueue.value = []
320
+
321
+ const wrapper = mount(NotificationBar, {
322
+ global: {
323
+ plugins: [vuetify],
324
+ },
325
+ })
326
+
327
+ await wrapper.vm.processNotificationQueue()
328
+
329
+ expect(wrapper.vm.isNotificationVisible).toBe(false)
330
+ expect(notificationServiceMock.removeNotification).not.toHaveBeenCalled()
331
+ })
332
+ })
@@ -0,0 +1,7 @@
1
+ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2
+
3
+ exports[`NotificationBar.vue > should render notification bar 1`] = `
4
+ "<div data-v-867cdd3c="" notification="[object Object]">
5
+ <v-snackbar-stub data-v-867cdd3c="" color="info" width="960px" class="" multiline="false" timer="false" timeout="-1" vertical="false" location="top" rounded="4" tile="false" variant="elevated" absolute="false" attach="false" closeonback="true" contained="false" disabled="false" modelvalue="true" zindex="2000" activatorprops="[object Object]" openonhover="false" closeoncontentclick="false" eager="false" locationstrategy="static" origin="auto" transition="v-snackbar-transition" role="status"></v-snackbar-stub>
6
+ </div>"
7
+ `;
@@ -0,0 +1,7 @@
1
+ export interface Notification {
2
+ id: string
3
+ message: string
4
+ type: 'info' | 'success' | 'warning' | 'error'
5
+ timeout?: number
6
+ icon?: string | null
7
+ }
@@ -0,0 +1,29 @@
1
+ import {Controls, Canvas, Meta, Source} from '@storybook/blocks';
2
+
3
+ import * as PageContainerStories from './PageContainer.stories';
4
+
5
+ <Meta of={PageContainerStories} />
6
+
7
+ # PageContainer
8
+
9
+ Le composant `PageContainer` est un conteneur transparent utilisé pour afficher une page.
10
+
11
+ <Canvas of={PageContainerStories.Default} />
12
+
13
+ # API
14
+
15
+ <Controls of={PageContainerStories.Default} />
16
+
17
+ # Exemple d'utilisation
18
+
19
+ <Source dark code={`
20
+ <script setup lang="ts">
21
+ import PageContainer from '@/components/PageContainer/PageContainer.vue'
22
+ </script>
23
+
24
+ <template>
25
+ <PageContainer color="primary">
26
+ Contenu de la page
27
+ </PageContainer>
28
+ </template>
29
+ `} />
@@ -0,0 +1,115 @@
1
+ import type { Meta, StoryObj } from '@storybook/vue3'
2
+ import PageContainer from './PageContainer.vue'
3
+ import { VCard } from 'vuetify/components'
4
+
5
+ const meta = {
6
+ title: 'Components/PageContainer',
7
+ component: PageContainer,
8
+ parameters: {
9
+ layout: 'fullscreen',
10
+ controls: { exclude: ['spacingClass', 'containerSize'] },
11
+ },
12
+ argTypes: {
13
+ size: {
14
+ options: ['xl', 'l', 'm', 's'],
15
+ control: { type: 'select' },
16
+ default: 'xl',
17
+ },
18
+ spacing: {
19
+ options: ['xs', 'sm', 'md', 'lg', 'xl'],
20
+ control: { type: 'select' },
21
+ default: undefined,
22
+ },
23
+ color: {
24
+ options: ['transparent', 'primary', 'secondary', 'accent', 'error', 'info', 'success', 'warning'],
25
+ control: { type: 'select' },
26
+ default: 'transparent',
27
+ },
28
+ },
29
+ } as Meta<typeof PageContainer>
30
+
31
+ export default meta
32
+
33
+ type Story = StoryObj<typeof meta>
34
+
35
+ export const Default: Story = {
36
+ args: {
37
+ default: 'Contenu de la page',
38
+ },
39
+ render: (args) => {
40
+ return {
41
+ components: { PageContainer },
42
+ setup() {
43
+ return { args }
44
+ },
45
+ template: `
46
+ <PageContainer :size="args.size" :spacing="args.spacing" :color="args.color">
47
+ {{ args.default }}
48
+ </PageContainer>
49
+ `,
50
+ }
51
+ },
52
+ }
53
+
54
+ export const Size: Story = {
55
+ args: {
56
+ default: 'Contenu de la page',
57
+ size: 's',
58
+ },
59
+ render: (args) => {
60
+ return {
61
+ components: { PageContainer },
62
+ setup() {
63
+ return { args }
64
+ },
65
+ template: `
66
+ <PageContainer :size="args.size" :spacing="args.spacing" :color="args.color">
67
+ {{ args.default }}
68
+ </PageContainer>
69
+ `,
70
+ }
71
+ },
72
+ }
73
+
74
+ export const Color: Story = {
75
+ args: {
76
+ default: 'Contenu de la page',
77
+ color: 'primary',
78
+ },
79
+ render: (args) => {
80
+ return {
81
+ components: { PageContainer },
82
+ setup() {
83
+ return { args }
84
+ },
85
+ template: `
86
+ <PageContainer :size="args.size" :spacing="args.spacing" :color="args.color">
87
+ <div class="pa-8">
88
+ {{ args.default }}
89
+ </div>
90
+ </PageContainer>
91
+ `,
92
+ }
93
+ },
94
+ }
95
+
96
+ export const Card: Story = {
97
+ args: {
98
+ default: 'Contenu de la page',
99
+ },
100
+ render: (args) => {
101
+ return {
102
+ components: { PageContainer, VCard },
103
+ setup() {
104
+ return { args }
105
+ },
106
+ template: `
107
+ <PageContainer :size="args.size" :spacing="args.spacing" :color="args.color">
108
+ <VCard class="pa-8">
109
+ {{ args.default }}
110
+ </VCard>
111
+ </PageContainer>
112
+ `,
113
+ }
114
+ },
115
+ }
@@ -0,0 +1,68 @@
1
+ <script setup lang="ts">
2
+ import { computed } from 'vue'
3
+ import { VSheet } from 'vuetify/components'
4
+ import { useDisplay } from 'vuetify'
5
+
6
+ const props = withDefaults(defineProps<{
7
+ size?: 'xl' | 'l' | 'm' | 's'
8
+ spacing?: 'xs' | 'sm' | 'md' | 'lg' | 'xl'
9
+ color?: string
10
+ }>(), {
11
+ size: 'xl',
12
+ spacing: undefined,
13
+ color: 'transparent',
14
+ })
15
+
16
+ const display = useDisplay()
17
+
18
+ const spacingMapping: Record<string, string> = {
19
+ xs: 'px-0',
20
+ sm: 'px-4',
21
+ md: 'px-8',
22
+ lg: 'px-8',
23
+ xl: 'px-8',
24
+ }
25
+
26
+ const spacingX = spacingMapping[display.name.value]
27
+
28
+ const spacingClass = computed(() => {
29
+ if (props.spacing) {
30
+ return `py-10 ${spacingMapping[props.spacing]}`
31
+ }
32
+
33
+ return `py-10 ${spacingX}`
34
+ })
35
+
36
+ const sizeMapping: Record<string, number> = {
37
+ xl: 1440,
38
+ l: 960,
39
+ m: 800,
40
+ s: 600,
41
+ }
42
+
43
+ const containerSize = computed(() => {
44
+ return sizeMapping[props.size]
45
+ })
46
+
47
+ defineExpose({
48
+ spacingClass,
49
+ containerSize,
50
+ })
51
+ </script>
52
+
53
+ <template>
54
+ <div :class="[spacingClass, 'vd-page-container d-flex justify-center']">
55
+ <VSheet
56
+ :width="containerSize"
57
+ :color="color"
58
+ >
59
+ <slot />
60
+ </VSheet>
61
+ </div>
62
+ </template>
63
+
64
+ <style lang="scss" scoped>
65
+ .vd-page-container {
66
+ flex: 1;
67
+ }
68
+ </style>
@@ -0,0 +1,56 @@
1
+ import { describe, it, expect } from 'vitest'
2
+ import { mount } from '@vue/test-utils'
3
+ import { vuetify } from '@tests/unit/setup'
4
+
5
+ import PageContainer from '../PageContainer.vue'
6
+
7
+ describe('PageContainer', () => {
8
+ it('render correctly', async () => {
9
+ const wrapper = mount(PageContainer, {
10
+ global: {
11
+ plugins: [vuetify],
12
+ },
13
+ })
14
+
15
+ expect(wrapper.html()).toMatchSnapshot()
16
+ })
17
+
18
+ it('render correctly with slot', async () => {
19
+ const wrapper = mount(PageContainer, {
20
+ slots: {
21
+ default: 'slot content',
22
+ },
23
+ global: {
24
+ plugins: [vuetify],
25
+ },
26
+ })
27
+
28
+ expect(wrapper.html()).toContain('slot content')
29
+ })
30
+
31
+ it('render correctly with spacing class', async () => {
32
+ const wrapper = mount(PageContainer, {
33
+ props: {
34
+ spacing: 'sm',
35
+ },
36
+ global: {
37
+ plugins: [vuetify],
38
+ },
39
+ })
40
+
41
+ expect(wrapper.vm.spacingClass).toBe('py-10 px-4')
42
+ })
43
+
44
+ it('containerSize computed property', async () => {
45
+ const wrapper = mount(PageContainer, {
46
+ props: {
47
+ size: 'l',
48
+ },
49
+ global: {
50
+ plugins: [vuetify],
51
+ },
52
+ })
53
+
54
+ expect(wrapper.vm.containerSize).toBe(960)
55
+ })
56
+ })
@@ -0,0 +1,7 @@
1
+ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2
+
3
+ exports[`PageContainer > render correctly 1`] = `
4
+ "<div data-v-fb0b02fd="" class="py-10 px-8 vd-page-container d-flex justify-center">
5
+ <div data-v-fb0b02fd="" class="v-sheet v-theme--light bg-transparent" style="width: 1440px;"></div>
6
+ </div>"
7
+ `;
@@ -0,0 +1,55 @@
1
+ import {Controls, Canvas, Meta, Source} from '@storybook/blocks';
2
+
3
+ import * as SkipLinkStories from './SkipLink.stories';
4
+
5
+ <Meta of={SkipLinkStories} />
6
+
7
+ # SkipLink
8
+
9
+ Le composant `SkipLink` est utilisé pour permettre à l'utilisateur utilisant la navigation au clavier de passer directement à une section de contenu.
10
+
11
+ Il est généralement placé en haut de la page et est caché visuellement. Lorsqu'il est activé, il devient visible et permet à l'utilisateur de sauter directement à une section de contenu.
12
+
13
+ Quand l'utilisateur change de page ou de vue, le focus est automatiquement placé sur l'élément de saut pour lui permettre de naviguer rapidement dans la page.
14
+
15
+ <Canvas of={SkipLinkStories.Default} />
16
+
17
+ ## API
18
+
19
+ <Controls of={SkipLinkStories.Default} />
20
+
21
+ ## Exemple d'utilisation avec Nuxt
22
+
23
+ <Source dark code={`
24
+ <template>
25
+ <div>
26
+ <SkipLink label="Aller au contenu" target="#main-content"/>
27
+ <div id="main-content">
28
+ <slot />
29
+ </div>
30
+ </div>
31
+ </template>
32
+ `} />
33
+
34
+ ## Exemple d'utilisation avec Vue
35
+
36
+ <Source dark code={`
37
+ <template>
38
+ <v-app>
39
+ <SkipLink target="#main-content"/>
40
+ <nav>
41
+ <ul>
42
+ <li>
43
+ <router-link to="/">index</router-link>
44
+ </li>
45
+ <li>
46
+ <router-link to="/other">other</router-link>
47
+ </li>
48
+ </ul>
49
+ </nav>
50
+ <div id="main-content">
51
+ <router-view />
52
+ </div>
53
+ </v-app>
54
+ </template>
55
+ `} />