@cnamts/synapse 0.0.9-alpha → 0.0.10-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 (55) hide show
  1. package/dist/design-system-v3.d.ts +631 -62
  2. package/dist/design-system-v3.js +3451 -2650
  3. package/dist/design-system-v3.umd.cjs +1 -1
  4. package/dist/style.css +1 -1
  5. package/package.json +1 -1
  6. package/src/components/DatePicker/Accessibilite.mdx +14 -0
  7. package/src/components/DatePicker/Accessibilite.stories.ts +191 -0
  8. package/src/components/DatePicker/AccessibiliteItems.ts +233 -0
  9. package/src/components/DatePicker/DatePicker.mdx +1 -6
  10. package/src/components/DatePicker/DatePicker.stories.ts +16 -16
  11. package/src/components/DatePicker/DatePicker.vue +20 -6
  12. package/src/components/DatePicker/constants/ExpertiseLevelEnum.ts +4 -0
  13. package/src/components/FileList/FileList.mdx +103 -0
  14. package/src/components/FileList/FileList.stories.ts +562 -0
  15. package/src/components/FileList/FileList.vue +78 -0
  16. package/src/components/FileList/UploadItem/UploadItem.vue +270 -0
  17. package/src/components/FileList/UploadItem/locales.ts +9 -0
  18. package/src/components/FileList/tests/FileList.spec.ts +176 -0
  19. package/src/components/FilePreview/FilePreview.mdx +82 -0
  20. package/src/components/FilePreview/FilePreview.stories.ts +242 -0
  21. package/src/components/FilePreview/FilePreview.vue +68 -0
  22. package/src/components/FilePreview/config.ts +10 -0
  23. package/src/components/FilePreview/locales.ts +4 -0
  24. package/src/components/FilePreview/tests/FilePreview.spec.ts +124 -0
  25. package/src/components/FilePreview/tests/__snapshots__/FilePreview.spec.ts.snap +11 -0
  26. package/src/components/PeriodField/PeriodField.mdx +32 -0
  27. package/src/components/PeriodField/PeriodField.stories.ts +807 -0
  28. package/src/components/PeriodField/PeriodField.vue +355 -0
  29. package/src/components/PeriodField/tests/PeriodField.spec.ts +348 -0
  30. package/src/components/RangeField/Accessibilite.mdx +14 -0
  31. package/src/components/RangeField/Accessibilite.stories.ts +191 -0
  32. package/src/components/RangeField/AccessibiliteItems.ts +179 -0
  33. package/src/components/RangeField/constants/ExpertiseLevelEnum.ts +4 -0
  34. package/src/components/RatingPicker/Accessibilite.mdx +14 -0
  35. package/src/components/RatingPicker/Accessibilite.stories.ts +191 -0
  36. package/src/components/RatingPicker/AccessibiliteItems.ts +208 -0
  37. package/src/components/RatingPicker/constants/ExpertiseLevelEnum.ts +4 -0
  38. package/src/components/SearchListField/Accessibilite.mdx +14 -0
  39. package/src/components/SearchListField/Accessibilite.stories.ts +191 -0
  40. package/src/components/SearchListField/AccessibiliteItems.ts +310 -0
  41. package/src/components/SearchListField/constants/ExpertiseLevelEnum.ts +4 -0
  42. package/src/components/SelectBtnField/Accessibilite.mdx +14 -0
  43. package/src/components/SelectBtnField/Accessibilite.stories.ts +191 -0
  44. package/src/components/SelectBtnField/AccessibiliteItems.ts +191 -0
  45. package/src/components/SelectBtnField/constants/ExpertiseLevelEnum.ts +4 -0
  46. package/src/components/SyAlert/SyAlert.vue +11 -9
  47. package/src/components/TableToolbar/TableToolbar.mdx +130 -0
  48. package/src/components/TableToolbar/TableToolbar.stories.ts +935 -0
  49. package/src/components/TableToolbar/TableToolbar.vue +168 -0
  50. package/src/components/TableToolbar/config.ts +24 -0
  51. package/src/components/TableToolbar/locales.ts +6 -0
  52. package/src/components/TableToolbar/tests/TableToolbar.spec.ts +166 -0
  53. package/src/components/TableToolbar/tests/__snapshots__/TableToolbar.spec.ts.snap +359 -0
  54. package/src/components/index.ts +3 -0
  55. package/src/composables/rules/useFieldValidation.ts +17 -15
