@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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cnamts/synapse",
3
- "version": "1.0.22",
3
+ "version": "1.0.23",
4
4
  "private": false,
5
5
  "description": "CNAM DS v3",
6
6
  "type": "module",
@@ -18,7 +18,10 @@
18
18
  "require": "./dist/design-system-v3.umd.cjs"
19
19
  },
20
20
  "./style.css": "./dist/style.css",
21
- "./*": "./src/*"
21
+ "./*": {
22
+ "types": "./src/*",
23
+ "default": "./src/*"
24
+ }
22
25
  },
23
26
  "scripts": {
24
27
  "dev": "vite",
@@ -31,6 +34,7 @@
31
34
  "test:unit": "vitest run --exclude='**/*.a11y.spec.ts'",
32
35
  "test:a11y": "pnpm vitest run a11y.spec.ts",
33
36
  "a11y:report": "node ./scripts/a11y-report.mjs",
37
+ "a11y:status": "node generate-a11y-report.mjs",
34
38
  "prepare": "husky",
35
39
  "generate:changelog": "auto-changelog -o CHANGELOG-TEMP.md -p --starting-date 2024-10-10"
36
40
  },
@@ -83,7 +87,7 @@
83
87
  "eslint-plugin-storybook": "^0.11.3",
84
88
  "eslint-plugin-vue": "^9.30.0",
85
89
  "eslint-plugin-vuejs-accessibility": "^2.4.1",
86
- "happy-dom": "^20.0.0",
90
+ "happy-dom": "^20.8.8",
87
91
  "husky": "^9.1.6",
88
92
  "jsdom": "^27.3.0",
89
93
  "marked": "^16.0.0",
@@ -144,7 +148,9 @@
144
148
  "parse5": "7.1.2",
145
149
  "npm": "^11.8.1",
146
150
  "minimatch": ">=10.2.3",
147
- "immutable": ">=5.1.5"
151
+ "immutable": ">=5.1.5",
152
+ "flatted": ">=3.4.2",
153
+ "handlebars": ">=4.7.9"
148
154
  },
