@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
@@ -331,7 +331,7 @@ export const ReadOnly: Story = {
331
331
  v-model="ratingEmotion"
332
332
  label="Êtes-vous satisfait de ce service ?"
333
333
  type="emotion"
334
- read-only
334
+ readonly
335
335
  />
336
336
  </template>
337
337
  `,
@@ -159,6 +159,14 @@ export const Legende: StoryObj = {
159
159
  Problèmes relevés par Tanaguru
160
160
  </div>
161
161
  </div>
162
+ <div class="mt-4">
163
+ <p>Rapport d’audit manuel : <a href="/audits/SkipLink.xlsx" style="color:#0C41BD;">Voir le
164
+ rapport</a></p>
165
+ <p style="color: grey; font-size: 14px">Correctifs associés (<a
166
+ href="https://github.com/assurance-maladie-digital/design-system/issues/4012" target="_blank"
167
+ style="color:#0C41BD;"
168
+ >issue #4012</a>)</p>
169
+ </div>
162
170
  </div>
163
171
  `,
164
172
  }
@@ -39,7 +39,8 @@
39
39
  if (fail) return
40
40
  if (to.path === from.path) return
41
41
  nextTick(() => {
42
- skipLinkSpan.value?.focus()
42
+ const link = document.querySelector('a.sy-skip-link') as HTMLAnchorElement
43
+ if (link) link.focus()
43
44
  })
44
45
  })
45
46
  }
@@ -47,25 +48,26 @@
47
48
  </script>
48
49
 
49
50
  <template>
50
- <div class="vd-skip-link-container">
51
- <span
52
- ref="skipLinkSpan"
53
- tabindex="-1"
54
- />
51
+ <nav
52
+ aria-label="Liens d'évitement"
53
+ class="sy-skip-link-container"
54
+ >
55
+ <div ref="skipLinkSpan" />
55
56
 
56
57
  <a
58
+ ref="skipLink"
57
59
  :href="target"
58
- class="vd-skip-link text-primary d-block d-sr-only-focusable px-2"
60
+ class="sy-skip-link text-primary d-block d-sr-only-focusable px-2"
59
61
  >
60
62
  <slot>{{ label }}</slot>
61
63
  </a>
62
- </div>
64
+ </nav>
63
65
  </template>
64
66
 
65
67
  <style lang="scss" scoped>
66
68
  @use '/src/assets/tokens';
67
69
 
