@cnamts/synapse 1.0.22 → 1.0.23

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 (190) hide show
  1. package/dist/{DateFilter-B5n-ZkLi.js → DateFilter-Dc-gSGwk.js} +1 -1
  2. package/dist/{NumberFilter-CtiZ9uj8.js → NumberFilter-vP38Wp6j.js} +1 -1
  3. package/dist/{PeriodFilter-DzqiMb-b.js → PeriodFilter-Ba1uYUnT.js} +1 -1
  4. package/dist/{SelectFilter-BOYlF7rX.js → SelectFilter-BioGT6Nn.js} +1 -1
  5. package/dist/{TextFilter-BOFRNfcX.js → TextFilter-B84dpnoq.js} +1 -1
  6. package/dist/components/Accordion/Accordion.d.ts +13 -2
  7. package/dist/components/Accordion/composables/useAccordionState.d.ts +2 -1
  8. package/dist/components/Amelipro/AmeliproAutoCompleteField/AmeliproAutoCompleteField.d.ts +7 -7
  9. package/dist/components/Amelipro/AmeliproCheckbox/AmeliproCheckbox.d.ts +1 -1
  10. package/dist/components/Amelipro/AmeliproCustomSelector/AmeliproCustomSelector.d.ts +1 -1
  11. package/dist/components/Amelipro/AmeliproPostalAddressField/AmeliproPostalAddressField.d.ts +1 -1
  12. package/dist/components/Amelipro/AmeliproSelect/AmeliproSelect.d.ts +7 -7
  13. package/dist/components/Amelipro/AmeliproTabs/AmeliproTabs.d.ts +16 -16
  14. package/dist/components/Amelipro/AmeliproTextArea/AmeliproTextArea.d.ts +1 -1
  15. package/dist/components/Amelipro/AmeliproTextField/AmeliproTextField.d.ts +1 -1
  16. package/dist/components/Customs/Selects/SyAutocomplete/SyAutocomplete.d.ts +22 -1
  17. package/dist/components/Customs/Selects/SyAutocomplete/locales.d.ts +5 -0
  18. package/dist/components/Customs/Selects/SyInputSelect/SyInputSelect.d.ts +1 -1
  19. package/dist/components/Customs/Selects/SySelect/SySelect.d.ts +1 -1
  20. package/dist/components/Customs/Selects/SySelect/locales.d.ts +1 -0
  21. package/dist/components/Customs/SyCheckBoxGroup/SyCheckBoxGroup.d.ts +1 -1
  22. package/dist/components/Customs/SyCheckbox/SyCheckbox.d.ts +1 -1
  23. package/dist/components/Customs/SyRadioGroup/SyRadioGroup.d.ts +1 -1
  24. package/dist/components/Customs/SyTextField/SyTextField.d.ts +5 -2
  25. package/dist/components/DatePicker/CalendarMode/DatePicker.d.ts +13 -9
  26. package/dist/components/DatePicker/ComplexDatePicker/ComplexDatePicker.d.ts +7 -5
  27. package/dist/components/DatePicker/DateTextInput/DateTextInput.d.ts +2 -1
  28. package/dist/components/ErrorPage/ErrorPage.d.ts +3 -1
  29. package/dist/components/FileList/UploadItem/UploadItem.d.ts +6 -0
  30. package/dist/components/FileList/UploadItem/locales.d.ts +1 -4
  31. package/dist/components/FileUpload/FileUploadContent.d.ts +2 -0
  32. package/dist/components/FileUpload/validateFiles.d.ts +2 -1
  33. package/dist/components/HeaderBar/HeaderBar.d.ts +2 -1
  34. package/dist/components/HeaderBar/HeaderLogo/HeaderLogo.d.ts +2 -1
  35. package/dist/components/HeaderNavigationBar/HeaderNavigationBar.d.ts +2 -1
  36. package/dist/components/MonthPicker/MonthPicker.d.ts +1939 -0
  37. package/dist/components/MonthPicker/MonthPickerText/MonthPickerInput.d.ts +1899 -0
  38. package/dist/components/MonthPicker/MonthPickerText/useTextField.d.ts +21 -0
  39. package/dist/components/MonthPicker/MonthPickerVisual/MonthPickerVisual.d.ts +21 -0
  40. package/dist/components/MonthPicker/MonthPickerVisual/MonthPickerVisualProps.d.ts +12 -0
  41. package/dist/components/MonthPicker/MonthPickerVisual/MonthSelector.d.ts +11 -0
  42. package/dist/components/MonthPicker/MonthPickerVisual/VisualPickerFooter.d.ts +6 -0
  43. package/dist/components/MonthPicker/MonthPickerVisual/VisualPickerHeader.d.ts +14 -0
  44. package/dist/components/MonthPicker/MonthPickerVisual/YearSelector.d.ts +14 -0
  45. package/dist/components/MonthPicker/MonthPickerVisual/useMonthGrid.d.ts +9 -0
  46. package/dist/components/MonthPicker/MonthPickerVisual/useYearGrid.d.ts +8 -0
  47. package/dist/components/MonthPicker/MonthPickerVisual/utils.d.ts +8 -0
  48. package/dist/components/MonthPicker/locales.d.ts +12 -0
  49. package/dist/components/MonthPicker/useMonthPickerValidation.d.ts +25 -0
  50. package/dist/components/NirField/NirField.d.ts +3 -1
  51. package/dist/components/NotificationBar/Notification/Notification.d.ts +3 -0
  52. package/dist/components/PasswordField/PasswordField.d.ts +1 -1
  53. package/dist/components/PeriodField/PeriodField.d.ts +29 -21
  54. package/dist/components/PhoneField/PhoneField.d.ts +2 -1
  55. package/dist/components/SyBtnMenu/SyBtnMenu.d.ts +1 -1
  56. package/dist/components/SyHeading/SyHeading.a11y.test.d.ts +1 -0
  57. package/dist/components/SyHeading/SyHeading.d.ts +4 -2
  58. package/dist/components/SyHeading/SyHeading.test.d.ts +1 -0
  59. package/dist/components/SyTextArea/SyTextArea.d.ts +1 -1
  60. package/dist/components/Tables/SyServerTable/SyServerTable.d.ts +4 -4
  61. package/dist/components/Tables/SyTable/SyTable.d.ts +4 -4
  62. package/dist/components/Tables/common/SyTablePagination.d.ts +6 -6
  63. package/dist/components/index.d.ts +1 -0
  64. package/dist/design-system-v3.js +102 -99
  65. package/dist/design-system-v3.umd.cjs +126 -126
  66. package/dist/designTokens/tokens/cnam/cnamContextual.d.ts +5 -0
  67. package/dist/{main-CEl4J8_T.js → main-aLKwdMi1.js} +11167 -10522
  68. package/dist/main.d.ts +1 -0
  69. package/dist/style.css +1 -1
  70. package/package.json +10 -4
  71. package/src/assets/apTokens.scss +2 -2
  72. package/src/assets/overrides/_btns.scss +8 -0
  73. package/src/assets/overrides/_forms.scss +9 -0
  74. package/src/assets/overrides/_icons.scss +38 -9
  75. package/src/assets/overrides/_tables.scss +19 -0
  76. package/src/components/Accordion/Accordion.mdx +23 -9
  77. package/src/components/Accordion/Accordion.stories.ts +153 -3
  78. package/src/components/Accordion/Accordion.vue +7 -6
  79. package/src/components/Accordion/composables/__tests__/useAccordionState.spec.ts +40 -12
  80. package/src/components/Accordion/composables/useAccordionState.ts +3 -4
  81. package/src/components/Accordion/tests/accordion.spec.ts +131 -19
  82. package/src/components/Amelipro/AmeliproPagination/AmeliproPagination.mdx +3 -1
  83. package/src/components/Amelipro/AmeliproPagination/AmeliproPagination.stories.ts +8 -0
  84. package/src/components/BackBtn/accessibilite/Accessibility.mdx +62 -10
  85. package/src/components/BackToTopBtn/BackToTopBtn.stories.ts +9 -3
  86. package/src/components/BackToTopBtn/accessibilite/Accessibility.mdx +86 -6
  87. package/src/components/Captcha/tests/Captcha.spec.ts +0 -29
  88. package/src/components/Captcha/tests/__snapshots__/Captcha.spec.ts.snap +2 -110
  89. package/src/components/Customs/Selects/SelectBtnField/accessibilite/Accessibility.mdx +133 -10
  90. package/src/components/Customs/Selects/SyAutocomplete/SyAutocomplete.stories.ts +379 -93
  91. package/src/components/Customs/Selects/SyAutocomplete/SyAutocomplete.vue +144 -83
  92. package/src/components/Customs/Selects/SyAutocomplete/accessibilite/Accessibilite.stories.ts +40 -1
  93. package/src/components/Customs/Selects/SyAutocomplete/accessibilite/Accessibility.mdx +7 -1
  94. package/src/components/Customs/Selects/SyAutocomplete/locales.ts +5 -0
  95. package/src/components/Customs/Selects/SyAutocomplete/tests/SyAutocomplete.a11y.spec.ts +96 -0
  96. package/src/components/Customs/Selects/SyAutocomplete/tests/SyAutocomplete.spec.ts +234 -9
  97. package/src/components/Customs/Selects/SyAutocomplete/utils/ariaManager.ts +13 -3
  98. package/src/components/Customs/Selects/SyAutocomplete/utils/useSelectionLogic.ts +9 -10
  99. package/src/components/Customs/Selects/SySelect/SySelect.vue +46 -3
  100. package/src/components/Customs/Selects/SySelect/locales.ts +1 -0
  101. package/src/components/Customs/SyIcon/SyIcon.vue +1 -1
  102. package/src/components/Customs/SyIcon/tests/SyIcon.a11y.spec.ts +20 -0
  103. package/src/components/Customs/SyIconButton/SyIconButton.mdx +46 -0
  104. package/src/components/Customs/SyIconButton/SyIconButton.stories.ts +184 -0
  105. package/src/components/Customs/SyIconButton/SyIconButton.vue +38 -0
  106. package/src/components/Customs/SyIconButton/accessibilite/Accessibility.mdx +64 -0
  107. package/src/components/Customs/SyIconButton/tests/SyIconButton.a11y.spec.ts +87 -0
  108. package/src/components/Customs/SyIconButton/tests/SyIconButton.spec.ts +152 -0
  109. package/src/components/Customs/SyIconButton/tests/__snapshots__/SyIconButton.spec.ts.snap +61 -0
  110. package/src/components/Customs/SyPagination/SyPagination.vue +5 -5
  111. package/src/components/Customs/SyTextField/SyTextField.vue +20 -2
  112. package/src/components/Customs/SyTextField/accessibilite/Accessibility.mdx +67 -9
  113. package/src/components/Customs/SyTextField/tests/SyTextField.a11y.spec.ts +15 -0
  114. package/src/components/Customs/SyTextField/tests/SyTextField.spec.ts +36 -0
  115. package/src/components/DataList/accessibilite/Accessibility.mdx +79 -11
  116. package/src/components/DataListGroup/accessibilite/Accessibility.mdx +80 -11
  117. package/src/components/DownloadBtn/tests/DownloadBtn.a11y.spec.ts +25 -0
  118. package/src/components/ErrorPage/ErrorPage.stories.ts +113 -19
  119. package/src/components/ErrorPage/ErrorPage.vue +17 -2
  120. package/src/components/ErrorPage/tests/ErrorPage.a11y.spec.ts +17 -0
  121. package/src/components/ErrorPage/tests/ErrorPage.spec.ts +21 -1
  122. package/src/components/ErrorPage/tests/__snapshots__/ErrorPage.spec.ts.snap +0 -1
  123. package/src/components/ExternalLinks/tests/ExternalLinks.a11y.spec.ts +23 -0
  124. package/src/components/FileList/FileList.stories.ts +51 -1
  125. package/src/components/FileList/UploadItem/UploadItem.vue +13 -6
  126. package/src/components/FileList/UploadItem/locales.ts +3 -12
  127. package/src/components/FileList/accessibilite/Accessibility.mdx +3 -0
  128. package/src/components/FileUpload/FileUpload.vue +2 -1
  129. package/src/components/FileUpload/FileUploadContent.vue +2 -1
  130. package/src/components/FileUpload/tests/FileUpload.spec.ts +47 -0
  131. package/src/components/FileUpload/validateFiles.ts +5 -2
  132. package/src/components/FranceConnectBtn/accessibilite/Accessibility.mdx +62 -9
  133. package/src/components/HeaderBar/HeaderBar.vue +2 -1
  134. package/src/components/HeaderBar/HeaderLogo/HeaderLogo.vue +2 -1
  135. package/src/components/HeaderNavigationBar/HeaderNavigationBar.vue +2 -1
  136. package/src/components/LunarCalendar/accessibilite/Accessibility.mdx +74 -8
  137. package/src/components/LunarCalendar/tests/LunarCalendar.a11y.spec.ts +163 -0
  138. package/src/components/MaintenancePage/MaintenancePage.vue +1 -1
  139. package/src/components/MaintenancePage/tests/MaintenancePage.spec.ts +4 -5
  140. package/src/components/MaintenancePage/tests/__snapshots__/MaintenancePage.spec.ts.snap +0 -1
  141. package/src/components/MonthPicker/MonthPicker.mdx +35 -0
  142. package/src/components/MonthPicker/MonthPicker.stories.ts +527 -0
  143. package/src/components/MonthPicker/MonthPicker.vue +79 -0
  144. package/src/components/MonthPicker/MonthPickerText/MonthPickerInput.vue +89 -0
  145. package/src/components/MonthPicker/MonthPickerText/useTextField.ts +27 -0
  146. package/src/components/MonthPicker/MonthPickerVisual/MonthPickerVisual.vue +154 -0
  147. package/src/components/MonthPicker/MonthPickerVisual/MonthPickerVisualProps.ts +13 -0
  148. package/src/components/MonthPicker/MonthPickerVisual/MonthSelector.vue +137 -0
  149. package/src/components/MonthPicker/MonthPickerVisual/VisualPickerFooter.vue +60 -0
  150. package/src/components/MonthPicker/MonthPickerVisual/VisualPickerHeader.vue +149 -0
  151. package/src/components/MonthPicker/MonthPickerVisual/YearSelector.vue +143 -0
  152. package/src/components/MonthPicker/MonthPickerVisual/useMonthGrid.ts +45 -0
  153. package/src/components/MonthPicker/MonthPickerVisual/useYearGrid.ts +45 -0
  154. package/src/components/MonthPicker/MonthPickerVisual/utils.ts +17 -0
  155. package/src/components/MonthPicker/accessibilite/Accessibility.mdx +59 -0
  156. package/src/components/MonthPicker/locales.ts +12 -0
  157. package/src/components/MonthPicker/tests/MonthPicker.a11y.spec.ts +71 -0
  158. package/src/components/MonthPicker/tests/MonthPicker.spec.ts +1248 -0
  159. package/src/components/MonthPicker/tests/__snapshots__/MonthPicker.spec.ts.snap +2545 -0
  160. package/src/components/MonthPicker/useMonthPickerValidation.ts +30 -0
  161. package/src/components/NirField/NirField.mdx +1 -2
  162. package/src/components/NirField/NirField.stories.ts +66 -6
  163. package/src/components/NotFoundPage/tests/NotFoundPage.spec.ts +2 -3
  164. package/src/components/NotFoundPage/tests/__snapshots__/NotFoundPage.spec.ts.snap +22 -14
  165. package/src/components/NotificationBar/Notification/Notification.vue +3 -1
  166. package/src/components/NotificationBar/NotificationBar.stories.ts +154 -0
  167. package/src/components/NotificationBar/tests/NotificationBar.a11y.spec.ts +26 -0
  168. package/src/components/NotificationBar/tests/NotificationBar.spec.ts +60 -0
  169. package/src/components/RangeField/accessibilite/Accessibility.mdx +79 -11
  170. package/src/components/SkipLink/tests/SkipLink.a11y.spec.ts +23 -0
  171. package/src/components/StatusPage/StatusPage.stories.ts +118 -0
  172. package/src/components/StatusPage/StatusPage.vue +5 -3
  173. package/src/components/StatusPage/tests/StatusPage.a11y.spec.ts +22 -0
  174. package/src/components/StatusPage/tests/StatusPage.spec.ts +22 -0
  175. package/src/components/StatusPage/tests/__snapshots__/StatusPage.spec.ts.snap +22 -14
  176. package/src/components/SubHeader/tests/SubHeader.a11y.spec.ts +20 -0
  177. package/src/components/SyAlert/SyAlert.vue +1 -0
  178. package/src/components/SyAlert/accessibilite/Accessibility.mdx +79 -9
  179. package/src/components/SyAlert/tests/SyAlert.a11y.spec.ts +23 -0
  180. package/src/components/SyHeading/SyHeading.a11y.test.ts +149 -0
  181. package/src/components/SyHeading/SyHeading.test.ts +115 -0
  182. package/src/components/SyHeading/SyHeading.vue +5 -3
  183. package/src/components/SyTextArea/accessibilite/Accessibility.mdx +80 -8
  184. package/src/components/SyTextArea/tests/SyTextArea.a11y.spec.ts +151 -0
  185. package/src/components/ToolbarContainer/tests/ToolbarContainer.a11y.spec.ts +126 -0
  186. package/src/components/UploadWorkflow/tests/__snapshots__/UploadWorkflow.spec.ts.snap +2 -2
  187. package/src/components/index.ts +1 -0
  188. package/src/composables/useFormFieldErrorHandling.ts +11 -2
  189. package/src/designTokens/tokens/cnam/cnamContextual.ts +6 -1
  190. package/src/main.ts +2 -0
