@arbor-education/design-system.components 0.15.0 → 0.16.0

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 (297) hide show
  1. package/.gather/skills/write-stories/SKILL.md +207 -271
  2. package/.storybook/preview.ts +5 -0
  3. package/CHANGELOG.md +17 -0
  4. package/README.md +8 -0
  5. package/component-library.md +144 -13
  6. package/dist/components/articleCard/ArticleCard.stories.d.ts +137 -11
  7. package/dist/components/articleCard/ArticleCard.stories.d.ts.map +1 -1
  8. package/dist/components/articleCard/ArticleCard.stories.js +358 -91
  9. package/dist/components/articleCard/ArticleCard.stories.js.map +1 -1
  10. package/dist/components/avatar/Avatar.stories.d.ts +6 -6
  11. package/dist/components/avatar/Avatar.stories.d.ts.map +1 -1
  12. package/dist/components/avatar/Avatar.stories.js +393 -49
  13. package/dist/components/avatar/Avatar.stories.js.map +1 -1
  14. package/dist/components/avatarGroup/AvatarGroup.stories.d.ts +9 -7
  15. package/dist/components/avatarGroup/AvatarGroup.stories.d.ts.map +1 -1
  16. package/dist/components/avatarGroup/AvatarGroup.stories.js +688 -65
  17. package/dist/components/avatarGroup/AvatarGroup.stories.js.map +1 -1
  18. package/dist/components/banner/Banner.stories.d.ts.map +1 -1
  19. package/dist/components/banner/Banner.stories.js +7 -3
  20. package/dist/components/banner/Banner.stories.js.map +1 -1
  21. package/dist/components/card/Card.stories.d.ts +105 -4
  22. package/dist/components/card/Card.stories.d.ts.map +1 -1
  23. package/dist/components/card/Card.stories.js +336 -18
  24. package/dist/components/card/Card.stories.js.map +1 -1
  25. package/dist/components/combobox/Combobox.stories.d.ts +134 -21
  26. package/dist/components/combobox/Combobox.stories.d.ts.map +1 -1
  27. package/dist/components/combobox/Combobox.stories.js +676 -175
  28. package/dist/components/combobox/Combobox.stories.js.map +1 -1
  29. package/dist/components/datePicker/DatePicker.stories.d.ts +119 -27
  30. package/dist/components/datePicker/DatePicker.stories.d.ts.map +1 -1
  31. package/dist/components/datePicker/DatePicker.stories.js +575 -47
  32. package/dist/components/datePicker/DatePicker.stories.js.map +1 -1
  33. package/dist/components/dateTimePicker/DateTimePicker.stories.d.ts +155 -39
  34. package/dist/components/dateTimePicker/DateTimePicker.stories.d.ts.map +1 -1
  35. package/dist/components/dateTimePicker/DateTimePicker.stories.js +674 -103
  36. package/dist/components/dateTimePicker/DateTimePicker.stories.js.map +1 -1
  37. package/dist/components/editableText/EditableText.stories.d.ts +53 -12
  38. package/dist/components/editableText/EditableText.stories.d.ts.map +1 -1
  39. package/dist/components/editableText/EditableText.stories.js +401 -64
  40. package/dist/components/editableText/EditableText.stories.js.map +1 -1
  41. package/dist/components/formField/FormField.d.ts +4 -0
  42. package/dist/components/formField/FormField.d.ts.map +1 -1
  43. package/dist/components/formField/FormField.js +2 -1
  44. package/dist/components/formField/FormField.js.map +1 -1
  45. package/dist/components/formField/FormField.test.js +5 -0
  46. package/dist/components/formField/FormField.test.js.map +1 -1
  47. package/dist/components/formField/fieldset/Fieldset.stories.d.ts +56 -4
  48. package/dist/components/formField/fieldset/Fieldset.stories.d.ts.map +1 -1
  49. package/dist/components/formField/fieldset/Fieldset.stories.js +534 -28
  50. package/dist/components/formField/fieldset/Fieldset.stories.js.map +1 -1
  51. package/dist/components/formField/inputs/checkbox/CheckboxGroup.d.ts +3 -1
  52. package/dist/components/formField/inputs/checkbox/CheckboxGroup.d.ts.map +1 -1
  53. package/dist/components/formField/inputs/checkbox/CheckboxInput.js +1 -1
  54. package/dist/components/formField/inputs/checkbox/CheckboxInput.js.map +1 -1
  55. package/dist/components/formField/inputs/colourPickerDropdown/ColourPickerDropdown.stories.d.ts +95 -1
  56. package/dist/components/formField/inputs/colourPickerDropdown/ColourPickerDropdown.stories.d.ts.map +1 -1
  57. package/dist/components/formField/inputs/colourPickerDropdown/ColourPickerDropdown.stories.js +386 -9
  58. package/dist/components/formField/inputs/colourPickerDropdown/ColourPickerDropdown.stories.js.map +1 -1
  59. package/dist/components/formField/inputs/radio/RadioButtonGroup.d.ts +6 -2
  60. package/dist/components/formField/inputs/radio/RadioButtonGroup.d.ts.map +1 -1
  61. package/dist/components/formField/inputs/radio/RadioButtonGroup.js.map +1 -1
  62. package/dist/components/formField/inputs/radio/RadioButtonInput.stories.d.ts.map +1 -1
  63. package/dist/components/formField/inputs/radio/RadioButtonInput.stories.js +61 -49
  64. package/dist/components/formField/inputs/radio/RadioButtonInput.stories.js.map +1 -1
  65. package/dist/components/formField/inputs/selectDropdown/SelectDropdown.stories.d.ts +188 -166
  66. package/dist/components/formField/inputs/selectDropdown/SelectDropdown.stories.d.ts.map +1 -1
  67. package/dist/components/formField/inputs/selectDropdown/SelectDropdown.stories.js +821 -160
  68. package/dist/components/formField/inputs/selectDropdown/SelectDropdown.stories.js.map +1 -1
  69. package/dist/components/formField/inputs/time/TimeInput.stories.d.ts +176 -22
  70. package/dist/components/formField/inputs/time/TimeInput.stories.d.ts.map +1 -1
  71. package/dist/components/formField/inputs/time/TimeInput.stories.js +851 -92
  72. package/dist/components/formField/inputs/time/TimeInput.stories.js.map +1 -1
  73. package/dist/components/formField/label/Label.stories.d.ts +54 -5
  74. package/dist/components/formField/label/Label.stories.d.ts.map +1 -1
  75. package/dist/components/formField/label/Label.stories.js +238 -4
  76. package/dist/components/formField/label/Label.stories.js.map +1 -1
  77. package/dist/components/icoText/IcoText.stories.d.ts +32 -6
  78. package/dist/components/icoText/IcoText.stories.d.ts.map +1 -1
  79. package/dist/components/icoText/IcoText.stories.js +309 -14
  80. package/dist/components/icoText/IcoText.stories.js.map +1 -1
  81. package/dist/components/kpiCard/KPICard.stories.d.ts +100 -2
  82. package/dist/components/kpiCard/KPICard.stories.d.ts.map +1 -1
  83. package/dist/components/kpiCard/KPICard.stories.js +354 -10
  84. package/dist/components/kpiCard/KPICard.stories.js.map +1 -1
  85. package/dist/components/kvpList/KVPList.stories.d.ts +57 -4
  86. package/dist/components/kvpList/KVPList.stories.d.ts.map +1 -1
  87. package/dist/components/kvpList/KVPList.stories.js +403 -10
  88. package/dist/components/kvpList/KVPList.stories.js.map +1 -1
  89. package/dist/components/modal/Modal.stories.d.ts +113 -9
  90. package/dist/components/modal/Modal.stories.d.ts.map +1 -1
  91. package/dist/components/modal/Modal.stories.js +633 -13
  92. package/dist/components/modal/Modal.stories.js.map +1 -1
  93. package/dist/components/modal/modalManager/ModalManager.stories.d.ts +34 -10
  94. package/dist/components/modal/modalManager/ModalManager.stories.d.ts.map +1 -1
  95. package/dist/components/modal/modalManager/ModalManager.stories.js +463 -85
  96. package/dist/components/modal/modalManager/ModalManager.stories.js.map +1 -1
  97. package/dist/components/pill/Pill.d.ts.map +1 -1
  98. package/dist/components/pill/Pill.js +1 -1
  99. package/dist/components/pill/Pill.js.map +1 -1
  100. package/dist/components/pill/Pill.stories.d.ts.map +1 -1
  101. package/dist/components/pill/Pill.stories.js +11 -13
  102. package/dist/components/pill/Pill.stories.js.map +1 -1
  103. package/dist/components/row/Row.stories.d.ts +1 -2
  104. package/dist/components/row/Row.stories.d.ts.map +1 -1
  105. package/dist/components/row/Row.stories.js +360 -50
  106. package/dist/components/row/Row.stories.js.map +1 -1
  107. package/dist/components/searchBar/SearchBar.stories.d.ts +52 -4
  108. package/dist/components/searchBar/SearchBar.stories.d.ts.map +1 -1
  109. package/dist/components/searchBar/SearchBar.stories.js +428 -36
  110. package/dist/components/searchBar/SearchBar.stories.js.map +1 -1
  111. package/dist/components/section/Section.stories.d.ts +11 -41
  112. package/dist/components/section/Section.stories.d.ts.map +1 -1
  113. package/dist/components/section/Section.stories.js +494 -56
  114. package/dist/components/section/Section.stories.js.map +1 -1
  115. package/dist/components/singleUser/SingleUser.stories.d.ts +5 -4
  116. package/dist/components/singleUser/SingleUser.stories.d.ts.map +1 -1
  117. package/dist/components/singleUser/SingleUser.stories.js +303 -31
  118. package/dist/components/singleUser/SingleUser.stories.js.map +1 -1
  119. package/dist/components/slideoverManager/SlideoverManager.stories.d.ts +32 -11
  120. package/dist/components/slideoverManager/SlideoverManager.stories.d.ts.map +1 -1
  121. package/dist/components/slideoverManager/SlideoverManager.stories.js +380 -84
  122. package/dist/components/slideoverManager/SlideoverManager.stories.js.map +1 -1
  123. package/dist/components/table/DSDefaultColDef.d.ts.map +1 -1
  124. package/dist/components/table/DSDefaultColDef.js +4 -3
  125. package/dist/components/table/DSDefaultColDef.js.map +1 -1
  126. package/dist/components/table/Table.d.ts +6 -1
  127. package/dist/components/table/Table.d.ts.map +1 -1
  128. package/dist/components/table/Table.js +8 -3
  129. package/dist/components/table/Table.js.map +1 -1
  130. package/dist/components/table/Table.stories.d.ts +2 -0
  131. package/dist/components/table/Table.stories.d.ts.map +1 -1
  132. package/dist/components/table/Table.stories.js +357 -3
  133. package/dist/components/table/Table.stories.js.map +1 -1
  134. package/dist/components/table/TableFooter.stories.d.ts +49 -0
  135. package/dist/components/table/TableFooter.stories.d.ts.map +1 -0
  136. package/dist/components/table/TableFooter.stories.js +137 -0
  137. package/dist/components/table/TableFooter.stories.js.map +1 -0
  138. package/dist/components/table/TableHeader.stories.d.ts +93 -0
  139. package/dist/components/table/TableHeader.stories.d.ts.map +1 -0
  140. package/dist/components/table/TableHeader.stories.js +176 -0
  141. package/dist/components/table/TableHeader.stories.js.map +1 -0
  142. package/dist/components/table/cellEditors/DateCellEditor.stories.d.ts +44 -0
  143. package/dist/components/table/cellEditors/DateCellEditor.stories.d.ts.map +1 -0
  144. package/dist/components/table/cellEditors/DateCellEditor.stories.js +186 -0
  145. package/dist/components/table/cellEditors/DateCellEditor.stories.js.map +1 -0
  146. package/dist/components/table/cellRenderers/BooleanCellRenderer.stories.d.ts +40 -0
  147. package/dist/components/table/cellRenderers/BooleanCellRenderer.stories.d.ts.map +1 -0
  148. package/dist/components/table/cellRenderers/BooleanCellRenderer.stories.js +209 -0
  149. package/dist/components/table/cellRenderers/BooleanCellRenderer.stories.js.map +1 -0
  150. package/dist/components/table/cellRenderers/ButtonCellRenderer.stories.d.ts +48 -0
  151. package/dist/components/table/cellRenderers/ButtonCellRenderer.stories.d.ts.map +1 -0
  152. package/dist/components/table/cellRenderers/ButtonCellRenderer.stories.js +244 -0
  153. package/dist/components/table/cellRenderers/ButtonCellRenderer.stories.js.map +1 -0
  154. package/dist/components/table/cellRenderers/CheckboxCellRenderer.d.ts.map +1 -1
  155. package/dist/components/table/cellRenderers/CheckboxCellRenderer.js +3 -1
  156. package/dist/components/table/cellRenderers/CheckboxCellRenderer.js.map +1 -1
  157. package/dist/components/table/cellRenderers/CheckboxCellRenderer.stories.d.ts +64 -0
  158. package/dist/components/table/cellRenderers/CheckboxCellRenderer.stories.d.ts.map +1 -0
  159. package/dist/components/table/cellRenderers/CheckboxCellRenderer.stories.js +241 -0
  160. package/dist/components/table/cellRenderers/CheckboxCellRenderer.stories.js.map +1 -0
  161. package/dist/components/table/cellRenderers/DefaultCellRenderer.stories.d.ts +55 -0
  162. package/dist/components/table/cellRenderers/DefaultCellRenderer.stories.d.ts.map +1 -0
  163. package/dist/components/table/cellRenderers/DefaultCellRenderer.stories.js +245 -0
  164. package/dist/components/table/cellRenderers/DefaultCellRenderer.stories.js.map +1 -0
  165. package/dist/components/table/cellRenderers/InlineTextCellRenderer.stories.d.ts +67 -0
  166. package/dist/components/table/cellRenderers/InlineTextCellRenderer.stories.d.ts.map +1 -0
  167. package/dist/components/table/cellRenderers/InlineTextCellRenderer.stories.js +221 -0
  168. package/dist/components/table/cellRenderers/InlineTextCellRenderer.stories.js.map +1 -0
  169. package/dist/components/table/cellRenderers/SelectDropdownCellRenderer.stories.d.ts +75 -0
  170. package/dist/components/table/cellRenderers/SelectDropdownCellRenderer.stories.d.ts.map +1 -0
  171. package/dist/components/table/cellRenderers/SelectDropdownCellRenderer.stories.js +270 -0
  172. package/dist/components/table/cellRenderers/SelectDropdownCellRenderer.stories.js.map +1 -0
  173. package/dist/components/table/columnFilters/BooleanFilter.stories.d.ts +57 -0
  174. package/dist/components/table/columnFilters/BooleanFilter.stories.d.ts.map +1 -0
  175. package/dist/components/table/columnFilters/BooleanFilter.stories.js +198 -0
  176. package/dist/components/table/columnFilters/BooleanFilter.stories.js.map +1 -0
  177. package/dist/components/table/columnFilters/TimeFilter.stories.d.ts +58 -0
  178. package/dist/components/table/columnFilters/TimeFilter.stories.d.ts.map +1 -0
  179. package/dist/components/table/columnFilters/TimeFilter.stories.js +207 -0
  180. package/dist/components/table/columnFilters/TimeFilter.stories.js.map +1 -0
  181. package/dist/components/table/pagination/PaginationPanel.stories.d.ts +113 -0
  182. package/dist/components/table/pagination/PaginationPanel.stories.d.ts.map +1 -0
  183. package/dist/components/table/pagination/PaginationPanel.stories.js +272 -0
  184. package/dist/components/table/pagination/PaginationPanel.stories.js.map +1 -0
  185. package/dist/components/table/tableControls/TableControls.stories.d.ts +151 -0
  186. package/dist/components/table/tableControls/TableControls.stories.d.ts.map +1 -0
  187. package/dist/components/table/tableControls/TableControls.stories.js +356 -0
  188. package/dist/components/table/tableControls/TableControls.stories.js.map +1 -0
  189. package/dist/components/table/tableControls/TableSettingsDropdown.d.ts +27 -1
  190. package/dist/components/table/tableControls/TableSettingsDropdown.d.ts.map +1 -1
  191. package/dist/components/table/tableControls/TableSettingsDropdown.js +53 -26
  192. package/dist/components/table/tableControls/TableSettingsDropdown.js.map +1 -1
  193. package/dist/components/table/tableControls/TableSettingsDropdown.test.d.ts +2 -0
  194. package/dist/components/table/tableControls/TableSettingsDropdown.test.d.ts.map +1 -0
  195. package/dist/components/table/tableControls/TableSettingsDropdown.test.js +178 -0
  196. package/dist/components/table/tableControls/TableSettingsDropdown.test.js.map +1 -0
  197. package/dist/components/tabs/Tabs.stories.d.ts +22 -4
  198. package/dist/components/tabs/Tabs.stories.d.ts.map +1 -1
  199. package/dist/components/tabs/Tabs.stories.js +398 -22
  200. package/dist/components/tabs/Tabs.stories.js.map +1 -1
  201. package/dist/components/tabs/TabsItem.stories.d.ts +54 -1
  202. package/dist/components/tabs/TabsItem.stories.d.ts.map +1 -1
  203. package/dist/components/tabs/TabsItem.stories.js +61 -9
  204. package/dist/components/tabs/TabsItem.stories.js.map +1 -1
  205. package/dist/components/toast/Toast.stories.d.ts +103 -10
  206. package/dist/components/toast/Toast.stories.d.ts.map +1 -1
  207. package/dist/components/toast/Toast.stories.js +409 -47
  208. package/dist/components/toast/Toast.stories.js.map +1 -1
  209. package/dist/components/toggle/Toggle.stories.d.ts +61 -46
  210. package/dist/components/toggle/Toggle.stories.d.ts.map +1 -1
  211. package/dist/components/toggle/Toggle.stories.js +311 -122
  212. package/dist/components/toggle/Toggle.stories.js.map +1 -1
  213. package/dist/components/tooltip/Tooltip.stories.d.ts +78 -6
  214. package/dist/components/tooltip/Tooltip.stories.d.ts.map +1 -1
  215. package/dist/components/tooltip/Tooltip.stories.js +413 -7
  216. package/dist/components/tooltip/Tooltip.stories.js.map +1 -1
  217. package/dist/components/tooltip/TooltipWrapper.stories.d.ts +71 -7
  218. package/dist/components/tooltip/TooltipWrapper.stories.d.ts.map +1 -1
  219. package/dist/components/tooltip/TooltipWrapper.stories.js +238 -10
  220. package/dist/components/tooltip/TooltipWrapper.stories.js.map +1 -1
  221. package/dist/index.css +8 -0
  222. package/dist/index.css.map +1 -1
  223. package/dist/utils/PopupParentContext.stories.d.ts +17 -0
  224. package/dist/utils/PopupParentContext.stories.d.ts.map +1 -0
  225. package/dist/utils/PopupParentContext.stories.js +266 -0
  226. package/dist/utils/PopupParentContext.stories.js.map +1 -0
  227. package/dist/utils/getDefaultPopupParent.d.ts.map +1 -1
  228. package/dist/utils/getDefaultPopupParent.js +6 -0
  229. package/dist/utils/getDefaultPopupParent.js.map +1 -1
  230. package/package.json +1 -1
  231. package/src/components/articleCard/ArticleCard.stories.tsx +524 -111
  232. package/src/components/avatar/Avatar.stories.tsx +504 -59
  233. package/src/components/avatarGroup/AvatarGroup.stories.tsx +977 -175
  234. package/src/components/banner/Banner.stories.tsx +7 -3
  235. package/src/components/card/Card.stories.tsx +466 -36
  236. package/src/components/combobox/Combobox.stories.tsx +867 -260
  237. package/src/components/datePicker/DatePicker.stories.tsx +777 -60
  238. package/src/components/dateTimePicker/DateTimePicker.stories.tsx +910 -132
  239. package/src/components/editableText/EditableText.stories.tsx +567 -91
  240. package/src/components/formField/FormField.test.tsx +6 -0
  241. package/src/components/formField/FormField.tsx +5 -0
  242. package/src/components/formField/fieldset/Fieldset.stories.tsx +761 -51
  243. package/src/components/formField/inputs/checkbox/CheckboxGroup.tsx +1 -1
  244. package/src/components/formField/inputs/checkbox/CheckboxInput.tsx +1 -1
  245. package/src/components/formField/inputs/colourPickerDropdown/ColourPickerDropdown.stories.tsx +504 -11
  246. package/src/components/formField/inputs/radio/RadioButtonGroup.tsx +17 -4
  247. package/src/components/formField/inputs/radio/RadioButtonInput.stories.tsx +71 -59
  248. package/src/components/formField/inputs/selectDropdown/SelectDropdown.stories.tsx +1079 -168
  249. package/src/components/formField/inputs/time/TimeInput.stories.tsx +1140 -104
  250. package/src/components/formField/label/Label.stories.tsx +317 -8
  251. package/src/components/icoText/IcoText.stories.tsx +442 -31
  252. package/src/components/kpiCard/KPICard.stories.tsx +475 -30
  253. package/src/components/kvpList/KVPList.stories.tsx +593 -26
  254. package/src/components/modal/Modal.stories.tsx +963 -26
  255. package/src/components/modal/modalManager/ModalManager.stories.tsx +612 -454
  256. package/src/components/pill/Pill.stories.tsx +11 -13
  257. package/src/components/pill/Pill.tsx +1 -0
  258. package/src/components/row/Row.stories.tsx +474 -58
  259. package/src/components/searchBar/SearchBar.stories.tsx +570 -38
  260. package/src/components/section/Section.stories.tsx +723 -70
  261. package/src/components/singleUser/SingleUser.stories.tsx +393 -34
  262. package/src/components/slideoverManager/SlideoverManager.stories.tsx +572 -342
  263. package/src/components/table/DSDefaultColDef.ts +25 -5
  264. package/src/components/table/Table.stories.tsx +411 -3
  265. package/src/components/table/Table.tsx +9 -2
  266. package/src/components/table/TableFooter.stories.tsx +196 -0
  267. package/src/components/table/TableHeader.stories.tsx +251 -0
  268. package/src/components/table/cellEditors/DateCellEditor.stories.tsx +245 -0
  269. package/src/components/table/cellRenderers/BooleanCellRenderer.stories.tsx +278 -0
  270. package/src/components/table/cellRenderers/ButtonCellRenderer.stories.tsx +333 -0
  271. package/src/components/table/cellRenderers/CheckboxCellRenderer.stories.tsx +337 -0
  272. package/src/components/table/cellRenderers/CheckboxCellRenderer.tsx +5 -1
  273. package/src/components/table/cellRenderers/DefaultCellRenderer.stories.tsx +342 -0
  274. package/src/components/table/cellRenderers/InlineTextCellRenderer.stories.tsx +292 -0
  275. package/src/components/table/cellRenderers/SelectDropdownCellRenderer.stories.tsx +369 -0
  276. package/src/components/table/columnFilters/BooleanFilter.stories.tsx +268 -0
  277. package/src/components/table/columnFilters/TimeFilter.stories.tsx +281 -0
  278. package/src/components/table/pagination/PaginationPanel.stories.tsx +327 -0
  279. package/src/components/table/tableControls/TableControls.stories.tsx +415 -0
  280. package/src/components/table/tableControls/TableSettingsDropdown.test.tsx +207 -0
  281. package/src/components/table/tableControls/TableSettingsDropdown.tsx +103 -39
  282. package/src/components/tabs/Tabs.stories.tsx +540 -60
  283. package/src/components/tabs/TabsItem.stories.tsx +82 -8
  284. package/src/components/toast/Toast.stories.tsx +539 -77
  285. package/src/components/toggle/Toggle.stories.tsx +371 -135
  286. package/src/components/tooltip/Tooltip.stories.tsx +606 -15
  287. package/src/components/tooltip/TooltipWrapper.stories.tsx +348 -12
  288. package/src/docs/Contributing.mdx +241 -0
  289. package/src/docs/UsingComponents.mdx +93 -0
  290. package/src/docs/Welcome.mdx +68 -0
  291. package/src/global.scss +7 -0
  292. package/src/utils/PopupParentContext.stories.tsx +367 -0
  293. package/src/utils/getDefaultPopupParent.ts +6 -0
  294. package/.ralph/storybook-upgrade/knowledge.md +0 -308
  295. package/.ralph/storybook-upgrade/prd.json +0 -777
  296. package/.ralph/storybook-upgrade/progress.md +0 -342
  297. package/src/components/table/TableWIP.mdx +0 -3