68
- .vd-skip-link {
70
+ .sy-skip-link {
69
71
  z-index: 150;
70
72
  position: fixed;
71
73
  top: 0;
@@ -1,17 +1,20 @@
1
1
  // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2
2
 
3
3
  exports[`SkipLink > renders correctly 1`] = `
4
- <div class="vd-skip-link-container">
5
- <span tabindex="-1"></span>
4
+ <nav
5
+ aria-label="Liens d'évitement"
6
+ class="sy-skip-link-container"
7
+ >
8
+ <div></div>
6
9
  <a
7
10
  class="
8
11
  d-block
9
12
  d-sr-only-focusable
10
13
  px-2
14
+ sy-skip-link
11
15
  text-primary
12
- vd-skip-link
13
16
  "
14
17
  href="#main"
15
18
  >Aller au contenu principal</a>
16
- </div>
19
+ </nav>
17
20
  `;
@@ -2,6 +2,7 @@ import { describe, it, expect, beforeEach, vi } from 'vitest'
2
2
  import { mount } from '@vue/test-utils'
3
3
  import { createRouter, createWebHistory } from 'vue-router'
4
4
  import SkipLink from '../SkipLink.vue'
5
+ import { locales } from '../locales'
5
6
 
6
7
  // Create a mock router
7
8
  const router = createRouter({
@@ -28,19 +29,132 @@ describe('SkipLink', () => {
28
29
  })
29
30
 
30
31
  it('focuses the skip link on route change', async () => {
31
- const wrapper = mount(SkipLink, {
32
+ // Monter le composant
33
+ mount(SkipLink, {
32
34
  global: {
33
35
  plugins: [router],
34
36
  },
35
37
  })
36
38
 
37
- // Create a mock for the focus method
38
- const spy = vi.spyOn(wrapper.vm.$refs.skipLinkSpan as HTMLLinkElement, 'focus')
39
+ // Espionner querySelector et focus
40
+ const linkElement = document.createElement('a')
41
+ const focusSpy = vi.spyOn(linkElement, 'focus')
42
+ vi.spyOn(document, 'querySelector').mockImplementation(() => linkElement)
39
43
 
40
- // Trigger the route change
44
+ // Déclencher le changement de route
41
45
  await router.push('/about')
42
- await router.isReady() // Ensure the router is ready
46
+ await router.isReady()
47
+ await new Promise(resolve => setTimeout(resolve, 0))
48
+
49
+ expect(focusSpy).toHaveBeenCalled()
50
+ })
51
+
52
+ it('accepte des props personnalisées', () => {
53
+ const customLabel = 'Accéder au contenu'
54
+ const customTarget = '#content'
55
+
56
+ const wrapper = mount(SkipLink, {
57
+ props: {
58
+ label: customLabel,
59
+ target: customTarget,
60
+ },
61
+ })
62
+
63
+ const link = wrapper.find('a.sy-skip-link')
64
+ expect(link.text()).toBe(customLabel)
65
+ expect(link.attributes('href')).toBe(customTarget)
66
+ })
67
+
68
+ it('utilise les valeurs par défaut', () => {
69
+ const wrapper = mount(SkipLink)
70
+
71
+ const link = wrapper.find('a.sy-skip-link')
72
+ expect(link.text()).toBe(locales.label)
73
+ expect(link.attributes('href')).toBe('#main')
74
+ })
75
+
76
+ it('contient les attributs d\'accessibilité corrects', () => {
77
+ const wrapper = mount(SkipLink)
78
+
79
+ expect(wrapper.find('nav').attributes('aria-label')).toBe('Liens d\'évitement')
80
+ expect(wrapper.find('a.sy-skip-link').exists()).toBe(true)
81
+ })
82
+
83
+ it('ne déplace pas le focus si la navigation échoue', async () => {
84
+ // Mock du router avec un hook afterEach qui simule un échec
85
+ const mockRouter = {
86
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- This is a generic type
87
+ afterEach: (callback: any) => {
88
+ // Simuler un appel avec fail=true
89
+ callback(
90
+ { path: '/new-path' },
91
+ { path: '/' },
92
+ true, // fail=true
93
+ )
94
+ },
95
+ }
96
+
97
+ // Espionner querySelector et focus
98
+ const linkElement = document.createElement('a')
99
+ const focusSpy = vi.spyOn(linkElement, 'focus')
100
+ vi.spyOn(document, 'querySelector').mockImplementation(() => linkElement)
101
+
102
+ // Mock de getCurrentInstance pour injecter notre router
103
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
104
+ vi.spyOn(require('vue'), 'getCurrentInstance').mockReturnValue({
105
+ appContext: {
106
+ app: {
107
+ config: {
108
+ globalProperties: {
109
+ $router: mockRouter,
110
+ },
111
+ },
112
+ },
113
+ },
114
+ })
115
+
116
+ mount(SkipLink)
117
+ await new Promise(resolve => setTimeout(resolve, 0))
118
+
119
+ expect(focusSpy).not.toHaveBeenCalled()
120
+ })
121
+
122
+ it('ne déplace pas le focus si le chemin reste identique', async () => {
123
+ // Mock du router avec un hook afterEach où to.path = from.path
124
+ const mockRouter = {
125
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- This is a generic type
126
+ afterEach: (callback: any) => {
127
+ // Simuler un appel avec les mêmes chemins
128
+ callback(
129
+ { path: '/same-path' },
130
+ { path: '/same-path' },
131
+ false,
132
+ )
133
+ },
134
+ }
135
+
136
+ // Espionner querySelector et focus
137
+ const linkElement = document.createElement('a')
138
+ const focusSpy = vi.spyOn(linkElement, 'focus')
139
+ vi.spyOn(document, 'querySelector').mockImplementation(() => linkElement)
140
+
141
+ // Mock de getCurrentInstance
142
+ /* eslint-disable */
143
+ vi.spyOn(require('vue'), 'getCurrentInstance').mockReturnValue({
144
+ appContext: {
145
+ app: {
146
+ config: {
147
+ globalProperties: {
148
+ $router: mockRouter,
149
+ },
150
+ },
151
+ },
152
+ },
153
+ })
154
+
155
+ mount(SkipLink)
156
+ await new Promise(resolve => setTimeout(resolve, 0))
43
157
 
44
- expect(spy).toHaveBeenCalled()
158
+ expect(focusSpy).not.toHaveBeenCalled()
45
159
  })
46
160
  })
@@ -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/SyAlert.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/4010" target="_blank" style="color:#0C41BD;">issue #4010</a>)</p>
165
+ </div>
162
166
  `,
163
167
  }
164
168
  },
@@ -25,12 +25,8 @@ const showAlert = ref(true);
25
25
  </script>
26
26
 
27
27
  <template>
28
- <div class="d-flex flex-wrap align-center justify-center">
29
- <SyAlert v-model="showAlert" type="warning" variant="tonal" :closable="true">
30
- <template #default>This is a warning alert</template>
31
- </SyAlert>
32
-
33
- <VBtn v-if="!showAlert" color="primary" @click="showAlert = true" class="ma-6">Réinitialiser</VBtn>
34
- </div>
28
+ <SyAlert v-model="showAlert" type="warning" variant="tonal" :closable="true">
29
+ Ceci est une alerte de type "warning".
30
+ </SyAlert>
35
31
  </template>
36
32
  `} />
@@ -13,6 +13,13 @@ const meta = {
13
13
  modelValue: true,
14
14
  },
15
15
  argTypes: {
16
+ modelValue: {
17
+ control: { type: 'boolean' },
18
+ description: 'Contrôle l\'affichage de l\'alerte',
19
+ table: {
20
+ category: 'props',
21
+ },
22
+ },
16
23
  type: {
17
24
  options: ['info', 'warning', 'success', 'error'],
18
25
  control: { type: 'select' },
@@ -37,8 +44,8 @@ export const Default: Story = {
37
44
  name: 'Template',
38
45
  code: `<template>
39
46
  <div class="d-flex flex-wrap align-center justify-center">
40
- <SyAlert v-model="showAlert" type="success" variant="tonal" :closable="true">
41
- <template #default>This is a success alert</template>
47
+ <SyAlert v-model="showAlert" type="success" variant="tonal" :closable="true" style="width: 100%">
48
+ <template #default>Contenu de l'alerte</template>
42
49
  </SyAlert>
43
50
 
44
51
  <VBtn v-if="!showAlert" color="primary" @click="showAlert = true">
@@ -65,7 +72,7 @@ export const Default: Story = {
65
72
  type: 'success',
66
73
  closable: true,
67
74
  variant: 'tonal',
68
- default: 'Alert content',
75
+ default: 'Contenu de l\'alerte',
69
76
  },
70
77
  render: (args) => {
71
78
  return {
@@ -75,7 +82,7 @@ export const Default: Story = {
75
82
  },
76
83
  template: `
77
84
  <div class="d-flex flex-wrap align-center justify-center">
78
- <SyAlert v-model="args.modelValue" :type="args.type" :variant="args.variant" :closable="args.closable">
85
+ <SyAlert v-model="args.modelValue" :type="args.type" :variant="args.variant" :closable="args.closable" style="width: 100%">
79
86
  <template #default>{{ args.default }}</template>
80
87
  </SyAlert>
81
88
  <VBtn v-if="!args.modelValue" color="primary" @click="args.modelValue = true" class="ma-6">
@@ -94,8 +101,8 @@ export const Outlined: Story = {
94
101
  name: 'Template',
95
102
  code: `<template>
96
103
  <div class="d-flex flex-wrap align-center justify-center">
97
- <SyAlert v-model="showAlert" type="warning" variant="outlined" :closable="true">
98
- <template #default>This is a warning alert</template>
104
+ <SyAlert v-model="showAlert" type="warning" variant="outlined" :closable="true" style="width: 100%">
105
+ <template #default>Contenu de l'alerte</template>
99
106
  </SyAlert>
100
107
 
101
108
  <VBtn v-if="!showAlert" color="primary" @click="showAlert = true">
@@ -121,7 +128,7 @@ export const Outlined: Story = {
121
128
  type: 'warning',
122
129
  closable: true,
123
130
  variant: 'outlined',
124
- default: 'Alert content',
131
+ default: 'Contenu de l\'alerte',
125
132
  },
126
133
  render: (args) => {
127
134
  return {
@@ -131,7 +138,7 @@ export const Outlined: Story = {
131
138
  },
132
139
  template: `
133
140
  <div class="d-flex flex-wrap align-center justify-center">
134
- <SyAlert v-model="args.modelValue" :type="args.type" :variant="args.variant" :closable="args.closable">
141
+ <SyAlert v-model="args.modelValue" :type="args.type" :variant="args.variant" :closable="args.closable" style="width: 100%">
135
142
  <template #default>{{ args.default }}</template>
136
143
  </SyAlert>
137
144
  <VBtn v-if="!args.modelValue" color="primary" @click="args.modelValue = true" class="ma-6">
@@ -150,8 +157,8 @@ export const SlotIcon: Story = {
150
157
  name: 'Template',
151
158
  code: `<template>
152
159
  <div class="d-flex flex-wrap align-center justify-center">
153
- <SyAlert v-model="showAlert" type="success" variant="tonal" :closable="true">
154
- <template #default>This is a success alert</template>
160
+ <SyAlert v-model="showAlert" type="success" variant="tonal" :closable="true" style="width: 100%">
161
+ <template #default>Contenu de l'alerte</template>
155
162
  <template #icon>{{ icon }}</template>
156
163
  </SyAlert>
157
164
 
@@ -180,7 +187,7 @@ export const SlotIcon: Story = {
180
187
  type: 'success',
181
188
  closable: true,
182
189
  variant: 'tonal',
183
- default: 'Alert content',
190
+ default: 'Contenu de l\'alerte',
184
191
  icon: 'M21.1,12.5L22.5,13.91L15.97,20.5L12.5,17L13.9,15.59L15.97,17.67L21.1,12.5M10,17L13,20H3V18C3,15.79 6.58,14 11,14L12.89,14.11L10,17M11,4A4,4 0 0,1 15,8A4,4 0 0,1 11,12A4,4 0 0,1 7,8A4,4 0 0,1 11,4Z',
185
192
  },
186
193
  render: (args) => {
@@ -191,7 +198,7 @@ export const SlotIcon: Story = {
191
198
  },
192
199
  template: `
193
200
  <div class="d-flex flex-wrap align-center justify-center">
194
- <SyAlert v-model="args.modelValue" :type="args.type" :variant="args.variant" :closable="args.closable">
201
+ <SyAlert v-model="args.modelValue" :type="args.type" :variant="args.variant" :closable="args.closable" style="width: 100%">
195
202
  <template #default>{{ args.default }}</template>
196
203
  <template #icon>{{ args.icon }}</template>
197
204
  </SyAlert>
@@ -1,5 +1,5 @@
1
1
  <script setup lang="ts">
2
- import { computed } from 'vue'
2
+ import { computed, ref, onMounted, useAttrs } from 'vue'
3
3
  import { locales } from './locales'
4
4
  import {
5
5
  mdiAlertOutline,
@@ -8,6 +8,7 @@
8
8
  mdiInformationOutline,
9
9
  mdiClose,
10
10
  } from '@mdi/js'
11
+ import type { VIcon } from 'vuetify/components'
11
12
 
12
13
  const show = defineModel<boolean>({
13
14
  default: true,
@@ -23,6 +24,8 @@
23
24
  variant: 'tonal',
24
25
  })
25
26
 
27
+ const attrs = useAttrs()
28
+
26
29
  const prependIcon = computed(() => {
27
30
  return {
28
31
  info: mdiInformationOutline,
@@ -43,58 +46,71 @@
43
46
  prependIcon,
44
47
  dismissAlert,
45
48
  })
49
+
50
+ const alertIcon = ref<typeof VIcon | null>(null)
51
+ onMounted(() => {
52
+ alertIcon.value?.$el?.querySelector('svg')?.setAttribute('role', 'presentation')
53
+ })
46
54
  </script>
47
55
 
48
56
  <template>
49
- <VAlert
50
- v-model="show"
51
- :type="props.type"
52
- :closable="props.closable"
53
- :variant="props.variant"
54
- :class="`alert alert--${props.type}`"
55
- :color="props.type"
56
- :border="props.variant === 'tonal' ? 'start' : false"
57
+ <div
58
+ class="sy-alert"
59
+ role="alert"
57
60
  >
58
- <template #prepend>
59
- <VIcon
60
- class="alert-icon"
61
- size="1.5rem"
62
- >
63
- <slot name="icon">
64
- {{ prependIcon }}
65
- </slot>
66
- </VIcon>
67
- </template>
68
-
69
- <template #default>
70
- <slot />
71
- </template>
72
-
73
- <template
74
- v-if="props.closable"
75
- #close
61
+ <VAlert
62
+ v-model="show"
63
+ v-bind="attrs"
64
+ :type="props.type"
65
+ :closable="props.closable"
66
+ :variant="props.variant"
67
+ :class="`alert alert--${props.type}`"
68
+ :color="props.type"
69
+ :border="props.variant === 'tonal' ? 'start' : false"
76
70
  >
77
- <VBtn
78
- :color="props.variant === 'outlined' ? undefined : 'primary'"
79
- :ripple="false"
80
- variant="text"
81
- width="auto"
82
- height="100%"
83
- class="alert-close-btn"
84
- @click="dismissAlert"
85
- >
71
+ <template #prepend>
86
72
  <VIcon
87
- size="small"
73
+ ref="alertIcon"
74
+ class="alert-icon"
75
+ size="1.5rem"
76
+ role="presentation"
88
77
  >
89
- {{ mdiClose }}
78
+ <slot name="icon">
79
+ {{ prependIcon }}
80
+ </slot>
90
81
  </VIcon>
82
+ </template>
83
+
84
+ <template #default>
85
+ <slot />
86
+ </template>
91
87
 
92
- <span>
93
- {{ locales.close }}
94
- </span>
95
- </VBtn>
96
- </template>
97
- </VAlert>
88
+ <template
89
+ v-if="props.closable"
90
+ #close
91
+ >
92
+ <VBtn
93
+ :color="props.variant === 'outlined' ? undefined : 'primary'"
94
+ :ripple="false"
95
+ variant="text"
96
+ width="auto"
97
+ height="100%"
98
+ class="alert-close-btn"
99
+ @click="dismissAlert"
100
+ >
101
+ <VIcon
102
+ size="small"
103
+ >
104
+ {{ mdiClose }}
105
+ </VIcon>
106
+
107
+ <span>
108
+ {{ locales.close }}
109
+ </span>
110
+ </VBtn>
111
+ </template>
112
+ </VAlert>
113
+ </div>
98
114
  </template>
99
115
 
100
116
  <style lang="scss" scoped>
@@ -128,17 +144,24 @@
128
144
  .alert-close-btn {
129
145
  cursor: pointer;
130
146
  line-height: 0;
131
-
132
- .v-btn__overlay {
133
- display: none;
134
- }
135
- }
136
-
137
- .v-btn {
138
147
  text-transform: none;
139
148
  font-weight: bold;
140
149
  font-size: 0.75rem;
141
150
  letter-spacing: normal;
151
+
152
+ &:focus-visible {
153
+ outline: solid 2px black !important;
154
+ outline-color: var(--v-primary-base) !important;
155
+ outline-offset: 2px !important;
156
+
157
+ &::after {
158
+ visibility: hidden;
159
+ }
160
+ }
161
+
162
+ .v-btn__overlay {
163
+ display: none;
164
+ }
142
165
  }
143
166
 
144
167
  @media screen and (width <= 440px) {
@@ -254,4 +277,18 @@
254
277
  )
255
278
  );
256
279
  }
280
+
281
+ .v-alert.v-theme--dark {
282
+ &.v-alert--variant-outlined {
283
+ background-color: tokens.$white-base !important;
284
+ }
285
+
286
+ .alert-close-btn {
287
+ color: black !important;
288
+
289
+ &:focus-visible {
290
+ outline-color: black !important;
291
+ }
292
+ }
293
+ }
257
294
  </style>
@@ -46,7 +46,16 @@ describe('Alert', () => {
46
46
  modelValue: false,
47
47
  })
48
48
 
49
- expect(wrapper.html()).toBeFalsy()
49
+ expect(wrapper.html()).toMatchInlineSnapshot(`
50
+ <div
51
+ class="sy-alert"
52
+ message="message"
53
+ role="alert"
54
+ title="title"
55
+ >
56
+ <!---->
57
+ </div>
58
+ `)
50
59
 
51
60
  await wrapper.setProps({
52
61
  modelValue: true,
@@ -79,7 +88,16 @@ describe('Alert', () => {
79
88
 
80
89
  await closeBtn.element.click()
81
90
 
82
- expect(wrapper.html()).toBeFalsy()
91
+ expect(wrapper.html()).toMatchInlineSnapshot(`
92
+ <div
93
+ class="sy-alert"
94
+ message="message"
95
+ role="alert"
96
+ title="title"
97
+ >
98
+ <!---->
99
+ </div>
100
+ `)
83
101
  expect(wrapper.emitted('update:modelValue')![0]![0]).toBe(false)
84
102
  })
85
103