149
155
  "patchedDependencies": {
150
156
  "vuetify@3.12.2": "patches/vuetify@3.12.2.patch"
@@ -334,8 +334,8 @@ $colors-interactive-negative-primary-action-hover: #99dbf2;
334
334
  $colors-interactive-negative-primary-action-pressed: #66c9ec;
335
335
  $colors-interactive-negative-primary-action-disabled: #dddede;
336
336
  $colors-interactive-positive-secondary-action-enabled: rgb(255 255 255 / 0%);
337
- $colors-interactive-positive-secondary-action-hover: #ccedf9;
338
- $colors-interactive-positive-secondary-action-pressed: #99dbf2;
337
+ $colors-interactive-positive-secondary-action-hover: #f7fcfe;
338
+ $colors-interactive-positive-secondary-action-pressed: #ccedf9;
339
339
  $colors-interactive-positive-secondary-action-disabled: rgb(255 255 255 / 0%);
340
340
  $colors-interactive-negative-secondary-action-enabled: rgb(255 255 255 / 0%);
341
341
  $colors-interactive-negative-secondary-action-hover: rgb(255 255 255 / 8%);
@@ -1,3 +1,5 @@
1
+ @use '@/assets/apTokens';
2
+
1
3
  .v-btn {
2
4
  text-transform: inherit !important;
3
5
  font-weight: 700 !important;
@@ -13,3 +15,9 @@
13
15
  .v-btn.v-tab {
14
16
  text-transform: uppercase !important;
15
17
  }
18
+
19
+ .theme-ap .v-btn {
20
+ &:hover {
21
+ background-color: apTokens.$colors-interactive-positive-secondary-action-hover !important;
22
+ }
23
+ }
@@ -12,3 +12,12 @@
12
12
  .v-radio .v-selection-control {
13
13
  min-height: 40px;
14
14
  }
15
+
16
+ // AP theme: inset the loader bar to match the larger field border-radius (12px)
17
+ .theme-ap,
18
+ .theme-ap2026 {
19
+ .v-field__loader .v-progress-linear {
20
+ margin-inline: 10px;
21
+ width: calc(100% - 20px);
22
+ }
23
+ }
@@ -1,20 +1,49 @@
1
1
  @use '@/assets/apTokens';
2
2
  @use '@/assets/amelipro/apTokens2026';
3
+ @use '@/assets/tokens';
3
4
 
4
5
  .v-theme--ap.v-btn--icon .v-icon {
5
- color: apTokens.$colors-icon-accent-primary !important;
6
+ color: apTokens.$colors-icon-accent-primary;
6
7
  transition: color 0.2s ease;
7
-
8
- &:hover {
9
- color: apTokens.$colors-icon-accent-secondary !important;
10
- }
11
8
  }
12
9
 
13
10
  .v-theme--ap2026.v-btn--icon .v-icon {
14
- color: apTokens2026.$ap-blue-darken1 !important;
11
+ color: apTokens2026.$ap-blue-darken1;
12
+ transition: color 0.2s ease;
13
+ }
14
+
15
+ .v-theme--cnam.v-btn--icon .v-icon,
16
+ .v-theme--pa.v-btn--icon .v-icon {
17
+ color: tokens.$colors-icon-accent-primary;
18
+ transition: color 0.2s ease;
19
+ }
20
+
21
+ .v-theme--ap .v-btn--icon:hover,
22
+ .v-theme--ap2026 .v-btn--icon:hover,
23
+ .v-theme--cnam .v-btn--icon:hover,
24
+ .v-theme--pa .v-btn--icon:hover {
25
+ opacity: 0.08;
26
+ }
27
+
28
+ .v-theme--ap.v-icon,
29
+ .v-theme--ap2026.v-icon,
30
+ .v-theme--cnam.v-icon,
31
+ .v-theme--pa.v-icon {
15
32
  transition: color 0.2s ease;
33
+ }
34
+
35
+ .v-theme--ap.v-btn--icon .v-ripple__animation {
36
+ background-color: apTokens.$colors-icon-accent-primary !important;
37
+ opacity: 0.18;
38
+ }
39
+
40
+ .v-theme--ap2026.v-btn--icon .v-ripple__animation {
41
+ background-color: apTokens2026.$ap-blue-darken1 !important;
42
+ opacity: 0.18;
43
+ }
16
44
 
17
- &:hover {
18
- color: apTokens2026.$ap-blue-darken2 !important;
19
- }
45
+ .v-theme--cnam.v-btn--icon .v-ripple__animation,
46
+ .v-theme--pa.v-btn--icon .v-ripple__animation {
47
+ background-color: tokens.$colors-icon-accent-primary !important;
48
+ opacity: 0.18;
20
49
  }
@@ -1,4 +1,5 @@
1
1
  @use '@/assets/tokens';
2
+ @use '@/assets/apTokens';
2
3
 
3
4
  .v-data-table thead th .v-data-table-header__content {
4
5
  opacity: 0.65;
@@ -16,3 +17,21 @@
16
17
  background: tokens.$primary-base !important;
17
18
  }
18
19
  }
20
+
21
+ // Pagination color variables
22
+ .theme-cnam,
23
+ .theme-pa {
24
+ --pagination-color: #{tokens.$primary-base};
25
+ --pagination-border: #{tokens.$primary-base};
26
+ --pagination-ellipsis: #{tokens.$primary-base};
27
+ --pagination-focused-background-color: rgba(tokens.$primary-base, 0.1);
28
+ --pagination-background-color: #{tokens.$primary-base};
29
+ }
30
+
31
+ .theme-ap {
32
+ --pagination-color: #{apTokens.$primary-base};
33
+ --pagination-border: #{apTokens.$primary-base};
34
+ --pagination-ellipsis: #{apTokens.$primary-base};
35
+ --pagination-focused-background-color: rgba(apTokens.$primary-base, 0.1);
36
+ --pagination-background-color: #{apTokens.$primary-base};
37
+ }
@@ -16,6 +16,17 @@ L'accordéon standard affiche une liste d'éléments avec un titre et un contenu
16
16
 
17
17
  <Canvas of={AccordionStories.Default} />
18
18
 
19
+ ## API
20
+
21
+ <Controls of={AccordionStories.Default} />
22
+
23
+ ## Événements
24
+
25
+ ### update:modelValue
26
+
27
+ - **Type** : `(value: string[]) => void`
28
+ - **Description** : Émis lors de l'ouverture ou la fermeture d'un élément. La valeur est le tableau mis à jour des identifiants des éléments ouverts.
29
+
19
30
  ## Contenu avec objet
20
31
 
21
32
  L'accordéon peut également afficher un contenu structuré sous forme d'objet avec un titre et un contenu détaillé.
@@ -34,26 +45,28 @@ Vous pouvez également utiliser des slots pour personnaliser le rendu des titres
34
45
 
35
46
  <Canvas of={AccordionStories.WithSlots} />
36
47
 
37
- <div className="header">
38
- <h1>Accessibilité</h1>
39
- </div>
48
+ ## Contrôle programmatique (v-model)
40
49
 
41
- Le composant Accordion respecte les normes d'accessibilité ARIA :
50
+ Le composant supporte `v-model` pour contrôler programmatiquement les éléments ouverts.
51
+ La valeur est un tableau de chaînes correspondant aux `id` des éléments ouverts.
42
52
 
43
- - Chaque bouton de dévoilement est contenu dans un élément de titre (h1-h6)
44
- - Chaque bouton possède les attributs `aria-expanded` et `aria-controls`
45
- - Chaque section de contenu a l'attribut `role="region"` et `aria-labelledby` qui pointe vers l'ID du bouton
53
+ <Canvas of={AccordionStories.WithVModel} />
46
54
 
47
- ## API
55
+ ## Éléments pré-ouverts
48
56
 
49
- <Controls of={AccordionStories.Default} />
57
+ Vous pouvez pré-ouvrir des éléments en initialisant le `v-model` avec les identifiants souhaités.
58
+
59
+ <Canvas of={AccordionStories.PreOpened} />
50
60
 
51
61
  ## Exemple d'utilisation
52
62
 
53
63
  <Source dark code={`
54
64
  <script setup lang="ts">
65
+ import { ref } from 'vue'
55
66
  import { Accordion } from '@cnamts/synapse'
56
67
 
68
+ const openItems = ref<string[]>([])
69
+
57
70
  const accordionItems = [
58
71
  { id: 'item1', title: 'Section 1', content: 'Contenu de la section 1' },
59
72
  { id: 'item2', title: 'Section 2', content: 'Contenu de la section 2' },
@@ -71,6 +84,7 @@ const accordionItems = [
71
84
  <template>
72
85
  <main>
73
86
  <Accordion
87
+ v-model="openItems"
74
88
  :items="accordionItems"
75
89
  :heading-level="3"
76
90
  />
@@ -1,5 +1,7 @@
1
1
  import type { Meta, StoryObj } from '@storybook/vue3'
2
+ import { fn } from '@storybook/test'
2
3
  import Accordion from './Accordion.vue'
4
+ import { ref } from 'vue'
3
5
 
4
6
  const meta: Meta<typeof Accordion> = {
5
7
  title: 'Composants/Données/Accordion',
@@ -8,20 +10,40 @@ const meta: Meta<typeof Accordion> = {
8
10
  layout: 'fullscreen',
9
11
  },
10
12
  argTypes: {
11
- items: {
13
+ 'items': {
12
14
  control: { type: 'object' },
13
15
  description: 'Liste des éléments de l\'accordéon',
14
16
  },
15
- headingLevel: {
17
+ 'headingLevel': {
16
18
  control: { type: 'number', min: 1, max: 6 },
17
19
  description: 'Niveau de titre pour les boutons de dévoilement',
18
20
  default: 3,
19
21
  },
20
- groupId: {
22
+ 'groupId': {
21
23
  control: { type: 'text' },
22
24
  description: 'Identifiant de groupe pour synchroniser le focus entre plusieurs accordions',
23
25
  default: 'default',
24
26
  },
27
+ 'modelValue': {
28
+ control: 'object',
29
+ description: 'Liste des identifiants des éléments ouverts (v-model)',
30
+ table: {
31
+ type: { summary: 'string[]' },
32
+ category: 'props',
33
+ defaultValue: { summary: '[]' },
34
+ },
35
+ },
36
+ 'onUpdate:modelValue': {
37
+ action: 'update:modelValue',
38
+ description: 'Événement émis lors de l\'ouverture ou la fermeture d\'un élément',
39
+ table: {
40
+ type: { summary: '(value: string[]) => void' },
41
+ category: 'events',
42
+ },
43
+ },
44
+ },
45
+ args: {
46
+ 'onUpdate:modelValue': fn(),
25
47
  },
26
48
  }
27
49
 
@@ -339,3 +361,131 @@ export const WithSlots: Story = {
339
361
  `,
340
362
  }),