@@ -0,0 +1,415 @@
1
+ import type { Meta, StoryObj } from '@storybook/react-vite';
2
+ import {
3
+ Controls,
4
+ Heading as DocHeading,
5
+ Markdown,
6
+ Primary as DocPrimary,
7
+ Stories,
8
+ Subtitle,
9
+ Title,
10
+ } from '@storybook/addon-docs/blocks';
11
+ import { fn } from 'storybook/test';
12
+ import { TableControls } from './TableControls';
13
+
14
+ // ---------------------------------------------------------------------------
15
+ // Docs page content
16
+ // ---------------------------------------------------------------------------
17
+
18
+ const DESCRIPTION_INTRO = [
19
+ '`TableControls` is the pre-composed toolbar slot for the Arbor data table.',
20
+ 'It renders a two-group flex row: a **start group** (bulk actions, undo, redo, hide columns)',
21
+ 'and an **end group** (search, download, settings, help, expand).',
22
+ '',
23
+ 'Every slot is opt-in — only pass the props for the controls you need.',
24
+ 'If neither group has any props, the component returns `null` and renders nothing.',
25
+ '',
26
+ 'Pass the entire `<TableControls />` element as the `headerContent` prop of `<Table />`.',
27
+ ].join('\n');
28
+
29
+ const USAGE_GUIDANCE = [
30
+ '### When to use',
31
+ '',
32
+ '- As the `headerContent` of a `<Table />` to give it a standard toolbar',
33
+ '- When you need a consistent mix of bulk actions, column visibility, search, and settings',
34
+ '- When consumers need access to the full set of standard table controls in one composable component',
35
+ '',
36
+ '---',
37
+ '',
38
+ '### When NOT to use',
39
+ '',
40
+ '| Situation | Alternative |',
41
+ '|---|---|',
42
+ '| You only need a search bar | Pass `<SearchBar />` directly as `headerContent` |',
43
+ '| You need a custom toolbar layout | Compose your own header with individual components |',
44
+ '| The table has no toolbar at all | Omit `headerContent` entirely |',
45
+ '',
46
+ '---',
47
+ '',
48
+ '### Start group vs end group',
49
+ '',
50
+ '| Prop | Group | Description |',
51
+ '|---|---|---|',
52
+ '| `bulkActions` | Start | Array of `{ displayName, callback, disabled? }` — shown as a dropdown when rows are selected |',
53
+ '| `onUndo` | Start | Renders an undo icon button |',
54
+ '| `onRedo` | Start | Renders a redo icon button |',
55
+ '| `showHideColumns` | Start | Renders the `HideColumnsDropdown` |',
56
+ '| `hasSearch` | End | Renders the `SearchBar` |',
57
+ '| `onDownload` | End | Renders a download icon button |',
58
+ '| `onTooltipsToggle` | End | Renders a tooltip-toggle icon button (aria-pressed) |',
59
+ '| `showSettings` | End | Renders the `TableSettingsDropdown` |',
60
+ '| `onHelp` | End | Renders a help icon button |',
61
+ '| `onExpandToggle` | End | Renders an expand/shrink icon button |',
62
+ ].join('\n');
63
+
64
+ const DEVELOPER_NOTES = [
65
+ '### Wiring into `<Table />`',
66
+ '',
67
+ '```tsx',
68
+ "import { Table, TableControls } from '@arbor-education/design-system.components';",
69
+ '',
70
+ 'function MyTable() {',
71
+ ' return (',
72
+ ' <Table',
73
+ ' rowData={rows}',
74
+ ' columnDefs={colDefs}',
75
+ ' headerContent={(',
76
+ ' <TableControls',
77
+ ' bulkActions={[',
78
+ ' { displayName: "Export selected", callback: api => exportCsv(api) },',
79
+ ' { displayName: "Delete selected", callback: api => deleteRows(api) },',
80
+ ' ]}',
81
+ ' onUndo={handleUndo}',
82
+ ' showHideColumns',
83
+ ' onDownload={handleDownload}',
84
+ ' showSettings',
85
+ ' onHelp={openHelpPanel}',
86
+ ' onExpandToggle={setExpanded}',
87
+ ' />',
88
+ ' )}',
89
+ ' />',
90
+ ' );',
91
+ '}',
92
+ '```',
93
+ '',
94
+ '---',
95
+ '',
96
+ '### `BulkAction` type',
97
+ '',
98
+ '```ts',
99
+ "import type { BulkAction } from '@arbor-education/design-system.components';",
100
+ '',
101
+ 'type BulkAction = {',
102
+ ' displayName: string;',
103
+ ' callback: (gridApi: GridApi) => void;',
104
+ ' disabled?: boolean;',
105
+ '};',
106
+ '```',
107
+ '',
108
+ '---',
109
+ '',
110
+ '### `HideColumnsDropdown` and `GridApiContext`',
111
+ '',
112
+ 'When `showHideColumns` is `true`, `TableControls` renders `HideColumnsDropdown`.',
113
+ '`HideColumnsDropdown` reads the column list from `GridApiContext` — this context is provided',
114
+ 'automatically by `<Table />`. When used outside a `<Table />`, pass the `hideColumnsProps`',
115
+ 'prop to supply columns manually:',
116
+ '',
117
+ '```tsx',
118
+ '<TableControls',
119
+ ' showHideColumns',
120
+ ' hideColumnsProps={{',
121
+ ' columns: myColumns,',
122
+ ' onSelectionChanged: handleVisibilityChange,',
123
+ ' }}',
124
+ '/>',
125
+ '```',
126
+ '',
127
+ '---',
128
+ '',
129
+ '### TypeScript types',
130
+ '',
131
+ '```ts',
132
+ "import { TableControls } from '@arbor-education/design-system.components';",
133
+ "import type { TableControlsProps, BulkAction } from '@arbor-education/design-system.components';",
134
+ '```',
135
+ ].join('\n');
136
+
137
+ const RELATED_COMPONENTS = [
138
+ '## Related components',
139
+ '',
140
+ '[Table](?path=/docs/components-table--docs) · [PaginationPanel](?path=/docs/components-table-paginationpanel--docs) · [SearchBar](?path=/docs/components-searchbar--docs) · [Button](?path=/docs/components-button--docs)',
141
+ ].join('\n');
142
+
143
+ const PROPS_INTRO = 'The preview below is wired to the **Controls** panel — toggle props to see the toolbar update in real time.';
144
+
145
+ // ---------------------------------------------------------------------------
146
+ // Custom DocsPage
147
+ // ---------------------------------------------------------------------------
148
+
149
+ function TableControlsDocsPage() {
150
+ return (
151
+ <>
152
+ <Title />
153
+ <Subtitle />
154
+ <Markdown>{DESCRIPTION_INTRO}</Markdown>
155
+ <DocHeading>Interactive example</DocHeading>
156
+ <Markdown>{PROPS_INTRO}</Markdown>
157
+ <DocPrimary />
158
+ <Controls />
159
+ <DocHeading>Usage guidance</DocHeading>
160
+ <Markdown>{USAGE_GUIDANCE}</Markdown>
161
+ <DocHeading>Developer notes</DocHeading>
162
+ <Markdown>{DEVELOPER_NOTES}</Markdown>
163
+ <DocHeading>Examples</DocHeading>
164
+ <Stories title="" />
165
+ <Markdown>{RELATED_COMPONENTS}</Markdown>
166
+ </>
167
+ );
168
+ }
169
+
170
+ // ---------------------------------------------------------------------------
171
+ // Meta
172
+ // ---------------------------------------------------------------------------
173
+
174
+ const meta = {
175
+ title: 'Components/Table/TableControls',
176
+ component: TableControls,
177
+ tags: ['autodocs'],
178
+ parameters: {
179
+ layout: 'padded',
180
+ docs: { page: TableControlsDocsPage },
181
+ },
182
+ argTypes: {
183
+ bulkActions: {
184
+ description: 'Array of bulk action definitions. Each action has a `displayName`, a `callback(gridApi)`, and an optional `disabled` flag. Renders as a `BulkActionsDropdown` in the start group.',
185
+ control: false,
186
+ table: { type: { summary: 'BulkAction[]' }, defaultValue: { summary: 'undefined' } },
187
+ },
188
+ onUndo: {
189
+ description: 'Renders an undo icon button in the start group. Called with no arguments on click.',
190
+ control: false,
191
+ table: { type: { summary: '() => void' }, defaultValue: { summary: 'undefined' } },
192
+ },
193
+ onRedo: {
194
+ description: 'Renders a redo icon button in the start group. Called with no arguments on click.',
195
+ control: false,
196
+ table: { type: { summary: '() => void' }, defaultValue: { summary: 'undefined' } },
197
+ },
198
+ showHideColumns: {
199
+ description: 'When `true`, renders the `HideColumnsDropdown` in the start group. Reads column visibility from `GridApiContext` (provided by `<Table />`). Pass `hideColumnsProps` for manual column control.',
200
+ control: 'boolean',
201
+ table: { type: { summary: 'boolean' }, defaultValue: { summary: 'false' } },
202
+ },
203
+ hasSearch: {
204
+ description: 'When `true`, renders a `SearchBar` in the end group.',
205
+ control: 'boolean',
206
+ table: { type: { summary: 'boolean' }, defaultValue: { summary: 'false' } },
207
+ },
208
+ onDownload: {
209
+ description: 'Renders a download icon button in the end group. Called with no arguments on click.',
210
+ control: false,
211
+ table: { type: { summary: '() => void' }, defaultValue: { summary: 'undefined' } },
212
+ },
213
+ showSettings: {
214
+ description: 'When `true`, renders the `TableSettingsDropdown` in the end group.',
215
+ control: 'boolean',
216
+ table: { type: { summary: 'boolean' }, defaultValue: { summary: 'false' } },
217
+ },
218
+ onHelp: {
219
+ description: 'Renders a help icon button in the end group. Called with no arguments on click.',
220
+ control: false,
221
+ table: { type: { summary: '() => void' }, defaultValue: { summary: 'undefined' } },
222
+ },
223
+ isExpanded: {
224
+ description: 'Controls the expand/shrink icon state when `onExpandToggle` is provided. `true` = shrink icon; `false` = expand icon.',
225
+ control: 'boolean',
226
+ table: { type: { summary: 'boolean' }, defaultValue: { summary: 'false' } },
227
+ },
228
+ onExpandToggle: {
229
+ description: 'Renders an expand/shrink icon button in the end group. Called with the new expanded state (`boolean`) on click.',
230
+ control: false,
231
+ table: { type: { summary: '(expanded: boolean) => void' }, defaultValue: { summary: 'undefined' } },
232
+ },
233
+ },
234
+ args: {
235
+ onUndo: fn(),
236
+ onRedo: fn(),
237
+ onDownload: fn(),
238
+ onHelp: fn(),
239
+ onExpandToggle: fn(),
240
+ },
241
+ } satisfies Meta<typeof TableControls>;
242
+
243
+ export default meta;
244
+ type Story = StoryObj<typeof TableControls>;
245
+
246
+ // ---------------------------------------------------------------------------
247
+ // Helper
248
+ // ---------------------------------------------------------------------------
249
+
250
+ const withDescription = (story: Story, description: string): Story => ({
251
+ ...story,
252
+ parameters: {
253
+ ...story.parameters,
254
+ docs: { ...story.parameters?.docs, description: { story: description } },
255
+ },
256
+ });
257
+
258
+ // ---------------------------------------------------------------------------
259
+ // Stories
260
+ // ---------------------------------------------------------------------------
261
+
262
+ export const Default: Story = withDescription(
263
+ {
264
+ args: {
265
+ onUndo: fn(),
266
+ onRedo: fn(),
267
+ onDownload: fn(),
268
+ showSettings: true,
269
+ onHelp: fn(),
270
+ onExpandToggle: fn(),
271
+ },
272
+ },
273
+ 'The interactive canvas — toggle Controls to add or remove toolbar slots. '
274
+ + 'Every prop is opt-in: only slots with a prop provided will render. '
275
+ + 'If no props are passed at all, `TableControls` returns `null`.',
276
+ );
277
+
278
+ export const StartGroupOnly: Story = withDescription(
279
+ {
280
+ parameters: { controls: { disable: true } },
281
+ args: {
282
+ bulkActions: [
283
+ { displayName: 'Export selected', callback: fn() },
284
+ { displayName: 'Delete selected', callback: fn(), disabled: true },
285
+ ],
286
+ onUndo: fn(),
287
+ onRedo: fn(),
288
+ showHideColumns: false,
289
+ },
290
+ },
291
+ 'Only the start group: bulk actions dropdown, undo, and redo. '
292
+ + 'No end-group props are passed so that side is omitted entirely. '
293
+ + '`showHideColumns` is `false` here because `HideColumnsDropdown` needs a `GridApiContext` (provided by `<Table />`) to read column data — '
294
+ + 'see the **Within a Table** story for a fully wired example.',
295
+ );
296
+
297
+ export const EndGroupOnly: Story = withDescription(
298
+ {
299
+ parameters: { controls: { disable: true } },
300
+ args: {
301
+ hasSearch: true,
302
+ onDownload: fn(),
303
+ showSettings: true,
304
+ onHelp: fn(),
305
+ onExpandToggle: fn(),
306
+ },
307
+ },
308
+ 'Only the end group: search bar, download, settings dropdown, help, and expand. '
309
+ + 'No start-group props are passed so that side is omitted entirely.',
310
+ );
311
+
312
+ export const BulkActionsOnly: Story = withDescription(
313
+ {
314
+ parameters: {
315
+ controls: { disable: true },
316
+ docs: {
317
+ source: {
318
+ language: 'tsx',
319
+ code: `
320
+ import { Table, TableControls } from '@arbor-education/design-system.components';
321
+
322
+ function MyTable() {
323
+ return (
324
+ <Table
325
+ rowData={rows}
326
+ columnDefs={colDefs}
327
+ rowSelection={{ mode: 'multiRow', checkboxes: true }}
328
+ headerContent={(
329
+ <TableControls
330
+ bulkActions={[
331
+ { displayName: 'Export selected', callback: api => api.exportDataAsCsv() },
332
+ { displayName: 'Delete selected', callback: handleDelete, disabled: !canDelete },
333
+ ]}
334
+ />
335
+ )}
336
+ />
337
+ );
338
+ }
339
+ export default MyTable;
340
+ `.trim(),
341
+ },
342
+ },
343
+ },
344
+ args: {
345
+ bulkActions: [
346
+ { displayName: 'Export selected', callback: fn() },
347
+ { displayName: 'Archive selected', callback: fn() },
348
+ { displayName: 'Delete selected', callback: fn(), disabled: true },
349
+ ],
350
+ },
351
+ },
352
+ 'Only the `bulkActions` slot in the start group — no other controls. '
353
+ + 'Bulk actions render as a `BulkActionsDropdown`. The `callback` receives the AG Grid `GridApi` instance '
354
+ + 'so you can directly call `api.exportDataAsCsv()`, `api.getSelectedRows()`, etc. '
355
+ + 'Pass `disabled: true` to a specific action to grey it out (e.g. "Delete selected" when nothing is selected).',
356
+ );
357
+
358
+ export const FullToolbar: Story = withDescription(
359
+ {
360
+ parameters: {
361
+ controls: { disable: true },
362
+ docs: {
363
+ source: {
364
+ language: 'tsx',
365
+ code: `
366
+ import { Table, TableControls } from '@arbor-education/design-system.components';
367
+
368
+ function FullTableExample() {
369
+ return (
370
+ <Table
371
+ rowData={rows}
372
+ columnDefs={colDefs}
373
+ rowSelection={{ mode: 'multiRow', checkboxes: true }}
374
+ headerContent={(
375
+ <TableControls
376
+ bulkActions={[
377
+ { displayName: 'Export selected', callback: api => api.exportDataAsCsv() },
378
+ { displayName: 'Delete selected', callback: handleDelete },
379
+ ]}
380
+ onUndo={handleUndo}
381
+ onRedo={handleRedo}
382
+ showHideColumns
383
+ onDownload={handleDownload}
384
+ showSettings
385
+ onHelp={openHelpPanel}
386
+ onExpandToggle={setExpanded}
387
+ />
388
+ )}
389
+ />
390
+ );
391
+ }
392
+ export default FullTableExample;
393
+ `.trim(),
394
+ },
395
+ },
396
+ },
397
+ args: {
398
+ bulkActions: [
399
+ { displayName: 'Export selected', callback: fn() },
400
+ { displayName: 'Delete selected', callback: fn() },
401
+ ],
402
+ onUndo: fn(),
403
+ onRedo: fn(),
404
+ showHideColumns: false,
405
+ onDownload: fn(),
406
+ showSettings: true,
407
+ onHelp: fn(),
408
+ onExpandToggle: fn(),
409
+ },
410
+ },
411
+ 'The full toolbar with both groups populated: bulk actions, undo, redo, hide columns, download, settings, help, and expand. '
412
+ + 'This is the maximum control density — only include the slots your users actually need. '
413
+ + '`showHideColumns` renders a `HideColumnsDropdown` that reads columns from `GridApiContext`; '
414
+ + 'in this standalone preview it is disabled since there is no enclosing `<Table />` providing that context.',
415
+ );
@@ -0,0 +1,207 @@
1
+ import { describe, expect, test, vi } from 'vitest';
2
+ import { render, screen } from '@testing-library/react';
3
+ import userEvent from '@testing-library/user-event';
4
+ import '@testing-library/jest-dom/vitest';
5
+ import { TableSettingsDropdown } from './TableSettingsDropdown';
6
+ import { TableSettingsContext } from '../TableSettingsContext';
7
+ import { TABLE_SPACING } from '../tableConsts';
8
+ import { useTableSettings } from '../useTableSettings';
9
+
10
+ const buildContextValue = (overrides: Partial<ReturnType<typeof useTableSettings>> = {}) => ({
11
+ settings: {
12
+ hasColumnBorders: false,
13
+ tableSpacing: TABLE_SPACING.S,
14
+ areCellColorsEnabled: true,
15
+ },
16
+ setTableSpacing: vi.fn(),
17
+ setHasColumnBorders: vi.fn(),
18
+ resetSettings: vi.fn(),
19
+ setAreCellColorsEnabled: vi.fn(),
20
+ ...overrides,
21
+ });
22
+
23
+ const renderDropdown = (
24
+ ui: React.ReactElement,
25
+ contextValue = buildContextValue(),
26
+ ) => {
27
+ return render(
28
+ <TableSettingsContext.Provider value={contextValue}>
29
+ {ui}
30
+ </TableSettingsContext.Provider>,
31
+ );
32
+ };
33
+
34
+ const openDropdown = async () => {
35
+ const trigger = screen.getByRole('button', { name: /table settings/i });
36
+ await userEvent.click(trigger);
37
+ };
38
+
39
+ describe('TableSettingsDropdown', () => {
40
+ describe('default items (no items prop)', () => {
41
+ test('renders the settings trigger button', () => {
42
+ renderDropdown(<TableSettingsDropdown />);
43
+ expect(screen.getByRole('button', { name: /table settings/i })).toBeInTheDocument();
44
+ });
45
+
46
+ test('shows table spacing radio group when opened', async () => {
47
+ renderDropdown(<TableSettingsDropdown />);
48
+ await openDropdown();
49
+ expect(await screen.findByRole('group', { name: /table spacing/i })).toBeInTheDocument();
50
+ });
51
+
52
+ test('shows column borders checkbox when opened', async () => {
53
+ renderDropdown(<TableSettingsDropdown />);
54
+ await openDropdown();
55
+ expect(await screen.findByRole('checkbox', { name: /column borders/i })).toBeInTheDocument();
56
+ });
57
+
58
+ test('shows cell colours checkbox when opened', async () => {
59
+ renderDropdown(<TableSettingsDropdown />);
60
+ await openDropdown();
61
+ expect(await screen.findByRole('checkbox', { name: /cell colours/i })).toBeInTheDocument();
62
+ });
63
+
64
+ test('shows reset button when opened', async () => {
65
+ renderDropdown(<TableSettingsDropdown />);
66
+ await openDropdown();
67
+ expect(await screen.findByRole('button', { name: /reset table settings/i })).toBeInTheDocument();
68
+ });
69
+ });
70
+
71
+ describe('items prop - opt-in to specific built-in items', () => {
72
+ test('shows only table spacing when items contains only tableSpacing', async () => {
73
+ renderDropdown(<TableSettingsDropdown items={['tableSpacing']} />);
74
+ await openDropdown();
75
+ expect(await screen.findByRole('group', { name: /table spacing/i })).toBeInTheDocument();
76
+ expect(screen.queryByRole('checkbox', { name: /column borders/i })).not.toBeInTheDocument();
77
+ expect(screen.queryByRole('checkbox', { name: /cell colours/i })).not.toBeInTheDocument();
78
+ });
79
+
80
+ test('shows both style checkboxes when style is included in items', async () => {
81
+ renderDropdown(<TableSettingsDropdown items={['style']} />);
82
+ await openDropdown();
83
+ expect(await screen.findByRole('checkbox', { name: /column borders/i })).toBeInTheDocument();
84
+ expect(screen.getByRole('checkbox', { name: /cell colours/i })).toBeInTheDocument();
85
+ });
86
+
87
+ test('does not render table spacing when not included in items', async () => {
88
+ renderDropdown(<TableSettingsDropdown items={['style']} />);
89
+ await openDropdown();
90
+ await screen.findByRole('checkbox', { name: /column borders/i });
91
+ expect(screen.queryByRole('group', { name: /table spacing/i })).not.toBeInTheDocument();
92
+ });
93
+
94
+ test('does not render style checkboxes when style is not included in items', async () => {
95
+ renderDropdown(<TableSettingsDropdown items={['tableSpacing']} />);
96
+ await openDropdown();
97
+ await screen.findByRole('group', { name: /table spacing/i });
98
+ expect(screen.queryByRole('checkbox', { name: /column borders/i })).not.toBeInTheDocument();
99
+ expect(screen.queryByRole('checkbox', { name: /cell colours/i })).not.toBeInTheDocument();
100
+ });
101
+ });
102
+
103
+ describe('separator item', () => {
104
+ test('renders a separator when separator is included in items', async () => {
105
+ renderDropdown(<TableSettingsDropdown items={['tableSpacing', 'separator', 'style']} />);
106
+ await openDropdown();
107
+ await screen.findByRole('group', { name: /table spacing/i });
108
+ // 2 separators: 1 from the items array, 1 always-present above the reset button
109
+ expect(screen.getAllByRole('separator')).toHaveLength(2);
110
+ });
111
+ });
112
+
113
+ describe('custom items - checkboxGroup', () => {
114
+ test('renders a checkbox group with the provided legend', async () => {
115
+ const customItem = { type: 'checkboxGroup' as const, legend: 'View options', options: [{ label: 'Show archived rows', checked: false, onChange: vi.fn() }] };
116
+ renderDropdown(<TableSettingsDropdown items={[customItem]} />);
117
+ await openDropdown();
118
+ expect(await screen.findByRole('group', { name: /view options/i })).toBeInTheDocument();
119
+ });
120
+
121
+ test('renders custom checkbox options with provided labels', async () => {
122
+ const customItem = { type: 'checkboxGroup' as const, options: [{ label: 'Show archived rows', checked: false, onChange: vi.fn() }] };
123
+ renderDropdown(<TableSettingsDropdown items={[customItem]} />);
124
+ await openDropdown();
125
+ expect(await screen.findByRole('checkbox', { name: /show archived rows/i })).toBeInTheDocument();
126
+ });
127
+
128
+ test('reflects the checked state of a custom checkbox option', async () => {
129
+ const customItem = { type: 'checkboxGroup' as const, options: [{ label: 'Show archived rows', checked: true, onChange: vi.fn() }] };
130
+ renderDropdown(<TableSettingsDropdown items={[customItem]} />);
131
+ await openDropdown();
132
+ const checkbox = await screen.findByRole('checkbox', { name: /show archived rows/i });
133
+ expect(checkbox).toBeChecked();
134
+ });
135
+
136
+ test('calls onChange with the new checked value when a custom checkbox option is clicked', async () => {
137
+ const onChange = vi.fn();
138
+ const customItem = { type: 'checkboxGroup' as const, options: [{ label: 'Show archived rows', checked: false, onChange }] };
139
+ renderDropdown(<TableSettingsDropdown items={[customItem]} />);
140
+ await openDropdown();
141
+ const checkbox = await screen.findByRole('checkbox', { name: /show archived rows/i });
142
+ await userEvent.click(checkbox);
143
+ expect(onChange).toHaveBeenCalledWith(true);
144
+ });
145
+ });
146
+
147
+ describe('custom items - radioGroup', () => {
148
+ const buildRadioItem = (overrides = {}) => ({
149
+ type: 'radioGroup' as const,
150
+ legend: 'Grouping',
151
+ name: 'ds-grouping',
152
+ checkedValue: 'week',
153
+ onChange: vi.fn(),
154
+ options: [
155
+ { label: 'Day', value: 'day' },
156
+ { label: 'Week', value: 'week' },
157
+ ],
158
+ ...overrides,
159
+ });
160
+
161
+ test('renders a radio group with the provided legend', async () => {
162
+ renderDropdown(<TableSettingsDropdown items={[buildRadioItem()]} />);
163
+ await openDropdown();
164
+ expect(await screen.findByRole('group', { name: /grouping/i })).toBeInTheDocument();
165
+ });
166
+
167
+ test('renders custom radio options with provided labels', async () => {
168
+ renderDropdown(<TableSettingsDropdown items={[buildRadioItem()]} />);
169
+ await openDropdown();
170
+ expect(await screen.findByRole('radio', { name: /day/i })).toBeInTheDocument();
171
+ expect(screen.getByRole('radio', { name: /week/i })).toBeInTheDocument();
172
+ });
173
+
174
+ test('reflects the checkedValue by checking the matching radio', async () => {
175
+ renderDropdown(<TableSettingsDropdown items={[buildRadioItem({ checkedValue: 'day' })]} />);
176
+ await openDropdown();
177
+ expect(await screen.findByRole('radio', { name: /day/i })).toBeChecked();
178
+ expect(screen.getByRole('radio', { name: /week/i })).not.toBeChecked();
179
+ });
180
+
181
+ test('calls onChange with the selected value when a radio option is clicked', async () => {
182
+ const onChange = vi.fn();
183
+ renderDropdown(<TableSettingsDropdown items={[buildRadioItem({ onChange })]} />);
184
+ await openDropdown();
185
+ const dayRadio = await screen.findByRole('radio', { name: /day/i });
186
+ await userEvent.click(dayRadio);
187
+ expect(onChange).toHaveBeenCalledWith('day');
188
+ });
189
+ });
190
+
191
+ describe('reset button', () => {
192
+ test('reset button is always present regardless of items prop', async () => {
193
+ renderDropdown(<TableSettingsDropdown items={['tableSpacing']} />);
194
+ await openDropdown();
195
+ expect(await screen.findByRole('button', { name: /reset table settings/i })).toBeInTheDocument();
196
+ });
197
+
198
+ test('calls resetSettings from context when reset button is clicked', async () => {
199
+ const resetSettings = vi.fn();
200
+ renderDropdown(<TableSettingsDropdown />, buildContextValue({ resetSettings }));
201
+ await openDropdown();
202
+ const resetButton = await screen.findByRole('button', { name: /reset table settings/i });
203
+ await userEvent.click(resetButton);
204
+ expect(resetSettings).toHaveBeenCalledTimes(1);
205
+ });
206
+ });
207
+ });