@cnamts/synapse 1.0.20 → 1.0.21

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 (135) hide show
  1. package/dist/{DateFilter-XURUmpMl.js → DateFilter-uN8OURoP.js} +1 -1
  2. package/dist/{NumberFilter-BZc0O8wV.js → NumberFilter-sm1dQNQi.js} +1 -1
  3. package/dist/{PeriodFilter-ZNdXcl3p.js → PeriodFilter-Cklsxnh9.js} +1 -1
  4. package/dist/{SelectFilter-DshYU5OK.js → SelectFilter-CWefj27Z.js} +1 -1
  5. package/dist/{TextFilter-D_c5dRPl.js → TextFilter-Ddyj885L.js} +1 -1
  6. package/dist/components/Customs/SyCheckBoxGroup/SyCheckBoxGroup.d.ts +160 -0
  7. package/dist/components/Customs/SyCheckBoxGroup/locales.d.ts +3 -0
  8. package/dist/components/Customs/SyCheckBoxGroup/types.d.ts +10 -0
  9. package/dist/components/Customs/SyCheckbox/SyCheckbox.d.ts +1545 -2
  10. package/dist/components/Customs/SyRadioGroup/SyRadioGroup.d.ts +1495 -2
  11. package/dist/components/DeclarationAccessibilityPage/DeclarationAccessibilityPage.d.ts +60 -0
  12. package/dist/components/ErrorPage/ErrorPage.d.ts +1 -12
  13. package/dist/components/ErrorPage/locales.d.ts +18 -3
  14. package/dist/components/FileUpload/FileUpload.d.ts +2 -0
  15. package/dist/components/MaintenancePage/locales.d.ts +18 -2
  16. package/dist/components/NotFoundPage/locales.d.ts +20 -4
  17. package/dist/components/StatusPage/StatusPage.d.ts +39 -0
  18. package/dist/components/UploadWorkflow/UploadWorkflow.d.ts +13 -3
  19. package/dist/components/index.d.ts +3 -0
  20. package/dist/design-system-v3.js +126 -123
  21. package/dist/design-system-v3.umd.cjs +163 -163
  22. package/dist/{main-CuI6xaPq.js → main-CWniLr0s.js} +15191 -14668
  23. package/dist/style.css +1 -1
  24. package/dist/utils/theme/index.d.ts +6 -0
  25. package/package.json +7 -4
  26. package/src/components/ContextualMenu/ContextualMenu.stories.ts +0 -3
  27. package/src/components/ContextualMenu/accessibilite/Accessibility.mdx +67 -11
  28. package/src/components/CookieBanner/CookieBanner.stories.ts +11 -20
  29. package/src/components/CookieBanner/CookieBanner.vue +20 -5
  30. package/src/components/CookieBanner/accessibilite/Accessibility.mdx +67 -11
  31. package/src/components/CookieBanner/tests/CookieBanner.spec.ts +48 -4
  32. package/src/components/Customs/SyCheckBoxGroup/SyCheckBoxGroup.mdx +32 -0
  33. package/src/components/Customs/SyCheckBoxGroup/SyCheckBoxGroup.stories.ts +856 -0
  34. package/src/components/Customs/SyCheckBoxGroup/SyCheckBoxGroup.vue +334 -0
  35. package/src/components/Customs/SyCheckBoxGroup/accessibilite/Accessibility.mdx +243 -0
  36. package/src/components/Customs/SyCheckBoxGroup/locales.ts +3 -0
  37. package/src/components/Customs/SyCheckBoxGroup/tests/SyCheckBoxGroup.a11y.spec.ts +30 -0
  38. package/src/components/Customs/SyCheckBoxGroup/tests/SyCheckBoxGroup.spec.ts +152 -0
  39. package/src/components/Customs/SyCheckBoxGroup/types.ts +10 -0
  40. package/src/components/Customs/SyCheckbox/SyCheckbox.vue +16 -27
  41. package/src/components/Customs/SyCheckbox/accessibilite/Accessibility.mdx +1 -1
  42. package/src/components/Customs/SyForm/SyForm.a11y.spec.ts +1 -1
  43. package/src/components/Customs/SyRadioGroup/SyRadioGroup.vue +16 -43
  44. package/src/components/DatePicker/CalendarMode/DatePicker.vue +35 -11
  45. package/src/components/DatePicker/ComplexDatePicker/ComplexDatePicker.stories.ts +43 -2
  46. package/src/components/DatePicker/DateTextInput/DateTextInput.vue +48 -21
  47. package/src/components/DatePicker/DateTextInput/NoCalendar.stories.ts +98 -0
  48. package/src/components/DeclarationAccessibilityPage/DeclarationAccessibilityPage.mdx +83 -0
  49. package/src/components/DeclarationAccessibilityPage/DeclarationAccessibilityPage.stories.ts +502 -0
  50. package/src/components/DeclarationAccessibilityPage/DeclarationAccessibilityPage.vue +428 -0
  51. package/src/components/DeclarationAccessibilityPage/accessibilite/Accessibility.mdx +75 -0
  52. package/src/components/DeclarationAccessibilityPage/tests/DeclarationAccessibilityPage.a11y.spec.ts +53 -0
  53. package/src/components/DeclarationAccessibilityPage/tests/DeclarationAccessibilityPage.spec.ts +59 -0
  54. package/src/components/DiacriticPicker/DiacriticPicker.vue +20 -1
  55. package/src/components/ErrorPage/ErrorPage.mdx +6 -16
  56. package/src/components/ErrorPage/ErrorPage.stories.ts +16 -87
  57. package/src/components/ErrorPage/ErrorPage.vue +38 -125
  58. package/src/components/ErrorPage/accessibilite/Accessibility.mdx +68 -6
  59. package/src/components/ErrorPage/assets/error-ap.svg +1774 -0
  60. package/src/components/ErrorPage/locales.ts +21 -3
  61. package/src/components/ErrorPage/tests/ErrorPage.a11y.spec.ts +5 -13
  62. package/src/components/ErrorPage/tests/ErrorPage.spec.ts +2 -41
  63. package/src/components/ErrorPage/tests/__snapshots__/ErrorPage.spec.ts.snap +8 -266
  64. package/src/components/FileUpload/FileUpload.vue +5 -0
  65. package/src/components/FooterBar/FooterBar.stories.ts +18 -14
  66. package/src/components/FooterBar/defaultSocialMediaLinks.ts +6 -4
  67. package/src/components/MaintenancePage/MaintenancePage.mdx +1 -1
  68. package/src/components/MaintenancePage/MaintenancePage.vue +15 -7
  69. package/src/components/MaintenancePage/accessibilite/Accessibility.mdx +61 -6
  70. package/src/components/MaintenancePage/assets/maintenance-ap.svg +1718 -0
  71. package/src/components/MaintenancePage/locales.ts +24 -3
  72. package/src/components/MaintenancePage/tests/MaintenancePage.a11y.spec.ts +75 -3
  73. package/src/components/MaintenancePage/tests/MaintenancePage.spec.ts +42 -2
  74. package/src/components/MaintenancePage/tests/__snapshots__/MaintenancePage.spec.ts.snap +3 -2
  75. package/src/components/NotFoundPage/NotFoundPage.mdx +1 -1
  76. package/src/components/NotFoundPage/NotFoundPage.stories.ts +3 -3
  77. package/src/components/NotFoundPage/NotFoundPage.vue +16 -11
  78. package/src/components/NotFoundPage/accessibilite/Accessibility.mdx +78 -6
  79. package/src/components/NotFoundPage/assets/not-found-ap.svg +2061 -0
  80. package/src/components/NotFoundPage/locales.ts +24 -4
  81. package/src/components/NotFoundPage/tests/NotFoundPage.a11y.spec.ts +168 -4
  82. package/src/components/NotFoundPage/tests/NotFoundPage.spec.ts +100 -12
  83. package/src/components/NotFoundPage/tests/__snapshots__/NotFoundPage.spec.ts.snap +2 -2
  84. package/src/components/NotificationBar/NotificationBar.mdx +2 -2
  85. package/src/components/NotificationBar/accessibilite/Accessibility.mdx +68 -8
  86. package/src/components/PageContainer/tests/PageContainer.a11y.spec.ts +14 -7
  87. package/src/components/PhoneField/PhoneField.stories.ts +46 -38
  88. package/src/components/SocialMediaLinks/DefaultSocialMediaLinks.ts +6 -4
  89. package/src/components/SocialMediaLinks/SocialMediaLinks.mdx +7 -5
  90. package/src/components/SocialMediaLinks/SocialMediaLinks.stories.ts +17 -13
  91. package/src/components/SocialMediaLinks/SocialMediaLinks.vue +9 -1
  92. package/src/components/SocialMediaLinks/accessibilite/Accessibility.mdx +63 -11
  93. package/src/components/SocialMediaLinks/tests/DefaultSocialMediaLinks.spec.ts +5 -5
  94. package/src/components/SocialMediaLinks/tests/SocialMediaLinks.a11y.spec.ts +59 -0
  95. package/src/components/SocialMediaLinks/tests/SocialMediaLinks.spec.ts +9 -7
  96. package/src/components/StatusPage/StatusPage.mdx +22 -0
  97. package/src/components/StatusPage/StatusPage.stories.ts +193 -0
  98. package/src/components/StatusPage/StatusPage.vue +145 -0
  99. package/src/components/StatusPage/accessibilite/Accessibility.mdx +81 -0
  100. package/src/components/StatusPage/tests/StatusPage.a11y.spec.ts +29 -0
  101. package/src/components/StatusPage/tests/StatusPage.spec.ts +50 -0
  102. package/src/components/StatusPage/tests/__snapshots__/StatusPage.spec.ts.snap +270 -0
  103. package/src/components/TableToolbar/TableToolbar.stories.ts +6 -6
  104. package/src/components/TableToolbar/TableToolbar.vue +1 -1
  105. package/src/components/TableToolbar/tests/__snapshots__/TableToolbar.spec.ts.snap +0 -5
  106. package/src/components/UploadWorkflow/UploadWorkflow.mdx +11 -1
  107. package/src/components/UploadWorkflow/UploadWorkflow.stories.ts +107 -3
  108. package/src/components/UploadWorkflow/UploadWorkflow.vue +35 -24
  109. package/src/components/UploadWorkflow/tests/UploadWorkflow.spec.ts +48 -0
  110. package/src/components/UploadWorkflow/tests/__snapshots__/UploadWorkflow.spec.ts.snap +9 -5
  111. package/src/components/UploadWorkflow/useFileList.ts +7 -0
  112. package/src/components/index.ts +3 -0
  113. package/src/composables/rules/tests/useFieldValidation.spec.ts +39 -3
  114. package/src/composables/rules/useFieldValidation.ts +24 -9
  115. package/src/stories/Accessibilite/KitDePreAudit/Preaudit.mdx +7 -0
  116. package/src/utils/theme/index.ts +19 -0
  117. package/src/utils/theme/tests/useThemeLocales.spec.ts +245 -0
  118. package/dist/components/MaintenancePage/index.d.ts +0 -2
  119. package/src/components/Customs/SyPagination/tests/SyPagination.a11y.spec.ts +0 -27
  120. package/src/components/Customs/SyTabs/tests/SyTabs.a11y.spec.ts +0 -51
  121. package/src/components/DataListItem/tests/DataListItem.a11y.spec.ts +0 -31
  122. package/src/components/DatePicker/CalendarMode/tests/DatePicker.a11y.spec.ts +0 -27
  123. package/src/components/DatePicker/ComplexDatePicker/tests/ComplexDatePicker.a11y.spec.ts +0 -26
  124. package/src/components/DatePicker/DateTextInput/tests/DateTextInput.a11y.spec.ts +0 -27
  125. package/src/components/DownloadBtn/tests/DownloadBtn.a11y.spec.ts +0 -26
  126. package/src/components/ExternalLinks/tests/ExternalLinks.a11y.spec.ts +0 -39
  127. package/src/components/HeaderNavigationBar/tests/HeaderNavigationBar.a11y.spec.ts +0 -45
  128. package/src/components/HeaderToolbar/tests/HeaderToolbar.a11y.spec.ts +0 -25
  129. package/src/components/LunarCalendar/tests/LunarCalendar.a11y.spec.ts +0 -31
  130. package/src/components/MaintenancePage/index.ts +0 -3
  131. package/src/components/PageContainer/Accessibilite/AccessibilityGuide.mdx +0 -0
  132. package/src/components/PaginatedTable/tests/PaginatedTable.a11y.spec.ts +0 -43
  133. package/src/components/PhoneField/tests/PhoneField.a11y.spec.ts +0 -34
  134. /package/src/components/NotFoundPage/assets/{not-found.svg → not-found-cnam.svg} +0 -0
  135. /package/src/components/PageContainer/{Accessibilite → accessibilite}/Accessibility.mdx +0 -0