341
363
  }
364
+
365
+ export const WithVModel: Story = {
366
+ parameters: {
367
+ sourceCode: [
368
+ {
369
+ language: 'vue',
370
+ name: 'Template',
371
+ code: `<script setup lang="ts">
372
+ import { ref } from 'vue'
373
+ import { Accordion } from '@cnamts/synapse'
374
+
375
+ const openItems = ref<string[]>([])
376
+
377
+ const items = [
378
+ { id: 'item1', title: 'Section 1', content: 'Contenu de la section 1' },
379
+ { id: 'item2', title: 'Section 2', content: 'Contenu de la section 2' },
380
+ { id: 'item3', title: 'Section 3', content: 'Contenu de la section 3' },
381
+ ]
382
+
383
+ function openAll() {
384
+ openItems.value = items.map(i => i.id)
385
+ }
386
+
387
+ function closeAll() {
388
+ openItems.value = []
389
+ }
390
+ </script>
391
+
392
+ <template>
393
+ <Accordion
394
+ v-model="openItems"
395
+ :items="items"
396
+ :heading-level="3"
397
+ />
398
+
399
+ <p>Éléments ouverts : {{ openItems }}</p>
400
+
401
+ <v-btn variant="outlined" color="primary" @click="openAll">Tout ouvrir</v-btn>
402
+ <v-btn variant="outlined" color="primary" @click="closeAll">Tout fermer</v-btn>
403
+ </template>`,
404
+ },
405
+ ],
406
+ },
407
+ args: {
408
+ items: defaultItems,
409
+ headingLevel: 3,
410
+ },
411
+ render: args => ({
412
+ components: { Accordion },
413
+ setup() {
414
+ const openItems = ref<string[]>([])
415
+
416
+ const openAll = () => {
417
+ openItems.value = (args.items ?? []).map((i: { id: string }) => i.id)
418
+ }
419
+
420
+ const closeAll = () => {
421
+ openItems.value = []
422
+ }
423
+
424
+ return { args, openItems, openAll, closeAll }
425
+ },
426
+ template: `
427
+ <div class="pa-4">
428
+ <Accordion v-bind="args" v-model="openItems" />
429
+
430
+ <div class="mt-4" style="font-family: monospace; color: #666;">
431
+ Éléments ouverts : {{ openItems }}
432
+ </div>
433
+
434
+ <div class="mt-2 d-flex ga-2">
435
+ <v-btn variant="outlined" color="primary" @click="openAll">Tout ouvrir</v-btn>
436
+ <v-btn variant="outlined" color="primary" @click="closeAll">Tout fermer</v-btn>
437
+ </div>
438
+ </div>
439
+ `,
440
+ }),
441
+ }
442
+
443
+ export const PreOpened: Story = {
444
+ parameters: {
445
+ sourceCode: [
446
+ {
447
+ language: 'vue',
448
+ name: 'Template',
449
+ code: `<script setup lang="ts">
450
+ import { ref } from 'vue'
451
+ import { Accordion } from '@cnamts/synapse'
452
+
453
+ // Pré-ouvrir la section 2
454
+ const openItems = ref<string[]>(['item2'])
455
+ </script>
456
+
457
+ <template>
458
+ <Accordion
459
+ v-model="openItems"
460
+ :items="[
461
+ { id: 'item1', title: 'Section 1', content: 'Contenu de la section 1' },
462
+ { id: 'item2', title: 'Section 2', content: 'Contenu de la section 2' },
463
+ { id: 'item3', title: 'Section 3', content: 'Contenu de la section 3' },
464
+ ]"
465
+ :heading-level="3"
466
+ />
467
+ </template>`,
468
+ },
469
+ ],
470
+ },
471
+ args: {
472
+ items: defaultItems,
473
+ headingLevel: 3,
474
+ },
475
+ render: args => ({
476
+ components: { Accordion },
477
+ setup() {
478
+ const openItems = ref<string[]>(['item2'])
479
+ return { args, openItems }
480
+ },
481
+ template: `
482
+ <div class="pa-4">
483
+ <Accordion v-bind="args" v-model="openItems" />
484
+
485
+ <div class="mt-4" style="font-family: monospace; color: #666;">
486
+ Éléments ouverts : {{ openItems }}
487
+ </div>
488
+ </div>
489
+ `,
490
+ }),
491
+ }
@@ -2,7 +2,6 @@
2
2
  import useCustomizableOptions, { type CustomizableOptions } from '@/composables/useCustomizableOptions'