@@ -0,0 +1,242 @@
1
+ import type { Meta, StoryObj } from '@storybook/vue3'
2
+ import { onMounted, ref } from 'vue'
3
+ import FilePreview from './FilePreview.vue'
4
+ import FileUpload from '../FileUpload/FileUpload.vue'
5
+
6
+ const meta: Meta = {
7
+ title: 'Composants/Données/FilePreview',
8
+ component: FilePreview,
9
+ argTypes: {
10
+ file: {
11
+ control: false,
12
+ table: {
13
+ type: {
14
+ summary: 'File | Blob',
15
+ },
16
+ category: 'props',
17
+ },
18
+ description: 'Fichier à afficher',
19
+ },
20
+ options: {
21
+ control: {
22
+ type: 'object',
23
+ },
24
+ table: {
25
+ type: {
26
+ summary: 'Object',
27
+ detail: `{
28
+ pdf?: Record<string, string>,
29
+ image?: Record<string, string>,
30
+ }`,
31
+ },
32
+ category: 'props',
33
+ },
34
+ description: 'Configuration des attributs pour les balises `object` et `img`. Par défaut, l\'image a une description vide.',
35
+ },
36
+ locales: {
37
+ description: 'Traductions',
38
+ control: {
39
+ type: 'object',
40
+ },
41
+ table: {
42
+ category: 'props',
43
+ type: {
44
+ summary: 'Record<string, string>',
45
+ },
46
+ defaultValue: {
47
+ summary: 'locales',
48
+ detail: `{
49
+ previewNotAvailable: 'Impossible de prévisualiser le fichier.',
50
+ previewTypeNotAvailable: 'Impossible de prévisualiser ce type de fichier.',
51
+ }`,
52
+ },
53
+ },
54
+ },
55
+ default: {
56
+ control: {
57
+ type: 'text',
58
+ },
59
+ table: {
60
+ category: 'slots',
61
+ },
62
+ description: 'Remplace le contenu par défaut affiché quand le fichier n\'est pas une image ou un pdf',
63
+ },
64
+ },
65
+ } satisfies Meta<typeof FilePreview>
66
+
67
+ export default meta
68
+
69
+ type Story = StoryObj<typeof FilePreview>
70
+
71
+ export const Default: Story = {
72
+ render: args => ({
73
+ components: { FilePreview },
74
+ template: `
75
+ <div>
76
+ <input type="file" @change="file = $event.target.files[0]" id="file-test-upload" />
77
+ <FilePreview v-bind="args" :file >
78
+ <template #default v-if="args.default">
79
+ {{ args.default }}
80
+ </template>
81
+ </FilePreview>
82
+ </div>
83
+ `,
84
+ setup: () => {
85
+ const file = ref<File | undefined>()
86
+ return { args, file }
87
+ },
88
+ }),
89
+ parameters: {
90
+ sourceCode: [
91
+ {
92
+ name: 'Template',
93
+ code: `
94
+ <div>
95
+ <input type="file" @change="file = $event.target.files[0]" />
96
+ <FilePreview :file="file" />
97
+ </div>
98
+ `,
99
+ },
100
+ {
101
+ name: 'Script',
102
+ code: `
103
+ import { ref } from 'vue'
104
+ import { FilePreview } from '@cnamts/synapse'
105
+
106
+ const file = ref<File | undefined>()`,
107
+ },
108
+ ],
109
+ },
110
+ }
111
+
112
+ export const UnsupportedFile: Story = {
113
+ render: args => ({
114
+ components: { FilePreview },
115
+ template: `
116
+ <FilePreview v-bind="args" :file="file" >
117
+ <template #default v-if="args.default">
118
+ {{ args.default }}
119
+ </template>
120
+ </FilePreview>
121
+ `,
122
+ setup: () => {
123
+ const file = ref({
124
+ name: 'document.txt',
125
+ size: 1000,
126
+ type: 'text/plain',
127
+ } as File)
128
+ return { args, file }
129
+ },
130
+ }),
131
+ parameters: {
132
+ sourceCode: [
133
+ {
134
+ name: 'Template',
135
+ code: `
136
+ <FilePreview :file="file" />
137
+ `,
138
+ },
139
+ {
140
+ name: 'Script',
141
+ code: `
142
+ import { ref } from 'vue'
143
+ import { FilePreview } from '@cnamts/synapse'
144
+
145
+ const file = ref({
146
+ name: 'document.txt',
147
+ size: 1000,
148
+ type: 'text/plain',
149
+ } as File)`,
150
+ },
151
+ ],
152
+ },
153
+ }
154
+
155
+ export const FromApi: Story = {
156
+ render: args => ({
157
+ components: { FilePreview },
158
+ template: `
159
+ <FilePreview v-bind="args" :file="file">
160
+ <template #default v-if="args.default">
161
+ {{ args.default }}
162
+ </template>
163
+ </FilePreview>
164
+ `,
165
+ setup: () => {
166
+ const file = ref<File | Blob | undefined>()
167
+
168
+ onMounted(() => {
169
+ fetch('https://picsum.photos/seed/picsum/750/350')
170
+ .then(res => res.blob())
171
+ .then(blob => file.value = blob)
172
+ })
173
+ return { args, file }
174
+ },
175
+ }),
176
+ parameters: {
177
+ sourceCode: [
178
+ {
179
+ name: 'Template',
180
+ code: `
181
+ <FilePreview :file="file" />
182
+ `,
183
+ },
184
+ {
185
+ name: 'Script',
186
+ code: `
187
+ import { onMounted, ref } from 'vue'
188
+ import { FilePreview } from '@cnamts/synapse'
189
+
190
+ const file = ref<File | Blob | undefined>()
191
+
192
+ onMounted(() => {
193
+ fetch('https://picsum.photos/seed/picsum/750/350')
194
+ .then(res => res.blob())
195
+ .then(blob => file.value = blob)
196
+ })`,
197
+ },
198
+ ],
199
+ },
200
+ }
201
+
202
+ export const WithFileUpload: Story = {
203
+ render: args => ({
204
+ components: { FilePreview, FileUpload },
205
+ template: `
206
+ <div>
207
+ <FileUpload v-model="files" class="mb-4"/>
208
+ <FilePreview v-bind="args" :file="files[0]">
209
+ <template #default v-if="args.default">
210
+ {{ args.default }}
211
+ </template>
212
+ </FilePreview>
213
+ </div>
214
+ `,
215
+ setup: () => {
216
+ const files = ref<File[]>([])
217
+
218
+ return { args, files }
219
+ },
220
+ }),
221
+ parameters: {
222
+ sourceCode: [
223
+ {
224
+ name: 'Template',
225
+ code: `
226
+ <div>
227
+ <FileUpload v-model="files" class="mb-4"/>
228
+ <FilePreview :file="files[0]"/>
229
+ </div>
230
+ `,
231
+ },
232
+ {
233
+ name: 'Script',
234
+ code: `
235
+ import { ref } from 'vue'
236
+ import { FilePreview, FileUpload } from '@cnamts/synapse'
237
+
238
+ const files = ref<File[]>([])`,
239
+ },
240
+ ],
241
+ },
242
+ }
@@ -0,0 +1,68 @@
1
+ <script setup lang="ts">
2
+ import deepmerge from 'deepmerge'
3
+ import { computed, onUnmounted, ref, watch } from 'vue'
4
+ import { config } from './config'
5
+ import { locales as defaultLocales } from './locales'
6
+
7
+ const props = withDefaults(defineProps<{
8
+ file?: File | Blob
9
+ options?: {
10
+ pdf?: Record<string, string>
11
+ image?: Record<string, string>
12
+ }
13
+ locales?: typeof defaultLocales
14
+ }>(), {
15
+ file: undefined,
16
+ options: undefined,
17
+ locales: () => defaultLocales,
18
+ })
19
+
20
+ const fileURL = ref('')
21
+ const isPdf = computed(() => props.file?.type === 'application/pdf')
22
+ const isImage = computed(() => props.file ? /^image\//.test(props.file.type) : false)
23
+ const filePreviewOptions = computed(() => deepmerge(config, props.options || {}))
24
+
25
+ const getFileURL = () => {
26
+ if (!props.file || !(isPdf.value || isImage.value)) return
27
+ fileURL.value = URL.createObjectURL(props.file)
28
+ }
29
+
30
+ const revokeFileURL = () => {
31
+ URL.revokeObjectURL(fileURL.value)
32
+ }
33
+
34
+ watch(() => props.file, getFileURL, { immediate: true })
35
+
36
+ onUnmounted(revokeFileURL)
37
+ </script>
38
+
39
+ <template>
40
+ <div
41
+ v-if="file"
42
+ class="sy-file-preview"
43
+ >
44
+ <object
45
+ v-if="isPdf"
46
+ :data="fileURL"
47
+ v-bind="filePreviewOptions.pdf"
48
+ type="application/pdf"
49
+ @load="revokeFileURL"
50
+ >
51
+ <p class="mb-0">{{ locales.previewNotAvailable }}</p>
52
+ </object>
53
+
54
+ <img
55
+ v-else-if="isImage"
56
+ :src="fileURL"
57
+ :alt="filePreviewOptions.image.alt || ''"
58
+ v-bind="filePreviewOptions.image"
59
+ @load="revokeFileURL"
60
+ >
61
+
62
+ <slot v-else>
63
+ <p class="mb-0">
64
+ {{ locales.previewTypeNotAvailable }}
65
+ </p>
66
+ </slot>
67
+ </div>
68
+ </template>
@@ -0,0 +1,10 @@
1
+ export const config = {
2
+ pdf: {
3
+ height: '556px',
4
+ width: '100%',
5
+ },
6
+ image: {
7
+ // https://github.com/vuejs/core/issues/2801
8
+ style: 'width: 100%;',
9
+ },
10
+ }
@@ -0,0 +1,4 @@
1
+ export const locales = {
2
+ previewNotAvailable: 'Impossible de prévisualiser le fichier.',
3
+ previewTypeNotAvailable: 'Impossible de prévisualiser ce type de fichier.',
4
+ }
@@ -0,0 +1,124 @@
1
+ import { describe, it, expect, vi, beforeEach } from 'vitest'
2
+ import { VueWrapper, mount } from '@vue/test-utils'
3
+
4
+ import FilePreview from '../FilePreview.vue'
5
+ import { vuetify } from '@tests/unit/setup'
6
+ import { locales } from '../locales'
7
+
8
+ const testFileImg = {
9
+ name: 'avatar.png',
10
+ size: 1000,
11
+ type: 'image/png',
12
+ } as File
13
+
14
+ const testFilePdf = {
15
+ name: 'document.pdf',
16
+ size: 1000,
17
+ type: 'application/pdf',
18
+ } as File
19
+
20
+ describe('FilePreview', async () => {
21
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
22
+ let wrapper: VueWrapper<any>
23
+ global.URL.createObjectURL = vi.fn()
24
+
25
+ beforeEach(() => {
26
+ wrapper = mount(FilePreview, {
27
+ global: {
28
+ plugins: [vuetify],
29
+ },
30
+ })
31
+ })
32
+
33
+ it('renders correctly with an image', async () => {
34
+ await wrapper.setProps({ file: testFileImg })
35
+
36
+ expect(wrapper.html()).toMatchSnapshot()
37
+
38
+ wrapper.unmount()
39
+ })
40
+
41
+ it('renders correctly with a pdf', async () => {
42
+ await wrapper.setProps({ file: testFilePdf })
43
+
44
+ expect(wrapper.html()).toMatchSnapshot()
45
+
46
+ wrapper.unmount()
47
+ })
48
+
49
+ it('render correctly with a file that is not an image or pdf', async () => {
50
+ await wrapper.setProps({
51
+ file: {
52
+ name: 'document.txt',
53
+ size: 1000,
54
+ type: 'text/plain',
55
+ } as File,
56
+ })
57
+
58
+ expect(wrapper.text()).toContain(locales.previewTypeNotAvailable)
59
+
60
+ wrapper.unmount()
61
+ })
62
+
63
+ it('updates the preview when the file changes', async () => {
64
+ await wrapper.setProps({ file: testFileImg })
65
+
66
+ expect(wrapper.find('img').exists()).toBe(true)
67
+
68
+ await wrapper.setProps({
69
+ file: testFilePdf,
70
+ })
71
+
72
+ expect(wrapper.find('img').exists()).toBe(false)
73
+ expect(wrapper.find('object').exists()).toBe(true)
74
+
75
+ await wrapper.setProps({
76
+ file: null,
77
+ })
78
+
79
+ expect(wrapper.find('img').exists()).toBe(false)
80
+ expect(wrapper.find('object').exists()).toBe(false)
81
+ expect(wrapper.text()).toBe('')
82
+
83
+ wrapper.unmount()
84
+ })
85
+
86
+ it('with options', async () => {
87
+ await wrapper.setProps({
88
+ file: testFileImg,
89
+ options: {
90
+ image: {
91
+ alt: 'Photo de paysage montagneux.',
92
+ },
93
+ },
94
+ })
95
+
96
+ expect(wrapper.html()).toMatchSnapshot()
97
+
98
+ wrapper.unmount()
99
+ })
100
+
101
+ it('show an error when the type is not supported', async () => {
102
+ await wrapper.setProps({
103
+ file: {
104
+ name: 'document.txt',
105
+ size: 1000,
106
+ type: 'text/plain',
107
+ } as File,
108
+ })
109
+
110
+ expect(wrapper.text()).toContain(locales.previewTypeNotAvailable)
111
+
112
+ wrapper.unmount()
113
+ })
114
+
115
+ it('show nothing when the file is null', async () => {
116
+ await wrapper.setProps({
117
+ file: null,
118
+ })
119
+
120
+ expect(wrapper.html()).toMatchInlineSnapshot(`"<!--v-if-->"`)
121
+
122
+ wrapper.unmount()
123
+ })
124
+ })
@@ -0,0 +1,11 @@
1
+ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2
+
3
+ exports[`FilePreview > renders correctly with a pdf 1`] = `
4
+ "<div class="sy-file-preview"><object height="556px" width="100%" type="application/pdf">
5
+ <p class="mb-0">Impossible de prévisualiser le fichier.</p>
6
+ </object></div>"
7
+ `;
8
+
9
+ exports[`FilePreview > renders correctly with an image 1`] = `"<div class="sy-file-preview"><img alt="" style="width: 100%;"></div>"`;
10
+
11
+ exports[`FilePreview > with options 1`] = `"<div class="sy-file-preview"><img alt="Photo de paysage montagneux." style="width: 100%;"></div>"`;
@@ -0,0 +1,32 @@
1
+ import {Meta, Canvas, Controls, Source} from '@storybook/blocks';
2
+ import * as PeriodFieldStories from "./PeriodField.stories.ts";
3
+
4
+ <Meta of={PeriodFieldStories}/>
5
+
6
+ # PeriodField
7
+
8
+ Le composant `PeriodField` est utilisé pour permettre à l’utilisateur de saisir une période.
9
+
10
+ <Canvas story={{height: '550px'}} of={PeriodFieldStories.Default}/>
11
+
12
+ # API
13
+
14
+ <Controls of={PeriodFieldStories.Default}/>
15
+
16
+ ## Utilisation de base
17
+
18
+ <Source
19
+ dark code={`
20
+ <script setup lang="ts">
21
+ import { ref } from 'vue'
22
+ import { PeriodField } from '@cnamts/synapse'
23
+
24
+ const selectedPeriod = ref({ from: null, to: null })
25
+ </script>
26
+
27
+ <template>
28
+ <PeriodField v-model="selectedPeriod" />
29
+ <p>Selected Period: {{ selectedPeriod }}</p>
30
+ </template>
31
+ `}
32
+ />