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

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 (304) hide show
  1. package/.gather/skills/write-stories/SKILL.md +207 -271
  2. package/.storybook/preview.ts +5 -0
  3. package/CHANGELOG.md +23 -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 +3 -0
  131. package/dist/components/table/Table.stories.d.ts.map +1 -1
  132. package/dist/components/table/Table.stories.js +384 -5
  133. package/dist/components/table/Table.stories.js.map +1 -1
  134. package/dist/components/table/Table.test.js +30 -0
  135. package/dist/components/table/Table.test.js.map +1 -1
  136. package/dist/components/table/TableFooter.stories.d.ts +49 -0
  137. package/dist/components/table/TableFooter.stories.d.ts.map +1 -0
  138. package/dist/components/table/TableFooter.stories.js +137 -0
  139. package/dist/components/table/TableFooter.stories.js.map +1 -0
  140. package/dist/components/table/TableHeader.stories.d.ts +93 -0
  141. package/dist/components/table/TableHeader.stories.d.ts.map +1 -0
  142. package/dist/components/table/TableHeader.stories.js +176 -0
  143. package/dist/components/table/TableHeader.stories.js.map +1 -0
  144. package/dist/components/table/cellEditors/DateCellEditor.stories.d.ts +44 -0
  145. package/dist/components/table/cellEditors/DateCellEditor.stories.d.ts.map +1 -0
  146. package/dist/components/table/cellEditors/DateCellEditor.stories.js +186 -0
  147. package/dist/components/table/cellEditors/DateCellEditor.stories.js.map +1 -0
  148. package/dist/components/table/cellRenderers/BooleanCellRenderer.stories.d.ts +40 -0
  149. package/dist/components/table/cellRenderers/BooleanCellRenderer.stories.d.ts.map +1 -0
  150. package/dist/components/table/cellRenderers/BooleanCellRenderer.stories.js +209 -0
  151. package/dist/components/table/cellRenderers/BooleanCellRenderer.stories.js.map +1 -0
  152. package/dist/components/table/cellRenderers/ButtonCellRenderer.stories.d.ts +48 -0
  153. package/dist/components/table/cellRenderers/ButtonCellRenderer.stories.d.ts.map +1 -0
  154. package/dist/components/table/cellRenderers/ButtonCellRenderer.stories.js +244 -0
  155. package/dist/components/table/cellRenderers/ButtonCellRenderer.stories.js.map +1 -0
  156. package/dist/components/table/cellRenderers/CheckboxCellRenderer.d.ts.map +1 -1
  157. package/dist/components/table/cellRenderers/CheckboxCellRenderer.js +3 -1
  158. package/dist/components/table/cellRenderers/CheckboxCellRenderer.js.map +1 -1
  159. package/dist/components/table/cellRenderers/CheckboxCellRenderer.stories.d.ts +64 -0
  160. package/dist/components/table/cellRenderers/CheckboxCellRenderer.stories.d.ts.map +1 -0
  161. package/dist/components/table/cellRenderers/CheckboxCellRenderer.stories.js +241 -0
  162. package/dist/components/table/cellRenderers/CheckboxCellRenderer.stories.js.map +1 -0
  163. package/dist/components/table/cellRenderers/DefaultCellRenderer.stories.d.ts +55 -0
  164. package/dist/components/table/cellRenderers/DefaultCellRenderer.stories.d.ts.map +1 -0
  165. package/dist/components/table/cellRenderers/DefaultCellRenderer.stories.js +245 -0
  166. package/dist/components/table/cellRenderers/DefaultCellRenderer.stories.js.map +1 -0
  167. package/dist/components/table/cellRenderers/InlineTextCellRenderer.stories.d.ts +67 -0
  168. package/dist/components/table/cellRenderers/InlineTextCellRenderer.stories.d.ts.map +1 -0
  169. package/dist/components/table/cellRenderers/InlineTextCellRenderer.stories.js +221 -0
  170. package/dist/components/table/cellRenderers/InlineTextCellRenderer.stories.js.map +1 -0
  171. package/dist/components/table/cellRenderers/SelectDropdownCellRenderer.stories.d.ts +75 -0
  172. package/dist/components/table/cellRenderers/SelectDropdownCellRenderer.stories.d.ts.map +1 -0
  173. package/dist/components/table/cellRenderers/SelectDropdownCellRenderer.stories.js +270 -0
  174. package/dist/components/table/cellRenderers/SelectDropdownCellRenderer.stories.js.map +1 -0
  175. package/dist/components/table/columnFilters/BooleanFilter.stories.d.ts +57 -0
  176. package/dist/components/table/columnFilters/BooleanFilter.stories.d.ts.map +1 -0
  177. package/dist/components/table/columnFilters/BooleanFilter.stories.js +198 -0
  178. package/dist/components/table/columnFilters/BooleanFilter.stories.js.map +1 -0
  179. package/dist/components/table/columnFilters/TimeFilter.stories.d.ts +58 -0
  180. package/dist/components/table/columnFilters/TimeFilter.stories.d.ts.map +1 -0
  181. package/dist/components/table/columnFilters/TimeFilter.stories.js +207 -0
  182. package/dist/components/table/columnFilters/TimeFilter.stories.js.map +1 -0
  183. package/dist/components/table/pagination/PaginationPanel.stories.d.ts +113 -0
  184. package/dist/components/table/pagination/PaginationPanel.stories.d.ts.map +1 -0
  185. package/dist/components/table/pagination/PaginationPanel.stories.js +272 -0
  186. package/dist/components/table/pagination/PaginationPanel.stories.js.map +1 -0
  187. package/dist/components/table/tableControls/HideColumnsDropdown.d.ts.map +1 -1
  188. package/dist/components/table/tableControls/HideColumnsDropdown.js +9 -3
  189. package/dist/components/table/tableControls/HideColumnsDropdown.js.map +1 -1
  190. package/dist/components/table/tableControls/TableControls.stories.d.ts +151 -0
  191. package/dist/components/table/tableControls/TableControls.stories.d.ts.map +1 -0
  192. package/dist/components/table/tableControls/TableControls.stories.js +356 -0
  193. package/dist/components/table/tableControls/TableControls.stories.js.map +1 -0
  194. package/dist/components/table/tableControls/TableSettingsDropdown.d.ts +27 -1
  195. package/dist/components/table/tableControls/TableSettingsDropdown.d.ts.map +1 -1
  196. package/dist/components/table/tableControls/TableSettingsDropdown.js +53 -26
  197. package/dist/components/table/tableControls/TableSettingsDropdown.js.map +1 -1
  198. package/dist/components/table/tableControls/TableSettingsDropdown.test.d.ts +2 -0
  199. package/dist/components/table/tableControls/TableSettingsDropdown.test.d.ts.map +1 -0
  200. package/dist/components/table/tableControls/TableSettingsDropdown.test.js +178 -0
  201. package/dist/components/table/tableControls/TableSettingsDropdown.test.js.map +1 -0
  202. package/dist/components/tabs/Tabs.stories.d.ts +22 -4
  203. package/dist/components/tabs/Tabs.stories.d.ts.map +1 -1
  204. package/dist/components/tabs/Tabs.stories.js +398 -22
  205. package/dist/components/tabs/Tabs.stories.js.map +1 -1
  206. package/dist/components/tabs/TabsItem.stories.d.ts +54 -1
  207. package/dist/components/tabs/TabsItem.stories.d.ts.map +1 -1
  208. package/dist/components/tabs/TabsItem.stories.js +61 -9
  209. package/dist/components/tabs/TabsItem.stories.js.map +1 -1
  210. package/dist/components/toast/Toast.stories.d.ts +103 -10
  211. package/dist/components/toast/Toast.stories.d.ts.map +1 -1
  212. package/dist/components/toast/Toast.stories.js +409 -47
  213. package/dist/components/toast/Toast.stories.js.map +1 -1
  214. package/dist/components/toggle/Toggle.stories.d.ts +61 -46
  215. package/dist/components/toggle/Toggle.stories.d.ts.map +1 -1
  216. package/dist/components/toggle/Toggle.stories.js +311 -122
  217. package/dist/components/toggle/Toggle.stories.js.map +1 -1
  218. package/dist/components/tooltip/Tooltip.stories.d.ts +78 -6
  219. package/dist/components/tooltip/Tooltip.stories.d.ts.map +1 -1
  220. package/dist/components/tooltip/Tooltip.stories.js +413 -7
  221. package/dist/components/tooltip/Tooltip.stories.js.map +1 -1
  222. package/dist/components/tooltip/TooltipWrapper.stories.d.ts +71 -7
  223. package/dist/components/tooltip/TooltipWrapper.stories.d.ts.map +1 -1
  224. package/dist/components/tooltip/TooltipWrapper.stories.js +238 -10
  225. package/dist/components/tooltip/TooltipWrapper.stories.js.map +1 -1
  226. package/dist/index.css +8 -0
  227. package/dist/index.css.map +1 -1
  228. package/dist/utils/PopupParentContext.stories.d.ts +17 -0
  229. package/dist/utils/PopupParentContext.stories.d.ts.map +1 -0
  230. package/dist/utils/PopupParentContext.stories.js +266 -0
  231. package/dist/utils/PopupParentContext.stories.js.map +1 -0
  232. package/dist/utils/getDefaultPopupParent.d.ts.map +1 -1
  233. package/dist/utils/getDefaultPopupParent.js +6 -0
  234. package/dist/utils/getDefaultPopupParent.js.map +1 -1
  235. package/package.json +1 -1
  236. package/src/components/articleCard/ArticleCard.stories.tsx +524 -111
  237. package/src/components/avatar/Avatar.stories.tsx +504 -59
  238. package/src/components/avatarGroup/AvatarGroup.stories.tsx +977 -175
  239. package/src/components/banner/Banner.stories.tsx +7 -3
  240. package/src/components/card/Card.stories.tsx +466 -36
  241. package/src/components/combobox/Combobox.stories.tsx +867 -260
  242. package/src/components/datePicker/DatePicker.stories.tsx +777 -60
  243. package/src/components/dateTimePicker/DateTimePicker.stories.tsx +910 -132
  244. package/src/components/editableText/EditableText.stories.tsx +567 -91
  245. package/src/components/formField/FormField.test.tsx +6 -0
  246. package/src/components/formField/FormField.tsx +5 -0
  247. package/src/components/formField/fieldset/Fieldset.stories.tsx +761 -51
  248. package/src/components/formField/inputs/checkbox/CheckboxGroup.tsx +1 -1
  249. package/src/components/formField/inputs/checkbox/CheckboxInput.tsx +1 -1
  250. package/src/components/formField/inputs/colourPickerDropdown/ColourPickerDropdown.stories.tsx +504 -11
  251. package/src/components/formField/inputs/radio/RadioButtonGroup.tsx +17 -4
  252. package/src/components/formField/inputs/radio/RadioButtonInput.stories.tsx +71 -59
  253. package/src/components/formField/inputs/selectDropdown/SelectDropdown.stories.tsx +1079 -168
  254. package/src/components/formField/inputs/time/TimeInput.stories.tsx +1140 -104
  255. package/src/components/formField/label/Label.stories.tsx +317 -8
  256. package/src/components/icoText/IcoText.stories.tsx +442 -31
  257. package/src/components/kpiCard/KPICard.stories.tsx +475 -30
  258. package/src/components/kvpList/KVPList.stories.tsx +593 -26
  259. package/src/components/modal/Modal.stories.tsx +963 -26
  260. package/src/components/modal/modalManager/ModalManager.stories.tsx +612 -454
  261. package/src/components/pill/Pill.stories.tsx +11 -13
  262. package/src/components/pill/Pill.tsx +1 -0
  263. package/src/components/row/Row.stories.tsx +474 -58
  264. package/src/components/searchBar/SearchBar.stories.tsx +570 -38
  265. package/src/components/section/Section.stories.tsx +723 -70
  266. package/src/components/singleUser/SingleUser.stories.tsx +393 -34
  267. package/src/components/slideoverManager/SlideoverManager.stories.tsx +572 -342
  268. package/src/components/table/DSDefaultColDef.ts +25 -5
  269. package/src/components/table/Table.stories.tsx +460 -5
  270. package/src/components/table/Table.test.tsx +53 -0
  271. package/src/components/table/Table.tsx +9 -2
  272. package/src/components/table/TableFooter.stories.tsx +196 -0
  273. package/src/components/table/TableHeader.stories.tsx +251 -0
  274. package/src/components/table/cellEditors/DateCellEditor.stories.tsx +245 -0
  275. package/src/components/table/cellRenderers/BooleanCellRenderer.stories.tsx +278 -0
  276. package/src/components/table/cellRenderers/ButtonCellRenderer.stories.tsx +333 -0
  277. package/src/components/table/cellRenderers/CheckboxCellRenderer.stories.tsx +337 -0
  278. package/src/components/table/cellRenderers/CheckboxCellRenderer.tsx +5 -1
  279. package/src/components/table/cellRenderers/DefaultCellRenderer.stories.tsx +342 -0
  280. package/src/components/table/cellRenderers/InlineTextCellRenderer.stories.tsx +292 -0
  281. package/src/components/table/cellRenderers/SelectDropdownCellRenderer.stories.tsx +369 -0
  282. package/src/components/table/columnFilters/BooleanFilter.stories.tsx +268 -0
  283. package/src/components/table/columnFilters/TimeFilter.stories.tsx +281 -0
  284. package/src/components/table/pagination/PaginationPanel.stories.tsx +327 -0
  285. package/src/components/table/tableControls/HideColumnsDropdown.tsx +11 -2
  286. package/src/components/table/tableControls/TableControls.stories.tsx +415 -0
  287. package/src/components/table/tableControls/TableSettingsDropdown.test.tsx +207 -0
  288. package/src/components/table/tableControls/TableSettingsDropdown.tsx +103 -39
  289. package/src/components/tabs/Tabs.stories.tsx +540 -60
  290. package/src/components/tabs/TabsItem.stories.tsx +82 -8
  291. package/src/components/toast/Toast.stories.tsx +539 -77
  292. package/src/components/toggle/Toggle.stories.tsx +371 -135
  293. package/src/components/tooltip/Tooltip.stories.tsx +606 -15
  294. package/src/components/tooltip/TooltipWrapper.stories.tsx +348 -12
  295. package/src/docs/Contributing.mdx +241 -0
  296. package/src/docs/UsingComponents.mdx +93 -0
  297. package/src/docs/Welcome.mdx +68 -0
  298. package/src/global.scss +7 -0
  299. package/src/utils/PopupParentContext.stories.tsx +367 -0
  300. package/src/utils/getDefaultPopupParent.ts +6 -0
  301. package/.ralph/storybook-upgrade/knowledge.md +0 -308
  302. package/.ralph/storybook-upgrade/prd.json +0 -777
  303. package/.ralph/storybook-upgrade/progress.md +0 -342
  304. package/src/components/table/TableWIP.mdx +0 -3
