@cnamts/synapse 0.0.14-alpha → 0.0.16-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 (108) hide show
  1. package/dist/components/CookiesSelection/CookiesSelection.d.ts +26 -26
  2. package/dist/components/Customs/SyInputSelect/SyInputSelect.d.ts +2 -2
  3. package/dist/components/Customs/SySelect/SySelect.d.ts +24 -12
  4. package/dist/components/Customs/SySelect/locales.d.ts +3 -0
  5. package/dist/components/Customs/SyTextField/SyTextField.d.ts +1393 -3
  6. package/dist/components/DatePicker/DatePicker.d.ts +3532 -22
  7. package/dist/components/DatePicker/DateTextInput.d.ts +1408 -11
  8. package/dist/components/DialogBox/config.d.ts +1 -1
  9. package/dist/components/DownloadBtn/DownloadBtn.d.ts +2 -0
  10. package/dist/components/LangBtn/LangBtn.d.ts +467 -1
  11. package/dist/components/LangBtn/config.d.ts +1 -3
  12. package/dist/components/NirField/NirField.d.ts +2805 -15
  13. package/dist/components/PasswordField/PasswordField.d.ts +2 -2
  14. package/dist/components/PeriodField/PeriodField.d.ts +7345 -325
  15. package/dist/components/PhoneField/PhoneField.d.ts +3 -3
  16. package/dist/components/SelectBtnField/SelectBtnField.d.ts +1 -1
  17. package/dist/components/SkipLink/SkipLink.d.ts +3 -2
  18. package/dist/components/SyAlert/SyAlert.d.ts +72 -1
  19. package/dist/components/UploadWorkflow/UploadWorkflow.d.ts +26 -26
  20. package/dist/components/UserMenuBtn/UserMenuBtn.d.ts +2 -0
  21. package/dist/components/index.d.ts +2 -0
  22. package/dist/composables/date/useDateFormat.d.ts +2 -2
  23. package/dist/composables/date/useDateFormatDayjs.d.ts +23 -0
  24. package/dist/composables/date/useDateInitializationDayjs.d.ts +18 -0
  25. package/dist/design-system-v3.js +4314 -3987
  26. package/dist/design-system-v3.umd.cjs +1 -1
  27. package/dist/style.css +1 -1
  28. package/dist/vuetifyConfig.d.ts +1 -0
  29. package/package.json +1 -1
  30. package/src/components/BackBtn/Accessibilite.stories.ts +4 -0
  31. package/src/components/BackBtn/BackBtn.vue +2 -1
  32. package/src/components/BackToTopBtn/Accessibilite.stories.ts +4 -0
  33. package/src/components/BackToTopBtn/BackToTopBtn.stories.ts +78 -21
  34. package/src/components/BackToTopBtn/BackToTopBtn.vue +15 -0
  35. package/src/components/BackToTopBtn/config.ts +2 -2
  36. package/src/components/BackToTopBtn/tests/__snapshots__/BackToTopBtn.spec.ts.snap +4 -4
  37. package/src/components/CopyBtn/Accessibilite.stories.ts +4 -0
  38. package/src/components/Customs/SyBtnSelect/SyBtnSelect.stories.ts +2 -2
  39. package/src/components/Customs/SyBtnSelect/SyBtnSelect.vue +0 -1
  40. package/src/components/Customs/SyInputSelect/SyInputSelect.stories.ts +3 -3
  41. package/src/components/Customs/SyInputSelect/SyInputSelect.vue +4 -4
  42. package/src/components/Customs/SySelect/SySelect.stories.ts +4 -0
  43. package/src/components/Customs/SySelect/SySelect.vue +75 -10
  44. package/src/components/Customs/SySelect/locales.ts +3 -0
  45. package/src/components/Customs/SySelect/tests/SySelect.spec.ts +24 -2
  46. package/src/components/Customs/SyTextField/Accessibilite.stories.ts +7 -0
  47. package/src/components/Customs/SyTextField/SyTextField.stories.ts +14 -1
  48. package/src/components/Customs/SyTextField/SyTextField.vue +85 -20
  49. package/src/components/DatePicker/ComplexDatePicker/ComplexDatePicker.vue +795 -0
  50. package/src/components/DatePicker/DatePicker.stories.ts +432 -1
  51. package/src/components/DatePicker/DatePicker.vue +143 -76
  52. package/src/components/DatePicker/DatePickerValidation.mdx +338 -0
  53. package/src/components/DatePicker/DatePickerValidation.stories.ts +30 -0
  54. package/src/components/DatePicker/DateTextInput.vue +87 -135
  55. package/src/components/DatePicker/docExamples/DatePickerBidirectionalValidation.vue +282 -0
  56. package/src/components/DatePicker/docExamples/DatePickerValidationExamples.vue +535 -0
  57. package/src/components/DatePicker/tests/DatePicker.spec.ts +33 -32
  58. package/src/components/DatePicker/tests/DateTextInput.spec.ts +83 -35
  59. package/src/components/DialogBox/DialogBox.stories.ts +5 -2
  60. package/src/components/DialogBox/DialogBox.vue +1 -1
  61. package/src/components/DialogBox/config.ts +1 -1
  62. package/src/components/DownloadBtn/Accessibilite.stories.ts +4 -0
  63. package/src/components/DownloadBtn/DownloadBtn.stories.ts +17 -8
  64. package/src/components/DownloadBtn/DownloadBtn.vue +13 -6
  65. package/src/components/DownloadBtn/tests/__snapshots__/DownloadBtn.spec.ts.snap +0 -2
  66. package/src/components/FranceConnectBtn/Accessibilite.stories.ts +4 -0
  67. package/src/components/HeaderBar/HeaderBurgerMenu/HeaderMenuItem/HeaderMenuItem.vue +3 -0
  68. package/src/components/HeaderBar/HeaderBurgerMenu/HeaderSubMenu/HeaderSubMenu.vue +3 -0
  69. package/src/components/HeaderBar/HeaderBurgerMenu/menu.scss +19 -0
  70. package/src/components/HeaderBar/HeaderMenuBtn/HeaderMenuBtn.vue +12 -2
  71. package/src/components/LangBtn/Accessibilite.stories.ts +4 -0
  72. package/src/components/LangBtn/LangBtn.stories.ts +1 -4
  73. package/src/components/LangBtn/LangBtn.vue +68 -9
  74. package/src/components/LangBtn/config.ts +0 -1
  75. package/src/components/LangBtn/tests/LangBtn.spec.ts +30 -2
  76. package/src/components/PageContainer/Accessibilite.stories.ts +36 -23
  77. package/src/components/PaginatedTable/PaginatedTable.stories.ts +144 -18
  78. package/src/components/PasswordField/PasswordField.stories.ts +6 -6
  79. package/src/components/PasswordField/PasswordField.vue +3 -3
  80. package/src/components/PeriodField/PeriodField.vue +4 -4
  81. package/src/components/PhoneField/PhoneField.stories.ts +216 -24
  82. package/src/components/PhoneField/PhoneField.vue +32 -2
  83. package/src/components/PhoneField/tests/PhoneField.spec.ts +161 -14
  84. package/src/components/RatingPicker/NumberPicker/NumberPicker.vue +2 -1
  85. package/src/components/RatingPicker/RatingPicker.stories.ts +1 -1
  86. package/src/components/SkipLink/Accessibilite.stories.ts +8 -0
  87. package/src/components/SkipLink/SkipLink.vue +11 -9
  88. package/src/components/SkipLink/tests/__snapshots__/skipLink.spec.ts.snap +7 -4
  89. package/src/components/SkipLink/tests/skipLink.spec.ts +120 -6
  90. package/src/components/SyAlert/Accessibilite.stories.ts +4 -0
  91. package/src/components/SyAlert/SyAlert.mdx +3 -7
  92. package/src/components/SyAlert/SyAlert.stories.ts +19 -12
  93. package/src/components/SyAlert/SyAlert.vue +88 -51
  94. package/src/components/SyAlert/tests/SyAlert.spec.ts +20 -2
  95. package/src/components/SyAlert/tests/__snapshots__/SyAlert.spec.ts.snap +83 -75
  96. package/src/components/UserMenuBtn/UserMenuBtn.stories.ts +56 -0
  97. package/src/components/UserMenuBtn/UserMenuBtn.vue +4 -2
  98. package/src/components/UserMenuBtn/tests/UserMenuBtn.spec.ts +41 -0
  99. package/src/components/index.ts +2 -0
  100. package/src/composables/date/useDateFormat.ts +17 -1
  101. package/src/composables/date/useDateFormatDayjs.ts +84 -0
  102. package/src/composables/date/useDateInitializationDayjs.ts +133 -0
  103. package/src/composables/rules/useFieldValidation.ts +26 -3
  104. package/src/stories/Accessibilite/Avancement/Avancement.mdx +12 -0
  105. package/src/stories/Accessibilite/Avancement/Avancement.stories.ts +134 -0
  106. package/src/stories/Accessibilite/KitDePreAudit/Echantillonnage.mdx +1 -1
  107. package/src/stories/GuideDuDev/LesBreackingChanges.mdx +31 -2
  108. package/src/components/LangBtn/tests/Config.spec.ts +0 -24