@@ -1,6 +1,26 @@
1
+ import imgUrlAp from './assets/not-found-ap.svg'
2
+ import imgUrlCnam from './assets/not-found-cnam.svg'
3
+
4
+ export const SUPPORT_ID_PARAM_NAME = 'support_id'
5
+ export const supportIdMessage = 'Votre identifiant de support est'
6
+
1
7
  export const locales = {
2
- code: '404',
3
- pageTitle: 'Page non trouvée',
4
- message: 'Cette page n’existe pas ou a été déplacée.',
5
- supportIdMessage: 'Votre identifiant de support est',
8
+ default: {
9
+ code: '404',
10
+ pageTitle: 'Page non trouvée',
11
+ message: 'Cette page n’existe pas ou a été déplacée.',
12
+ src: imgUrlCnam,
13
+ },
14
+ cnam: {
15
+ code: '404',
16
+ pageTitle: 'Page non trouvée',
17
+ message: 'Cette page n’existe pas ou a été déplacée.',
18
+ src: imgUrlCnam,
19
+ },
20
+ ap: {
21
+ code: '404',
22
+ pageTitle: 'Page non trouvée ou inexistante - Erreur 404',
23
+ message: 'La page que vous essayez d’afficher n’existe plus ou a été déplacée.',
24
+ src: imgUrlAp,
25
+ },
6
26
  }