3
3
  import { config } from '@/components/Accordion/config'
4
4
  import { mdiChevronRight } from '@mdi/js'
5
-
6
5
  // Importation des composables
7
6
  import useAccordionState from './composables/useAccordionState'
8
7
  import useAccordionGroupCommunication from './composables/useAccordionGroupCommunication'
@@ -12,7 +11,7 @@
12
11
  interface AccordionItem {
13
12
  id: string
14
13
  title: string
15
- content: string | Record<string, unknown>
14
+ content?: string | { title: string, content: string }
16
15
  headingLevel?: number
17
16
  disabled?: boolean
18
17
  }
@@ -38,6 +37,8 @@
38
37
  vuetifyOptions: () => ({}),
39
38
  })
40
39
 
40
+ const openItems = defineModel<string[]>({ default: () => [] })
41
+
41
42
  const options = useCustomizableOptions(config, props)
42
43
 
43
44
  // Génération d'un ID unique pour cette instance d'accordéon
@@ -49,7 +50,7 @@
49
50
  isItemOpen,
50
51
  isItemFocused,
51
52
  setFocus,
52
- } = useAccordionState()
53
+ } = useAccordionState(openItems)
53
54
 
54
55
  // Utilisation du composable pour gérer la communication entre accordéons
55
56
  const { emitFocusChange } = useAccordionGroupCommunication(
@@ -167,13 +168,13 @@
167
168
  {{ item.content }}
168
169
  </p>
169
170
  </template>
170
- <template v-else>
171
+ <template v-else-if="typeof item.content === 'object' && item.content !== null && 'title' in item.content && 'content' in item.content">
171
172
  <div class="sy-accordion-content-item">
172
173
  <p class="sy-accordion-content-text">
173
- <strong>{{ (item.content as any).title }}</strong>
174
+ <strong>{{ item.content.title }}</strong>
174
175
  </p>
175
176
  <p class="sy-accordion-content-text">
176
- {{ (item.content as any).content }}
177
+ {{ item.content.content }}
177
178
  </p>
178
179
  </div>
179
180
  </template>
@@ -1,10 +1,11 @@
1
1
  import { describe, it, expect } from 'vitest'
2
+ import { ref } from 'vue'
2
3
  import useAccordionState from '../useAccordionState'
3
4
 
4
5
  describe('useAccordionState', () => {
5
6
  describe('toggleItem', () => {
6
7
  it('adds an item to openItems when it is not already open', () => {
7
- const { toggleItem, isItemOpen, openItems } = useAccordionState()
8
+ const { toggleItem, isItemOpen, openItems } = useAccordionState(ref<string[]>([]))
8
9
 
9
10
  toggleItem('item1')
10
11
 
@@ -13,7 +14,7 @@ describe('useAccordionState', () => {
13
14
  })
14
15
 
15
16
  it('removes an item from openItems when it is already open', () => {
16
- const { toggleItem, isItemOpen, openItems } = useAccordionState()
17
+ const { toggleItem, isItemOpen, openItems } = useAccordionState(ref<string[]>([]))
17
18
 
18
19
  // Ouvrir d'abord l'élément
19
20
  toggleItem('item1')
@@ -29,7 +30,7 @@ describe('useAccordionState', () => {
29
30
 
30
31
  describe('isItemOpen', () => {
31
32
  it('returns true when the item is open', () => {
32
- const { toggleItem, isItemOpen } = useAccordionState()
33
+ const { toggleItem, isItemOpen } = useAccordionState(ref<string[]>([]))
33
34
 
34
35
  toggleItem('item1')
35
36
 
@@ -37,7 +38,7 @@ describe('useAccordionState', () => {
37
38
  })
38
39
 
39
40
  it('returns false when the item is not open', () => {
40
- const { isItemOpen } = useAccordionState()
41
+ const { isItemOpen } = useAccordionState(ref<string[]>([]))
41
42
 
42
43
  expect(isItemOpen('item1')).toBe(false)
43
44
  })
@@ -45,7 +46,7 @@ describe('useAccordionState', () => {
45
46
 
46
47
  describe('isItemFocused', () => {
47
48
  it('returns true when the item is focused', () => {
48
- const { setFocus, isItemFocused } = useAccordionState()
49
+ const { setFocus, isItemFocused } = useAccordionState(ref<string[]>([]))
49
50
 
50
51
  setFocus('item1')
51
52
 
@@ -53,7 +54,7 @@ describe('useAccordionState', () => {
53
54
  })
54
55
 
55
56
  it('returns false when the item is not focused', () => {
56
- const { isItemFocused } = useAccordionState()
57
+ const { isItemFocused } = useAccordionState(ref<string[]>([]))
57
58
 
58
59
  expect(isItemFocused('item1')).toBe(false)
59
60
  })
@@ -61,7 +62,7 @@ describe('useAccordionState', () => {
61
62
 
62
63
  describe('setFocus', () => {
63
64
  it('sets the focus on the specified item', () => {
64
- const { setFocus, focusedItemId } = useAccordionState()
65
+ const { setFocus, focusedItemId } = useAccordionState(ref<string[]>([]))
65
66
 
66
67
  setFocus('item1')
67
68
 
@@ -69,7 +70,7 @@ describe('useAccordionState', () => {
69
70
  })
70
71
 
71
72
  it('removes focus when null is passed', () => {
72
- const { setFocus, focusedItemId } = useAccordionState()
73
+ const { setFocus, focusedItemId } = useAccordionState(ref<string[]>([]))
73
74
 
74
75
  // D'abord définir le focus
75
76
  setFocus('item1')
@@ -82,7 +83,7 @@ describe('useAccordionState', () => {
82
83
  })
83
84
 
84
85
  it('does nothing when the same item is already focused', () => {
85
- const { setFocus, focusedItemId } = useAccordionState()
86
+ const { setFocus, focusedItemId } = useAccordionState(ref<string[]>([]))
86
87
 
87
88
  setFocus('item1')
88
89
  const originalFocusedItem = focusedItemId.value
@@ -96,7 +97,7 @@ describe('useAccordionState', () => {
96
97
 
97
98
  describe('focus behavior during toggle', () => {
98
99
  it('sets focus when opening an item', () => {
99
- const { toggleItem, focusedItemId } = useAccordionState()
100
+ const { toggleItem, focusedItemId } = useAccordionState(ref<string[]>([]))
100
101
 
101
102
  toggleItem('item1')
102
103
 
@@ -104,7 +105,7 @@ describe('useAccordionState', () => {
104
105
  })
105
106
 
106
107
  it('maintains focus when closing a non-focused item', () => {
107
- const { toggleItem, setFocus, focusedItemId } = useAccordionState()
108
+ const { toggleItem, setFocus, focusedItemId } = useAccordionState(ref<string[]>([]))
108
109
 
109
110
  // Ouvrir deux éléments
110
111
  toggleItem('item1')
@@ -128,7 +129,7 @@ describe('useAccordionState', () => {
128
129
  })
129
130
 
130
131
  it('removes focus when closing the focused item', () => {
131
- const { toggleItem, focusedItemId } = useAccordionState()
132
+ const { toggleItem, focusedItemId } = useAccordionState(ref<string[]>([]))
132
133
 
133
134
  // Ouvrir un élément (qui sera automatiquement focalisé)
134
135
  toggleItem('item1')
@@ -141,4 +142,31 @@ describe('useAccordionState', () => {
141
142
  expect(focusedItemId.value).toBeNull()
142
143
  })
143
144
  })
145
+
146
+ describe('external ref synchronization', () => {
147
+ it('uses the provided ref for openItems', () => {
148
+ const externalRef = ref<string[]>(['item1'])
149
+ const { isItemOpen } = useAccordionState(externalRef)
150
+
151
+ expect(isItemOpen('item1')).toBe(true)
152
+ })
153
+
154
+ it('mutates the provided ref when toggling', () => {
155
+ const externalRef = ref<string[]>([])
156
+ const { toggleItem } = useAccordionState(externalRef)
157
+
158
+ toggleItem('item1')
159
+
160
+ expect(externalRef.value).toContain('item1')
161
+ })
162
+
163
+ it('reflects external changes to the ref', () => {
164
+ const externalRef = ref<string[]>([])
165
+ const { isItemOpen } = useAccordionState(externalRef)
166
+
167
+ externalRef.value = ['item2']
168
+
169
+ expect(isItemOpen('item2')).toBe(true)
170
+ })
171
+ })
144
172
  })
@@ -1,4 +1,4 @@
1
- import { ref } from 'vue'
1
+ import { ref, type Ref } from 'vue'
2
2
 
3
3
  export interface AccordionState {
4
4
  openItems: { value: string[] }
@@ -9,8 +9,7 @@ export interface AccordionState {
9
9
  setFocus: (itemId: string | null) => void
10
10
  }
11
11
 
12
- export default function useAccordionState(): AccordionState {
13
- const openItems = ref<string[]>([])
12
+ export default function useAccordionState(openItems: Ref<string[]>): AccordionState {
14
13
  const focusedItemId = ref<string | null>(null)
15
14
 
16
15
  const toggleItem = (itemId: string) => {
@@ -23,7 +22,7 @@ export default function useAccordionState(): AccordionState {
23
22
  setFocus(itemId)
24
23
  }
25
24
  else {
26
- openItems.value.splice(index, 1)
25
+ openItems.value = openItems.value.filter(id => id !== itemId)
27
26
  if (!wasFocused) {
28
27
  setFocus(itemId)
29
28
  }