@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
@@ -1,110 +1,488 @@
1
1
  import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useState } from 'react';
3
+ import { Heading as DocHeading, Markdown, Stories, Subtitle, Title, } from '@storybook/addon-docs/blocks';
2
4
  import { ModalManager } from './ModalManager';
3
- import { ModalUtils } from '../../../utils/ModalUtils';
5
+ import { Modal } from '../../modal/Modal';
4
6
  import { Button } from '../../button/Button';
5
- import { Section } from '../../section/Section';
6
7
  import { FormField } from '../../formField/FormField';
7
- import { generateUuid } from '../../../utils/generateUuid';
8
- import { Modal } from '../Modal';
9
- import { fn } from 'storybook/test';
10
8
  import { Icon } from '../../icon/Icon';
9
+ // ---------------------------------------------------------------------------
10
+ // Docs page content
11
+ // ---------------------------------------------------------------------------
12
+ const DESCRIPTION_INTRO = [
13
+ '`ModalManager` is a singleton container that opens and closes modals programmatically via PubSub events.',
14
+ 'Mount it once near your app root, then call `ModalUtils.addModal(props)` from anywhere in the component',
15
+ 'tree — no prop drilling, no shared state. For modals that are tightly coupled to their trigger,',
16
+ 'prefer the raw [`Modal`](?path=/docs/components-modals-modal--docs) component with local state instead.',
17
+ ].join(' ');
18
+ const USAGE_GUIDANCE = [
19
+ '### When to use',
20
+ '',
21
+ '- **Deep action handlers** — triggering a modal from an API response, a table row action, or a',
22
+ ' notification handler that has no direct access to modal state',
23
+ '- **Cross-cutting concerns** — session-expiry dialogs, global error modals, or app-level confirmations',
24
+ ' that can be triggered from any module',
25
+ '- **Avoiding prop drilling** — when the modal trigger and the `Modal` render point are far apart in',
26
+ ' the component tree',
27
+ '',
28
+ '---',
29
+ '',
30
+ '### When NOT to use',
31
+ '',
32
+ '| Situation | Use instead |',
33
+ '|---|---|',
34
+ '| Modal is directly triggered by a parent component | Raw [`Modal`](?path=/docs/components-modals-modal--docs) with local `useState` |',
35
+ '| Multiple modals open simultaneously | Not supported — `ModalManager` holds one modal at a time |',
36
+ '| You need full control of `open`/`closeHandler` lifecycle | Raw `Modal` component |',
37
+ ].join('\n');
38
+ const DEVELOPER_NOTES = [
39
+ '### Setup',
40
+ '',
41
+ 'Mount `<ModalManager />` once, near your application root:',
42
+ '',
43
+ '```tsx',
44
+ "import { ModalManager } from '@arbor-education/design-system.components';",
45
+ '',
46
+ 'function App() {',
47
+ ' return (',
48
+ ' <>',
49
+ ' <ModalManager />',
50
+ ' <Router>',
51
+ ' {/* rest of your app */}',
52
+ ' </Router>',
53
+ ' </>',
54
+ ' );',
55
+ '}',
56
+ '```',
57
+ '',
58
+ '---',
59
+ '',
60
+ '### ModalUtils API',
61
+ '',
62
+ '```ts',
63
+ "import { ModalUtils } from '@arbor-education/design-system.components';",
64
+ '',
65
+ '// Open a modal — pass any ModalProps except open and closeHandler',
66
+ 'ModalUtils.addModal({',
67
+ ' title: "Confirm deletion",',
68
+ ' children: (',
69
+ ' <>',
70
+ ' <Modal.Body>Are you sure?</Modal.Body>',
71
+ ' <Modal.Footer>',
72
+ ' <Button onClick={() => ModalUtils.removeModal()}>Cancel</Button>',
73
+ ' <Button variant="primary-destructive" onClick={handleDelete}>Delete</Button>',
74
+ ' </Modal.Footer>',
75
+ ' </>',
76
+ ' ),',
77
+ '});',
78
+ '',
79
+ '// Close the current modal',
80
+ 'ModalUtils.removeModal();',
81
+ '',
82
+ '// Close all modals (also clears any queued modal state)',
83
+ 'ModalUtils.removeAllModals();',
84
+ '```',
85
+ '',
86
+ '---',
87
+ '',
88
+ '### Critical notes',
89
+ '',
90
+ '**Do NOT pass `closeHandler` via `ModalUtils.addModal()`.**',
91
+ '`ModalManager` always provides its own `closeHandler` — passing one in `modalProps` would override it',
92
+ 'and break the X button and Escape key dismissal.',
93
+ '',
94
+ '**`ModalManager` holds one modal at a time.**',
95
+ 'Calling `ModalUtils.addModal()` while a modal is open replaces it immediately. If you need a queue or',
96
+ 'multiple layered modals, manage that state yourself and call `addModal` once you are ready to show the next.',
97
+ '',
98
+ '**The `modal` prop sets an initial modal at mount.**',
99
+ 'Useful for route-based or server-rendered scenarios where the modal state is known before the component mounts.',
100
+ 'Once mounted, further changes are driven by `ModalUtils`.',
101
+ '',
102
+ '---',
103
+ '',
104
+ '### Accessibility',
105
+ '',
106
+ '- Focus trapping and restoration are inherited from the underlying `Modal` component',
107
+ '- `ModalManager` always provides a `closeHandler`, so the X button and Escape key are always active',
108
+ '- Ensure modal content includes an accessible title — use `title` prop or `Modal.Header` + `Modal.Title`',
109
+ '',
110
+ '---',
111
+ '',
112
+ '### TypeScript types',
113
+ '',
114
+ '```ts',
115
+ "import { ModalManager } from '@arbor-education/design-system.components';",
116
+ "import type { ModalProps } from '@arbor-education/design-system.components';",
117
+ '',
118
+ '// ModalManagerProps',
119
+ 'type ModalManagerProps = {',
120
+ ' modal?: ModalProps; // optional initial modal shown at mount',
121
+ '};',
122
+ '```',
123
+ ].join('\n');
124
+ const RELATED_COMPONENTS = [
125
+ '## Related components',
126
+ '',
127
+ '[Modal](?path=/docs/components-modals-modal--docs) · [Button](?path=/docs/components-button--docs) · [FormField](?path=/docs/components-formfield--docs)',
128
+ ].join('\n');
129
+ // ---------------------------------------------------------------------------
130
+ // Docs page
131
+ // ---------------------------------------------------------------------------
132
+ function ModalManagerDocsPage() {
133
+ return (_jsxs(_Fragment, { children: [_jsx(Title, {}), _jsx(Subtitle, {}), _jsx(Markdown, { children: DESCRIPTION_INTRO }), _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 })] }));
134
+ }
135
+ // ---------------------------------------------------------------------------
136
+ // Meta
137
+ // ---------------------------------------------------------------------------
11
138
  const meta = {
12
139
  title: 'Components/Modals/ModalManager',
140
+ component: ModalManager,
141
+ tags: ['autodocs'],
13
142
  parameters: {
14
- layout: 'fullscreen',
143
+ layout: 'padded',
15
144
  docs: {
16
- description: {
17
- component: 'The ModalManager is a container that manages the display of modals. It listens for events to add or remove modals.',
145
+ page: ModalManagerDocsPage,
146
+ },
147
+ },
148
+ argTypes: {
149
+ modal: {
150
+ control: false,
151
+ description: [
152
+ 'Optional initial modal to show at mount. Pass any `ModalProps` value — `open` and `closeHandler`',
153
+ 'are always managed by `ModalManager` itself and cannot be overridden.',
154
+ 'Once mounted, further changes are driven by `ModalUtils.addModal()` and `ModalUtils.removeModal()`.',
155
+ ].join(' '),
156
+ table: {
157
+ type: { summary: 'ModalProps' },
158
+ defaultValue: { summary: 'undefined' },
18
159
  },
19
160
  },
20
161
  },
21
- tags: ['autodocs'],
22
162
  };
23
163
  export default meta;
24
- let modalCount = 0;
25
- const addModalWithContent = (content) => {
26
- modalCount++;
27
- ModalUtils.addModal({
28
- children: content,
29
- });
164
+ // ---------------------------------------------------------------------------
165
+ // Helper
166
+ // ---------------------------------------------------------------------------
167
+ const withDescription = (story, description) => ({
168
+ ...story,
169
+ parameters: {
170
+ ...story.parameters,
171
+ docs: {
172
+ ...story.parameters?.docs,
173
+ description: {
174
+ story: description,
175
+ },
176
+ },
177
+ },
178
+ });
179
+ // ---------------------------------------------------------------------------
180
+ // Templates
181
+ // NOTE: Story templates use raw <Modal> with local useState so the portal
182
+ // renders to document.body and works correctly in Storybook's canvas context.
183
+ // Production code uses ModalUtils.addModal() — see each story's source code.
184
+ // ---------------------------------------------------------------------------
185
+ const DefaultTemplate = () => {
186
+ const [open, setOpen] = useState(false);
187
+ return (_jsxs(_Fragment, { children: [_jsx(Modal, { open: open, closeHandler: () => setOpen(false), title: "Assessment Period Added", children: _jsx(Modal.Body, { children: _jsx("p", { children: "The Spring 2026 assessment period has been added successfully. Pupils will now be able to submit work for grading." }) }) }), _jsx(Button, { onClick: () => setOpen(true), children: "Open modal" })] }));
30
188
  };
31
- const removeModal = () => {
32
- if (modalCount > 0) {
33
- modalCount--;
34
- }
35
- ModalUtils.removeModal();
189
+ const WithFooterActionsTemplate = () => {
190
+ const [open, setOpen] = useState(false);
191
+ return (_jsxs(_Fragment, { children: [_jsxs(Modal, { open: open, closeHandler: () => setOpen(false), title: "Edit Pupil Details", children: [_jsx(Modal.Body, { children: _jsx("p", { children: "Update the details below. Changes will be saved to the pupil record immediately." }) }), _jsx(Modal.Footer, { children: _jsxs("div", { style: { display: 'flex', gap: 'var(--spacing-small)', justifyContent: 'flex-end' }, children: [_jsx(Button, { variant: "secondary", onClick: () => setOpen(false), children: "Cancel" }), _jsx(Button, { variant: "primary", onClick: () => setOpen(false), children: "Save changes" })] }) })] }), _jsx(Button, { onClick: () => setOpen(true), children: "Edit pupil details" })] }));
36
192
  };
37
- export const Default = {
38
- render: () => (_jsxs(_Fragment, { children: [_jsx(ModalManager, {}), _jsx("div", { style: { padding: '1rem', display: 'flex', gap: '1rem' }, children: _jsx(Button, { onClick: () => addModalWithContent(_jsxs(_Fragment, { children: [_jsx(Modal.Header, { children: _jsx(Modal.Title, { children: "Simple Modal" }) }), _jsx(Modal.Body, { children: _jsx("p", { children: "This is a simple modal with basic content." }) })] })), children: "Open Simple Modal" }) })] })),
193
+ const WithFormContentTemplate = () => {
194
+ const [open, setOpen] = useState(false);
195
+ return (_jsxs(_Fragment, { children: [_jsxs(Modal, { open: open, closeHandler: () => setOpen(false), title: "Add New Staff Member", children: [_jsxs(Modal.Body, { children: [_jsx(FormField, { id: "staff-first-name", label: "First name", inputType: "text", inputProps: { placeholder: 'e.g. Sarah' } }), _jsx(FormField, { id: "staff-last-name", label: "Last name", inputType: "text", inputProps: { placeholder: 'e.g. Clarke' } }), _jsx(FormField, { id: "staff-email", label: "Email address", inputType: "text", inputProps: { placeholder: 'e.g. s.clarke@school.edu', type: 'email' }, fieldDescription: "The staff member will receive a login invitation at this address." })] }), _jsx(Modal.Footer, { children: _jsxs("div", { style: { display: 'flex', gap: 'var(--spacing-small)', justifyContent: 'flex-end' }, children: [_jsx(Button, { variant: "secondary", onClick: () => setOpen(false), children: "Cancel" }), _jsx(Button, { variant: "primary", onClick: () => setOpen(false), children: "Add staff member" })] }) })] }), _jsx(Button, { onClick: () => setOpen(true), children: "Add staff member" })] }));
39
196
  };
40
- export const WithHeaderAndFooter = {
41
- render: () => {
42
- const handleSave = () => {
43
- alert('Save clicked!');
44
- ModalUtils.removeModal();
45
- };
46
- const handleCancel = () => {
47
- ModalUtils.removeModal();
48
- };
49
- return (_jsxs(_Fragment, { children: [_jsx(ModalManager, {}), _jsx("div", { style: { padding: '1rem', display: 'flex', gap: '1rem' }, children: _jsx(Button, { onClick: () => addModalWithContent(_jsxs(_Fragment, { children: [_jsx(Modal.Header, { children: _jsx(Modal.Title, { children: "Modal with Header and Footer" }) }), _jsx(Modal.Body, { children: _jsx("p", { children: "This modal has both a header with a close button and a footer with action buttons." }) }), _jsx(Modal.Footer, { children: _jsxs("div", { style: { display: 'flex', gap: '1rem', justifyContent: 'flex-end' }, children: [_jsx(Button, { variant: "secondary", onClick: handleCancel, children: "Cancel" }), _jsx(Button, { variant: "primary", onClick: handleSave, children: "Save Changes" })] }) })] })), children: "Open Modal with Footer" }) })] }));
50
- },
197
+ const DeleteConfirmationTemplate = () => {
198
+ const [open, setOpen] = useState(false);
199
+ return (_jsxs(_Fragment, { children: [_jsxs(Modal, { open: open, title: "Remove Emily Clarke?", children: [_jsx(Modal.Body, { children: _jsx("p", { children: "Removing Emily Clarke will permanently delete her assessment records for this term. This action cannot be undone." }) }), _jsx(Modal.Footer, { children: _jsxs("div", { style: { display: 'flex', gap: 'var(--spacing-small)', justifyContent: 'flex-end' }, children: [_jsx(Button, { variant: "secondary", onClick: () => setOpen(false), children: "Cancel" }), _jsx(Button, { variant: "primary-destructive", onClick: () => setOpen(false), children: "Remove pupil" })] }) })] }), _jsx(Button, { variant: "secondary-destructive", onClick: () => setOpen(true), children: "Remove pupil" })] }));
51
200
  };
52
- export const WithSections = {
53
- render: () => (_jsxs(_Fragment, { children: [_jsx(ModalManager, {}), _jsx("div", { style: { padding: '1rem', display: 'flex', gap: '1rem' }, children: _jsx(Button, { onClick: () => addModalWithContent(_jsxs(_Fragment, { children: [_jsx(Modal.Header, { children: _jsx(Modal.Title, { children: "Modal with Sections" }) }), _jsxs(Modal.Body, { children: [_jsx(Section, { title: "Section 1", collapsible: true, children: _jsx("p", { children: "Aliquip ea tempor officia irure do qui culpa. Laborum proident laboris aliqua ad Lorem eiusmod fugiat dolore qui occaecat adipisicing. Dolore quis nisi occaecat commodo labore ad nulla aliquip in. Enim labore nisi laborum nostrud officia ad nulla ut quis. Aute excepteur quis amet aliquip. Excepteur Lorem cupidatat adipisicing consectetur ipsum Lorem." }) }), _jsx(Section, { title: "Section 2", collapsible: true, collapsed: true, children: _jsx("p", { children: "Veniam adipisicing culpa id esse duis in officia consequat veniam ad ullamco sint sunt anim. Incididunt exercitation dolor Lorem anim aute ad deserunt. Nulla culpa sint aliqua ea Lorem sint. Elit sit consectetur minim cillum consectetur pariatur Lorem labore consectetur nisi consectetur officia exercitation. Est consectetur commodo deserunt ipsum aliqua occaecat qui sunt consectetur." }) }), _jsx(Section, { title: "Section 3", collapsible: true, children: _jsx("p", { children: "Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo." }) })] })] })), children: "Open Modal with Sections" }) })] })),
201
+ const ErrorModalTemplate = () => {
202
+ const [open, setOpen] = useState(false);
203
+ return (_jsxs(_Fragment, { children: [_jsxs(Modal, { open: open, closeHandler: () => setOpen(false), children: [_jsxs(Modal.Header, { children: [_jsx(Icon, { name: "triangle-alert", size: 24, color: "var(--color-semantic-destructive-500)" }), _jsx(Modal.Title, { children: "Sorry, we cannot find this page" })] }), _jsxs(Modal.Body, { children: [_jsxs("p", { children: [_jsx("strong", { children: "Please try finding the page using the navigation." }), ' ', "If you cannot find what you are looking for, contact support with the error ID below."] }), _jsx("p", { style: { marginTop: 'var(--spacing-small)', fontFamily: 'monospace', fontSize: 'var(--font-size-2-13)' }, children: "ID: f47ecbd4-319b-11f0-a5ae-06f9b8bc5d95" })] }), _jsx(Modal.Footer, { children: _jsxs("div", { style: { display: 'flex', gap: 'var(--spacing-small)', justifyContent: 'flex-end' }, children: [_jsx(Button, { variant: "tertiary", onClick: () => setOpen(false), children: "Close" }), _jsx(Button, { variant: "primary", onClick: () => setOpen(false), children: "Contact support" })] }) })] }), _jsx(Button, { onClick: () => setOpen(true), children: "Simulate page not found" })] }));
54
204
  };
55
- export const WithFormFields = {
56
- render: () => {
57
- const handleSubmit = () => {
58
- alert('Form submitted!');
59
- ModalUtils.removeModal();
60
- };
61
- return (_jsxs(_Fragment, { children: [_jsx(ModalManager, {}), _jsx("div", { style: { padding: '1rem', display: 'flex', gap: '1rem' }, children: _jsx(Button, { onClick: () => addModalWithContent(_jsxs(_Fragment, { children: [_jsx(Modal.Header, { children: _jsx(Modal.Title, { children: "User Information Form" }) }), _jsxs(Modal.Body, { children: [_jsx(FormField, { id: generateUuid(), label: "Your Name", inputType: "text", inputProps: { placeholder: 'e.g. Jane Doe' }, fieldDescription: "Please enter your full name." }), _jsx(FormField, { id: generateUuid(), label: "Email Address", inputType: "text", inputProps: { placeholder: 'e.g. jane@example.com', type: 'email' } }), _jsx(FormField, { id: generateUuid(), label: "Message", inputType: "textarea", inputProps: { placeholder: 'Enter your message here...', rows: 4 }, fieldDescription: "Maximum 500 characters." }), _jsx(FormField, { id: generateUuid(), label: "Password", inputType: "selectDropdown", inputProps: {
62
- options: [{ label: 'Option 1', value: 'option1' }, { label: 'Option 2', value: 'option2' }, { label: 'Option 3', value: 'option3' }],
63
- onSelectionChange: fn(),
64
- } })] }), _jsx(Modal.Footer, { children: _jsxs("div", { style: { display: 'flex', gap: '1rem', justifyContent: 'flex-end' }, children: [_jsx(Button, { variant: "secondary", onClick: removeModal, children: "Cancel" }), _jsx(Button, { variant: "primary", onClick: handleSubmit, children: "Submit" })] }) })] })), children: "Open Form Modal" }) })] }));
205
+ // ---------------------------------------------------------------------------
206
+ // Stories
207
+ // ---------------------------------------------------------------------------
208
+ export const Default = withDescription({
209
+ render: DefaultTemplate,
210
+ parameters: {
211
+ controls: { disable: true },
212
+ docs: {
213
+ source: {
214
+ language: 'tsx',
215
+ code: `
216
+ import { ModalManager, ModalUtils, Modal, Button } from '@arbor-education/design-system.components';
217
+
218
+ // Mount once at your app root
219
+ function App() {
220
+ return (
221
+ <>
222
+ <ModalManager />
223
+ {/* rest of your app */}
224
+ </>
225
+ );
226
+ }
227
+
228
+ // Then trigger from anywhere:
229
+ function AssessmentActions() {
230
+ return (
231
+ <Button
232
+ onClick={() =>
233
+ ModalUtils.addModal({
234
+ title: 'Assessment Period Added',
235
+ children: (
236
+ <Modal.Body>
237
+ <p>The Spring 2026 assessment period has been added successfully.</p>
238
+ </Modal.Body>
239
+ ),
240
+ })
241
+ }
242
+ >
243
+ Open modal
244
+ </Button>
245
+ );
246
+ }
247
+ export default AssessmentActions;
248
+ `.trim(),
249
+ },
250
+ },
65
251
  },
66
- };
67
- export const WithScrollableContent = {
68
- render: () => {
69
- const handleClose = () => {
70
- ModalUtils.removeModal();
71
- };
72
- return (_jsxs(_Fragment, { children: [_jsx(ModalManager, {}), _jsx("div", { style: { padding: '1rem', display: 'flex', gap: '1rem' }, children: _jsx(Button, { onClick: () => addModalWithContent(_jsxs(_Fragment, { children: [_jsx(Modal.Header, { children: _jsx(Modal.Title, { children: "Long Content Modal" }) }), _jsxs(Modal.Body, { children: [_jsx(Section, { title: "Introduction", collapsible: true, children: _jsx("p", { children: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat." }) }), _jsxs(Section, { title: "Details", collapsible: true, children: [_jsx("p", { children: "Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum." }), _jsx(FormField, { id: generateUuid(), label: "Field 1", inputType: "text", inputProps: { placeholder: 'Enter value' } }), _jsx(FormField, { id: generateUuid(), label: "Field 2", inputType: "text", inputProps: { placeholder: 'Enter value' } })] }), _jsxs(Section, { title: "Additional Information", collapsible: true, children: [_jsx("p", { children: "Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt." }), _jsx(FormField, { id: generateUuid(), label: "Long Text Field", inputType: "textarea", inputProps: { placeholder: 'Enter long text', rows: 6 } })] }), _jsxs(Section, { title: "More Content", collapsible: true, children: [_jsx("p", { children: "Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam." }), _jsx(FormField, { id: generateUuid(), label: "Field 3", inputType: "number", inputProps: { placeholder: 'Enter number' } }), _jsx(FormField, { id: generateUuid(), label: "Field 4", inputType: "text", inputProps: { placeholder: 'Enter value' } })] }), _jsxs(Section, { title: "Final Section", collapsible: true, collapsed: true, children: [_jsx("p", { children: "At vero eos et accusamus et iusto odio dignissimos ducimus qui blanditiis praesentium voluptatum deleniti atque corrupti quos dolores et quas molestias excepturi sint occaecati cupiditate non provident, similique sunt in culpa qui officia deserunt mollitia animi, id est laborum et dolorum fuga." }), _jsx(FormField, { id: generateUuid(), label: "Final Field", inputType: "text", inputProps: { placeholder: 'Enter value' } })] })] }), _jsx(Modal.Footer, { children: _jsxs("div", { style: { display: 'flex', gap: '1rem', justifyContent: 'flex-end' }, children: [_jsx(Button, { variant: "secondary", onClick: handleClose, children: "Close" }), _jsx(Button, { variant: "primary", onClick: handleClose, children: "Save" })] }) })] })), children: "Open Scrollable Modal" }) })] }));
252
+ }, [
253
+ 'The canonical `ModalManager` usage pattern. Mount `<ModalManager />` once at the app root,',
254
+ 'then call `ModalUtils.addModal(props)` from any component in the tree.',
255
+ '`ModalManager` always provides its own `closeHandler` — the X button and Escape key are always active.',
256
+ 'Click the button to open the modal, then dismiss it with the X button or by pressing Escape.',
257
+ ].join(' '));
258
+ export const WithFooterActions = withDescription({
259
+ render: WithFooterActionsTemplate,
260
+ parameters: {
261
+ controls: { disable: true },
262
+ docs: {
263
+ source: {
264
+ language: 'tsx',
265
+ code: `
266
+ import { ModalUtils, Modal, Button } from '@arbor-education/design-system.components';
267
+
268
+ function EditPupilButton() {
269
+ return (
270
+ <Button
271
+ onClick={() =>
272
+ ModalUtils.addModal({
273
+ title: 'Edit Pupil Details',
274
+ children: (
275
+ <>
276
+ <Modal.Body>
277
+ <p>Update the details below. Changes will be saved to the pupil record immediately.</p>
278
+ </Modal.Body>
279
+ <Modal.Footer>
280
+ <div style={{ display: 'flex', gap: 'var(--spacing-small)', justifyContent: 'flex-end' }}>
281
+ <Button variant="secondary" onClick={() => ModalUtils.removeModal()}>
282
+ Cancel
283
+ </Button>
284
+ <Button variant="primary" onClick={() => ModalUtils.removeModal()}>
285
+ Save changes
286
+ </Button>
287
+ </div>
288
+ </Modal.Footer>
289
+ </>
290
+ ),
291
+ })
292
+ }
293
+ >
294
+ Edit pupil details
295
+ </Button>
296
+ );
297
+ }
298
+ export default EditPupilButton;
299
+ `.trim(),
300
+ },
301
+ },
73
302
  },
74
- };
75
- export const DestructiveAction = {
76
- render: () => {
77
- const handleDelete = () => {
78
- alert('Item deleted!');
79
- ModalUtils.removeModal();
80
- };
81
- const handleCancel = () => {
82
- ModalUtils.removeModal();
83
- };
84
- return (_jsxs(_Fragment, { children: [_jsx(ModalManager, {}), _jsx("div", { style: { padding: '1rem', display: 'flex', gap: '1rem' }, children: _jsx(Button, { onClick: () => addModalWithContent(_jsxs(_Fragment, { children: [_jsx(Modal.Header, { children: _jsx(Modal.Title, { children: "Confirm Deletion" }) }), _jsxs(Modal.Body, { children: [_jsx("p", { children: "Are you sure you want to delete this item? This action cannot be undone." }), _jsx("p", { style: { marginTop: '1rem', fontWeight: 'bold' }, children: "Item: Important Document.pdf" })] }), _jsx(Modal.Footer, { children: _jsxs("div", { style: { display: 'flex', gap: '1rem', justifyContent: 'flex-end' }, children: [_jsx(Button, { variant: "secondary", onClick: handleCancel, children: "Cancel" }), _jsx(Button, { variant: "primary-destructive", onClick: handleDelete, children: "Delete" })] }) })] })), children: "Open Deletion Confirmation Modal" }) })] }));
303
+ }, [
304
+ 'A modal with a full header, body, and footer. Use `Modal.Footer` for action buttons.',
305
+ 'Always place the primary action on the right and the cancel action on the left.',
306
+ 'Call `ModalUtils.removeModal()` inside button handlers to close the modal after the action completes.',
307
+ ].join(' '));
308
+ export const WithFormContent = withDescription({
309
+ render: WithFormContentTemplate,
310
+ parameters: {
311
+ controls: { disable: true },
312
+ docs: {
313
+ source: {
314
+ language: 'tsx',
315
+ code: `
316
+ import { ModalUtils, Modal, Button, FormField } from '@arbor-education/design-system.components';
317
+
318
+ function AddStaffButton() {
319
+ return (
320
+ <Button
321
+ onClick={() =>
322
+ ModalUtils.addModal({
323
+ title: 'Add New Staff Member',
324
+ children: (
325
+ <>
326
+ <Modal.Body>
327
+ <FormField
328
+ id="staff-first-name"
329
+ label="First name"
330
+ inputType="text"
331
+ inputProps={{ placeholder: 'e.g. Sarah' }}
332
+ />
333
+ <FormField
334
+ id="staff-last-name"
335
+ label="Last name"
336
+ inputType="text"
337
+ inputProps={{ placeholder: 'e.g. Clarke' }}
338
+ />
339
+ <FormField
340
+ id="staff-email"
341
+ label="Email address"
342
+ inputType="text"
343
+ inputProps={{ placeholder: 'e.g. s.clarke@school.edu', type: 'email' }}
344
+ fieldDescription="The staff member will receive a login invitation at this address."
345
+ />
346
+ </Modal.Body>
347
+ <Modal.Footer>
348
+ <div style={{ display: 'flex', gap: 'var(--spacing-small)', justifyContent: 'flex-end' }}>
349
+ <Button variant="secondary" onClick={() => ModalUtils.removeModal()}>Cancel</Button>
350
+ <Button variant="primary" onClick={() => ModalUtils.removeModal()}>Add staff member</Button>
351
+ </div>
352
+ </Modal.Footer>
353
+ </>
354
+ ),
355
+ })
356
+ }
357
+ >
358
+ Add staff member
359
+ </Button>
360
+ );
361
+ }
362
+ export default AddStaffButton;
363
+ `.trim(),
364
+ },
365
+ },
85
366
  },
86
- };
87
- export const SimpleConfirmation = {
88
- render: () => {
89
- const handleConfirm = () => {
90
- alert('Confirmed!');
91
- ModalUtils.removeModal();
92
- };
93
- const handleCancel = () => {
94
- ModalUtils.removeModal();
95
- };
96
- return (_jsxs(_Fragment, { children: [_jsx(ModalManager, {}), _jsx("div", { style: { padding: '1rem', display: 'flex', gap: '1rem' }, children: _jsx(Button, { onClick: () => addModalWithContent(_jsxs(_Fragment, { children: [_jsx(Modal.Header, { children: _jsx(Modal.Title, { children: "Confirm Action" }) }), _jsx(Modal.Body, { children: _jsx("p", { children: "Are you sure you want to proceed with this action?" }) }), _jsx(Modal.Footer, { children: _jsxs("div", { style: { display: 'flex', gap: '1rem', justifyContent: 'flex-end' }, children: [_jsx(Button, { variant: "secondary", onClick: handleCancel, children: "No" }), _jsx(Button, { variant: "primary", onClick: handleConfirm, children: "Yes" })] }) })] })), children: "Open Confirmation Modal" }) })] }));
367
+ }, [
368
+ 'A modal containing a form with `FormField` components. The `Modal.Body` provides a scrollable',
369
+ 'container so long forms scroll independently of the header and footer.',
370
+ 'Wrap form fields in `Modal.Body` and action buttons in `Modal.Footer` to preserve this layout.',
371
+ ].join(' '));
372
+ export const DeleteConfirmation = withDescription({
373
+ render: DeleteConfirmationTemplate,
374
+ parameters: {
375
+ controls: { disable: true },
376
+ docs: {
377
+ source: {
378
+ language: 'tsx',
379
+ code: `
380
+ import { ModalUtils, Modal, Button } from '@arbor-education/design-system.components';
381
+
382
+ function RemovePupilButton() {
383
+ const handleDelete = () => {
384
+ // perform the deletion...
385
+ ModalUtils.removeModal();
386
+ };
387
+
388
+ return (
389
+ <Button
390
+ variant="secondary-destructive"
391
+ onClick={() =>
392
+ ModalUtils.addModal({
393
+ title: 'Remove Emily Clarke?',
394
+ children: (
395
+ <>
396
+ <Modal.Body>
397
+ <p>
398
+ Removing Emily Clarke will permanently delete her assessment records for this term.
399
+ This action cannot be undone.
400
+ </p>
401
+ </Modal.Body>
402
+ <Modal.Footer>
403
+ <div style={{ display: 'flex', gap: 'var(--spacing-small)', justifyContent: 'flex-end' }}>
404
+ <Button variant="secondary" onClick={() => ModalUtils.removeModal()}>
405
+ Cancel
406
+ </Button>
407
+ <Button variant="primary-destructive" onClick={handleDelete}>
408
+ Remove pupil
409
+ </Button>
410
+ </div>
411
+ </Modal.Footer>
412
+ </>
413
+ ),
414
+ })
415
+ }
416
+ >
417
+ Remove pupil
418
+ </Button>
419
+ );
420
+ }
421
+ export default RemovePupilButton;
422
+ `.trim(),
423
+ },
424
+ },
97
425
  },
98
- };
99
- export const ExampleErrorModal = {
100
- render: () => {
101
- const handleConfirm = () => {
102
- ModalUtils.removeModal();
103
- };
104
- const handleCancel = () => {
105
- ModalUtils.removeModal();
106
- };
107
- return (_jsxs(_Fragment, { children: [_jsx(ModalManager, {}), _jsx("div", { style: { padding: '1rem', display: 'flex', gap: '1rem' }, children: _jsx(Button, { onClick: () => addModalWithContent(_jsxs(_Fragment, { children: [_jsxs(Modal.Header, { children: [_jsx(Icon, { name: "triangle-alert", size: 24, color: "var(--color-semantic-destructive-500)" }), _jsx(Modal.Title, { children: "Sorry, we can\u2019t find this page" })] }), _jsxs(Modal.Body, { children: [_jsxs("span", { children: [_jsx("b", { children: "Please try finding the page using the navigation." }), ' ', "If you can't find what you're looking for, there are two options depending on your support plan:"] }), _jsxs("ul", { children: [_jsxs("li", { children: [_jsx("b", { children: "Arbor supported:" }), ' ', "Press here to send an email to our Support Team using our Contact Us form (the error ID will be added automatically)."] }), _jsxs("li", { children: [_jsx("b", { children: "Partner supported:" }), ' ', "Please get in touch with your Support Partner, and let them know the error ID below."] })] }), _jsx("span", { children: "ID:f47ecbd4-319b-11f0-a5ae-06f9b8bc5d95" })] }), _jsx(Modal.Footer, { children: _jsxs("div", { style: { display: 'flex', gap: '1rem', justifyContent: 'flex-end' }, children: [_jsx(Button, { variant: "tertiary", onClick: handleCancel, children: "Close" }), _jsx(Button, { variant: "primary", onClick: handleConfirm, children: "Primary Action" })] }) })] })), children: "Open Deletion Confirmation Modal" }) })] }));
426
+ }, [
427
+ 'The Arbor delete confirmation pattern. Use `variant="secondary-destructive"` on the trigger button',
428
+ 'and `variant="primary-destructive"` on the confirm action inside the modal.',
429
+ 'Always name the item being deleted in the modal title so users are certain of what they are removing.',
430
+ 'Cancel goes on the left; the destructive action goes on the right.',
431
+ ].join(' '));
432
+ export const ErrorModal = withDescription({
433
+ render: ErrorModalTemplate,
434
+ parameters: {
435
+ controls: { disable: true },
436
+ docs: {
437
+ source: {
438
+ language: 'tsx',
439
+ code: `
440
+ import { ModalUtils, Modal, Button, Icon } from '@arbor-education/design-system.components';
441
+
442
+ function SimulateError() {
443
+ return (
444
+ <Button
445
+ onClick={() =>
446
+ ModalUtils.addModal({
447
+ children: (
448
+ <>
449
+ <Modal.Header>
450
+ <Icon name="triangle-alert" size={24} color="var(--color-semantic-destructive-500)" />
451
+ <Modal.Title>Sorry, we cannot find this page</Modal.Title>
452
+ </Modal.Header>
453
+ <Modal.Body>
454
+ <p>
455
+ <strong>Please try finding the page using the navigation.</strong>{' '}
456
+ If you cannot find what you are looking for, contact support with the error ID below.
457
+ </p>
458
+ <p style={{ fontFamily: 'monospace', fontSize: 'var(--font-size-2-13)' }}>
459
+ ID: f47ecbd4-319b-11f0-a5ae-06f9b8bc5d95
460
+ </p>
461
+ </Modal.Body>
462
+ <Modal.Footer>
463
+ <div style={{ display: 'flex', gap: 'var(--spacing-small)', justifyContent: 'flex-end' }}>
464
+ <Button variant="tertiary" onClick={() => ModalUtils.removeModal()}>Close</Button>
465
+ <Button variant="primary" onClick={() => ModalUtils.removeModal()}>Contact support</Button>
466
+ </div>
467
+ </Modal.Footer>
468
+ </>
469
+ ),
470
+ })
471
+ }
472
+ >
473
+ Simulate page not found
474
+ </Button>
475
+ );
476
+ }
477
+ export default SimulateError;
478
+ `.trim(),
479
+ },
480
+ },
108
481
  },
109
- };
482
+ }, [
483
+ 'An application-level error modal using `Modal.Header` for custom icon + title composition.',
484
+ 'Note that `title` is omitted from `ModalUtils.addModal()` here — when using `Modal.Header` manually,',
485
+ 'omit the `title` prop to avoid rendering two titles. Add `hideCloseButton` if you need to suppress',
486
+ 'the auto-rendered X button when placing `Modal.CloseButton` inside the header.',
487
+ ].join(' '));
110
488
  //# sourceMappingURL=ModalManager.stories.js.map