@@ -1,31 +1,195 @@
1
1
  // @vitest-environment jsdom
2
2
 
3
3
  import { describe, it } from 'vitest'
4
- import { mount } from '@vue/test-utils'
4
+ import { mount, flushPromises } from '@vue/test-utils'
5
5
  import { axe } from 'vitest-axe'
6
6
  import { assertNoA11yViolations } from '@tests/unit/accessibility/axeUtils'
7
7
  import NotFoundPage from '../NotFoundPage.vue'
8
8
  import { locales } from '../locales'
9
9
 
10
- // Scénario daccessibilité : page 404 avec code, message et identifiant de support.
10
+ // Scénario d'accessibilité : page 404 avec code, message et identifiant de support.
11
11
 
12
12
  describe('NotFoundPage – accessibility (axe)', () => {
13
+ it('has no obvious axe violations in default state', async () => {
14
+ const wrapper = mount(NotFoundPage)
15
+ await flushPromises()
16
+ await wrapper.vm.$nextTick()
17
+
18
+ const results = await axe(wrapper.element as HTMLElement)
19
+ assertNoA11yViolations(results, 'NotFoundPage – default state', {
20
+ ignoreRules: ['region'],
21
+ })
22
+ })
23
+
13
24
  it('has no obvious axe violations with support ID in URL', async () => {
14
25
  // Simuler une URL contenant un identifiant de support
15
26
  history.replaceState({}, '', `/not-found?support_id=1234567890123456789`)
16
27
 
17
28
  const wrapper = mount(NotFoundPage)
29
+ await flushPromises()
18
30
  await wrapper.vm.$nextTick()
19
31
 
20
- // Sassurer que le contenu principal est bien rendu
32
+ // S'assurer que le contenu principal est bien rendu
21
33
  const text = wrapper.text()
22
- if (!text.includes(locales.code) || !text.includes(locales.message)) {
34
+ if (!text.includes(locales.default.code) || !text.includes(locales.default.message)) {
23
35
  throw new Error('NotFoundPage main content not rendered as expected')
24
36
  }
25
37
 
38
+ // Vérifier que le support ID est affiché avec un formatage espacé
39
+ if (!text.includes('1234 5678 9012 3456 789')) {
40
+ throw new Error('Support ID not formatted correctly')
41
+ }
42
+
26
43
  const results = await axe(wrapper.element as HTMLElement)
27
44
  assertNoA11yViolations(results, 'NotFoundPage – with support ID', {
28
45
  ignoreRules: ['region'],
29
46
  })
30
47
  })
48
+
49
+ it('has no obvious axe violations with custom button text and href', async () => {
50
+ const wrapper = mount(NotFoundPage, {
51
+ props: {
52
+ btnText: 'Retour à l\'accueil',
53
+ btnHref: 'https://example.com',
54
+ },
55
+ })
56
+ await flushPromises()
57
+ await wrapper.vm.$nextTick()
58
+
59
+ const results = await axe(wrapper.element as HTMLElement)
60
+ assertNoA11yViolations(results, 'NotFoundPage – with custom button props', {
61
+ ignoreRules: ['region'],
62
+ })
63
+ })
64
+
65
+ it('has no obvious axe violations when button is hidden', async () => {
66
+ const wrapper = mount(NotFoundPage, {
67
+ props: {
68
+ hideBtn: true,
69
+ },
70
+ })
71
+ await flushPromises()
72
+ await wrapper.vm.$nextTick()
73
+
74
+ const results = await axe(wrapper.element as HTMLElement)
75
+ assertNoA11yViolations(results, 'NotFoundPage – button hidden', {
76
+ ignoreRules: ['region'],
77
+ })
78
+ })
79
+
80
+ it('has no obvious axe violations with router link navigation', async () => {
81
+ const wrapper = mount(NotFoundPage, {
82
+ props: {
83
+ btnText: 'Retour',
84
+ btnLink: '/home',
85
+ },
86
+ global: {
87
+ stubs: {
88
+ RouterLink: true,
89
+ },
90
+ },
91
+ })
92
+ await flushPromises()
93
+ await wrapper.vm.$nextTick()
94
+
95
+ const results = await axe(wrapper.element as HTMLElement)
96
+ assertNoA11yViolations(results, 'NotFoundPage – with router link', {
97
+ ignoreRules: ['region'],
98
+ })
99
+ })
100
+
101
+ it('has proper semantic structure for 404 error page', async () => {
102
+ const wrapper = mount(NotFoundPage)
103
+ await flushPromises()
104
+ await wrapper.vm.$nextTick()
105
+
106
+ const text = wrapper.text()
107
+ // Vérifier que le code 404 est présent
108
+ if (!text.includes('404')) {
109
+ throw new Error('404 code not found in page content')
110
+ }
111
+
112
+ // Vérifier que le message est présent
113
+ if (!text.includes('Page non trouvée')) {
114
+ throw new Error('Page title not found in page content')
115
+ }
116
+
117
+ const results = await axe(wrapper.element as HTMLElement)
118
+ assertNoA11yViolations(results, 'NotFoundPage – semantic structure', {
119
+ ignoreRules: ['region'],
120
+ })
121
+ })
122
+
123
+ it('has no obvious axe violations with illustration slot', async () => {
124
+ const wrapper = mount(NotFoundPage, {
125
+ slots: {
126
+ illustration: '<img src="/custom-404.svg" alt="" aria-hidden="true" />',
127
+ },
128
+ })
129
+ await flushPromises()
130
+ await wrapper.vm.$nextTick()
131
+
132
+ const img = wrapper.find('img')
133
+ if (img.exists() && img.attributes('alt') !== '') {
134
+ throw new Error('Illustration image should have empty alt for decorative purpose')
135
+ }
136
+
137
+ const results = await axe(wrapper.element as HTMLElement)
138
+ assertNoA11yViolations(results, 'NotFoundPage – custom illustration', {
139
+ ignoreRules: ['region'],
140
+ })
141
+ })
142
+
143
+ it('has correct ARIA attributes on illustration image', async () => {
144
+ const wrapper = mount(NotFoundPage)
145
+ await flushPromises()
146
+ await wrapper.vm.$nextTick()
147
+
148
+ const img = wrapper.find('img')
149
+ if (img.exists()) {
150
+ // L'image illustrative doit être marquée comme décoration
151
+ if (img.attributes('aria-hidden') !== 'true') {
152
+ throw new Error('Illustration should have aria-hidden="true"')
153
+ }
154
+
155
+ // L'alt doit être vide pour les images décoratives
156
+ if (img.attributes('alt') !== '') {
157
+ throw new Error('Decorative image should have empty alt text')
158
+ }
159
+ }
160
+
161
+ const results = await axe(wrapper.element as HTMLElement)
162
+ assertNoA11yViolations(results, 'NotFoundPage – ARIA attributes on image', {
163
+ ignoreRules: ['region'],
164
+ })
165
+ })
166
+
167
+ it('has no obvious axe violations with support ID containing spaces already', async () => {
168
+ // Tester avec un ID qui contient déjà des espacements
169
+ history.replaceState({}, '', `/not-found?support_id= ABCD EFGH IJKL `)
170
+
171
+ const wrapper = mount(NotFoundPage)
172
+ await flushPromises()
173
+ await wrapper.vm.$nextTick()
174
+
175
+ const results = await axe(wrapper.element as HTMLElement)
176
+ assertNoA11yViolations(results, 'NotFoundPage – support ID with spaces', {
177
+ ignoreRules: ['region'],
178
+ })
179
+ })
180
+
181
+ it('has no obvious axe violations with long support ID', async () => {
182
+ // Tester avec un ID très long
183
+ const longId = '12345678901234567890123456789012'
184
+ history.replaceState({}, '', `/not-found?support_id=${longId}`)
185
+
186
+ const wrapper = mount(NotFoundPage)
187
+ await flushPromises()
188
+ await wrapper.vm.$nextTick()
189
+
190
+ const results = await axe(wrapper.element as HTMLElement)
191
+ assertNoA11yViolations(results, 'NotFoundPage – long support ID', {
192
+ ignoreRules: ['region'],
193
+ })
194
+ })
31
195
  })
@@ -1,28 +1,116 @@
1
- import { describe, it, expect } from 'vitest'
2
- import { mount } from '@vue/test-utils'
3
- import { locales } from '../locales'
1
+ import { describe, it, expect, vi, beforeEach } from 'vitest'
2
+ import { mount, flushPromises } from '@vue/test-utils'
4
3
  import NotFoundPage from '../NotFoundPage.vue'
4
+ import StatusPage from '../../StatusPage/StatusPage.vue'
5
+
6
+ vi.mock('@/utils/theme', () => ({
7
+ useThemeLocales: () => ({
8
+ themeLocales: {
9
+ code: '404',
10
+ pageTitle: 'Page non trouvée',
11
+ message: 'Cette page n\'existe pas ou a été déplacée.',
12
+ src: '/src/components/NotFoundPage/assets/not-found.svg',
13
+ },
14
+ }),
15
+ }))
5
16
 
6
17
  describe('NotFoundPage', () => {
7
- it('renders correctly', () => {
18
+ beforeEach(() => {
19
+ history.replaceState({}, '', '/')
20
+ })
21
+
22
+ it('renders correctly', async () => {
8
23
  const wrapper = mount(NotFoundPage)
24
+ await flushPromises()
25
+ await wrapper.vm.$nextTick()
9
26
 
10
- expect(wrapper.text()).toContain(locales.code)
11
- expect(wrapper.text()).toContain(locales.message)
27
+ expect(wrapper.findComponent(StatusPage).exists()).toBe(true)
28
+ expect(wrapper.text()).toContain('404')
29
+ expect(wrapper.text()).toContain('Page non trouvée')
12
30
  expect(wrapper.html()).toMatchSnapshot()
13
31
  })
14
32
 
15
33
  it('display the support ID if provided in the url', async () => {
16
- history.replaceState(
17
- {},
18
- '',
19
- '/not-found?support_id=1234567890123456789',
20
- )
21
- const wrapper = mount(NotFoundPage)
34
+ history.replaceState({}, '', '/not-found?support_id=1234567890123456789')
22
35
 
36
+ const wrapper = mount(NotFoundPage)
37
+ await flushPromises()
23
38
  await wrapper.vm.$nextTick()
24
39
 
40
+ expect(wrapper.text()).toContain('Votre identifiant de support est')
25
41
  expect(wrapper.text()).toContain('1234 5678 9012 3456 789')
26
42
  expect(wrapper.html()).toMatchSnapshot()
27
43
  })
44
+
45
+ it('does not display support ID section when no support_id in url', async () => {
46
+ const wrapper = mount(NotFoundPage)
47
+ await flushPromises()
48
+ await wrapper.vm.$nextTick()
49
+
50
+ expect(wrapper.text()).not.toContain('Votre identifiant de support est')
51
+ })
52
+
53
+ it('renders StatusPage with correct props', async () => {
54
+ const wrapper = mount(NotFoundPage)
55
+ await flushPromises()
56
+ await wrapper.vm.$nextTick()
57
+
58
+ const statusPage = wrapper.findComponent(StatusPage)
59
+ expect(statusPage.props('code')).toBe('404')
60
+ expect(statusPage.props('pageTitle')).toBe('Page non trouvée')
61
+ expect(statusPage.props('message')).toBe('Cette page n\'existe pas ou a été déplacée.')
62
+ })
63
+
64
+ it('renders with custom button props', async () => {
65
+ const wrapper = mount(NotFoundPage, {
66
+ props: {
67
+ btnText: 'Retour à l\'accueil',
68
+ btnHref: '/',
69
+ },
70
+ })
71
+ await flushPromises()
72
+ await wrapper.vm.$nextTick()
73
+
74
+ const statusPage = wrapper.findComponent(StatusPage)
75
+ expect(statusPage.props('btnText')).toBe('Retour à l\'accueil')
76
+ expect(statusPage.props('btnHref')).toBe('/')
77
+ })
78
+
79
+ it('hides button when hideBtn prop is true', async () => {
80
+ const wrapper = mount(NotFoundPage, {
81
+ props: {
82
+ hideBtn: true,
83
+ },
84
+ })
85
+ await flushPromises()
86
+ await wrapper.vm.$nextTick()
87
+
88
+ expect(wrapper.findComponent(StatusPage).props('hideBtn')).toBe(true)
89
+ })
90
+
91
+ it('renders illustration with correct accessibility attributes', async () => {
92
+ const wrapper = mount(NotFoundPage)
93
+ await flushPromises()
94
+ await wrapper.vm.$nextTick()
95
+
96
+ const img = wrapper.find('img')
97
+ expect(img.exists()).toBe(true)
98
+ expect(img.attributes('src')).toBe('/src/components/NotFoundPage/assets/not-found.svg')
99
+ expect(img.attributes('alt')).toBe('')
100
+ expect(img.attributes('aria-hidden')).toBe('true')
101
+ })
102
+
103
+ it('renders custom illustration slot', async () => {
104
+ const wrapper = mount(NotFoundPage, {
105
+ slots: {
106
+ illustration: '<img src="/custom.svg" alt="" aria-hidden="true" />',
107
+ },
108
+ })
109
+ await flushPromises()
110
+ await wrapper.vm.$nextTick()
111
+
112
+ const img = wrapper.find('img')
113
+ expect(img.exists()).toBe(true)
114
+ expect(img.attributes('src')).toBe('/custom.svg')
115
+ })
28
116
  })
@@ -113,7 +113,7 @@ exports[`NotFoundPage > display the support ID if provided in the url 1`] = `
113
113
  </h1>
114
114
  <p>
115
115
  <span>
116
- Cette page nexiste pas ou a été déplacée.
116
+ Cette page n'existe pas ou a été déplacée.
117
117
  </span>
118
118
  </p>
119
119
  <p class="mt-4">
@@ -260,7 +260,7 @@ exports[`NotFoundPage > renders correctly 1`] = `
260
260
  </h1>
261
261
  <p>
262
262
  <span>
263
- Cette page nexiste pas ou a été déplacée.
263
+ Cette page n'existe pas ou a été déplacée.
264
264
  </span>
265
265
  </p>
266
266
  <!-- v-if -->
@@ -5,8 +5,8 @@ import * as NotificationBarStories from './NotificationBar.stories';
5
5
  <Meta of={NotificationBarStories} />
6
6
 
7
7
  <div className="header">
8
- <h1>Accessibilité du composant SyIcon</h1>
9
- <p>Le composant `SyIcon` est un wrapper autour du composant `v-icon` de Vuetify qui ajoute une gestion automatique de l'accessibilité selon les normes RGAA.</p>
8
+ <h1>Accessibilité du composant NotificationBar </h1>
9
+ <p>Le composant `NotificationBar` est un wrapper autour du composant `v-icon` de Vuetify qui ajoute une gestion automatique de l'accessibilité selon les normes RGAA.</p>
10
10
  </div>
11
11
 
12
12
  ##### Les NotificationsBar servent à notifier l’utilisateur soit d’un retour d’action.
@@ -1,15 +1,75 @@
1
- import { Meta, Story } from '@storybook/addon-docs';
1
+ import { Meta, Primary } from '@storybook/blocks';
2
2
  import * as NotificationBarStories from '../NotificationBar.stories.ts';
3
- import '@/stories/styles/shared.css';
3
+ import AccessibilityIcon from '@/common/imgs/accessibility-svgrepo-com.svg';
4
+ import {
5
+ AccessibilityGuideLayout,
6
+ CriteriaSection,
7
+ CriteriaCard,
8
+ DemoSection,
9
+ BestPracticesSection,
10
+ ResourcesSection,
11
+ } from '@/stories/accessibility/AccessibilityGuideLayout.mdx';
4
12
 
5
13
  <Meta of={NotificationBarStories} />
6
14
 
7
- <div className="header">
8
- <h1>Accessibilité</h1>
9
- </div>
10
15
 
11
-
12
- <div class="mt-2">
13
- <p>Rapport d’audit manuel : <a href="/audits/NotificationBar.xlsx" style={{ color:'#0C41BD' }}>Voir le rapport</a></p>
16
+ <AccessibilityGuideLayout
17
+ componentName="NotificationBar"
18
+ iconSrc={AccessibilityIcon}
19
+ >
20
+ <div class="mt-8">
21
+ <p>Rapport d’audit manuel : <a href="/audits/NotificationBar.xlsx" style={{ color:'#0C41BD' }}>Voir le rapport</a></p>
14
22
  <p style={{ color: 'grey', fontSize: '14px', marginTop: '0px' }}>Correctifs associés (<a href="https://github.com/assurance-maladie-digital/design-system-v3/issues/4029" target="_blank" style={{color:'#0C41BD'}}>issue #4029</a>)</p>
15
23
  </div>
24
+ <CriteriaSection>
25
+ <CriteriaCard icon="🔔" title="Annonce et rôles adaptés">
26
+ <ul>
27
+ <li><strong>Rôle dynamique</strong> : <code>role="status"</code> pour les notifications neutres et <code>role="alert"</code> pour les erreurs afin d’informer immédiatement les lecteurs d’écran.</li>
28
+ <li><strong>Icônes décoratives</strong> : les pictogrammes sont marqués comme décoratifs pour ne pas être annoncés inutilement.</li>
29
+ </ul>
30
+ </CriteriaCard>
31
+
32
+ <CriteriaCard icon="⌨️" title="Navigation clavier complète">
33
+ <ul>
34
+ <li><strong>Fermeture clavier</strong> : le bouton « Fermer » est focalisable, activable au clavier et accessible avec <kbd>Échap</kbd>.</li>
35
+ <li><strong>Focus visible</strong> : styles d’outline renforcés sur le bouton de fermeture pour chaque état de contraste.</li>
36
+ </ul>
37
+ </CriteriaCard>
38
+
39
+ <CriteriaCard icon="🕒" title="Temps de lecture maîtrisé">
40
+ <ul>
41
+ <li><strong>Pas d’expiration forcée</strong> : par défaut <code>timeout=-1</code> pour laisser le temps de lecture ; la temporisation reste configurable.</li>
42
+ <li><strong>File ou liste</strong> : <code>show-all</code> permet d’afficher toutes les notifications simultanément pour éviter la perte d’information.</li>
43
+ </ul>
44
+ </CriteriaCard>
45
+
46
+ <CriteriaCard icon="🖼️" title="Lisibilité et contraste">
47
+ <ul>
48
+ <li><strong>Contrastes définis</strong> : couleurs d’arrière‑plan et de texte prévues pour chaque type (info, success, warning, error).</li>
49
+ <li><strong>Adaptation mobile</strong> : réorganisation verticale automatique lorsque le message est long sur petits écrans.</li>
50
+ </ul>
51
+ </CriteriaCard>
52
+ </CriteriaSection>
53
+
54
+ <DemoSection componentName="NotificationBar">
55
+ <Primary />
56
+ </DemoSection>
57
+
58
+ <BestPracticesSection>
59
+ <ul>
60
+ <li>Privilégiez l’option <code>timeout=-1</code> pour laisser le temps de lecture, surtout pour les messages critiques.</li>
61
+ <li>Utilisez <code>type="error"</code> uniquement pour les situations bloquantes afin d’éviter la surcharge d’alertes.</li>
62
+ <li>Ajoutez un libellé explicite au bouton d’action (slot <code>action</code>) et assurez-vous qu’il soit atteignable au clavier.</li>
63
+ <li>Évitez d’empiler trop de notifications : activez <code>show-all</code> en cas de rafale pour limiter la perte contextuelle.</li>
64
+ <li>Ne déplacez pas le focus automatiquement sur la notification : laissez l’utilisateur garder son contexte d’interaction.</li>
65
+ </ul>
66
+ </BestPracticesSection>
67
+
68
+ <ResourcesSection>
69
+ <ul>
70
+ <li><a href="https://www.w3.org/WAI/ARIA/apg/patterns/alert/" target="_blank" rel="noopener noreferrer">WAI-ARIA Authoring Practices – Alert</a></li>
71
+ <li><a href="https://www.w3.org/WAI/ARIA/apg/patterns/status/" target="_blank" rel="noopener noreferrer">WAI-ARIA Authoring Practices – Status</a></li>
72
+ <li><a href="https://www.w3.org/WAI/WCAG21/quickref/#timing-adjustable" target="_blank" rel="noopener noreferrer">WCAG 2.1 – Timing Adjustable</a></li>
73
+ </ul>
74
+ </ResourcesSection>
75
+ </AccessibilityGuideLayout>
@@ -1,6 +1,6 @@
1
1
  // @vitest-environment jsdom
2
2
 
3
- import { describe, it, expect } from 'vitest'
3
+ import { describe, it, expect, afterEach } from 'vitest'
4
4
  import { mount } from '@vue/test-utils'
5
5
  import { axe } from 'vitest-axe'
6
6
  import { assertNoA11yViolations } from '@tests/unit/accessibility/axeUtils'
@@ -9,6 +9,10 @@ import PageContainer from '../PageContainer.vue'
9
9
  // Scénario d’accessibilité : conteneur de page enveloppant un contenu principal.
10
10
 
11
11
  describe('PageContainer – accessibility (axe)', () => {
12
+ afterEach(() => {
13
+ document.body.innerHTML = ''
14
+ })
15
+
12
16
  it('has no obvious axe violations with main content slot', async () => {
13
17
  const wrapper = mount(PageContainer, {
14
18
  slots: {
@@ -113,7 +117,7 @@ describe('PageContainer – accessibility (axe)', () => {
113
117
  role: 'navigation',
114
118
  },
115
119
  slots: {
116
- default: '<nav><ul><li><a href="#home">Accueil</a></li><li><a href="#about">À propos</a></li></ul></nav>',
120
+ default: '<ul><li><a href="#home">Accueil</a></li><li><a href="#about">À propos</a></li></ul>',
117
121
  },
118
122
  attachTo: document.body,
119
123
  })
@@ -154,7 +158,7 @@ describe('PageContainer – accessibility (axe)', () => {
154
158
  role: 'contentinfo',
155
159
  },
156
160
  slots: {
157
- default: '<footer><p>&copy; 2026 - Tous droits réservés</p></footer>',
161
+ default: '<div><p>&copy; 2026 - Tous droits réservés</p></div>',
158
162
  },
159
163
  attachTo: document.body,
160
164
  })
@@ -175,7 +179,7 @@ describe('PageContainer – accessibility (axe)', () => {
175
179
  role: 'banner',
176
180
  },
177
181
  slots: {
178
- default: '<header><h1>Logo</h1></header>',
182
+ default: '<div><h1>Logo</h1></div>',
179
183
  },
180
184
  attachTo: document.body,
181
185
  })
@@ -286,7 +290,7 @@ describe('PageContainer – accessibility (axe)', () => {
286
290
  uniqueId: 'footer',
287
291
  },
288
292
  slots: {
289
- default: '<footer><p>Informations de pied de page</p></footer>',
293
+ default: '<div><p>Informations de pied de page</p></div>',
290
294
  },
291
295
  attachTo: document.body,
292
296
  })
@@ -310,7 +314,7 @@ describe('PageContainer – accessibility (axe)', () => {
310
314
  uniqueId: 'header',
311
315
  },
312
316
  slots: {
313
- default: '<header><h1>En-tête</h1></header>',
317
+ default: '<div><h1>En-tête</h1></div>',
314
318
  },
315
319
  attachTo: document.body,
316
320
  })
@@ -461,9 +465,12 @@ describe('PageContainer – accessibility (axe)', () => {
461
465
  it('has proper color contrast with different background colors', async () => {
462
466
  const wrapper = mount(PageContainer, {
463
467
  props: {
464
- role: 'main',
468
+ role: 'region',
465
469
  color: 'surface',
466
470
  },
471
+ attrs: {
472
+ 'aria-label': 'Bloc coloré',
473
+ },
467
474
  slots: {
468
475
  default: '<h1>Texte contrasté</h1><p>Paragraphe avec contraste de couleur.</p>',
469
476
  },