@@ -1,185 +1,846 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useState } from 'react';
3
+ import { Controls, Heading as DocHeading, Markdown, Primary as DocPrimary, Stories, Subtitle, Title, } from '@storybook/addon-docs/blocks';
4
+ import { FormField } from '../../../formField/FormField';
1
5
  import { SelectDropdown } from './SelectDropdown';
6
+ // ---------------------------------------------------------------------------
7
+ // Docs page content
8
+ // ---------------------------------------------------------------------------
9
+ const DESCRIPTION_INTRO = [
10
+ 'SelectDropdown is a stylised dropdown select input that matches the Arbor design language.',
11
+ 'It supports single-select and multi-select, option grouping, icons, two-line item layouts,',
12
+ 'and both controlled and uncontrolled state management.',
13
+ '',
14
+ '> **Built on [Radix UI DropdownMenu](https://www.radix-ui.com/primitives/docs/components/dropdown-menu).**',
15
+ '> The dropdown panel renders via a portal — do **not** place SelectDropdown inside a container',
16
+ '> with `overflow: hidden`, or the panel will be clipped.',
17
+ ].join('\n');
18
+ const USAGE_GUIDANCE = [
19
+ '### When to use',
20
+ '',
21
+ '- Replacing a native `<select>` when you need the Arbor visual style',
22
+ '- Offering a list of options for single or multi-selection within a form',
23
+ '- Displaying options with icons, section headers, or group separators',
24
+ '- When built-in form serialisation is useful (a hidden `<input type="hidden">` is always rendered)',
25
+ '',
26
+ '---',
27
+ '',
28
+ '### When NOT to use',
29
+ '',
30
+ '| Situation | Use instead |',
31
+ '|---|---|',
32
+ '| Need native browser select for mobile or performance | Plain `<select>` inside a FormField |',
33
+ '| Need async search or type-to-filter | Combobox |',
34
+ '| Need individual options to be disabled | Custom `Dropdown` composition |',
35
+ '| Thousands of options | No virtualisation — consider a search-first pattern |',
36
+ ].join('\n');
37
+ const DEVELOPER_NOTES = [
38
+ '### Critical usage patterns',
39
+ '',
40
+ '**`errorText` does not exist on SelectDropdown.** Error text belongs on `FormField`. Pass `errorText`',
41
+ 'to `FormField` and use `inputType="selectDropdown"` — it automatically wires `hasError`,',
42
+ '`aria-invalid`, and `aria-describedby` for you.',
43
+ '',
44
+ '**`hasError` is visual-only.** It applies error styling to the trigger button but tells screen readers',
45
+ 'nothing. Always pair it with `aria-invalid={true}`.',
46
+ '',
47
+ '**`id` is dual-purpose.** It is set as both the `name` on the hidden form input and the `id` on the',
48
+ 'trigger button. Set this when using inside a `<form>` or when you need a `<label htmlFor>` to target the trigger.',
49
+ '',
50
+ '**Multi-select count display.** With `multiple` and no `placeholder` prop, the trigger shows',
51
+ '`"Select (2)"` when two items are selected. Setting a `placeholder` suppresses the count — the',
52
+ 'trigger shows your placeholder string instead. Choose deliberately.',
53
+ '',
54
+ '**Controlled vs uncontrolled.** Providing `selectedValues` activates controlled mode — the component',
55
+ 'stops managing its own state. You must update `selectedValues` via `onSelectionChange` or the',
56
+ 'dropdown will appear frozen. Do not mix `initialSelectedValues` with `selectedValues`.',
57
+ '',
58
+ '**Groups are automatic.** Add `group: "Year 7"` to options and the component clusters them under',
59
+ '`<h3>` section headers. Headers only appear when two or more distinct `group` values exist.',
60
+ '',
61
+ '---',
62
+ '',
63
+ '### Accessibility',
64
+ '',
65
+ '- Always pair `hasError` with `aria-invalid={true}` — `hasError` is visual-only',
66
+ '- Use `aria-describedBy` to link to your error/hint message element',
67
+ '- When used standalone (outside FormField), set `id` and associate a `<label htmlFor={id}>`',
68
+ '- When used inside `FormField`, label association and ARIA wiring are automatic — prefer that pattern',
69
+ '- Keyboard navigation is built in via Radix UI: `↑ ↓` to move through options, `Enter` to select, `Esc` to close',
70
+ '',
71
+ '---',
72
+ '',
73
+ '### TypeScript types',
74
+ '',
75
+ '```ts',
76
+ "import { SelectDropdown } from '@arbor-education/design-system.components';",
77
+ '',
78
+ 'function MySelect(props: SelectDropdown.Props) { ... }',
79
+ '```',
80
+ '',
81
+ '| Type | Description |',
82
+ '|---|---|',
83
+ '| `SelectDropdown.Props` | Full props interface (`SelectDropdownInputProps`) |',
84
+ ].join('\n');
85
+ const RELATED_COMPONENTS = [
86
+ '## Related components',
87
+ '',
88
+ '[FormField](?path=/docs/components-formfield--docs) · [Dropdown](?path=/docs/components-dropdown--docs) · [ColourPickerDropdown](?path=/docs/components-formfield-inputs-colourpickerdropdown--docs)',
89
+ ].join('\n');
90
+ const PROPS_INTRO = 'The preview below is wired to the **Controls** panel — tweak any prop to see the story update in real time.';
91
+ function SelectDropdownDocsPage() {
92
+ return (_jsxs(_Fragment, { children: [_jsx(Title, {}), _jsx(Subtitle, {}), _jsx(Markdown, { children: DESCRIPTION_INTRO }), _jsx(DocHeading, { children: "Interactive example" }), _jsx(Markdown, { children: PROPS_INTRO }), _jsx(DocPrimary, {}), _jsx(Controls, {}), _jsx(DocHeading, { children: "Usage guidance" }), _jsx(Markdown, { children: USAGE_GUIDANCE }), _jsx(DocHeading, { children: "Developer notes" }), _jsx(Markdown, { children: DEVELOPER_NOTES }), _jsx(DocHeading, { children: "Examples" }), _jsx(Stories, { title: "" }), _jsx(Markdown, { children: RELATED_COMPONENTS })] }));
93
+ }
94
+ // ---------------------------------------------------------------------------
95
+ // Option fixtures
96
+ // ---------------------------------------------------------------------------
97
+ const yearGroupOptions = [
98
+ { label: 'Year 7', value: 'year7' },
99
+ { label: 'Year 8', value: 'year8' },
100
+ { label: 'Year 9', value: 'year9' },
101
+ { label: 'Year 10', value: 'year10' },
102
+ { label: 'Year 11', value: 'year11' },
103
+ ];
104
+ const subjectOptions = [
105
+ { label: 'Mathematics', value: 'maths' },
106
+ { label: 'English', value: 'english' },
107
+ { label: 'Science', value: 'science' },
108
+ { label: 'History', value: 'history' },
109
+ { label: 'Geography', value: 'geography' },
110
+ { label: 'Art', value: 'art' },
111
+ ];
112
+ const subjectOptionsWithIcons = [
113
+ { label: 'Mathematics', value: 'maths', icon: 'chart-column-increasing' },
114
+ { label: 'English', value: 'english', icon: 'book-open' },
115
+ { label: 'Science', value: 'science', icon: 'circle-check' },
116
+ { label: 'History', value: 'history', icon: 'clock-3' },
117
+ { label: 'Geography', value: 'geography', icon: 'flag' },
118
+ ];
119
+ const attendanceReasonOptions = [
120
+ { label: 'Authorised absence', value: 'authorised', header: 'Planned' },
121
+ { label: 'Holiday', value: 'holiday', header: 'Planned' },
122
+ { label: 'Medical appointment', value: 'medical', header: 'Health' },
123
+ { label: 'Illness', value: 'illness', header: 'Health' },
124
+ { label: 'Unauthorised absence', value: 'unauthorised', header: 'Unplanned' },
125
+ { label: 'Late arrival', value: 'late', header: 'Unplanned' },
126
+ ];
127
+ const groupedYearOptions = [
128
+ { label: 'Year 7', value: 'year7', group: 'Lower school' },
129
+ { label: 'Year 8', value: 'year8', group: 'Lower school' },
130
+ { label: 'Year 9', value: 'year9', group: 'Lower school' },
131
+ { label: 'Year 10', value: 'year10', group: 'Upper school' },
132
+ { label: 'Year 11', value: 'year11', group: 'Upper school' },
133
+ { label: 'Year 12', value: 'year12', group: 'Sixth form' },
134
+ { label: 'Year 13', value: 'year13', group: 'Sixth form' },
135
+ ];
136
+ // ---------------------------------------------------------------------------
137
+ // Meta
138
+ // ---------------------------------------------------------------------------
2
139
  const meta = {
3
140
  title: 'Components/FormField/Inputs/SelectDropdown',
4
141
  component: SelectDropdown,
5
- };
6
- export const Default = {
7
- args: {
8
- title: 'titleValue',
9
- options: [
10
- { label: 'Option 1', value: 'option1' },
11
- { label: 'Option 2', value: 'option2' },
12
- { label: 'Option 3', value: 'option3' },
13
- ],
14
- multiple: false,
15
- onSelectionChange: (value) => { console.log(value); },
142
+ tags: ['autodocs'],
143
+ parameters: {
144
+ layout: 'padded',
145
+ docs: {
146
+ page: SelectDropdownDocsPage,
147
+ },
16
148
  },
17
- };
18
- export const WithIcon = {
19
- args: {
20
- title: 'titleValue',
21
- options: [
22
- { label: 'Option 1', value: 'option1', icon: '3-dot' },
23
- { label: 'Option 2', value: 'option2', icon: 'user' },
24
- { label: 'Option 3', value: 'option3', icon: 'chart-spline' },
25
- ],
26
- multiple: true,
27
- onSelectionChange: (value) => { console.log(value); },
149
+ argTypes: {
150
+ 'options': {
151
+ description: [
152
+ 'Array of option items to display in the dropdown.',
153
+ 'Each item must have a `value` string.',
154
+ '`label`, `icon`, `header`, and `group` are optional.',
155
+ ].join(' '),
156
+ control: false,
157
+ table: {
158
+ type: { summary: 'SelectDropdownItemProps[]' },
159
+ },
160
+ },
161
+ 'placeholder': {
162
+ description: [
163
+ 'Text shown in the trigger when nothing is selected.',
164
+ 'In multi-select mode, also shown when 2+ items are selected (suppressing the count display).',
165
+ 'Defaults to `"Select"`.',
166
+ ].join(' '),
167
+ control: 'text',
168
+ table: {
169
+ type: { summary: 'string' },
170
+ defaultValue: { summary: "'Select'" },
171
+ },
172
+ },
173
+ 'multiple': {
174
+ description: [
175
+ 'Enables multi-select mode.',
176
+ 'The dropdown stays open after each selection and items toggle on/off.',
177
+ ].join(' '),
178
+ control: 'boolean',
179
+ table: {
180
+ type: { summary: 'boolean' },
181
+ defaultValue: { summary: 'false' },
182
+ },
183
+ },
184
+ 'disabled': {
185
+ description: 'Disables the trigger button, preventing the dropdown from opening.',
186
+ control: 'boolean',
187
+ table: {
188
+ type: { summary: 'boolean' },
189
+ defaultValue: { summary: 'false' },
190
+ },
191
+ },
192
+ 'hasError': {
193
+ description: [
194
+ '**Visual only.** Applies error styling to the trigger button.',
195
+ 'Must be paired with `aria-invalid={true}` for screen reader support.',
196
+ 'Do not pass `errorText` here — that prop does not exist. Use `FormField` with `errorText` instead.',
197
+ ].join(' '),
198
+ control: 'boolean',
199
+ table: {
200
+ type: { summary: 'boolean' },
201
+ defaultValue: { summary: 'false' },
202
+ },
203
+ },
204
+ 'aria-invalid': {
205
+ description: [
206
+ 'Communicates an invalid state to screen readers via the trigger button.',
207
+ 'Always pair with `hasError`.',
208
+ ].join(' '),
209
+ control: 'boolean',
210
+ table: {
211
+ type: { summary: 'boolean' },
212
+ defaultValue: { summary: 'undefined' },
213
+ },
214
+ },
215
+ 'aria-describedBy': {
216
+ description: [
217
+ 'ID of the element that describes this field (e.g. an error message or hint).',
218
+ 'Passed through to the trigger button.',
219
+ ].join(' '),
220
+ control: 'text',
221
+ table: {
222
+ type: { summary: 'string' },
223
+ defaultValue: { summary: 'undefined' },
224
+ },
225
+ },
226
+ 'id': {
227
+ description: [
228
+ '**Dual-purpose.** Set as `name` on the hidden form input AND as `id` on the trigger button.',
229
+ 'Required for label association (`<label htmlFor={id}>`) and form submission.',
230
+ ].join(' '),
231
+ control: 'text',
232
+ table: {
233
+ type: { summary: 'string' },
234
+ defaultValue: { summary: 'undefined' },
235
+ },
236
+ },
237
+ 'alwaysShowPlaceholder': {
238
+ description: [
239
+ 'When `true`, the trigger always displays the placeholder regardless of what is selected.',
240
+ 'Use for action-style dropdowns (e.g. "Add filter") where the label must not change.',
241
+ ].join(' '),
242
+ control: 'boolean',
243
+ table: {
244
+ type: { summary: 'boolean' },
245
+ defaultValue: { summary: 'false' },
246
+ },
247
+ },
248
+ 'initialSelectedValues': {
249
+ description: [
250
+ 'Uncontrolled mode only — sets the initial selection on mount.',
251
+ 'Ignored if `selectedValues` is provided.',
252
+ ].join(' '),
253
+ control: false,
254
+ table: {
255
+ type: { summary: 'string[]' },
256
+ defaultValue: { summary: '[]' },
257
+ },
258
+ },
259
+ 'selectedValues': {
260
+ description: [
261
+ 'Activates fully controlled mode.',
262
+ 'You must update this via `onSelectionChange` — the component stops managing its own state.',
263
+ 'Do not mix with `initialSelectedValues`.',
264
+ ].join(' '),
265
+ control: false,
266
+ table: {
267
+ type: { summary: 'string[]' },
268
+ defaultValue: { summary: 'undefined' },
269
+ },
270
+ },
271
+ 'onSelectionChange': {
272
+ description: 'Fires after every selection change with the full updated array of selected values.',
273
+ action: 'onSelectionChange',
274
+ control: false,
275
+ table: {
276
+ type: { summary: '(value: string[]) => void' },
277
+ },
278
+ },
279
+ 'open': {
280
+ description: 'Controls the open/closed state of the dropdown externally.',
281
+ control: 'boolean',
282
+ table: {
283
+ type: { summary: 'boolean' },
284
+ defaultValue: { summary: 'undefined' },
285
+ },
286
+ },
287
+ 'onOpenChange': {
288
+ description: 'Fires when the dropdown attempts to open or close. Required when `open` is controlled.',
289
+ action: 'onOpenChange',
290
+ control: false,
291
+ table: {
292
+ type: { summary: '(open: boolean) => void' },
293
+ },
294
+ },
28
295
  },
29
296
  };
30
- export const DefaultWithGroups = {
31
- args: {
32
- title: 'titleValue',
33
- multiple: true,
34
- options: [
35
- { label: 'Option 5', value: 'option5', group: 'Group 1' },
36
- { label: 'Option 6', value: 'option6', group: 'Group 1' },
37
- { label: 'Option 7', value: 'option7', group: 'Group 2' },
38
- { label: 'Option 8', value: 'option8', group: 'Group 2' },
39
- { label: 'Option 1', value: 'option1' },
40
- { label: 'Option 2', value: 'option2' },
41
- { label: 'Option 3', value: 'option3' },
42
- { label: 'Option 4', value: 'option4' },
43
- { label: 'Option 9', value: 'option9' },
44
- ],
45
- onSelectionChange: (value) => { console.log(value); },
46
- },
47
- };
48
- export const DefaultMultiSelect = {
49
- args: {
50
- title: 'titleValue',
51
- options: [
52
- { label: 'Option 1', value: 'option1' },
53
- { label: 'Option 2', value: 'option2' },
54
- { label: 'Option 3', value: 'option3' },
55
- ],
56
- multiple: true,
57
- onSelectionChange: (value) => { console.log(value); },
297
+ export default meta;
298
+ // ---------------------------------------------------------------------------
299
+ // Helper: attach a per-story description to docs
300
+ // ---------------------------------------------------------------------------
301
+ const withDescription = (story, description) => ({
302
+ ...story,
303
+ parameters: {
304
+ ...story.parameters,
305
+ docs: {
306
+ ...story.parameters?.docs,
307
+ description: {
308
+ story: description,
309
+ },
310
+ },
58
311
  },
312
+ });
313
+ // ---------------------------------------------------------------------------
314
+ // Template components for stateful and composite stories
315
+ // ---------------------------------------------------------------------------
316
+ const WithInitialSelectionTemplate = () => (_jsx("div", { style: { maxWidth: '320px' }, children: _jsx(SelectDropdown, { placeholder: "Select a year group", initialSelectedValues: ['year9'], options: yearGroupOptions, onSelectionChange: values => console.log(values) }) }));
317
+ const MultiSelectTemplate = () => (_jsx("div", { style: { maxWidth: '320px' }, children: _jsx(SelectDropdown, { placeholder: "Select subjects", multiple: true, options: subjectOptions, onSelectionChange: values => console.log(values) }) }));
318
+ const MultiSelectWithCountTemplate = () => (_jsx("div", { style: { maxWidth: '320px' }, children: _jsx(SelectDropdown, { multiple: true, initialSelectedValues: ['maths', 'english', 'science'], options: subjectOptions, onSelectionChange: values => console.log(values) }) }));
319
+ const DisabledTemplate = () => (_jsx("div", { style: { maxWidth: '320px' }, children: _jsx(SelectDropdown, { placeholder: "Select a year group", disabled: true, options: yearGroupOptions, onSelectionChange: values => console.log(values) }) }));
320
+ const WithErrorTemplate = () => (_jsxs("div", { style: { maxWidth: '320px', display: 'flex', flexDirection: 'column', gap: 'var(--spacing-xsmall)' }, children: [_jsx(SelectDropdown, { placeholder: "Select a year group", hasError: true, "aria-invalid": true, "aria-describedBy": "year-group-error", options: yearGroupOptions, onSelectionChange: values => console.log(values) }), _jsx("span", { id: "year-group-error", style: { fontSize: '0.875rem', color: 'var(--color-feedback-danger-600)' }, children: "Please select a year group" })] }));
321
+ const WithIconsTemplate = () => (_jsx("div", { style: { maxWidth: '320px' }, children: _jsx(SelectDropdown, { placeholder: "Select a subject", options: subjectOptionsWithIcons, onSelectionChange: values => console.log(values) }) }));
322
+ const WithItemHeadersTemplate = () => (_jsx("div", { style: { maxWidth: '320px' }, children: _jsx(SelectDropdown, { placeholder: "Select absence reason", options: attendanceReasonOptions, onSelectionChange: values => console.log(values) }) }));
323
+ const WithGroupsTemplate = () => (_jsx("div", { style: { maxWidth: '320px' }, children: _jsx(SelectDropdown, { placeholder: "Select a year group", options: groupedYearOptions, onSelectionChange: values => console.log(values) }) }));
324
+ const WithGroupsMultiSelectTemplate = () => (_jsx("div", { style: { maxWidth: '320px' }, children: _jsx(SelectDropdown, { placeholder: "Select year groups", multiple: true, options: groupedYearOptions, onSelectionChange: values => console.log(values) }) }));
325
+ const AlwaysShowPlaceholderTemplate = () => (_jsx("div", { style: { maxWidth: '320px' }, children: _jsx(SelectDropdown, { placeholder: "Add filter", alwaysShowPlaceholder: true, multiple: true, options: subjectOptions, onSelectionChange: values => console.log(values) }) }));
326
+ const ControlledSingleSelectTemplate = () => {
327
+ const [selected, setSelected] = useState([]);
328
+ return (_jsxs("div", { style: { maxWidth: '320px', display: 'flex', flexDirection: 'column', gap: 'var(--spacing-small)' }, children: [_jsx(SelectDropdown, { placeholder: "Select a subject", selectedValues: selected, onSelectionChange: setSelected, options: subjectOptions }), _jsxs("span", { style: { fontSize: '0.875rem', color: 'var(--color-grey-600)' }, children: ["Selected:", ' ', selected.length > 0 ? selected.join(', ') : 'nothing yet'] })] }));
59
329
  };
60
- export const Placeholder = {
61
- args: {
62
- placeholder: 'placeholder',
63
- title: 'titleValue',
64
- options: [
65
- { label: 'Option 1', value: 'option1' },
66
- { label: 'Option 2', value: 'option2' },
67
- { label: 'Option 3', value: 'option3' },
68
- ],
69
- onSelectionChange: (value) => { console.log(value); },
70
- },
330
+ const ControlledOpenTemplate = () => {
331
+ const [isOpen, setIsOpen] = useState(false);
332
+ return (_jsxs("div", { style: { maxWidth: '320px', display: 'flex', flexDirection: 'column', gap: 'var(--spacing-small)' }, children: [_jsx("button", { type: "button", onClick: () => setIsOpen(true), children: "Open dropdown externally" }), _jsx(SelectDropdown, { placeholder: "Select a subject", open: isOpen, onOpenChange: setIsOpen, options: subjectOptions, onSelectionChange: (values) => {
333
+ console.log(values);
334
+ setIsOpen(false);
335
+ } })] }));
71
336
  };
72
- export const EmptyPlaceHolder = {
337
+ const InsideFormFieldTemplate = () => (_jsx("div", { style: { maxWidth: '320px' }, children: _jsx(FormField, { label: "Year group", id: "year-group-field", inputType: "selectDropdown", inputProps: {
338
+ placeholder: 'Select a year group',
339
+ options: yearGroupOptions,
340
+ onSelectionChange: values => console.log(values),
341
+ } }) }));
342
+ const InsideFormFieldWithErrorTemplate = () => (_jsx("div", { style: { maxWidth: '320px' }, children: _jsx(FormField, { label: "Year group", id: "year-group-error-field", inputType: "selectDropdown", errorText: "Please select a year group", inputProps: {
343
+ placeholder: 'Select a year group',
344
+ options: yearGroupOptions,
345
+ onSelectionChange: values => console.log(values),
346
+ } }) }));
347
+ // ---------------------------------------------------------------------------
348
+ // Stories
349
+ // ---------------------------------------------------------------------------
350
+ export const Default = withDescription({
73
351
  args: {
74
- placeholder: '',
75
- options: [
76
- { label: 'Option 1', value: 'option1' },
77
- { label: 'Option 2', value: 'option2' },
78
- { label: 'Option 3', value: 'option3' },
79
- ],
352
+ options: yearGroupOptions,
353
+ placeholder: 'Select a year group',
354
+ multiple: false,
80
355
  disabled: false,
81
- onSelectionChange: (value) => { console.log(value); },
82
356
  },
83
- };
84
- export const WithError = {
85
- args: {
86
- title: 'titleValue',
87
- options: [
88
- { label: 'Option 1', value: 'option1' },
89
- { label: 'Option 2', value: 'option2' },
90
- { label: 'Option 3', value: 'option3' },
91
- ],
92
- disabled: false,
93
- errorText: 'This field is required',
94
- onSelectionChange: (value) => { console.log(value); },
357
+ render: args => (_jsx("div", { style: { maxWidth: '320px' }, children: _jsx(SelectDropdown, { ...args }) })),
358
+ }, 'The interactive canvas — every prop is wired to the Controls panel above. Use `options`, `placeholder`, `multiple`, and `disabled` to explore the full range of configurations.');
359
+ export const WithInitialSelection = withDescription({
360
+ render: WithInitialSelectionTemplate,
361
+ parameters: {
362
+ controls: { disable: true },
363
+ docs: {
364
+ source: {
365
+ language: 'tsx',
366
+ code: `
367
+ import { SelectDropdown } from '@arbor-education/design-system.components';
368
+
369
+ function WithInitialSelectionExample() {
370
+ return (
371
+ <SelectDropdown
372
+ placeholder="Select a year group"
373
+ initialSelectedValues={['year9']}
374
+ options={[
375
+ { label: 'Year 7', value: 'year7' },
376
+ { label: 'Year 8', value: 'year8' },
377
+ { label: 'Year 9', value: 'year9' },
378
+ { label: 'Year 10', value: 'year10' },
379
+ { label: 'Year 11', value: 'year11' },
380
+ ]}
381
+ onSelectionChange={(values) => console.log(values)}
382
+ />
383
+ );
384
+ }
385
+ export default WithInitialSelectionExample;
386
+ `.trim(),
387
+ },
388
+ },
95
389
  },
96
- };
97
- export const Disabled = {
98
- args: {
99
- title: 'titleValue',
100
- options: [
101
- { label: 'Option 1', value: 'option1' },
102
- { label: 'Option 2', value: 'option2' },
103
- { label: 'Option 3', value: 'option3' },
104
- ],
105
- disabled: true,
106
- onSelectionChange: (value) => { console.log(value); },
390
+ }, 'Uncontrolled mode with a pre-selected value via `initialSelectedValues`. The component manages its own state after mount — no `selectedValues` prop is needed. Use this when you only need to set a default and let the user take over.');
391
+ export const MultiSelect = withDescription({
392
+ render: MultiSelectTemplate,
393
+ parameters: {
394
+ controls: { disable: true },
395
+ docs: {
396
+ source: {
397
+ language: 'tsx',
398
+ code: `
399
+ import { SelectDropdown } from '@arbor-education/design-system.components';
400
+
401
+ function MultiSelectExample() {
402
+ return (
403
+ <SelectDropdown
404
+ placeholder="Select subjects"
405
+ multiple
406
+ options={[
407
+ { label: 'Mathematics', value: 'maths' },
408
+ { label: 'English', value: 'english' },
409
+ { label: 'Science', value: 'science' },
410
+ { label: 'History', value: 'history' },
411
+ { label: 'Geography', value: 'geography' },
412
+ ]}
413
+ onSelectionChange={(values) => console.log(values)}
414
+ />
415
+ );
416
+ }
417
+ export default MultiSelectExample;
418
+ `.trim(),
419
+ },
420
+ },
107
421
  },
108
- };
109
- export const WithGroups = {
110
- args: {
111
- title: 'titleValue',
112
- options: [
113
- { label: 'Option 1', value: 'option1', group: 'Group 1' },
114
- { label: 'Option 2', value: 'option2', group: 'Group 1' },
115
- { label: 'Option 4', value: 'option4', group: 'Group 2' },
116
- { label: 'Option 5', value: 'option5', group: 'Group 2' },
117
- { label: 'Option 3', value: 'option3' },
118
- { label: 'Option 6', value: 'option6' },
119
- { label: 'Option 7', value: 'option7' },
120
- ],
121
- onSelectionChange: (value) => { console.log(value); },
422
+ }, '`multiple={true}` allows selecting more than one item. The dropdown stays open after each selection so users can make multiple choices without re-opening. Items toggle on and off.');
423
+ export const MultiSelectWithCount = withDescription({
424
+ render: MultiSelectWithCountTemplate,
425
+ parameters: {
426
+ controls: { disable: true },
427
+ docs: {
428
+ source: {
429
+ language: 'tsx',
430
+ code: `
431
+ import { SelectDropdown } from '@arbor-education/design-system.components';
432
+
433
+ function MultiSelectWithCountExample() {
434
+ return (
435
+ // No placeholder prop — the trigger shows "Select (3)" when items are selected.
436
+ // Setting a placeholder would suppress this count display.
437
+ <SelectDropdown
438
+ multiple
439
+ initialSelectedValues={['maths', 'english', 'science']}
440
+ options={[
441
+ { label: 'Mathematics', value: 'maths' },
442
+ { label: 'English', value: 'english' },
443
+ { label: 'Science', value: 'science' },
444
+ { label: 'History', value: 'history' },
445
+ ]}
446
+ onSelectionChange={(values) => console.log(values)}
447
+ />
448
+ );
449
+ }
450
+ export default MultiSelectWithCountExample;
451
+ `.trim(),
452
+ },
453
+ },
122
454
  },
123
- };
124
- export const MultilineItems = {
125
- args: {
126
- title: 'titleValue',
127
- options: [
128
- { label: 'Option 1', value: 'option1', header: 'header1' },
129
- { label: 'Option 2', value: 'option2', header: 'header2' },
130
- { label: 'Option 3', value: 'option3', header: 'header3' },
131
- { label: 'Option 4', value: 'option4', header: 'header4' },
132
- ],
133
- onSelectionChange: (value) => { console.log(value); },
455
+ }, [
456
+ 'When `multiple` is true and **no `placeholder` prop is set**, the trigger shows `"Select (3)"` when three items are selected.',
457
+ 'Setting a `placeholder` suppresses this count — the trigger shows your placeholder text instead.',
458
+ 'Choose deliberately based on whether you want users to see how many items are selected.',
459
+ ].join(' '));
460
+ export const Disabled = withDescription({
461
+ render: DisabledTemplate,
462
+ parameters: {
463
+ controls: { disable: true },
464
+ docs: {
465
+ source: {
466
+ language: 'tsx',
467
+ code: `
468
+ import { SelectDropdown } from '@arbor-education/design-system.components';
469
+
470
+ function DisabledExample() {
471
+ return (
472
+ <SelectDropdown
473
+ placeholder="Select a year group"
474
+ disabled
475
+ options={[
476
+ { label: 'Year 7', value: 'year7' },
477
+ { label: 'Year 8', value: 'year8' },
478
+ { label: 'Year 9', value: 'year9' },
479
+ ]}
480
+ onSelectionChange={(values) => console.log(values)}
481
+ />
482
+ );
483
+ }
484
+ export default DisabledExample;
485
+ `.trim(),
486
+ },
487
+ },
134
488
  },
135
- };
136
- export const MultilineItemsGrouped = {
137
- args: {
138
- title: 'titleValue',
139
- options: [
140
- { label: 'Option 1', value: 'option1', header: 'header1' },
141
- { label: 'Option 2', value: 'option2', header: 'header2' },
142
- { label: 'Option 3', value: 'option3', header: 'header3' },
143
- { label: 'Option 4', value: 'option4', header: 'header4' },
144
- { label: 'Option 5', value: 'option5', header: 'header5', group: 'Group 1' },
145
- { label: 'Option 6', value: 'option6', header: 'header6', group: 'Group 1' },
146
- { label: 'Option 7', value: 'option7', header: 'header7', group: 'Group 2' },
147
- { label: 'Option 8', value: 'option8', header: 'header8', group: 'Group 2' },
148
- { label: 'Option 9', value: 'option9', header: 'header9' },
149
- ],
150
- onSelectionChange: (value) => { console.log(value); },
489
+ }, 'The `disabled` prop prevents the trigger from opening. Use when the field is not applicable in the current context — for example, a dependent field that requires another field to be filled first.');
490
+ export const WithError = withDescription({
491
+ render: WithErrorTemplate,
492
+ parameters: {
493
+ controls: { disable: true },
494
+ docs: {
495
+ source: {
496
+ language: 'tsx',
497
+ code: `
498
+ import { SelectDropdown } from '@arbor-education/design-system.components';
499
+
500
+ function WithErrorExample() {
501
+ return (
502
+ <div>
503
+ <SelectDropdown
504
+ placeholder="Select a year group"
505
+ hasError
506
+ aria-invalid
507
+ aria-describedBy="year-group-error"
508
+ options={[
509
+ { label: 'Year 7', value: 'year7' },
510
+ { label: 'Year 8', value: 'year8' },
511
+ { label: 'Year 9', value: 'year9' },
512
+ ]}
513
+ onSelectionChange={(values) => console.log(values)}
514
+ />
515
+ <span id="year-group-error">Please select a year group</span>
516
+ </div>
517
+ );
518
+ }
519
+ export default WithErrorExample;
520
+ `.trim(),
521
+ },
522
+ },
151
523
  },
152
- };
153
- export const MultilineItemsGroupedMultiSelect = {
154
- args: {
155
- title: 'titleValue',
156
- multiple: true,
524
+ }, [
525
+ '`hasError` applies error styling to the trigger. `aria-invalid` tells screen readers the field is invalid.',
526
+ '`aria-describedBy` links to the error message element by ID.',
527
+ '**All three should be used together for an accessible error state.**',
528
+ 'When using `FormField`, these are wired automatically via `errorText` — prefer the `InsideFormFieldWithError` pattern.',
529
+ ].join(' '));
530
+ export const WithIcons = withDescription({
531
+ render: WithIconsTemplate,
532
+ parameters: {
533
+ controls: { disable: true },
534
+ docs: {
535
+ source: {
536
+ language: 'tsx',
537
+ code: `
538
+ import { SelectDropdown } from '@arbor-education/design-system.components';
539
+
540
+ function WithIconsExample() {
541
+ return (
542
+ <SelectDropdown
543
+ placeholder="Select a subject"
544
+ options={[
545
+ { label: 'Mathematics', value: 'maths', icon: 'chart-column-increasing' },
546
+ { label: 'English', value: 'english', icon: 'book-open' },
547
+ { label: 'Science', value: 'science', icon: 'circle-check' },
548
+ { label: 'History', value: 'history', icon: 'clock-3' },
549
+ { label: 'Geography', value: 'geography', icon: 'flag' },
550
+ ]}
551
+ onSelectionChange={(values) => console.log(values)}
552
+ />
553
+ );
554
+ }
555
+ export default WithIconsExample;
556
+ `.trim(),
557
+ },
558
+ },
559
+ },
560
+ }, 'Options can include an `icon` prop to display an icon to the left of the label. Use icon names from the Arbor Icon component.');
561
+ export const WithItemHeaders = withDescription({
562
+ render: WithItemHeadersTemplate,
563
+ parameters: {
564
+ controls: { disable: true },
565
+ docs: {
566
+ source: {
567
+ language: 'tsx',
568
+ code: `
569
+ import { SelectDropdown } from '@arbor-education/design-system.components';
570
+
571
+ function WithItemHeadersExample() {
572
+ return (
573
+ <SelectDropdown
574
+ placeholder="Select absence reason"
575
+ options={[
576
+ { label: 'Authorised absence', value: 'authorised', header: 'Planned' },
577
+ { label: 'Holiday', value: 'holiday', header: 'Planned' },
578
+ { label: 'Medical appointment', value: 'medical', header: 'Health' },
579
+ { label: 'Illness', value: 'illness', header: 'Health' },
580
+ { label: 'Unauthorised absence', value: 'unauthorised', header: 'Unplanned' },
581
+ ]}
582
+ onSelectionChange={(values) => console.log(values)}
583
+ />
584
+ );
585
+ }
586
+ export default WithItemHeadersExample;
587
+ `.trim(),
588
+ },
589
+ },
590
+ },
591
+ }, 'The `header` prop on an option renders a small label above the option text, creating a two-line item layout. Use this to add context to complex options — for example, showing the category of an absence type alongside its name.');
592
+ export const WithGroups = withDescription({
593
+ render: WithGroupsTemplate,
594
+ parameters: {
595
+ controls: { disable: true },
596
+ docs: {
597
+ source: {
598
+ language: 'tsx',
599
+ code: `
600
+ import { SelectDropdown } from '@arbor-education/design-system.components';
601
+
602
+ function WithGroupsExample() {
603
+ return (
604
+ <SelectDropdown
605
+ placeholder="Select a year group"
606
+ options={[
607
+ { label: 'Year 7', value: 'year7', group: 'Lower school' },
608
+ { label: 'Year 8', value: 'year8', group: 'Lower school' },
609
+ { label: 'Year 9', value: 'year9', group: 'Lower school' },
610
+ { label: 'Year 10', value: 'year10', group: 'Upper school' },
611
+ { label: 'Year 11', value: 'year11', group: 'Upper school' },
612
+ { label: 'Year 12', value: 'year12', group: 'Sixth form' },
613
+ { label: 'Year 13', value: 'year13', group: 'Sixth form' },
614
+ ]}
615
+ onSelectionChange={(values) => console.log(values)}
616
+ />
617
+ );
618
+ }
619
+ export default WithGroupsExample;
620
+ `.trim(),
621
+ },
622
+ },
623
+ },
624
+ }, 'Adding a `group` string to options automatically clusters them under section headings. Group headers are only rendered when two or more distinct group values exist. Items without a `group` are rendered ungrouped.');
625
+ export const WithGroupsMultiSelect = withDescription({
626
+ render: WithGroupsMultiSelectTemplate,
627
+ parameters: {
628
+ controls: { disable: true },
629
+ docs: {
630
+ source: {
631
+ language: 'tsx',
632
+ code: `
633
+ import { SelectDropdown } from '@arbor-education/design-system.components';
634
+
635
+ function WithGroupsMultiSelectExample() {
636
+ return (
637
+ <SelectDropdown
638
+ placeholder="Select year groups"
639
+ multiple
640
+ options={[
641
+ { label: 'Year 7', value: 'year7', group: 'Lower school' },
642
+ { label: 'Year 8', value: 'year8', group: 'Lower school' },
643
+ { label: 'Year 9', value: 'year9', group: 'Lower school' },
644
+ { label: 'Year 10', value: 'year10', group: 'Upper school' },
645
+ { label: 'Year 11', value: 'year11', group: 'Upper school' },
646
+ ]}
647
+ onSelectionChange={(values) => console.log(values)}
648
+ />
649
+ );
650
+ }
651
+ export default WithGroupsMultiSelectExample;
652
+ `.trim(),
653
+ },
654
+ },
655
+ },
656
+ }, 'Grouped options combined with multi-select. The dropdown stays open while users make multiple selections across groups.');
657
+ export const AlwaysShowPlaceholder = withDescription({
658
+ render: AlwaysShowPlaceholderTemplate,
659
+ parameters: {
660
+ controls: { disable: true },
661
+ docs: {
662
+ source: {
663
+ language: 'tsx',
664
+ code: `
665
+ import { SelectDropdown } from '@arbor-education/design-system.components';
666
+
667
+ function AlwaysShowPlaceholderExample() {
668
+ return (
669
+ <SelectDropdown
670
+ placeholder="Add filter"
671
+ alwaysShowPlaceholder
672
+ multiple
673
+ options={[
674
+ { label: 'Mathematics', value: 'maths' },
675
+ { label: 'English', value: 'english' },
676
+ { label: 'Science', value: 'science' },
677
+ ]}
678
+ onSelectionChange={(values) => console.log(values)}
679
+ />
680
+ );
681
+ }
682
+ export default AlwaysShowPlaceholderExample;
683
+ `.trim(),
684
+ },
685
+ },
686
+ },
687
+ }, '`alwaysShowPlaceholder` keeps the trigger label fixed regardless of selection state. Use for action-style dropdowns (e.g. "Add filter") where the trigger label should not change to reflect the selected value.');
688
+ export const ControlledSingleSelect = withDescription({
689
+ render: ControlledSingleSelectTemplate,
690
+ parameters: {
691
+ controls: { disable: true },
692
+ docs: {
693
+ source: {
694
+ language: 'tsx',
695
+ code: `
696
+ import { useState } from 'react';
697
+ import { SelectDropdown } from '@arbor-education/design-system.components';
698
+
699
+ function ControlledSingleSelectExample() {
700
+ const [selected, setSelected] = useState<string[]>([]);
701
+
702
+ return (
703
+ <SelectDropdown
704
+ placeholder="Select a subject"
705
+ selectedValues={selected}
706
+ onSelectionChange={setSelected}
707
+ options={[
708
+ { label: 'Mathematics', value: 'maths' },
709
+ { label: 'English', value: 'english' },
710
+ { label: 'Science', value: 'science' },
711
+ { label: 'History', value: 'history' },
712
+ ]}
713
+ />
714
+ );
715
+ }
716
+ export default ControlledSingleSelectExample;
717
+ `.trim(),
718
+ },
719
+ },
720
+ },
721
+ }, [
722
+ 'Fully controlled single-select. Providing `selectedValues` activates controlled mode — the component stops managing its own state.',
723
+ 'You **must** update `selectedValues` in response to `onSelectionChange`, otherwise the dropdown will appear frozen.',
724
+ 'Use this pattern when the selected value needs to be derived from, or synchronised with, external state.',
725
+ ].join(' '));
726
+ export const ControlledOpen = withDescription({
727
+ render: ControlledOpenTemplate,
728
+ parameters: {
729
+ controls: { disable: true },
730
+ docs: {
731
+ source: {
732
+ language: 'tsx',
733
+ code: `
734
+ import { useState } from 'react';
735
+ import { SelectDropdown } from '@arbor-education/design-system.components';
736
+
737
+ function ControlledOpenExample() {
738
+ const [isOpen, setIsOpen] = useState(false);
739
+
740
+ return (
741
+ <div>
742
+ <button type="button" onClick={() => setIsOpen(true)}>Open dropdown externally</button>
743
+ <SelectDropdown
744
+ placeholder="Select a subject"
745
+ open={isOpen}
746
+ onOpenChange={setIsOpen}
747
+ options={[
748
+ { label: 'Mathematics', value: 'maths' },
749
+ { label: 'English', value: 'english' },
750
+ { label: 'Science', value: 'science' },
751
+ ]}
752
+ onSelectionChange={(values) => {
753
+ console.log(values);
754
+ setIsOpen(false);
755
+ }}
756
+ />
757
+ </div>
758
+ );
759
+ }
760
+ export default ControlledOpenExample;
761
+ `.trim(),
762
+ },
763
+ },
764
+ },
765
+ }, '`open` and `onOpenChange` let you control the dropdown visibility externally. Use when you need to open the dropdown programmatically — from a toolbar button, a keyboard shortcut, or after completing a validation step.');
766
+ export const InsideFormField = withDescription({
767
+ render: InsideFormFieldTemplate,
768
+ parameters: {
769
+ controls: { disable: true },
770
+ docs: {
771
+ source: {
772
+ language: 'tsx',
773
+ code: `
774
+ import { FormField } from '@arbor-education/design-system.components';
775
+
776
+ function InsideFormFieldExample() {
777
+ return (
778
+ <FormField
779
+ label="Year group"
780
+ id="year-group-field"
781
+ inputType="selectDropdown"
782
+ inputProps={{
783
+ placeholder: 'Select a year group',
157
784
  options: [
158
- { label: 'Option 5', value: 'option5', header: 'header5', group: 'Group 1' },
159
- { label: 'Option 6', value: 'option6', header: 'header6', group: 'Group 1' },
160
- { label: 'Option 7', value: 'option7', header: 'header7', group: 'Group 2' },
161
- { label: 'Option 8', value: 'option8', header: 'header8', group: 'Group 2' },
162
- { label: 'Option 1', value: 'option1', header: 'header1' },
163
- { label: 'Option 2', value: 'option2', header: 'header2' },
164
- { label: 'Option 3', value: 'option3', header: 'header3' },
165
- { label: 'Option 4', value: 'option4', header: 'header4' },
166
- { label: 'Option 9', value: 'option9', header: 'header9' },
785
+ { label: 'Year 7', value: 'year7' },
786
+ { label: 'Year 8', value: 'year8' },
787
+ { label: 'Year 9', value: 'year9' },
788
+ { label: 'Year 10', value: 'year10' },
789
+ { label: 'Year 11', value: 'year11' },
167
790
  ],
168
- onSelectionChange: (value) => { console.log(value); },
791
+ onSelectionChange: (values) => console.log(values),
792
+ }}
793
+ />
794
+ );
795
+ }
796
+ export default InsideFormFieldExample;
797
+ `.trim(),
798
+ },
799
+ },
169
800
  },
170
- };
171
- export const ControlledOpen = {
172
- args: {
173
- title: 'titleValue',
801
+ }, [
802
+ 'The recommended way to use SelectDropdown in forms.',
803
+ '`FormField` with `inputType="selectDropdown"` provides an accessible `<label>` linked to the trigger,',
804
+ 'and automatically wires `aria-invalid` and `aria-describedby` when `errorText` is set.',
805
+ 'Pass SelectDropdown props via `inputProps`.',
806
+ ].join(' '));
807
+ export const InsideFormFieldWithError = withDescription({
808
+ render: InsideFormFieldWithErrorTemplate,
809
+ parameters: {
810
+ controls: { disable: true },
811
+ docs: {
812
+ source: {
813
+ language: 'tsx',
814
+ code: `
815
+ import { FormField } from '@arbor-education/design-system.components';
816
+
817
+ function InsideFormFieldWithErrorExample() {
818
+ return (
819
+ <FormField
820
+ label="Year group"
821
+ id="year-group-error-field"
822
+ inputType="selectDropdown"
823
+ errorText="Please select a year group"
824
+ inputProps={{
825
+ placeholder: 'Select a year group',
174
826
  options: [
175
- { label: 'Option 1', value: 'option1' },
176
- { label: 'Option 2', value: 'option2' },
177
- { label: 'Option 3', value: 'option3' },
827
+ { label: 'Year 7', value: 'year7' },
828
+ { label: 'Year 8', value: 'year8' },
829
+ { label: 'Year 9', value: 'year9' },
178
830
  ],
179
- open: true,
180
- onOpenChange: (open) => { console.log('open changed:', open); },
181
- onSelectionChange: (value) => { console.log(value); },
831
+ onSelectionChange: (values) => console.log(values),
832
+ }}
833
+ />
834
+ );
835
+ }
836
+ export default InsideFormFieldWithErrorExample;
837
+ `.trim(),
838
+ },
839
+ },
182
840
  },
183
- };
184
- export default meta;
841
+ }, [
842
+ 'Error state via `FormField`. Setting `errorText` on `FormField` automatically passes `hasError`,',
843
+ '`aria-invalid`, and `aria-describedby` to the SelectDropdown, and renders the error message below the field.',
844
+ 'This is the correct and recommended pattern for accessible form validation.',
845
+ ].join(' '));
185
846
  //# sourceMappingURL=SelectDropdown.stories.js.map