@@ -0,0 +1,184 @@
1
+ import type { Meta, StoryObj } from '@storybook/vue3'
2
+ import { fn } from '@storybook/test'
3
+ import SyIconButton from './SyIconButton.vue'
4
+ import { mdiClose, mdiAlert, mdiMagnify } from '@mdi/js'
5
+
6
+ const meta = {
7
+ title: 'Composants/Boutons/SyIconButton',
8
+ component: SyIconButton,
9
+ decorators: [
10
+ () => ({
11
+ template: '<div style="padding: 20px;"><story/></div>',
12
+ }),
13
+ ],
14
+ argTypes: {
15
+ 'icon': {
16
+ control: { type: 'text' },
17
+ description: 'Nom de l\'icône à afficher (format Material Design Icons)',
18
+ },
19
+ 'label': {
20
+ control: { type: 'text' },
21
+ description: 'Texte accessible obligatoire porté par `aria-label` sur le bouton',
22
+ },
23
+ 'color': {
24
+ control: { type: 'text' },
25
+ description: 'Couleur de l\'icône (nom de couleur Vuetify)',
26
+ },
27
+ 'size': {
28
+ options: ['x-small', 'small', 'default', 'large', 'x-large'],
29
+ control: {
30
+ type: 'select',
31
+ },
32
+ description: 'Taille du bouton et de l\'icône',
33
+ default: 'default',
34
+ },
35
+ 'disabled': {
36
+ control: { type: 'boolean' },
37
+ description: 'Désactive le bouton',
38
+ default: false,
39
+ },
40
+ 'onClick-icon-button': {
41
+ action: 'click-icon-button',
42
+ description: 'Événement émis lors du clic sur le bouton',
43
+ },
44
+ 'variant': {
45
+ options: ['flat', 'elevated', 'tonal', 'outlined', 'text', 'plain'],
46
+ control: { type: 'select' },
47
+ description: 'Variante visuelle du bouton',
48
+ },
49
+ },
50
+ } satisfies Meta<typeof SyIconButton>
51
+
52
+ export default meta
53
+
54
+ type Story = StoryObj<typeof meta>
55
+
56
+ export const Default: Story = {
57
+ parameters: {
58
+ sourceCode: [
59
+ {
60
+ name: 'Template',
61
+ language: 'vue',
62
+ code: `
63
+ <template>
64
+ <SyIconButton :icon="mdiClose" label="Fermer" @click-icon-button="handleClick" />
65
+ </template>`,
66
+ },
67
+ ],
68
+ },
69
+ args: {
70
+ 'icon': mdiClose,
71
+ 'label': 'Fermer',
72
+ 'onClick-icon-button': fn(),
73
+ },
74
+ render: args => ({
75
+ components: { SyIconButton },
76
+ setup() {
77
+ const handleClick = () => {
78
+ alert('Bouton cliqué !')
79
+ }
80
+ return { args, mdiClose, handleClick }
81
+ },
82
+ template: `
83
+ <div class="pa-4">
84
+ <SyIconButton v-bind="args" @click-icon-button="handleClick" />
85
+ </div>
86
+ `,
87
+ }),
88
+ }
89
+
90
+ export const Disabled: Story = {
91
+ parameters: {
92
+ sourceCode: [
93
+ {
94
+ name: 'Template',
95
+ language: 'vue',
96
+ code: `
97
+ <template>
98
+ <SyIconButton :icon="mdiClose" label="Fermer" :disabled="true" />
99
+ </template>`,
100
+ },
101
+ ],
102
+ },
103
+ args: {
104
+ 'icon': mdiClose,
105
+ 'label': 'Fermer',
106
+ 'disabled': true,
107
+ 'onClick-icon-button': fn(),
108
+ },
109
+ render: args => ({
110
+ components: { SyIconButton },
111
+ setup() {
112
+ return { args, mdiClose }
113
+ },
114
+ template: `
115
+ <div class="pa-4">
116
+ <SyIconButton v-bind="args" />
117
+ </div>
118
+ `,
119
+ }),
120
+ }
121
+
122
+ export const WithColor: Story = {
123
+ parameters: {
124
+ sourceCode: [
125
+ {
126
+ name: 'Template',
127
+ language: 'vue',
128
+ code: `
129
+ <template>
130
+ <SyIconButton :icon="mdiAlert" label="Alerte" color="red" />
131
+ </template>`,
132
+ },
133
+ ],
134
+ },
135
+ args: {
136
+ 'icon': mdiAlert,
137
+ 'label': 'Alerte',
138
+ 'color': 'red',
139
+ 'onClick-icon-button': fn(),
140
+ },
141
+ render: args => ({
142
+ components: { SyIconButton },
143
+ setup() {
144
+ return { args, mdiAlert }
145
+ },
146
+ template: `
147
+ <div class="pa-4">
148
+ <SyIconButton v-bind="args" />
149
+ </div>
150
+ `,
151
+ }),
152
+ }
153
+
154
+ export const WithSize: Story = {
155
+ parameters: {
156
+ sourceCode: [
157
+ {
158
+ name: 'Template',
159
+ language: 'vue',
160
+ code: `
161
+ <template>
162
+ <SyIconButton :icon="mdiMagnify" label="Rechercher" size="small" />
163
+ </template>`,
164
+ },
165
+ ],
166
+ },
167
+ args: {
168
+ 'icon': mdiMagnify,
169
+ 'label': 'Rechercher',
170
+ 'onClick-icon-button': fn(),
171
+ },
172
+ render: args => ({
173
+ components: { SyIconButton },
174
+ setup() {
175
+ const sizes = ['x-small', 'small', 'default', 'large', 'x-large']
176
+ return { args, mdiMagnify, sizes }
177
+ },
178
+ template: `
179
+ <div class="pa-4 d-flex align-center ga-4">
180
+ <SyIconButton v-for="size in sizes" :key="size" v-bind="args" :size="size" />
181
+ </div>
182
+ `,
183
+ }),
184
+ }
@@ -0,0 +1,38 @@
1
+ <script setup lang="ts">
2
+ import type { IconValue } from 'vuetify/lib/composables/icons.mjs'
3
+ import SyIcon from '../SyIcon/SyIcon.vue'
4
+
5
+ type Size = 'x-small' | 'small' | 'default' | 'large' | 'x-large'
6
+ type Variant = 'flat' | 'text' | 'elevated' | 'tonal' | 'outlined' | 'plain'
7
+ type Color = 'primary' | 'secondary' | 'success' | 'error' | 'warning' | 'info' | (string & {})
8
+
9
+ const props = defineProps<{
10
+ icon: IconValue
11
+ label: string
12
+ color?: Color
13
+ size?: Size
14
+ disabled?: boolean
15
+ variant?: Variant
16
+ }>()
17
+
18
+ defineEmits<{ 'click-icon-button': [] }>()
19
+ </script>
20
+
21
+ <template>
22
+ <v-btn
23
+ :disabled="props.disabled"
24
+ :aria-label="props.label"
25
+ :variant="props.variant ?? 'text'"
26
+ :size="props.size"
27
+ class="rounded-circle"
28
+ icon
29
+ @click="$emit('click-icon-button')"
30
+ >
31
+ <SyIcon
32
+ :icon="props.icon"
33
+ :size="props.size"
34
+ :color="props.color"
35
+ :decorative="true"
36
+ />
37
+ </v-btn>
38
+ </template>
@@ -0,0 +1,64 @@
1
+ import { Meta, Primary } from '@storybook/blocks';
2
+ import * as SyIconButtonStories from '../SyIconButton.stories';
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';
12
+
13
+ <Meta of={SyIconButtonStories} />
14
+
15
+ <AccessibilityGuideLayout
16
+ componentName="SyIconButton"
17
+ iconSrc={AccessibilityIcon}
18
+ >
19
+ <CriteriaSection>
20
+ <CriteriaCard icon="🔍" title="Structure sémantique">
21
+ <ul>
22
+ <li><strong>Élément bouton</strong> : le composant repose sur un <code>v-btn</code> qui génère un élément <code>&lt;button&gt;</code> natif — il est nativement reconnu par les lecteurs d'écran et les outils d'assistance</li>
23
+ <li><strong>Prop <code>label</code> obligatoire</strong> : la prop <code>label</code> est requise et est portée par le bouton via <code>aria-label</code> — elle constitue le nom accessible du bouton</li>
24
+ <li><strong>Icône décorative</strong> : le composant <code>SyIcon</code> interne reçoit <code>:decorative="true"</code> — l'icône est ignorée par les lecteurs d'écran car le bouton porte déjà le nom accessible via <code>aria-label</code></li>
25
+ <li><strong>Prop <code>disabled</code></strong> : lorsque <code>disabled</code> est à <code>true</code>, l'attribut <code>disabled</code> est appliqué nativement sur le <code>&lt;button&gt;</code>, le rendant inaccessible au clavier et annoncé comme désactivé par les lecteurs d'écran</li>
26
+ <li><strong>Prop <code>variant</code></strong> : par défaut à <code>text</code> — les autres variantes (<code>outlined</code>, <code>tonal</code>, etc.) n'affectent pas l'accessibilité du bouton</li>
27
+ </ul>
28
+ </CriteriaCard>
29
+
30
+ <CriteriaCard icon="⌨️" title="Navigation clavier">
31
+ <ul>
32
+ <li><strong>Focusable</strong> : le bouton est naturellement focusable via <kbd>Tab</kbd> grâce à l'élément <code>&lt;button&gt;</code> natif</li>
33
+ <li><strong>Activation</strong> : le bouton peut être activé via <kbd>Entrée</kbd> ou <kbd>Espace</kbd> nativement</li>
34
+ <li><strong>État désactivé</strong> : lorsque <code>disabled</code> est à <code>true</code>, le bouton est retiré de l'ordre de tabulation et ne peut pas être activé au clavier</li>
35
+ <li><strong>Événement</strong> : le composant émet <code>click-icon-button</code> au clic ou à l'activation clavier</li>
36
+ </ul>
37
+ </CriteriaCard>
38
+ </CriteriaSection>
39
+
40
+ <DemoSection componentName="SyIconButton">
41
+ <Primary />
42
+ </DemoSection>
43
+
44
+ <BestPracticesSection>
45
+ <ul>
46
+ <li>La prop <code>label</code> est <strong>obligatoire</strong> — sans elle, le bouton icône n'a pas de nom accessible et sera incompréhensible pour les utilisateurs de lecteurs d'écran</li>
47
+ <li>Le <code>label</code> doit décrire l'<strong>action</strong> du bouton et non l'icône elle-même (ex. <em>"Fermer"</em> plutôt que <em>"Croix"</em>)</li>
48
+ <li>Ne jamais fournir un <code>label</code> vide (<code>""</code>) — cela rendrait le bouton inaccessible</li>
49
+ <li>Utiliser <code>disabled</code> plutôt que de masquer le bouton lorsqu'il est temporairement indisponible, afin de préserver la cohérence de la mise en page</li>
50
+ <li>Ne pas ajouter d'<code>aria-label</code> supplémentaire sur le composant parent — celui du <code>v-btn</code> est suffisant</li>
51
+ <li>La prop <code>variant</code> ne doit pas être choisie pour des raisons d'accessibilité — s'assurer que le contraste reste suffisant quelle que soit la variante utilisée (WCAG 1.4.3)</li>
52
+ </ul>
53
+ </BestPracticesSection>
54
+
55
+ <ResourcesSection>
56
+ <ul>
57
+ <li><a href="https://www.w3.org/WAI/WCAG21/Understanding/name-role-value.html" target="_blank" style={{ color: '#0C41BD' }}>WCAG 4.1.2 – Name, Role, Value</a></li>
58
+ <li><a href="https://www.w3.org/WAI/WCAG21/Understanding/non-text-content.html" target="_blank" style={{ color: '#0C41BD' }}>WCAG 1.1.1 – Non-text Content</a></li>
59
+ <li><a href="https://www.w3.org/WAI/WCAG21/Understanding/keyboard.html" target="_blank" style={{ color: '#0C41BD' }}>WCAG 2.1.1 – Keyboard</a></li>
60
+ <li><a href="https://www.w3.org/WAI/WCAG21/Understanding/contrast-minimum.html" target="_blank" style={{ color: '#0C41BD' }}>WCAG 1.4.3 – Contrast (Minimum)</a></li>
61
+ <li><a href="https://www.w3.org/WAI/ARIA/apg/patterns/button/" target="_blank" style={{ color: '#0C41BD' }}>ARIA Authoring Practices – Button Pattern</a></li>
62
+ </ul>
63
+ </ResourcesSection>
64
+ </AccessibilityGuideLayout>
@@ -0,0 +1,87 @@
1
+ // @vitest-environment jsdom
2
+ import { describe, it } from 'vitest'
3
+ import { mount } from '@vue/test-utils'
4
+ import { axe } from 'vitest-axe'
5
+ import { assertNoA11yViolations } from '@tests/unit/accessibility/axeUtils'
6
+ import SyIconButton from '../SyIconButton.vue'
7
+
8
+ const globalStubs = {
9
+ global: {
10
+ stubs: {
11
+ 'v-btn': {
12
+ template: '<button :aria-label="ariaLabel" :disabled="disabled"><slot></slot></button>',
13
+ props: ['ariaLabel', 'disabled', 'size', 'variant', 'icon'],
14
+ },
15
+ 'SyIcon': {
16
+ template: '<span class="sy-icon" :aria-hidden="decorative ? true : undefined"></span>',
17
+ props: ['icon', 'color', 'size', 'decorative'],
18
+ },
19
+ },
20
+ },
21
+ }
22
+
23
+ describe('SyIconButton – accessibility (axe)', () => {
24
+ it('has no obvious axe violations in default state', async () => {
25
+ const wrapper = mount(SyIconButton, {
26
+ props: {
27
+ icon: 'mdi-close',
28
+ label: 'Fermer',
29
+ },
30
+ ...globalStubs,
31
+ })
32
+
33
+ const results = await axe(wrapper.element as HTMLElement)
34
+ assertNoA11yViolations(results, 'SyIconButton – default state', {
35
+ ignoreRules: ['region'],
36
+ })
37
+ })
38
+
39
+ it('has no obvious axe violations when disabled', async () => {
40
+ const wrapper = mount(SyIconButton, {
41
+ props: {
42
+ icon: 'mdi-close',
43
+ label: 'Fermer',
44
+ disabled: true,
45
+ },
46
+ ...globalStubs,
47
+ })
48
+
49
+ const results = await axe(wrapper.element as HTMLElement)
50
+ assertNoA11yViolations(results, 'SyIconButton – disabled', {
51
+ ignoreRules: ['region'],
52
+ })
53
+ })
54
+
55
+ it('has no obvious axe violations with color and size', async () => {
56
+ const wrapper = mount(SyIconButton, {
57
+ props: {
58
+ icon: 'mdi-alert',
59
+ label: 'Alerte importante',
60
+ color: 'primary',
61
+ size: 'large',
62
+ },
63
+ ...globalStubs,
64
+ })
65
+
66
+ const results = await axe(wrapper.element as HTMLElement)
67
+ assertNoA11yViolations(results, 'SyIconButton – color and size', {
68
+ ignoreRules: ['region'],
69
+ })
70
+ })
71
+
72
+ it('has aria-label on the button', async () => {
73
+ const wrapper = mount(SyIconButton, {
74
+ props: {
75
+ icon: 'mdi-close',
76
+ label: 'Fermer',
77
+ },
78
+ ...globalStubs,
79
+ })
80
+
81
+ const results = await axe(wrapper.element as HTMLElement)
82
+
83
+ assertNoA11yViolations(results, 'SyIconButton – aria-label on button', {
84
+ ignoreRules: ['region'],
85
+ })
86
+ })
87
+ })
@@ -0,0 +1,152 @@
1
+ import { describe, it, expect } from 'vitest'
2
+ import { mount } from '@vue/test-utils'
3
+ import SyIconButton from '../SyIconButton.vue'
4
+
5
+ const globalStubs = {
6
+ global: {
7
+ stubs: {
8
+ 'v-btn': {
9
+ template: '<button :aria-label="ariaLabel" :disabled="disabled" @click="$emit(\'click\', $event)"><slot></slot></button>',
10
+ props: ['ariaLabel', 'disabled', 'size', 'variant', 'icon'],
11
+ emits: ['click'],
12
+ },
13
+ 'SyIcon': {
14
+ template: '<span class="sy-icon" :aria-hidden="decorative ? true : undefined"></span>',
15
+ props: ['icon', 'color', 'size', 'decorative'],
16
+ },
17
+ },
18
+ },
19
+ }
20
+
21
+ describe('SyIconButton', () => {
22
+ it('renders correctly', () => {
23
+ const wrapper = mount(SyIconButton, {
24
+ props: {
25
+ icon: 'mdi-close',
26
+ label: 'Fermer',
27
+ },
28
+ ...globalStubs,
29
+ })
30
+
31
+ expect(wrapper.html()).toMatchSnapshot()
32
+ })
33
+
34
+ it('renders a button with the correct aria-label', () => {
35
+ const wrapper = mount(SyIconButton, {
36
+ props: {
37
+ icon: 'mdi-close',
38
+ label: 'Fermer',
39
+ },
40
+ ...globalStubs,
41
+ })
42
+
43
+ const button = wrapper.find('button')
44
+ expect(button.exists()).toBe(true)
45
+ expect(button.attributes('aria-label')).toBe('Fermer')
46
+ })
47
+
48
+ it('renders the icon as decorative (aria-hidden="true")', () => {
49
+ const wrapper = mount(SyIconButton, {
50
+ props: {
51
+ icon: 'mdi-close',
52
+ label: 'Fermer',
53
+ },
54
+ ...globalStubs,
55
+ })
56
+
57
+ const icon = wrapper.find('.sy-icon')
58
+ expect(icon.exists()).toBe(true)
59
+ expect(icon.attributes('aria-hidden')).toBe('true')
60
+ })
61
+
62
+ it('renders the correct icon', () => {
63
+ const wrapper = mount(SyIconButton, {
64
+ props: {
65
+ icon: 'mdi-close',
66
+ label: 'Fermer',
67
+ },
68
+ ...globalStubs,
69
+ })
70
+
71
+ expect(wrapper.find('.sy-icon').exists()).toBe(true)
72
+ })
73
+
74
+ it('disables the button when disabled prop is true', () => {
75
+ const wrapper = mount(SyIconButton, {
76
+ props: {
77
+ icon: 'mdi-close',
78
+ label: 'Fermer',
79
+ disabled: true,
80
+ },
81
+ ...globalStubs,
82
+ })
83
+
84
+ expect(wrapper.find('button').attributes('disabled')).toBeDefined()
85
+ })
86
+
87
+ it('emits click-icon-button when clicked', async () => {
88
+ const wrapper = mount(SyIconButton, {
89
+ props: {
90
+ icon: 'mdi-close',
91
+ label: 'Fermer',
92
+ },
93
+ ...globalStubs,
94
+ })
95
+
96
+ await wrapper.find('button').trigger('click')
97
+ expect(wrapper.emitted('click-icon-button')).toBeTruthy()
98
+ })
99
+
100
+ it('passes the size prop to v-btn', () => {
101
+ const wrapper = mount(SyIconButton, {
102
+ props: {
103
+ icon: 'mdi-close',
104
+ label: 'Fermer',
105
+ size: 'large',
106
+ },
107
+ ...globalStubs,
108
+ })
109
+
110
+ expect(wrapper.html()).toMatchSnapshot()
111
+ })
112
+
113
+ it('passes the color prop to SyIcon', () => {
114
+ const wrapper = mount(SyIconButton, {
115
+ props: {
116
+ icon: 'mdi-close',
117
+ label: 'Fermer',
118
+ color: 'primary',
119
+ },
120
+ ...globalStubs,
121
+ })
122
+
123
+ expect(wrapper.html()).toMatchSnapshot()
124
+ })
125
+
126
+ it('passes the variant prop to v-btn', () => {
127
+ const wrapper = mount(SyIconButton, {
128
+ props: {
129
+ icon: 'mdi-close',
130
+ label: 'Fermer',
131
+ variant: 'outlined',
132
+ },
133
+ ...globalStubs,
134
+ })
135
+
136
+ expect(wrapper.html()).toMatchSnapshot()
137
+ })
138
+
139
+ it('uses text variant by default', () => {
140
+ const wrapper = mount(SyIconButton, {
141
+ props: {
142
+ icon: 'mdi-close',
143
+ label: 'Fermer',
144
+ },
145
+ ...globalStubs,
146
+ })
147
+
148
+ const button = wrapper.find('button')
149
+ expect(button.exists()).toBe(true)
150
+ expect(wrapper.html()).toMatchSnapshot()
151
+ })
152
+ })
@@ -0,0 +1,61 @@
1
+ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2
+
3
+ exports[`SyIconButton > passes the color prop to SyIcon 1`] = `
4
+ <button
5
+ aria-label="Fermer"
6
+ class="rounded-circle"
7
+ >
8
+ <span
9
+ aria-hidden="true"
10
+ class="sy-icon"
11
+ ></span>
12
+ </button>
13
+ `;
14
+
15
+ exports[`SyIconButton > passes the size prop to v-btn 1`] = `
16
+ <button
17
+ aria-label="Fermer"
18
+ class="rounded-circle"
19
+ >
20
+ <span
21
+ aria-hidden="true"
22
+ class="sy-icon"
23
+ ></span>
24
+ </button>
25
+ `;
26
+
27
+ exports[`SyIconButton > passes the variant prop to v-btn 1`] = `
28
+ <button
29
+ aria-label="Fermer"
30
+ class="rounded-circle"
31
+ >
32
+ <span
33
+ aria-hidden="true"
34
+ class="sy-icon"
35
+ ></span>
36
+ </button>
37
+ `;
38
+
39
+ exports[`SyIconButton > renders correctly 1`] = `
40
+ <button
41
+ aria-label="Fermer"
42
+ class="rounded-circle"
43
+ >
44
+ <span
45
+ aria-hidden="true"
46
+ class="sy-icon"
47
+ ></span>
48
+ </button>
49
+ `;
50
+
51
+ exports[`SyIconButton > uses text variant by default 1`] = `
52
+ <button
53
+ aria-label="Fermer"
54
+ class="rounded-circle"
55
+ >
56
+ <span
57
+ aria-hidden="true"
58
+ class="sy-icon"
59
+ ></span>
60
+ </button>
61
+ `;
@@ -384,8 +384,8 @@
384
384
  display: inline-block;
