@cnamts/synapse 0.0.7-alpha → 0.0.8-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 (102) hide show
  1. package/dist/design-system-v3.d.ts +325 -368
  2. package/dist/design-system-v3.js +2791 -2637
  3. package/dist/design-system-v3.umd.cjs +1 -10
  4. package/dist/style.css +1 -1
  5. package/package.json +10 -2
  6. package/src/assets/settings.scss +2 -2
  7. package/src/assets/tokens.scss +107 -112
  8. package/src/components/BackBtn/BackBtn.vue +4 -4
  9. package/src/components/BackToTopBtn/BackToTopBtn.vue +1 -0
  10. package/src/components/CollapsibleList/CollapsibleList.mdx +1 -1
  11. package/src/components/CollapsibleList/CollapsibleList.vue +43 -44
  12. package/src/components/ContextualMenu/ContextualMenu.mdx +118 -0
  13. package/src/components/ContextualMenu/ContextualMenu.stories.ts +430 -0
  14. package/src/components/ContextualMenu/ContextualMenu.vue +101 -0
  15. package/src/components/ContextualMenu/tests/ContextualMenu.spec.ts +115 -0
  16. package/src/components/ContextualMenu/tests/__snapshots__/ContextualMenu.spec.ts.snap +10 -0
  17. package/src/components/ContextualMenu/types.ts +5 -0
  18. package/src/components/CookieBanner/CookieBanner.stories.ts +1 -2
  19. package/src/components/CookieBanner/CookieBanner.vue +13 -10
  20. package/src/components/CookieBanner/tests/__snapshots__/CookieBanner.spec.ts.snap +17 -15
  21. package/src/components/CookiesSelection/CookiesInformation/CookiesInformation.vue +6 -1
  22. package/src/components/CookiesSelection/CookiesInformation/locales.ts +1 -0
  23. package/src/components/CookiesSelection/CookiesTable/CookiesTable.vue +1 -0
  24. package/src/components/CookiesSelection/tests/__snapshots__/CookiesSelection.spec.ts.snap +17 -15
  25. package/src/components/CopyBtn/CopyBtn.vue +7 -7
  26. package/src/components/Customs/SyBtnSelect/SyBtnSelect.vue +26 -26
  27. package/src/components/Customs/SyInputSelect/SyInputSelect.vue +24 -24
  28. package/src/components/Customs/SySelect/SySelect.vue +27 -26
  29. package/src/components/DataList/DataList.stories.ts +3 -2
  30. package/src/components/DataList/DataList.vue +1 -1
  31. package/src/components/DataListGroup/DataListGroup.stories.ts +3 -2
  32. package/src/components/DataListItem/DataListItem.vue +12 -12
  33. package/src/components/DialogBox/DialogBox.mdx +28 -2
  34. package/src/components/DialogBox/DialogBox.stories.ts +1 -1
  35. package/src/components/DialogBox/DialogBox.vue +3 -2
  36. package/src/components/DownloadBtn/DownloadBtn.vue +2 -1
  37. package/src/components/ExternalLinks/ExternalLinks.mdx +86 -0
  38. package/src/components/ExternalLinks/ExternalLinks.stories.ts +553 -0
  39. package/src/components/ExternalLinks/ExternalLinks.vue +200 -0
  40. package/src/components/ExternalLinks/config.ts +34 -0
  41. package/src/components/ExternalLinks/locales.ts +4 -0
  42. package/src/components/ExternalLinks/tests/ExternalLinks.spec.ts +154 -0
  43. package/src/components/ExternalLinks/tests/__snapshots__/ExternalLinks.spec.ts.snap +159 -0
  44. package/src/components/FooterBar/FooterBar.vue +105 -80
  45. package/src/components/FranceConnectBtn/FranceConnectBtn.vue +14 -13
  46. package/src/components/HeaderBar/HeaderBar.vue +3 -3
  47. package/src/components/HeaderBar/HeaderBurgerMenu/HeaderBurgerMenu.vue +11 -7
  48. package/src/components/HeaderBar/HeaderBurgerMenu/HeaderMenuItem/HeaderMenuItem.vue +5 -5
  49. package/src/components/HeaderBar/HeaderBurgerMenu/HeaderMenuSection/HeaderMenuSection.vue +2 -2
  50. package/src/components/HeaderBar/HeaderBurgerMenu/HeaderSubMenu/HeaderSubMenu.vue +10 -8
  51. package/src/components/HeaderBar/HeaderLogo/HeaderLogo.vue +2 -2
  52. package/src/components/HeaderBar/HeaderLogo/logos/Logo-mobile.vue +2 -1
  53. package/src/components/HeaderBar/HeaderLogo/logos/Logo.vue +2 -1
  54. package/src/components/HeaderBar/HeaderMenuBtn/HeaderMenuBtn.vue +10 -10
  55. package/src/components/HeaderBar/consts.scss +1 -1
  56. package/src/components/HeaderLoading/HeaderLoading.vue +12 -11
  57. package/src/components/HeaderNavigationBar/HeaderNavigationBar.vue +2 -1
  58. package/src/components/HeaderNavigationBar/HorizontalNavbar/HorizontalNavbar.vue +9 -9
  59. package/src/components/HeaderToolbar/HeaderToolbar.vue +215 -202
  60. package/src/components/LangBtn/LangBtn.vue +8 -6
  61. package/src/components/LogoBrandSection/LogoBrandSection.stories.ts +2 -2
  62. package/src/components/NirField/NirField.stories.ts +7 -7
  63. package/src/components/NirField/NirField.vue +44 -47
  64. package/src/components/NotFoundPage/NotFoundPage.stories.ts +33 -2
  65. package/src/components/NotFoundPage/NotFoundPage.vue +17 -0
  66. package/src/components/NotificationBar/NotificationBar.mdx +5 -5
  67. package/src/components/NotificationBar/NotificationBar.stories.ts +410 -314
  68. package/src/components/NotificationBar/NotificationBar.vue +43 -41
  69. package/src/components/PageContainer/PageContainer.vue +4 -4
  70. package/src/components/PasswordField/PasswordField.mdx +70 -0
  71. package/src/components/PasswordField/PasswordField.stories.ts +213 -0
  72. package/src/components/PasswordField/PasswordField.vue +189 -0
  73. package/src/components/PasswordField/config.ts +11 -0
  74. package/src/components/PasswordField/locales.ts +4 -0
  75. package/src/components/PasswordField/tests/PasswordField.spec.ts +96 -0
  76. package/src/components/PhoneField/PhoneField.mdx +0 -2
  77. package/src/components/PhoneField/PhoneField.stories.ts +10 -50
  78. package/src/components/PhoneField/PhoneField.vue +34 -34
  79. package/src/components/SkipLink/SkipLink.vue +10 -10
  80. package/src/components/SocialMediaLinks/SocialMediaLinks.vue +28 -26
  81. package/src/components/SubHeader/SubHeader.vue +32 -31
  82. package/src/components/SyAlert/SyAlert.vue +12 -12
  83. package/src/components/UserMenuBtn/UserMenuBtn.vue +1 -1
  84. package/src/components/UserMenuBtn/config.ts +1 -1
  85. package/src/components/index.ts +10 -7
  86. package/src/designTokens/index.ts +6 -4
  87. package/src/designTokens/{bootstrapColors.md → paColors.md} +1 -1
  88. package/src/designTokens/tokens/cnam/cnamLightTheme.ts +2 -0
  89. package/src/designTokens/tokens/pa/paColors.ts +171 -0
  90. package/src/designTokens/tokens/pa/paContextual.ts +58 -0
  91. package/src/designTokens/tokens/pa/paDarkTheme.ts +5 -0
  92. package/src/designTokens/tokens/pa/paLightTheme.ts +123 -0
  93. package/src/designTokens/tokens/pa/paSemantic.ts +87 -0
  94. package/src/stories/GuideDuDev/CreerUneIssue.mdx +64 -0
  95. package/src/stories/GuideDuDev/{CommentUtiliserLesRules.mdx → UtiliserLesRules.mdx} +2 -2
  96. package/src/stories/GuideDuDev/components.stories.ts +9 -7
  97. package/src/stories/Guidelines/Vuetify/Vuetify.stories.ts +163 -88
  98. package/src/stories/Guidelines/Vuetify/VuetifyItems.ts +250 -23
  99. package/src/temp/TestDTComponent.vue +5 -6
  100. package/src/designTokens/tokens/bootstrap/bootstrapColors.ts +0 -158
  101. package/src/designTokens/tokens/bootstrap/bootstrapLightTheme.ts +0 -22
  102. package/src/stories/GuideDuDev/CommentContribuer.mdx +0 -22
