@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,248 @@
1
+ <script setup lang="ts">
2
+ import { computed } from 'vue'
3
+ import { VAlert, VIcon, VBtn } from 'vuetify/components'
4
+ import { locales } from './locales'
5
+ import {
6
+ mdiAlertOutline,
7
+ mdiAlertOctagonOutline,
8
+ mdiCheckCircleOutline,
9
+ mdiInformationOutline,
10
+ mdiClose,
11
+ } from '@mdi/js'
12
+
13
+ const show = defineModel<boolean>({
14
+ default: true,
15
+ })
16
+
17
+ const props = withDefaults(defineProps<{
18
+ type?: 'success' | 'info' | 'warning' | 'error'
19
+ closable?: boolean
20
+ variant?: 'tonal' | 'outlined'
21
+ }>(), {
22
+ type: 'info',
23
+ closable: false,
24
+ variant: 'tonal',
25
+ })
26
+
27
+ const prependIcon = computed(() => {
28
+ return {
29
+ info: mdiInformationOutline,
30
+ success: mdiCheckCircleOutline,
31
+ warning: mdiAlertOutline,
32
+ error: mdiAlertOctagonOutline,
33
+ }[props.type]
34
+ })
35
+
36
+ function dismissAlert() {
37
+ show.value = false
38
+ }
39
+
40
+ defineExpose({
41
+ prependIcon,
42
+ dismissAlert,
43
+ })
44
+ </script>
45
+
46
+ <template>
47
+ <VAlert
48
+ v-model="show"
49
+ :type
50
+ :closable
51
+ :variant
52
+ :class="`alert alert--${type}`"
53
+ :color="type"
54
+ :border="variant === 'tonal' ? 'start' : false"
55
+ >
56
+ <template #prepend>
57
+ <VIcon
58
+ class="alert-icon"
59
+ size="1.5rem"
60
+ >
61
+ <slot name="icon">
62
+ {{ prependIcon }}
63
+ </slot>
64
+ </VIcon>
65
+ </template>
66
+
67
+ <template #default>
68
+ <slot />
69
+ </template>
70
+
71
+ <template
72
+ v-if="closable"
73
+ #close
74
+ >
75
+ <VBtn
76
+ :color="variant === 'outlined' ? undefined : 'primary'"
77
+ :ripple="false"
78
+ variant="text"
79
+ width="auto"
80
+ height="100%"
81
+ class="alert-close-btn"
82
+ @click="dismissAlert"
83
+ >
84
+ <VIcon
85
+ size="small"
86
+ >
87
+ {{ mdiClose }}
88
+ </VIcon>
89
+
90
+ <span>
91
+ {{ locales.close }}
92
+ </span>
93
+ </VBtn>
94
+ </template>
95
+ </VAlert>
96
+ </template>
97
+
98
+ <style lang="scss" scoped>
99
+ @use '@/assets/tokens.scss';
100
+
101
+ .alert {
102
+ padding: tokens.$padding-4;
103
+ }
104
+
105
+ .alert-icon {
106
+ border-radius: 50%;
107
+ }
108
+
109
+ :deep(.v-alert__underlay) {
110
+ display: none;
111
+ }
112
+
113
+ :deep(.v-alert__close) {
114
+ align-self: center;
115
+ }
116
+
117
+ :deep(.v-btn--variant-text .v-btn__overlay) {
118
+ background: transparent !important;
119
+ }
120
+
121
+ .alert-icon :deep(.v-icon__svg) {
122
+ height: 1.5rem !important;
123
+ }
124
+
125
+ .alert-close-btn {
126
+ cursor: pointer;
127
+ line-height: 0;
128
+
129
+ .v-btn__overlay {
130
+ display: none;
131
+ }
132
+ }
133
+
134
+ @media screen and (max-width: 440px) {
135
+ .alert {
136
+ display: flex;
137
+ flex-direction: column;
138
+ background-color: tokens.$white-base;
139
+
140
+ :deep(.v-alert__content) {
141
+ align-self: flex-start !important;
142
+ margin-top: tokens.$padding-6;
143
+ }
144
+
145
+ :deep(.v-alert__close) {
146
+ margin-top: tokens.$padding-4;
147
+ align-self: flex-end;
148
+ }
149
+
150
+ .v-alert__prepend > .v-icon {
151
+ background: none !important;
152
+ }
153
+ }
154
+ }
155
+
156
+ @media screen and (min-width: 441px) {
157
+ .alert{
158
+ .alert-icon {
159
+ width: 3.5rem !important;
160
+ height: 3.5rem !important;
161
+ display: grid;
162
+ place-items: center;
163
+ }
164
+ }
165
+ }
166
+
167
+ @mixin redesign($type, $map) {
168
+ &.alert--#{$type}.v-alert--variant-tonal {
169
+ background: map-get($map, 'background') !important;
170
+ color: tokens.$colors-text-base !important;
171
+ :deep(.v-alert__border) {
172
+ border-color: map-get($map, 'border') !important;
173
+ opacity: 1 !important;
174
+ border-inline-start-width: 4px;
175
+ }
176
+ }
177
+
178
+ &.alert--#{$type}.v-alert--variant-outlined {
179
+ background: transparent !important;
180
+ :deep(.v-alert__border) {
181
+ border-color: map-get($map, 'border') !important;
182
+ opacity: 1 !important;
183
+ }
184
+ }
185
+
186
+ &.alert--#{$type},
187
+ &.text-#{$type} {
188
+ .v-alert__prepend > .v-icon {
189
+ background: map-get($map, 'icon-bg');
190
+ :deep(svg) {
191
+ fill: map-get($map, 'accent');
192
+ }
193
+ }
194
+ }
195
+
196
+ &.text-#{$type} {
197
+ color: map-get($map, 'accent') !important;
198
+ border-color: map-get($map, 'accent') !important;
199
+ }
200
+ }
201
+
202
+ .v-alert {
203
+ @include redesign(
204
+ 'warning',
205
+ (
206
+ 'background': tokens.$colors-background-warning,
207
+ 'accent': tokens.$colors-border-warning,
208
+ 'border': tokens.$colors-border-warning,
209
+ 'icon':tokens.$colors-icon-warning,
210
+ 'icon-bg': tokens.$colors-background-warning-subdued,
211
+ )
212
+ );
213
+
214
+ @include redesign(
215
+ 'success',
216
+ (
217
+ 'background': tokens.$colors-background-success,
218
+ 'accent': tokens.$colors-border-success,
219
+ 'border': tokens.$colors-border-success,
220
+ 'icon':tokens.$colors-icon-success,
221
+ 'icon-bg': tokens.$colors-background-success-subdued,
222
+ )
223
+ );
224
+
225
+ @include redesign(
226
+ 'error',
227
+ (
228
+ 'background': tokens.$colors-background-error,
229
+ 'accent': tokens.$colors-border-error,
230
+ 'border': tokens.$colors-border-error,
231
+ 'icon':tokens.$colors-icon-error,
232
+ 'icon-bg': tokens.$colors-background-error-subdued,
233
+ )
234
+ );
235
+
236
+ @include redesign(
237
+ 'info',
238
+ (
239
+ 'background': tokens.$colors-background-info,
240
+ 'accent': tokens.$colors-border-info,
241
+ 'border': tokens.$colors-border-info,
242
+ 'icon':tokens.$colors-icon-info,
243
+ 'icon-bg': tokens.$colors-background-info-subdued,
244
+ )
245
+ );
246
+ }
247
+
248
+ </style>
@@ -0,0 +1,3 @@
1
+ export const locales = {
2
+ close: 'Fermer',
3
+ }
@@ -0,0 +1,105 @@
1
+ import { describe, it, expect } from 'vitest'
2
+ import { mount } from '@vue/test-utils'
3
+ import { vuetify } from '@tests/unit/setup'
4
+
5
+ import Alert from '../Alert.vue'
6
+ import { VBtn } from 'vuetify/components'
7
+
8
+ describe('Alert', () => {
9
+ it('render correctly', async () => {
10
+ const wrapper = mount(Alert, {
11
+ props: {
12
+ title: 'title',
13
+ message: 'message',
14
+ type: 'success',
15
+ },
16
+ slots: {
17
+ default: 'slot content',
18
+ },
19
+ global: {
20
+ plugins: [vuetify],
21
+ },
22
+ })
23
+
24
+ expect(wrapper.html()).toMatchSnapshot()
25
+ })
26
+
27
+ it('show and hide correctly when modelValue is updated', async () => {
28
+ const wrapper = mount(Alert, {
29
+ props: {
30
+ title: 'title',
31
+ message: 'message',
32
+ type: 'success',
33
+ },
34
+ slots: {
35
+ default: 'slot content',
36
+ },
37
+ global: {
38
+ plugins: [vuetify],
39
+ },
40
+ })
41
+
42
+ expect(wrapper.text()).toContain('title')
43
+ expect(wrapper.text()).toContain('slot content')
44
+
45
+ await wrapper.setProps({
46
+ modelValue: false,
47
+ })
48
+
49
+ expect(wrapper.html()).toBeFalsy()
50
+
51
+ await wrapper.setProps({
52
+ modelValue: true,
53
+ })
54
+
55
+ expect(wrapper.text()).toContain('title')
56
+ expect(wrapper.text()).toContain('slot content')
57
+ })
58
+
59
+ it('hide when the close btn is clicked', async () => {
60
+ const wrapper = mount(Alert, {
61
+ props: {
62
+ title: 'title',
63
+ message: 'message',
64
+ type: 'warning',
65
+ variant: 'outlined',
66
+ closable: true,
67
+ },
68
+ slots: {
69
+ default: 'slot content',
70
+ },
71
+ global: {
72
+ plugins: [vuetify],
73
+ },
74
+ })
75
+
76
+ expect(wrapper.html()).not.toBeFalsy()
77
+
78
+ const closeBtn = wrapper.findComponent(VBtn)
79
+
80
+ await closeBtn.element.click()
81
+
82
+ expect(wrapper.html()).toBeFalsy()
83
+ expect(wrapper.emitted('update:modelValue')![0]![0]).toBe(false)
84
+ })
85
+
86
+ it('prependIcon computed property', async () => {
87
+ const wrapper = mount(Alert, {
88
+ props: {
89
+ title: 'title',
90
+ message: 'message',
91
+ type: 'warning',
92
+ variant: 'outlined',
93
+ closable: true,
94
+ },
95
+ slots: {
96
+ default: 'slot content',
97
+ },
98
+ global: {
99
+ plugins: [vuetify],
100
+ },
101
+ })
102
+
103
+ expect(wrapper.vm.prependIcon).toBe('M12,2L1,21H23M12,6L19.53,19H4.47M11,10V14H13V10M11,16V18H13V16')
104
+ })
105
+ })
@@ -0,0 +1,15 @@
1
+ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2
+
3
+ exports[`Alert > render correctly 1`] = `
4
+ "<div data-v-284427ba="" class="v-alert v-alert--border v-alert--border-start v-theme--light text-success v-alert--density-default v-alert--variant-tonal alert alert--success" role="alert" message="message">
5
+ <!----><span class="v-alert__underlay"></span>
6
+ <div class="v-alert__border"></div>
7
+ <div class="v-alert__prepend"><i data-v-284427ba="" class="M12 2C6.5 2 2 6.5 2 12S6.5 22 12 22 22 17.5 22 12 17.5 2 12 2M12 20C7.59 20 4 16.41 4 12S7.59 4 12 4 20 7.59 20 12 16.41 20 12 20M16.59 7.58L10 14.17L7.41 11.59L6 13L10 17L18 9L16.59 7.58Z mdi v-icon notranslate v-theme--light alert-icon" style="font-size: 1.5rem; height: 1.5rem; width: 1.5rem;" aria-hidden="true"></i></div>
8
+ <div class="v-alert__content">
9
+ <div class="v-alert-title">title</div>
10
+ <!---->slot content
11
+ </div>
12
+ <!---->
13
+ <!---->
14
+ </div>"
15
+ `;
@@ -0,0 +1,26 @@
1
+ import {Controls, Canvas, Meta, Source, Story} from '@storybook/blocks';
2
+ import * as BackBtnStories from './BackBtn.stories';
3
+
4
+ <Meta of={BackBtnStories} />
5
+
6
+ # BackBtn
7
+
8
+ L’élément `BackBtn` est utilisé pour permettre à l’utilisateur de revenir à la page précédente.
9
+
10
+ <Story of={BackBtnStories.Info} />
11
+
12
+ <Canvas of={BackBtnStories.Default} />
13
+
14
+ # API
15
+
16
+ <Controls of={BackBtnStories.Default} />
17
+
18
+ <Source dark code={`
19
+ <script setup lang="ts">
20
+ import BackBtn from '@/components/BackBtn/BackBtn.vue'
21
+ </script>
22
+
23
+ <template>
24
+ <BackBtn/>
25
+ </template>
26
+ `} />
@@ -0,0 +1,138 @@
1
+ import type { Meta, StoryObj } from '@storybook/vue3'
2
+
3
+ import BackBtn from './BackBtn.vue'
4
+ import Alert from '../Alert/Alert.vue'
5
+
6
+ import { VSheet } from 'vuetify/components'
7
+
8
+ const meta = {
9
+ title: 'Components/BackBtn',
10
+ component: BackBtn,
11
+ parameters: {
12
+ layout: 'fullscreen',
13
+ },
14
+ argTypes: {
15
+ dark: {
16
+ control: 'boolean',
17
+ },
18
+ hideBackIcon: {
19
+ control: 'boolean',
20
+ },
21
+ },
22
+ } as Meta<typeof BackBtn>
23
+
24
+ export default meta
25
+
26
+ type Story = StoryObj<typeof meta>
27
+
28
+ export const Default: Story = {
29
+ args: {
30
+ dark: false,
31
+ },
32
+ render: (args) => {
33
+ return {
34
+ components: { BackBtn, VSheet },
35
+ setup() {
36
+ return { args }
37
+ },
38
+ template: `
39
+ <VSheet
40
+ v-if="args.dark"
41
+ color="primary"
42
+ class="pa-4"
43
+ >
44
+ <BackBtn v-bind="args" />
45
+ </VSheet>
46
+ <VSheet
47
+ v-else
48
+ class="pa-4"
49
+ >
50
+ <BackBtn v-bind="args" />
51
+ </VSheet>
52
+ `,
53
+ }
54
+ },
55
+ }
56
+
57
+ export const Dark: Story = {
58
+ args: {
59
+ dark: true,
60
+ },
61
+ render: (args) => {
62
+ return {
63
+ components: { BackBtn, VSheet },
64
+ setup() {
65
+ return { args }
66
+ },
67
+ template: `
68
+ <VSheet
69
+ color="primary"
70
+ class="pa-4"
71
+ >
72
+ <BackBtn v-bind="args" />
73
+ </VSheet>
74
+ `,
75
+ }
76
+ },
77
+ }
78
+
79
+ export const HideBackIcon: Story = {
80
+ args: {
81
+ hideBackIcon: true,
82
+ },
83
+ render: (args) => {
84
+ return {
85
+ components: { BackBtn },
86
+ setup() {
87
+ return { args }
88
+ },
89
+ template: `
90
+ <VSheet class="pa-4">
91
+ <BackBtn v-bind="args" />
92
+ </VSheet>
93
+ `,
94
+ }
95
+ },
96
+ }
97
+
98
+ export const DarkAndHideBackIcon: Story = {
99
+ args: {
100
+ dark: true,
101
+ hideBackIcon: true,
102
+ },
103
+ render: (args) => {
104
+ return {
105
+ components: { BackBtn },
106
+ setup() {
107
+ return { args }
108
+ },
109
+ template: `
110
+ <VSheet
111
+ color="primary"
112
+ class="pa-4"
113
+ >
114
+ <BackBtn v-bind="args" />
115
+ </VSheet>
116
+ `,
117
+ }
118
+ },
119
+ }
120
+
121
+ export const Info: Story = {
122
+ render: (args) => {
123
+ return {
124
+ components: { Alert },
125
+ setup() {
126
+ return { args }
127
+ },
128
+ template: `
129
+ <Alert v-model="args.modelValue" :type="args.type" :variant="tonal" :closable="false">
130
+ <template #default>Par défaut, le composant BackBtn n'effectue aucune action, vous devez
131
+ implémenter une redirection ou une action lors du clic sur le bouton.
132
+ </template>
133
+ </Alert>
134
+ `,
135
+ }
136
+ },
137
+ tags: ['!dev'],
138
+ }
@@ -0,0 +1,60 @@
1
+ <script lang="ts" setup>
2
+ import { ref, computed } from 'vue'
3
+ import { VIcon, VBtn } from 'vuetify/components'
4
+ import { mdiArrowLeft } from '@mdi/js'
5
+ import { locales } from './locales'
6
+
7
+ const props = defineProps<{
8
+ hideBackIcon?: boolean
9
+ dark?: boolean
10
+ }>()
11
+
12
+ const backIcon = ref(mdiArrowLeft)
13
+ const isDark = computed(() => props.dark ?? false)
14
+ const iconColor = computed(() => isDark.value ? 'white' : 'primary')
15
+ const buttonVariant = computed(() => isDark.value ? 'outlined' : 'text')
16
+ const buttonTheme = computed(() => isDark.value ? 'dark' : 'light')
17
+ const buttonColor = computed(() => isDark.value ? 'white' : 'primary')
18
+
19
+ const buttonClasses = computed(() => ({
20
+ 'px-0': !isDark.value,
21
+ 'pr-1': !isDark.value && !props.hideBackIcon,
22
+ }))
23
+
24
+ </script>
25
+
26
+ <template>
27
+ <VBtn
28
+ v-bind="$attrs"
29
+ :variant="buttonVariant"
30
+ :theme="buttonTheme"
31
+ :color="buttonColor"
32
+ :outlined="isDark"
33
+ :class="['vd-back-btn', 'text-none', buttonClasses]"
34
+ >
35
+ <slot name="icon">
36
+ <VIcon
37
+ v-if="!props.hideBackIcon"
38
+ :color="iconColor"
39
+ :class="{ 'ml-n1': isDark }"
40
+ class="mr-1"
41
+ >
42
+ {{ backIcon }}
43
+ </VIcon>
44
+ </slot>
45
+
46
+ <slot>
47
+ {{ locales.label }}
48
+ </slot>
49
+ </VBtn>
50
+ </template>
51
+
52
+ <style lang="scss" scoped>
53
+ // Désactiver l'état de hover sur le thème clair
54
+ .v-btn.v-theme--light:deep() {
55
+ .v-btn__underlay,
56
+ .v-btn__overlay {
57
+ display: none;
58
+ }
59
+ }
60
+ </style>
@@ -0,0 +1,3 @@
1
+ export const locales = {
2
+ label: 'Retour',
3
+ }
@@ -0,0 +1,103 @@
1
+ import { mount } from '@vue/test-utils'
2
+ import { expect, describe, it } from 'vitest'
3
+ import BackBtn from '../BackBtn.vue'
4
+ import { vuetify } from '@tests/unit/setup'
5
+ import { VIcon, VBtn } from 'vuetify/components'
6
+
7
+ describe('BackBtn', () => {
8
+ it('render correctly', async () => {
9
+ const wrapper = mount(BackBtn, {
10
+ global: {
11
+ plugins: [vuetify],
12
+ },
13
+ component: {
14
+ VIcon,
15
+ VBtn,
16
+ },
17
+ })
18
+
19
+ expect(wrapper.html()).toMatchSnapshot()
20
+ })
21
+
22
+ it('hides back icon when hideBackIcon is true', () => {
23
+ const wrapper = mount(BackBtn, {
24
+ global: {
25
+ plugins: [vuetify],
26
+ },
27
+ component: {
28
+ VIcon,
29
+ VBtn,
30
+ },
31
+ props: { hideBackIcon: true },
32
+ })
33
+ expect(wrapper.find('VIcon').exists()).toBe(false)
34
+ })
35
+
36
+ it('applies correct button classes based on props', () => {
37
+ const wrapper = mount(BackBtn, {
38
+ global: {
39
+ plugins: [vuetify],
40
+ },
41
+ component: {
42
+ VIcon,
43
+ VBtn,
44
+ },
45
+ props: { dark: false, hideBackIcon: false },
46
+ })
47
+ expect(wrapper.find('.vd-back-btn').classes()).toContain('pr-1')
48
+ })
49
+
50
+ it('applies correct button classes when dark is true and hideBackIcon is false', () => {
51
+ const wrapper = mount(BackBtn, {
52
+ global: {
53
+ plugins: [vuetify],
54
+ },
55
+ component: {
56
+ VIcon,
57
+ VBtn,
58
+ },
59
+ props: { dark: true, hideBackIcon: false },
60
+ })
61
+ expect(wrapper.find('.vd-back-btn').classes()).not.toContain('pr-1')
62
+ })
63
+ it('returns false when dark prop is not provided', () => {
64
+ const wrapper = mount(BackBtn, {
65
+ global: {
66
+ plugins: [vuetify],
67
+ },
68
+ component: {
69
+ VIcon,
70
+ VBtn,
71
+ },
72
+ })
73
+ expect(wrapper.vm.dark).toBe(false)
74
+ })
75
+
76
+ it('returns true when dark prop is true', () => {
77
+ const wrapper = mount(BackBtn, {
78
+ global: {
79
+ plugins: [vuetify],
80
+ },
81
+ component: {
82
+ VIcon,
83
+ VBtn,
84
+ },
85
+ props: { dark: true },
86
+ })
87
+ expect(wrapper.vm.dark).toBe(true)
88
+ })
89
+
90
+ it('returns false when dark prop is false', () => {
91
+ const wrapper = mount(BackBtn, {
92
+ global: {
93
+ plugins: [vuetify],
94
+ },
95
+ component: {
96
+ VIcon,
97
+ VBtn,
98
+ },
99
+ props: { dark: false },
100
+ })
101
+ expect(wrapper.vm.dark).toBe(false)
102
+ })
103
+ })
@@ -0,0 +1,9 @@
1
+ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2
+
3
+ exports[`BackBtn > render correctly 1`] = `
4
+ "<button data-v-efb74141="" type="button" class="v-btn v-theme--light text-primary v-btn--density-default v-btn--size-default v-btn--variant-text vd-back-btn text-none px-0 pr-1" outlined="false"><span class="v-btn__overlay"></span><span class="v-btn__underlay"></span>
5
+ <!----><span class="v-btn__content" data-no-activator=""><i data-v-efb74141="" class="M20,11V13H8L13.5,18.5L12.08,19.92L4.16,12L12.08,4.08L13.5,5.5L8,11H20Z mdi v-icon notranslate v-theme--light v-icon--size-default text-primary mr-1" aria-hidden="true"></i>Retour</span>
6
+ <!---->
7
+ <!---->
8
+ </button>"
9
+ `;