385
385
  padding: 0.5rem 0.75rem;
386
386
  text-decoration: none;
387
- color: tokens.$primary-base;
388
- border: 1px solid tokens.$primary-base;
387
+ color: var(--pagination-color);
388
+ border: 1px solid var(--pagination-border);
389
389
  border-radius: 4px;
390
390
  transition: all 0.2s ease;
391
391
  font-size: 0.875rem;
@@ -393,11 +393,11 @@
393
393
 
394
394
  &:hover,
395
395
  &:focus {
396
- background-color: rgba(tokens.$primary-base, 0.1);
396
+ background-color: var(--pagination-focused-background-color);
397
397
  }
398
398
 
399
399
  &[aria-current='page'] {
400
- background-color: tokens.$primary-base;
400
+ background-color: var(--pagination-background-color);
401
401
  color: white;
402
402
  font-weight: 500;
403
403
  }
@@ -414,7 +414,7 @@
414
414
  border: none;
415
415
  pointer-events: none;
416
416
  display: inline-block;
417
- color: tokens.$primary-base;
417
+ color: var(--pagination-ellipsis);
418
418
  padding: 0.6rem;
419
419
  }
420
420
  }
@@ -667,7 +667,7 @@
667
667
  >
668
668
  <!-- Prepend -->
669
669
  <template