@@ -0,0 +1,430 @@
1
+ import { fn } from '@storybook/test'
2
+ import type { Meta, StoryObj } from '@storybook/vue3'
3
+
4
+ import ContextualMenu from './ContextualMenu.vue'
5
+ import { ref, watch } from 'vue'
6
+
7
+ const meta = {
8
+ title: 'Composants/Navigation/ContextualMenu',
9
+ component: ContextualMenu,
10
+ decorators: [
11
+ () => ({
12
+ template: '<div style="padding: 20px;"><story/></div>',
13
+ }),
14
+ ],
15
+ parameters: {
16
+ layout: 'fullscreen',
17
+ },
18
+ argTypes: {
19
+ 'items': {
20
+ control: 'object',
21
+ description: 'Les éléments du menu',
22
+ table: {
23
+ type: {
24
+ summary: 'Array<{ text: string, hash: string, level?: 1 | 2 | 3 | 4 | 5 | 6 }>',
25
+ },
26
+ },
27
+ },
28
+ 'modelValue': {
29
+ description: 'Le hash de l’élément actif',
30
+ control: 'text',
31
+ table: {
32
+ type: {
33
+ summary: 'string',
34
+ },
35
+ category: 'props',
36
+ },
37
+ },
38
+ 'onUpdate:modelValue': {
39
+ description: 'Événement émis lorsqu’un élément est cliqué',
40
+ table: {
41
+ type: {
42
+ summary: 'string',
43
+ },
44
+ category: 'events',
45
+ },
46
+ },
47
+ },
48
+ } satisfies Meta<typeof ContextualMenu>
49
+
50
+ export default meta
51
+
52
+ type Story = StoryObj<typeof meta>
53
+
54
+ export const Default: Story = {
55
+ args: {
56
+ 'items': [
57
+ {
58
+ text: 'Titre 1',
59
+ hash: '#example-1',
60
+ },
61
+ {
62
+ text: 'Titre 2',
63
+ hash: '#example-2',
64
+ },
65
+ {
66
+ text: 'Titre 3',
67
+ hash: '#example-3',
68
+ },
69
+ ],
70
+ 'modelValue': '#example-1',
71
+ 'onUpdate:modelValue': fn(),
72
+ },
73
+ render: (args) => {
74
+ return {
75
+ components: { ContextualMenu },
76
+ setup() {
77
+ const hash = ref<string | null | undefined>()
78
+ watch(() => args.modelValue, (value) => {
79
+ hash.value = value
80
+ }, { immediate: true })
81
+ return { args, hash }
82
+ },
83
+ template: `
84
+ <ContextualMenu
85
+ v-bind="args"
86
+ v-model="hash"
87
+ />
88
+ `,
89
+ }
90
+ },
91
+ parameters: {
92
+ sourceCode: [
93
+ {
94
+ name: 'Template',
95
+ code: `<template>
96
+ <ContextualMenu
97
+ :items="items"
98
+ />
99
+ </template>
100
+ `,
101
+ },
102
+ {
103
+ name: 'Script',
104
+ code: `<script setup lang="ts">
105
+ import { ContextualMenu } from '@cnamts/synapse'
106
+
107
+ const items = [
108
+ {
109
+ text: 'Titre 1',
110
+ hash: '#example-1',
111
+ },
112
+ {
113
+ text: 'Titre 2',
114
+ hash: '#example-2',
115
+ },
116
+ {
117
+ text: 'Titre 3,
118
+ hash: '#example-3',
119
+ },
120
+ ]
121
+ </script>
122
+ `,
123
+ },
124
+ ],
125
+ },
126
+ }
127
+
128
+ export const WithAnchors: Story = {
129
+ args: {
130
+ 'items': [{
131
+ text: 'section 1',
132
+ hash: '#section-1',
133
+ }, {
134
+ text: 'section 2',
135
+ hash: '#section-2',
136
+ }, {
137
+ text: 'section 3',
138
+ hash: '#section-3',
139
+ }, {
140
+ text: 'section 4',
141
+ hash: '#section-4',
142
+ }, {
143
+ text: 'section 5',
144
+ hash: '#section-5',
145
+ }],
146
+ 'modelValue': '#section-1',
147
+ 'onUpdate:modelValue': fn(),
148
+ },
149
+ render: (args) => {
150
+ return {
151
+ components: { ContextualMenu },
152
+ setup() {
153
+ const hash = ref<string | null | undefined>()
154
+ watch(() => args.modelValue, (value) => {
155
+ hash.value = value
156
+ }, { immediate: true })
157
+ return { args, hash }
158
+ },
159
+ template: `
160
+ <div style="display: flex; flex-direction: row; justify-content: space-evenly; padding-top: 20px; padding-bottom: 20px; place-items: center; width: 500px;">
161
+ <div style="width: 200px">
162
+ <ContextualMenu
163
+ v-bind="args"
164
+ v-model="hash"
165
+ />
166
+ </div>
167
+ <div style="border: 1px solid black; max-height: 500px; overflow-y: auto; scroll-behavior: smooth;">
168
+ <section id="section-1" style="padding: 20px">
169
+ <h2>section 1</h2>
170
+ <p style="max-width: 300px">
171
+ Lorem ipsum dolor sit amet, consectetur adipisicing elit. Alias, quae eligendi modi, rem consectetur cum labore voluptate nostrum molestiae asperiores dolorum, saepe perspiciatis quisquam provident placeat aut distinctio minima dolor.
172
+ Temporibus consequatur consectetur sequi. Sequi tempora velit soluta? Nam error, nesciunt molestiae provident possimus voluptas tempore porro at officia sint exercitationem dolore debitis eaque temporibus accusantium soluta? In, maxime excepturi.
173
+ </p>
174
+ </section>
175
+ <section id="section-2" style="padding: 20px">
176
+ <h2>section 2</h2>
177
+ <p style="max-width: 300px">
178
+ Lorem ipsum dolor sit amet, consectetur adipisicing elit. Alias, quae eligendi modi, rem consectetur cum labore voluptate nostrum molestiae asperiores dolorum, saepe perspiciatis quisquam provident placeat aut distinctio minima dolor.
179
+ Temporibus consequatur consectetur sequi. Sequi tempora velit soluta? Nam error, nesciunt molestiae provident possimus voluptas tempore porro at officia sint exercitationem dolore debitis eaque temporibus accusantium soluta? In, maxime excepturi.
180
+ </p>
181
+ </section>
182
+ <section id="section-3" style="padding: 20px">
183
+ <h2>section 3</h2>
184
+ <p style="max-width: 300px">
185
+ Lorem ipsum dolor sit amet, consectetur adipisicing elit. Alias, quae eligendi modi, rem consectetur cum labore voluptate nostrum molestiae asperiores dolorum, saepe perspiciatis quisquam provident placeat aut distinctio minima dolor.
186
+ Temporibus consequatur consectetur sequi. Sequi tempora velit soluta? Nam error, nesciunt molestiae provident possimus voluptas tempore porro at officia sint exercitationem dolore debitis eaque temporibus accusantium soluta? In, maxime excepturi.
187
+ </p>
188
+ </section>
189
+ <section id="section-4" style="padding: 20px">
190
+ <h2>section 4</h2>
191
+ <p style="max-width: 300px">
192
+ Lorem ipsum dolor sit amet, consectetur adipisicing elit. Alias, quae eligendi modi, rem consectetur cum labore voluptate nostrum molestiae asperiores dolorum, saepe perspiciatis quisquam provident placeat aut distinctio minima dolor.
193
+ Temporibus consequatur consectetur sequi. Sequi tempora velit soluta? Nam error, nesciunt molestiae provident possimus voluptas tempore porro at officia sint exercitationem dolore debitis eaque temporibus accusantium soluta? In, maxime excepturi.
194
+ </p>
195
+ </section>
196
+ <section id="section-5" style="padding: 20px">
197
+ <h2>section 5</h2>
198
+ <p style="max-width: 300px">
199
+ Lorem ipsum dolor sit amet, consectetur adipisicing elit. Alias, quae eligendi modi, rem consectetur cum labore voluptate nostrum molestiae asperiores dolorum, saepe perspiciatis quisquam provident placeat aut distinctio minima dolor.
200
+ Temporibus consequatur consectetur sequi. Sequi tempora velit soluta? Nam error, nesciunt molestiae provident possimus voluptas tempore porro at officia sint exercitationem dolore debitis eaque temporibus accusantium soluta? In, maxime excepturi.
201
+ </p>
202
+ </section>
203
+ </div>
204
+ </div>
205
+ `,
206
+ }
207
+ },
208
+ parameters: {
209
+ sourceCode: [
210
+ {
211
+ name: 'Template',
212
+ code: `<template>
213
+ <div class="wrapper">
214
+ <div class="menu">
215
+ <ContextualMenu
216
+ v-model="hash"
217
+ :items
218
+ />
219
+ </div>
220
+ <div class="content">
221
+ <section id="section-1">
222
+ <h2>section 1</h2>
223
+ <p>
224
+ Lorem ipsum dolor sit amet, consectetur adipisicing elit. Alias, quae eligendi modi, rem consectetur cum labore voluptate nostrum molestiae asperiores dolorum, saepe perspiciatis quisquam provident placeat aut distinctio minima dolor.
225
+ Temporibus consequatur consectetur sequi. Sequi tempora velit soluta? Nam error, nesciunt molestiae provident possimus voluptas tempore porro at officia sint exercitationem dolore debitis eaque temporibus accusantium soluta? In, maxime excepturi.
226
+ </p>
227
+ </section>
228
+ <section id="section-2">
229
+ <h2>section 2</h2>
230
+ <p>
231
+ Lorem ipsum dolor sit amet, consectetur adipisicing elit. Alias, quae eligendi modi, rem consectetur cum labore voluptate nostrum molestiae asperiores dolorum, saepe perspiciatis quisquam provident placeat aut distinctio minima dolor.
232
+ Temporibus consequatur consectetur sequi. Sequi tempora velit soluta? Nam error, nesciunt molestiae provident possimus voluptas tempore porro at officia sint exercitationem dolore debitis eaque temporibus accusantium soluta? In, maxime excepturi.
233
+ </p>
234
+ </section>
235
+ <section id="section-3">
236
+ <h2>section 3</h2>
237
+ <p>
238
+ Lorem ipsum dolor sit amet, consectetur adipisicing elit. Alias, quae eligendi modi, rem consectetur cum labore voluptate nostrum molestiae asperiores dolorum, saepe perspiciatis quisquam provident placeat aut distinctio minima dolor.
239
+ Temporibus consequatur consectetur sequi. Sequi tempora velit soluta? Nam error, nesciunt molestiae provident possimus voluptas tempore porro at officia sint exercitationem dolore debitis eaque temporibus accusantium soluta? In, maxime excepturi.
240
+ </p>
241
+ </section>
242
+ <section id="section-4">
243
+ <h2>section 4</h2>
244
+ <p>
245
+ Lorem ipsum dolor sit amet, consectetur adipisicing elit. Alias, quae eligendi modi, rem consectetur cum labore voluptate nostrum molestiae asperiores dolorum, saepe perspiciatis quisquam provident placeat aut distinctio minima dolor.
246
+ Temporibus consequatur consectetur sequi. Sequi tempora velit soluta? Nam error, nesciunt molestiae provident possimus voluptas tempore porro at officia sint exercitationem dolore debitis eaque temporibus accusantium soluta? In, maxime excepturi.
247
+ </p>
248
+ </section>
249
+ <section id="section-5">
250
+ <h2>section 5</h2>
251
+ <p>
252
+ Lorem ipsum dolor sit amet, consectetur adipisicing elit. Alias, quae eligendi modi, rem consectetur cum labore voluptate nostrum molestiae asperiores dolorum, saepe perspiciatis quisquam provident placeat aut distinctio minima dolor.
253
+ Temporibus consequatur consectetur sequi. Sequi tempora velit soluta? Nam error, nesciunt molestiae provident possimus voluptas tempore porro at officia sint exercitationem dolore debitis eaque temporibus accusantium soluta? In, maxime excepturi.
254
+ </p>
255
+ </section>
256
+ </div>
257
+ </div>
258
+ </template>
259
+ `,
260
+ },
261
+ {
262
+ name: 'Script',
263
+ code: `<script setup lang="ts">
264
+ import { ContextualMenu } from '@cnamts/synapse'
265
+ import { ref } from 'vue'
266
+
267
+ const items = [{
268
+ text: 'section 1',
269
+ hash: '#section-1',
270
+ }, {
271
+ text: 'section 2',
272
+ hash: '#section-2',
273
+ }, {
274
+ text: 'section 3',
275
+ hash: '#section-3',
276
+ }, {
277
+ text: 'section 4',
278
+ hash: '#section-4',
279
+ }, {
280
+ text: 'section 5',
281
+ hash: '#section-5',
282
+ }]
283
+
284
+ const hash = ref<string | null>(null)
285
+ </script>
286
+ `,
287
+ },
288
+ {
289
+ name: 'Style',
290
+ code: `<style lang="scss" scoped>
291
+ .wrapper {
292
+ display: flex;
293
+ flex-direction: row;
294
+ justify-content: space-evenly;
295
+ place-items: center;
296
+ height: 100dvh;
297
+ width: 500px;
298
+ }
299
+ .menu {
300
+ width: 200px;
301
+ }
302
+ .content {
303
+ border: 1px solid black;
304
+ height: 500px;
305
+ overflow-y: auto;
306
+ scroll-behavior: smooth;
307
+ }
308
+
309
+ section {
310
+ padding: 20px;
311
+ }
312
+
313
+ section p {
314
+ max-width: 300px;
315
+ }
316
+
317
+ </style>
318
+ `,
319
+ },
320
+ ],
321
+ },
322
+ }
323
+
324
+ export const levels: Story = {
325
+ args: {
326
+ 'items': [
327
+ {
328
+ text: 'Level 1',
329
+ hash: '#example-1',
330
+ },
331
+ {
332
+ text: 'Level 2',
333
+ hash: '#example-2',
334
+ level: 2,
335
+ },
336
+ {
337
+ text: 'Level 3',
338
+ hash: '#example-3',
339
+ level: 3,
340
+ },
341
+ {
342
+ text: 'Level 4',
343
+ hash: '#example-4',
344
+ level: 4,
345
+ },
346
+ {
347
+ text: 'Level 5',
348
+ hash: '#example-5',
349
+ level: 5,
350
+ },
351
+ {
352
+ text: 'Level 6',
353
+ hash: '#example-6',
354
+ level: 6,
355
+ },
356
+ ],
357
+ 'modelValue': '#example-1',
358
+ 'onUpdate:modelValue': fn(),
359
+ },
360
+ render: (args) => {
361
+ return {
362
+ components: { ContextualMenu },
363
+ setup() {
364
+ const hash = ref<string | null | undefined>()
365
+ watch(() => args.modelValue, (value) => {
366
+ hash.value = value
367
+ }, { immediate: true })
368
+ return { args, hash }
369
+ },
370
+ template: `
371
+ <ContextualMenu
372
+ v-bind="args"
373
+ v-model="hash"
374
+ />
375
+ `,
376
+ }
377
+ },
378
+ parameters: {
379
+ sourceCode: [
380
+ {
381
+ name: 'Template',
382
+ code: `<template>
383
+ <ContextualMenu
384
+ :items="items"
385
+ />
386
+ </template>
387
+ `,
388
+ },
389
+ {
390
+ name: 'Script',
391
+ code: `<script setup lang="ts">
392
+ import { ContextualMenu } from '@cnamts/synapse'
393
+
394
+ const items = [
395
+ {
396
+ text: 'Level 1',
397
+ hash: '#example-1',
398
+ },
399
+ {
400
+ text: 'Level 2',
401
+ hash: '#example-2',
402
+ level: 2,
403
+ },
404
+ {
405
+ text: 'Level 3',
406
+ hash: '#example-3',
407
+ level: 3,
408
+ },
409
+ {
410
+ text: 'Level 4',
411
+ hash: '#example-4',
412
+ level: 4,
413
+ },
414
+ {
415
+ text: 'Level 5',
416
+ hash: '#example-5',
417
+ level: 5,
418
+ },
419
+ {
420
+ text: 'Level 6',
421
+ hash: '#example-6',
422
+ level: 6,
423
+ },
424
+ ]
425
+ </script>
426
+ `,
427
+ },
428
+ ],
429
+ },
430
+ }
@@ -0,0 +1,101 @@
1
+ <script setup lang="ts">
2
+ import { type MenuItem } from './types'
3
+ import { onMounted, watch } from 'vue'
4
+
5
+ const model = defineModel<string | null>()
6
+
7
+ defineProps<{
8
+ items: MenuItem[]
9
+ }>()
10
+
11
+ onMounted(() => {
12
+ if (!model.value && window.location.hash) {
13
+ model.value = window.location.hash
14
+ }
15
+ addEventListener('hashchange', () => {
16
+ model.value = window.location.hash
17
+ })
18
+ })
19
+
20
+ watch(
21
+ model,
22
+ (newValue, oldValue) => {
23
+ if (newValue && newValue !== oldValue) {
24
+ setHash(newValue)
25
+ }
26
+ },
27
+ { immediate: true },
28
+ )
29
+
30
+ function setHash(hash: string) {
31
+ if (window.location.hash === hash) {
32
+ return
33
+ }
34
+
35
+ window.location.hash = hash
36
+ model.value = hash
37
+ }
38
+ </script>
39
+
40
+ <template>
41
+ <ul
42
+ v-if="items.length"
43
+ class="vd-contextual-menu"
44
+ >
45
+ <li
46
+ v-for="{ text, hash, level } in items"
47
+ :key="hash"
48
+ >
49
+ <a
50
+ :href="hash"
51
+ :class="{
52
+ 'text-primary active': model === hash,
53
+ 'text-medium-emphasis': model !== hash,
54
+ 'ps-4': level === 2,
55
+ 'ps-6': level === 3,
56
+ 'ps-9': level === 4,
57
+ 'ps-12': level === 5,
58
+ 'ps-14': level === 6,
59
+ }"
60
+ class="d-flex align-center text-decoration-none text-body-1 px-4 py-2"
61
+ @click.prevent.stop="setHash(hash)"
62
+ v-text="text"
63
+ />
64
+ </li>
65
+ </ul>
66
+ </template>
67
+
68
+ <style lang="scss" scoped>
69
+ ul {
70
+ list-style: none;
71
+ }
72
+
73
+ a {
74
+ position: relative;
75
+ transition: none;
76
+
77
+ &::before {
78
+ content: '';
79
+ width: 2px;
80
+ background: rgb(0 0 0 / 60%);
81
+ position: absolute;
82
+ left: 0;
83
+ height: 100%;
84
+ }
85
+
86
+ &::after {
87
+ content: '';
88
+ width: 4px;
89
+ border-radius: 0 2px 2px 0;
90
+ background: currentcolor;
91
+ position: absolute;
92
+ left: 0;
93
+ height: 100%;
94
+ opacity: 0;
95
+ }
96
+
97
+ &.active::after {
98
+ opacity: 1;
99
+ }
100
+ }
101
+ </style>
@@ -0,0 +1,115 @@
1
+ import { describe, it, expect } from 'vitest'
2
+ import { mount } from '@vue/test-utils'
3
+
4
+ import ContextualMenu from '../ContextualMenu.vue'
5
+ import { vuetify } from '@tests/unit/setup'
6
+
7
+ describe('ContextualMenu', () => {
8
+ it('renders correctly with items', () => {
9
+ const wrapper = mount(ContextualMenu, {
10
+ global: {
11
+ plugins: [vuetify],
12
+ },
13
+ props: {
14
+ items: [
15
+ {
16
+ text: 'Titre 1',
17
+ hash: '#example-1',
18
+ },
19
+ {
20
+ text: 'Titre 2',
21
+ hash: '#example-2',
22
+ },
23
+ ],
24
+ },
25
+ })
26
+
27
+ expect(wrapper.html()).toMatchSnapshot()
28
+ })
29
+
30
+ it('renders correctly without items', () => {
31
+ const wrapper = mount(ContextualMenu, {
32
+ global: {
33
+ plugins: [vuetify],
34
+ },
35
+ props: {
36
+ items: [],
37
+ },
38
+ })
39
+
40
+ expect(wrapper.html()).toMatchSnapshot()
41
+ })
42
+
43
+ it('emit an update:modelValue event when an item is updated', async () => {
44
+ const wrapper = mount(ContextualMenu, {
45
+ global: {
46
+ plugins: [vuetify],
47
+ },
48
+ props: {
49
+ items: [
50
+ {
51
+ text: 'Titre 1',
52
+ hash: '#example-1',
53
+ },
54
+ {
55
+ text: 'Titre 2',
56
+ hash: '#example-2',
57
+ },
58
+ ],
59
+ },
60
+ })
61
+
62
+ await wrapper.find('a').trigger('click')
63
+
64
+ expect(wrapper.emitted('update:modelValue')?.[0]).toEqual(['#example-1'])
65
+ })
66
+
67
+ it('update the highlighted item when the modelValue is updated', async () => {
68
+ const wrapper = mount(ContextualMenu, {
69
+ global: {
70
+ plugins: [vuetify],
71
+ },
72
+ props: {
73
+ items: [
74
+ {
75
+ text: 'Titre 1',
76
+ hash: '#example-1',
77
+ },
78
+ {
79
+ text: 'Titre 2',
80
+ hash: '#example-2',
81
+ },
82
+ ],
83
+ modelValue: '#example-2',
84
+ },
85
+ })
86
+
87
+ expect(wrapper.find('[href="#example-2"]').classes()).toContain('active')
88
+ })
89
+
90
+ it('initialize with the content of location.href', async () => {
91
+ window.location.hash = '#example-2'
92
+
93
+ const wrapper = mount(ContextualMenu, {
94
+ global: {
95
+ plugins: [vuetify],
96
+ },
97
+ props: {
98
+ items: [
99
+ {
100
+ text: 'Titre 1',
101
+ hash: '#example-1',
102
+ },
103
+ {
104
+ text: 'Titre 2',
105
+ hash: '#example-2',
106
+ },
107
+ ],
108
+ },
109
+ })
110
+
111
+ await wrapper.vm.$nextTick()
112
+ expect(wrapper.find('[href="#example-2"]').classes()).toContain('active')
113
+ expect(wrapper.emitted('update:modelValue')?.[0]).toEqual(['#example-2'])
114
+ })
115
+ })
@@ -0,0 +1,10 @@
1
+ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2
+
3
+ exports[`ContextualMenu > renders correctly with items 1`] = `
4
+ "<ul data-v-b65f24a2="" class="vd-contextual-menu">
5
+ <li data-v-b65f24a2=""><a data-v-b65f24a2="" href="#example-1" class="text-medium-emphasis d-flex align-center text-decoration-none text-body-1 px-4 py-2">Titre 1</a></li>
6
+ <li data-v-b65f24a2=""><a data-v-b65f24a2="" href="#example-2" class="text-medium-emphasis d-flex align-center text-decoration-none text-body-1 px-4 py-2">Titre 2</a></li>
7
+ </ul>"
8
+ `;
9
+
10
+ exports[`ContextualMenu > renders correctly without items 1`] = `"<!--v-if-->"`;
@@ -0,0 +1,5 @@
1
+ export interface MenuItem {
2
+ text: string
3
+ hash: string
4
+ level?: number
5
+ }
@@ -617,7 +617,6 @@ export const Customization: Story = {
617
617
  @reject="onReject"
618
618
  @customize="onCustomize"
619
619
  v-model="modelValue"
620
- :cookiesRoute="cookiesRoute"
621
620
  :vuetifyOptions="vuetifyOptions"
622
621
  />
623
622
  </template>`,
@@ -659,7 +658,7 @@ export const Customization: Story = {
659
658
  }
660
659
 
661
660
  const vuetifyOptions = {
662
- sheet: {
661
+ banner: {
663
662
  color: '#ced9eb',
664
663
  },
665
664
  customizeBtn: {