@@ -66,6 +66,7 @@ export declare const createVuetifyInstance: () => {
66
66
  getDiff: (date: unknown, comparing: unknown, unit?: string | undefined) => number;
67
67
  getWeekArray: (date: unknown, firstDayOfWeek?: string | number | undefined) => unknown[][];
68
68
  getWeekdays: (firstDayOfWeek?: string | number | undefined) => string[];
69
+ getWeek: (date: unknown, firstDayOfWeek?: string | number | undefined, firstWeekMinSize?: number | undefined) => number;
69
70
  getMonth: (date: unknown) => number;
70
71
  setMonth: (date: unknown, month: number) => unknown;
71
72
  getDate: (date: unknown) => number;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cnamts/synapse",
3
- "version": "0.0.14-alpha",
3
+ "version": "0.0.16-alpha",
4
4
  "private": false,
5
5
  "description": "CNAM DS v3",
6
6
  "type": "module",
@@ -159,6 +159,10 @@ export const Legende: StoryObj = {
159
159
  </div>
160
160
  </div>
161
161
  </div>
162
+ <div class="mt-4">
163
+ <p>Rapport d’audit manuel : <a href="/audits/BackBtn.xlsx" style="color:#0C41BD;">Voir le rapport</a></p>
164
+ <p style="color: grey; font-size: 14px">Correctifs associés (<a href="https://github.com/assurance-maladie-digital/design-system/issues/4006" target="_blank" style="color:#0C41BD;">issue #4006</a>)</p>
165
+ </div>
162
166
  `,
163
167
  }
164
168
  },
@@ -16,6 +16,7 @@
16
16
  const buttonVariant = computed(() => isDark.value ? 'outlined' : 'text')
17
17
  const buttonTheme = computed(() => isDark.value ? 'dark' : 'light')
18
18
  const buttonColor = computed(() => isDark.value ? 'white' : 'primary')
19
+ const buttonBgColor = computed(() => isDark.value ? 'transparent' : props.backgroundColor)
19
20
 
20
21
  const buttonClasses = computed(() => ({
21
22
  'px-0': !isDark.value,
@@ -30,7 +31,7 @@
30
31
  :variant="buttonVariant"
31
32
  :theme="buttonTheme"
32
33
  :color="buttonColor"
33
- :class="['sy-back-btn', 'text-none', buttonClasses, `bg-${props.backgroundColor}`]"
34
+ :class="['sy-back-btn', 'text-none', buttonClasses, `bg-${buttonBgColor}`]"
34
35
  >
35
36
  <slot name="icon">
36
37
  <VIcon
@@ -158,6 +158,10 @@ export const Legende: StoryObj = {
158
158
  Problèmes relevés par Tanaguru
159
159
  </div>
160
160
  </div>
161
+ <div class="mt-4">
162
+ <p>Rapport d’audit manuel : <a href="/audits/BackToTopBtn.xlsx" style="color:#0C41BD;">Voir le rapport</a></p>
163
+ <p style="color: grey; font-size: 14px">Correctifs associés (<a href="https://github.com/assurance-maladie-digital/design-system/issues/4022" target="_blank" style="color:#0C41BD;">issue #4022</a>)</p>
164
+ </div>
161
165
  </div>
162
166
  `,
163
167
  }
@@ -12,24 +12,69 @@ const meta = {
12
12
  argTypes: {
13
13
  threshold: {
14
14
  control: { type: 'range', max: 2000 },
15
+ description: 'Distance par rapport au bas de la page à partir de laquelle le bouton apparaît',
16
+ table: {
17
+ type: { summary: 'number' },
18
+ },
15
19
  },
16
20
  nudgeRight: {
17
21
  control: { type: 'range' },
22
+ description: 'Décalage du bouton par rapport au bord droit de la fenêtre',
18
23
  default: 16,
24
+ table: {
25
+ type: { summary: 'number' },
26
+ },
19
27
  },
20
28
  nudgeBottom: {
21
29
  control: { type: 'range' },
30
+ description: 'Décalage du bouton par rapport au bord inférieur de la fenêtre',
22
31
  default: 16,
32
+ table: {
33
+ type: { summary: 'number' },
34
+ },
23
35
  },
24
36
  target: {
25
37
  control: { type: 'text' },
38
+ description: 'ID du conteneur devant être scrollé',
39
+ table: {
40
+ type: { summary: 'string' },
41
+ },
26
42
  },
27
43
  default: {
44
+ description: 'Texte du bouton',
28
45
  control: { type: 'text' },
29
46
  },
30
47
  icon: {
48
+ description: 'Icône du bouton',
31
49
  control: { type: 'text' },
32
50
  },
51
+ vuetifyOptions: {
52
+ control: { type: 'object' },
53
+ description: 'Options de personnalisation du bouton',
54
+ default: () => ({
55
+ btn: {
56
+ variant: 'outlined',
57
+ color: 'primary',
58
+ class: 'text-wrap px-0 px-md-4',
59
+ },
60
+ icon: {
61
+ color: 'primary',
62
+ size: 'medium',
63
+ class: 'ml-0 ml-md-2',
64
+ },
65
+ }),
66
+ table: {
67
+ category: 'props',
68
+ type: {
69
+ summary: 'object',
70
+ detail: `
71
+ {
72
+ btn: Record<string, unknown>,
73
+ icon: Record<string, unknown>,
74
+ }`,
75
+ },
76
+ },
77
+ },
33
78
  },
34
79
  args: {
35
80
  threshold: 120,
@@ -92,8 +137,8 @@ export const Default: Story = {
92
137
  },
93
138
  icon: {
94
139
  color: 'primary',
95
- size: 'small',
96
- class: 'ml-0 ml-md-1',
140
+ size: 'medium',
141
+ class: 'ml-0 ml-md-2',
97
142
  },
98
143
  },
99
144
  },
@@ -105,16 +150,20 @@ export const Default: Story = {
105
150
  VSheet,
106
151
  },
107
152
  setup() {
108
- return { args }
153
+ const { default: defaultSlot, ...props } = args
154
+ return {
155
+ defaultSlot,
156
+ props,
157
+ }
109
158
  },
110
159
  template: `
111
- <VCard
112
- id="target"
113
- width="100%"
114
- max-height="200px"
115
- class="overflow-y-auto"
116
- style="scroll-behavior: smooth"
117
- >
160
+ <VCard
161
+ id="target"
162
+ width="100%"
163
+ max-height="200px"
164
+ class="overflow-y-auto"
165
+ style="scroll-behavior: smooth"
166
+ >
118
167
  <VSheet
119
168
  height="600px"
120
169
  class="d-flex flex-column align-center"
@@ -123,8 +172,8 @@ export const Default: Story = {
123
172
  Haut de la section.
124
173
  </p>
125
174
  </VSheet>
126
- <BackToTopBtn v-bind="args" :vuetify-options="args.vuetifyOptions">
127
- {{args.default}}
175
+ <BackToTopBtn v-bind="props" :vuetify-options="props.vuetifyOptions">
176
+ {{ defaultSlot }}
128
177
  </BackToTopBtn>
129
178
  </VCard>
130
179
  `,
@@ -208,7 +257,11 @@ export const Customization: Story = {
208
257
  BackToTopBtn,
209
258
  },
210
259
  setup() {
211
- return { args }
260
+ const { default: defaultSlot, ...props } = args
261
+ return {
262
+ defaultSlot,
263
+ props,
264
+ }
212
265
  },
213
266
  template: `
214
267
  <VCard
@@ -226,8 +279,8 @@ export const Customization: Story = {
226
279
  Haut de la section.
227
280
  </p>
228
281
  </VSheet>
229
- <BackToTopBtn v-bind="args" :vuetify-options="args.vuetifyOptions">
230
- {{args.default}}
282
+ <BackToTopBtn v-bind="props" :vuetify-options="props.vuetifyOptions">
283
+ {{defaultSlot}}
231
284
  </BackToTopBtn>
232
285
  </VCard>
233
286
  `,
@@ -311,7 +364,11 @@ export const CustomPosition: Story = {
311
364
  BackToTopBtn,
312
365
  },
313
366
  setup() {
314
- return { args }
367
+ const { default: defaultSlot, ...props } = args
368
+ return {
369
+ defaultSlot,
370
+ props,
371
+ }
315
372
  },
316
373
  template: `
317
374
  <VCard
@@ -330,12 +387,12 @@ export const CustomPosition: Story = {
330
387
  </p>
331
388
  </VSheet>
332
389
  <BackToTopBtn
333
- v-bind="args"
334
- :nudge-right="args.nudgeRight"
335
- :nudge-bottom="args.nudgeBottom"
336
- :vuetify-options="args.vuetifyOptions"
390
+ v-bind="props"
391
+ :nudge-right="props.nudgeRight"
392
+ :nudge-bottom="props.nudgeBottom"
393
+ :vuetify-options="props.vuetifyOptions"
337
394
  >
338
- {{args.default}}
395
+ {{defaultSlot}}
339
396
  </BackToTopBtn>
340
397
  </VCard>
341
398
  `,
@@ -135,4 +135,19 @@
135
135
  .v-btn--variant-outlined {
136
136
  background: white;
137
137
  }
138
+
139
+ .vd-back-to-top-btn:deep() {
140
+ .v-btn__underlay,
141
+ .v-btn__overlay {
142
+ display: none;
143
+ }
144
+ }
145
+
146
+ .vd-back-to-top-btn:focus-visible {
147
+ outline: 0;
148
+ }
149
+
150
+ .vd-back-to-top-btn:focus-visible::after {
151
+ opacity: 1;
152
+ }
138
153
  </style>
@@ -6,7 +6,7 @@ export const config = {
6
6
  },
7
7
  icon: {
8
8
  color: 'primary',
9
- size: 'small',
10
- class: 'ml-0 ml-md-1',
9
+ size: 'medium',
10
+ class: 'ml-0 ml-md-2',
11
11
  },
12
12
  }
@@ -39,13 +39,13 @@ exports[`BackToTopBtn > renders correctly 1`] = `
39
39
  M13,20H11V8L5.5,13.5L4.08,12.08L12,4.16L19.92,12.08L18.5,13.5L13,8V20Z
40
40
  mdi
41
41
  ml-0
42
- ml-md-1
42
+ ml-md-2
43
43
  notranslate
44
44
  text-primary
45
45
  v-icon
46
- v-icon--size-small
47
46
  v-theme--light
48
47
  "
48
+ style="font-size: medium;"
49
49
  ></i>
50
50
  </span>
51
51
  <!---->
@@ -93,13 +93,13 @@ exports[`BackToTopBtn > renders correctly when nudgeBottom and nudgeRight are se
93
93
  M13,20H11V8L5.5,13.5L4.08,12.08L12,4.16L19.92,12.08L18.5,13.5L13,8V20Z
94
94
  mdi
95
95
  ml-0
96
- ml-md-1
96
+ ml-md-2
97
97
  notranslate
98
98
  text-primary
99
99
  v-icon
100
- v-icon--size-small
101
100
  v-theme--light
102
101
  "
102
+ style="font-size: medium;"
103
103
  ></i>
104
104
  </span>
105
105
  <!---->
@@ -159,6 +159,10 @@ export const Legende: StoryObj = {
159
159
  </div>
160
160
  </div>
161
161
  </div>
162
+ <div class="mt-4">
163
+ <p>Rapport d’audit manuel : <a href="/audits/CopyBtn.xlsx" style="color:#0C41BD;">Voir le rapport</a></p>
164
+ <p style="color: grey; font-size: 14px">Correctifs associés (<a href="https://github.com/assurance-maladie-digital/design-system/issues/4008" target="_blank" style="color:#0C41BD;">issue #4008</a>)</p>
165
+ </div>
162
166
  `,
163
167
  }
164
168
  },
@@ -506,7 +506,7 @@ export const WithCustomStyles: Story = {
506
506
  <VIcon
507
507
  :icon="mdiAccount"
508
508
  class="mr-2"
509
- color="red"
509
+ color="secondary"
510
510
  />
511
511
  </template>
512
512
  </SyBtnSelect>
@@ -544,7 +544,7 @@ const items = ['Option 1', 'Option 2']
544
544
  <VIcon
545
545
  :icon="mdiAccount"
546
546
  class="mr-2"
547
- color="red"
547
+ color="secondary"
548
548
  />
549
549
  </template>
550
550
  </SyBtnSelect>
@@ -215,7 +215,6 @@
215
215
 
216
216
  <style lang="scss" scoped>
217
217
  @use '@/assets/tokens';
218
- @use '@/assets/tokens' as *;
219
218
 
220
219
  .sy-user-menu-btn-ctn {
221
220
  position: relative;
@@ -21,7 +21,7 @@ const meta = {
21
21
  valueKey: { control: 'text' },
22
22
  vuetifyOptions: { control: 'object' },
23
23
  displayAsterisk: { control: 'boolean' },
24
- readOnly: { control: 'boolean' },
24
+ readonly: { control: 'boolean' },
25
25
  },
26
26
  } as Meta<typeof SyInputSelect>
27
27
 
@@ -73,7 +73,7 @@ export const Default: Story = {
73
73
  color: 'primary',
74
74
  },
75
75
  },
76
- readOnly: false,
76
+ readonly: false,
77
77
  },
78
78
  render: (args) => {
79
79
  return {
@@ -86,7 +86,7 @@ export const Default: Story = {
86
86
  <SyInputSelect
87
87
  v-bind="args"
88
88
  :vuetify-options="args.vuetifyOptions"
89
- :read-only="args.readOnly"
89
+ :readonly="args.readonly"
90
90
  />
91
91
  </div>
92
92
  <br/><br/><br/><br/>
@@ -15,7 +15,7 @@
15
15
  errorMessages?: string | string[]
16
16
  isHeaderToolbar?: boolean
17
17
  displayAsterisk?: boolean
18
- readOnly?: boolean
18
+ readonly?: boolean
19
19
  }>(), {
20
20
  modelValue: null,
21
21
  items: () => [],
@@ -27,7 +27,7 @@
27
27
  errorMessages: () => [],
28
28
  isHeaderToolbar: false,
29
29
  displayAsterisk: false,
30
- readOnly: false,
30
+ readonly: false,
31
31
  })
32
32
 
33
33
  const options = useCustomizableOptions(defaultOptions, props)
@@ -39,7 +39,7 @@
39
39
  const hasError = ref(false)
40
40
 
41
41
  const toggleMenu = () => {
42
- if (props.readOnly) return
42
+ if (props.readonly) return
43
43
  isOpen.value = !isOpen.value
44
44
  }
45
45
 
@@ -160,7 +160,7 @@
160
160
  :label="labelWithAsterisk"
161
161
  :title="labelWithAsterisk"
162
162
  role="menu"
163
- :read-only="props.readOnly"
163
+ :readonly="props.readonly"
164
164
  @click="checkForErrors"
165
165
  >
166
166
  <div
@@ -30,6 +30,10 @@ const meta: Meta<typeof SySelect> = {
30
30
  control: 'boolean',
31
31
  description: 'Retourne l\'objet complet sélectionné',
32
32
  },
33
+ clearable: {
34
+ control: 'boolean',
35
+ description: 'Permet de vider la sélection',
36
+ },
33
37
  },
34
38
  } as Meta<typeof SySelect>
35
39
 
@@ -1,7 +1,12 @@
1
1
  <script setup lang="ts">
2
- import { mdiInformation, mdiMenuDown } from '@mdi/js'
3
- import { ref, watch, onMounted, computed, type PropType } from 'vue'
2
+ import { mdiInformation, mdiMenuDown, mdiCloseCircle } from '@mdi/js'
3
+ import { ref, watch, onMounted, onUnmounted, computed, type PropType } from 'vue'
4
4
  import type { VTextField } from 'vuetify/components'
5
+ import { locales } from './locales'
6
+
7
+ export type ItemType = {
8
+ [key: string]: unknown
9
+ }
5
10
 
6
11
  const props = defineProps({
7
12
  modelValue: {
@@ -9,7 +14,7 @@
9
14
  default: null,
10
15
  },
11
16
  items: {
12
- type: Array,
17
+ type: Array as PropType<ItemType[]>,
13
18
  default: () => [],
14
19
  },
15
20
  label: {
@@ -60,7 +65,11 @@
60
65
  type: String,
61
66
  default: undefined,
62
67
  },
63
- readOnly: {
68
+ readonly: {
69
+ type: Boolean,
70
+ default: false,
71
+ },
72
+ clearable: {
64
73
  type: Boolean,
65
74
  default: false,
66
75
  },
@@ -76,17 +85,36 @@
76
85
  const labelRef = ref<HTMLElement | null>(null)
77
86
 
78
87
  const toggleMenu = () => {
79
- if (props.readOnly) return
88
+ if (props.readonly) return
80
89
  isOpen.value = !isOpen.value
90
+ if (isOpen.value) updateListPosition()
81
91
  }
82
92
  const closeList = () => {
83
93
  isOpen.value = false
84
94
  }
85
95
  const inputId = ref(`sy-select-${Math.random().toString(36).substring(7)}`)
86
96
 
97
+ const listStyles = ref<Record<string, string>>({})
98
+ const updateListPosition = () => {
99
+ if (input.value?.$el) {
100
+ const rect = input.value.$el.getBoundingClientRect()
101
+ listStyles.value = {
102
+ position: 'fixed',
103
+ top: `${rect.bottom}px`,
104
+ left: `${rect.left}px`,
105
+ width: `${rect.width}px`,
106
+ zIndex: '999',
107
+ }
108
+ }
109
+ }
110
+
87
111
  // eslint-disable-next-line @typescript-eslint/no-explicit-any -- This is a generic type
88
112
  const selectItem = (item: any) => {
89
- if (props.returnObject) {
113
+ if (item === null) {
114
+ selectedItem.value = null
115
+ emit('update:modelValue', null)
116
+ }
117
+ else if (props.returnObject) {
90
118
  selectedItem.value = item
91
119
  emit('update:modelValue', item)
92
120
  }
@@ -164,10 +192,21 @@
164
192
  }
165
193
  })
166
194
 
195
+ watch(isOpen, (open) => {
196
+ if (open) updateListPosition()
197
+ })
198
+
167
199
  onMounted(() => {
168
200
  if (labelRef.value) {
169
201
  labelWidth.value = labelRef.value.offsetWidth + 64
170
202
  }
203
+ window.addEventListener('scroll', updateListPosition, true)
204
+ window.addEventListener('resize', updateListPosition)
205
+ })
206
+
207
+ onUnmounted(() => {
208
+ window.removeEventListener('scroll', updateListPosition, true)
209
+ window.removeEventListener('resize', updateListPosition)
171
210
  })
172
211
 
173
212
  defineExpose({
@@ -194,7 +233,7 @@
194
233
  :rules="isRequired && !props.disableErrorHandling ? ['Le champ est requis.'] : []"
195
234
  :display-asterisk="displayAsterisk"
196
235
  :bg-color="props.bgColor"
197
- :readonly="props.readOnly"
236
+ readonly
198
237
  class="sy-select"
199
238
  :style="hasError ? { minWidth: `${labelWidth + 18}px`} : {minWidth: `${labelWidth}px`}"
200
239
  @click="toggleMenu"
@@ -208,6 +247,15 @@
208
247
  >
209
248
  {{ mdiInformation }}
210
249
  </VIcon>
250
+ <VIcon
251
+ v-if="props.clearable && selectedItemText"
252
+ class="sy-select__clear-icon"
253
+ :class="hasError ? 'mr-14' : 'mr-8'"
254
+ :aria-label="locales.clear"
255
+ @click.stop.prevent="selectItem(null)"
256
+ >
257
+ {{ mdiCloseCircle }}
258
+ </VIcon>
211
259
  <VIcon class="arrow">
212
260
  {{ mdiMenuDown }}
213
261
  </VIcon>
@@ -220,7 +268,10 @@
220
268
  <VList
221
269
  v-if="isOpen"
222
270
  class="v-list"
223
- :style="`min-width: ${input?.$el.offsetWidth}px`"
271
+ :style="{
272
+ minWidth: `${input?.$el.offsetWidth}px`,
273
+ ...listStyles
274
+ }"
224
275
  bg-color="white"
225
276
  @keydown.esc.prevent="isOpen = false"
226
277
  >
@@ -230,8 +281,14 @@
230
281
  :ref="'options-' + index"
231
282
  role="option"
232
283
  class="v-list-item"
233
- :aria-selected="selectedItem === item"
284
+ :aria-selected="props.returnObject
285
+ ? selectedItem && selectedItem[props.valueKey] === item[props.valueKey]
286
+ : selectedItem === item[props.valueKey]"
234
287
  :tabindex="index + 1"
288
+ :class="{ active: props.returnObject
289
+ ? selectedItem && selectedItem[props.valueKey] === item[props.valueKey]
290
+ : selectedItem === item[props.valueKey]
291
+ }"
235
292
  @click="selectItem(item)"
236
293
  >
237
294
  <VListItemTitle>
@@ -261,7 +318,6 @@
261
318
  }
262
319
 
263
320
  .v-list {
264
- position: absolute;
265
321
  left: inherit !important;
266
322
  margin-top: -22px;
267
323
  max-height: 300px;
@@ -280,12 +336,21 @@
280
336
  background-color: rgb(0 0 0 / 8%);
281
337
  }
282
338
 
339
+ .v-list-item.active {
340
+ background-color: rgb(0 0 0 / 8%);
341
+ }
342
+
283
343
  .v-icon {
284
344
  position: absolute;
285
345
  right: 10px;
286
346
  color: tokens.$grey-darken-20;
287
347
  }
288
348
 
349
+ .sy-select__clear-icon {
350
+ color: tokens.$grey-darken-20 !important;
351
+ opacity: var(--v-medium-emphasis-opacity) !important;
352
+ }
353
+
289
354
  :deep(.v-field__input) {
290
355
  color: tokens.$grey-darken-20;
291
356
  }
@@ -0,0 +1,3 @@
1
+ export const locales = {
2
+ clear: 'Effacer la sélection',
3
+ }
@@ -3,6 +3,10 @@ import { expect, describe, it } from 'vitest'
3
3
  import SySelect from '../SySelect.vue'
4
4
  import { vuetify } from '@tests/unit/setup'
5
5
 
6
+ type ItemType = {
7
+ [key: string]: unknown
8
+ }
9
+
6
10
  describe('SySelect.vue', () => {
7
11
  it('renders the component with default props', () => {
8
12
  const wrapper = mount(SySelect, {
@@ -123,7 +127,7 @@ describe('SySelect.vue', () => {
123
127
  })
124
128
 
125
129
  it('formats items correctly', () => {
126
- const items = ['Option 1', 'Option 2']
130
+ const items = ['Option 1', 'Option 2'] as unknown as ItemType[]
127
131
  const wrapper = mount(SySelect, {
128
132
  props: { items, textKey: 'text', valueKey: 'value' },
129
133
  global: {
@@ -306,7 +310,7 @@ describe('SySelect.vue', () => {
306
310
  it('emit the value when items is an array of string', async () => {
307
311
  const wrapper = mount(SySelect, {
308
312
  props: {
309
- items: ['Option 1', 'Option 2'],
313
+ items: ['Option 1', 'Option 2'] as unknown as ItemType[],
310
314
  },
311
315
  global: {
312
316
  plugins: [vuetify],
@@ -323,4 +327,22 @@ describe('SySelect.vue', () => {
323
327
  await secondItem!.trigger('click')
324
328
  expect(wrapper.emitted()['update:modelValue'][1]).toEqual(['Option 2'])
325
329
  })
330
+
331
+ it('is clearable when clearable is true', async () => {
332
+ const wrapper = mount(SySelect, {
333
+ props: {
334
+ modelValue: '1',
335
+ clearable: true,
336
+ items: [{ text: 'Option 1', value: '1' }, { text: 'Option 2', value: '2' }],
337
+ },
338
+ global: {
339
+ plugins: [vuetify],
340
+ },
341
+ })
342
+
343
+ const clearBtn = wrapper.find('.sy-select__clear-icon')
344
+ expect(clearBtn.exists()).toBe(true)
345
+ await clearBtn.trigger('click')
346
+ expect(wrapper.emitted()['update:modelValue'][0]).toEqual([null])
347
+ })
326
348
  })
@@ -217,6 +217,13 @@ export const Legende: StoryObj = {
217
217
  </div>
218
218
  </div>
219
219
  </div>
220
+ <div class="mt-4">
221
+ <p>Rapport d’audit manuel : <a href="/audits/SyTextField.xlsx" style="color:#0C41BD;">Voir le
222
+ rapport</a></p>
223
+ <p style="color: grey; font-size: 14px">Correctifs associés (<a
224
+ href="https://github.com/assurance-maladie-digital/design-system/issues/4028" target="_blank"
225
+ style="color:#0C41BD;">issue #4028</a>)</p>
226
+ </div>
220
227
  `,
221
228
  }
222
229
  },