670
- v-if="props.prependIcon || props.prependTooltip"
670
+ v-if="props.prependIcon || props.prependTooltip || $slots['prepend']"
671
671
  #prepend
672
672
  >
673
673
  <slot name="prepend">
@@ -706,7 +706,7 @@
706
706
 
707
707
  <!-- Append -->
708
708
  <template
709
- v-if="props.appendIcon || props.appendTooltip"
709
+ v-if="props.appendIcon || props.appendTooltip || $slots['append']"
710
710
  #append
711
711
  >
712
712
  <slot name="append">
@@ -743,6 +743,14 @@
743
743
  </slot>
744
744
  </template>
745
745
 
746
+ <!-- Default slot passthrough: renders inside v-field__input (flex-wrap container) -->
747
+ <template
748
+ v-if="$slots.default"
749
+ #default
750
+ >
751
+ <slot />
752
+ </template>
753
+
746
754
  <!-- Prepend inner -->
747
755
  <template #prepend-inner>
748
756
  <slot name="prepend-inner">
@@ -795,6 +803,16 @@
795
803
  <template #details>
796
804
  <slot name="details" />
797
805
  </template>
806
+
807
+ <template #loader="{ color: loaderColor, isActive: loaderActive }">
808
+ <VProgressLinear
809
+ v-if="loaderActive"
810
+ indeterminate
811
+ rounded
812
+ :color="loaderColor"
813
+ :aria-label="props.label ? `Chargement de ${props.label}` : 'Chargement en cours'"
814
+ />
815
+ </template>
798
816
  </VTextField>
799
817
 
800
818
  <div