@arbor-education/design-system.components 0.14.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 (320) hide show
  1. package/.gather/skills/write-stories/SKILL.md +207 -271
  2. package/.storybook/preview.ts +5 -0
  3. package/CHANGELOG.md +27 -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/number/NumberInput.d.ts.map +1 -1
  60. package/dist/components/formField/inputs/number/NumberInput.js +14 -2
  61. package/dist/components/formField/inputs/number/NumberInput.js.map +1 -1
  62. package/dist/components/formField/inputs/number/NumberInput.test.js +21 -0
  63. package/dist/components/formField/inputs/number/NumberInput.test.js.map +1 -1
  64. package/dist/components/formField/inputs/radio/RadioButtonGroup.d.ts +6 -2
  65. package/dist/components/formField/inputs/radio/RadioButtonGroup.d.ts.map +1 -1
  66. package/dist/components/formField/inputs/radio/RadioButtonGroup.js.map +1 -1
  67. package/dist/components/formField/inputs/radio/RadioButtonInput.stories.d.ts.map +1 -1
  68. package/dist/components/formField/inputs/radio/RadioButtonInput.stories.js +61 -49
  69. package/dist/components/formField/inputs/radio/RadioButtonInput.stories.js.map +1 -1
  70. package/dist/components/formField/inputs/selectDropdown/SelectDropdown.stories.d.ts +188 -166
  71. package/dist/components/formField/inputs/selectDropdown/SelectDropdown.stories.d.ts.map +1 -1
  72. package/dist/components/formField/inputs/selectDropdown/SelectDropdown.stories.js +821 -160
  73. package/dist/components/formField/inputs/selectDropdown/SelectDropdown.stories.js.map +1 -1
  74. package/dist/components/formField/inputs/time/TimeInput.stories.d.ts +176 -22
  75. package/dist/components/formField/inputs/time/TimeInput.stories.d.ts.map +1 -1
  76. package/dist/components/formField/inputs/time/TimeInput.stories.js +851 -92
  77. package/dist/components/formField/inputs/time/TimeInput.stories.js.map +1 -1
  78. package/dist/components/formField/label/Label.stories.d.ts +54 -5
  79. package/dist/components/formField/label/Label.stories.d.ts.map +1 -1
  80. package/dist/components/formField/label/Label.stories.js +238 -4
  81. package/dist/components/formField/label/Label.stories.js.map +1 -1
  82. package/dist/components/icoText/IcoText.stories.d.ts +32 -6
  83. package/dist/components/icoText/IcoText.stories.d.ts.map +1 -1
  84. package/dist/components/icoText/IcoText.stories.js +309 -14
  85. package/dist/components/icoText/IcoText.stories.js.map +1 -1
  86. package/dist/components/kpiCard/KPICard.stories.d.ts +100 -2
  87. package/dist/components/kpiCard/KPICard.stories.d.ts.map +1 -1
  88. package/dist/components/kpiCard/KPICard.stories.js +354 -10
  89. package/dist/components/kpiCard/KPICard.stories.js.map +1 -1
  90. package/dist/components/kvpList/KVPList.stories.d.ts +57 -4
  91. package/dist/components/kvpList/KVPList.stories.d.ts.map +1 -1
  92. package/dist/components/kvpList/KVPList.stories.js +403 -10
  93. package/dist/components/kvpList/KVPList.stories.js.map +1 -1
  94. package/dist/components/modal/Modal.stories.d.ts +113 -9
  95. package/dist/components/modal/Modal.stories.d.ts.map +1 -1
  96. package/dist/components/modal/Modal.stories.js +633 -13
  97. package/dist/components/modal/Modal.stories.js.map +1 -1
  98. package/dist/components/modal/modalManager/ModalManager.stories.d.ts +34 -10
  99. package/dist/components/modal/modalManager/ModalManager.stories.d.ts.map +1 -1
  100. package/dist/components/modal/modalManager/ModalManager.stories.js +463 -85
  101. package/dist/components/modal/modalManager/ModalManager.stories.js.map +1 -1
  102. package/dist/components/pill/Pill.d.ts.map +1 -1
  103. package/dist/components/pill/Pill.js +1 -1
  104. package/dist/components/pill/Pill.js.map +1 -1
  105. package/dist/components/pill/Pill.stories.d.ts.map +1 -1
  106. package/dist/components/pill/Pill.stories.js +11 -13
  107. package/dist/components/pill/Pill.stories.js.map +1 -1
  108. package/dist/components/row/Row.stories.d.ts +1 -2
  109. package/dist/components/row/Row.stories.d.ts.map +1 -1
  110. package/dist/components/row/Row.stories.js +360 -50
  111. package/dist/components/row/Row.stories.js.map +1 -1
  112. package/dist/components/searchBar/SearchBar.stories.d.ts +52 -4
  113. package/dist/components/searchBar/SearchBar.stories.d.ts.map +1 -1
  114. package/dist/components/searchBar/SearchBar.stories.js +428 -36
  115. package/dist/components/searchBar/SearchBar.stories.js.map +1 -1
  116. package/dist/components/section/Section.stories.d.ts +11 -41
  117. package/dist/components/section/Section.stories.d.ts.map +1 -1
  118. package/dist/components/section/Section.stories.js +494 -56
  119. package/dist/components/section/Section.stories.js.map +1 -1
  120. package/dist/components/singleUser/SingleUser.stories.d.ts +5 -4
  121. package/dist/components/singleUser/SingleUser.stories.d.ts.map +1 -1
  122. package/dist/components/singleUser/SingleUser.stories.js +303 -31
  123. package/dist/components/singleUser/SingleUser.stories.js.map +1 -1
  124. package/dist/components/slideoverManager/SlideoverManager.stories.d.ts +32 -11
  125. package/dist/components/slideoverManager/SlideoverManager.stories.d.ts.map +1 -1
  126. package/dist/components/slideoverManager/SlideoverManager.stories.js +380 -84
  127. package/dist/components/slideoverManager/SlideoverManager.stories.js.map +1 -1
  128. package/dist/components/table/DSDefaultColDef.d.ts.map +1 -1
  129. package/dist/components/table/DSDefaultColDef.js +4 -3
  130. package/dist/components/table/DSDefaultColDef.js.map +1 -1
  131. package/dist/components/table/Table.d.ts +7 -1
  132. package/dist/components/table/Table.d.ts.map +1 -1
  133. package/dist/components/table/Table.js +12 -5
  134. package/dist/components/table/Table.js.map +1 -1
  135. package/dist/components/table/Table.stories.d.ts +3 -0
  136. package/dist/components/table/Table.stories.d.ts.map +1 -1
  137. package/dist/components/table/Table.stories.js +445 -3
  138. package/dist/components/table/Table.stories.js.map +1 -1
  139. package/dist/components/table/Table.test.js +184 -0
  140. package/dist/components/table/Table.test.js.map +1 -1
  141. package/dist/components/table/TableFooter.stories.d.ts +49 -0
  142. package/dist/components/table/TableFooter.stories.d.ts.map +1 -0
  143. package/dist/components/table/TableFooter.stories.js +137 -0
  144. package/dist/components/table/TableFooter.stories.js.map +1 -0
  145. package/dist/components/table/TableHeader.stories.d.ts +93 -0
  146. package/dist/components/table/TableHeader.stories.d.ts.map +1 -0
  147. package/dist/components/table/TableHeader.stories.js +176 -0
  148. package/dist/components/table/TableHeader.stories.js.map +1 -0
  149. package/dist/components/table/cellEditors/DateCellEditor.stories.d.ts +44 -0
  150. package/dist/components/table/cellEditors/DateCellEditor.stories.d.ts.map +1 -0
  151. package/dist/components/table/cellEditors/DateCellEditor.stories.js +186 -0
  152. package/dist/components/table/cellEditors/DateCellEditor.stories.js.map +1 -0
  153. package/dist/components/table/cellEditors/NumberCellEditor.d.ts +13 -0
  154. package/dist/components/table/cellEditors/NumberCellEditor.d.ts.map +1 -0
  155. package/dist/components/table/cellEditors/NumberCellEditor.js +35 -0
  156. package/dist/components/table/cellEditors/NumberCellEditor.js.map +1 -0
  157. package/dist/components/table/cellRenderers/BooleanCellRenderer.stories.d.ts +40 -0
  158. package/dist/components/table/cellRenderers/BooleanCellRenderer.stories.d.ts.map +1 -0
  159. package/dist/components/table/cellRenderers/BooleanCellRenderer.stories.js +209 -0
  160. package/dist/components/table/cellRenderers/BooleanCellRenderer.stories.js.map +1 -0
  161. package/dist/components/table/cellRenderers/ButtonCellRenderer.stories.d.ts +48 -0
  162. package/dist/components/table/cellRenderers/ButtonCellRenderer.stories.d.ts.map +1 -0
  163. package/dist/components/table/cellRenderers/ButtonCellRenderer.stories.js +244 -0
  164. package/dist/components/table/cellRenderers/ButtonCellRenderer.stories.js.map +1 -0
  165. package/dist/components/table/cellRenderers/CheckboxCellRenderer.d.ts.map +1 -1
  166. package/dist/components/table/cellRenderers/CheckboxCellRenderer.js +3 -1
  167. package/dist/components/table/cellRenderers/CheckboxCellRenderer.js.map +1 -1
  168. package/dist/components/table/cellRenderers/CheckboxCellRenderer.stories.d.ts +64 -0
  169. package/dist/components/table/cellRenderers/CheckboxCellRenderer.stories.d.ts.map +1 -0
  170. package/dist/components/table/cellRenderers/CheckboxCellRenderer.stories.js +241 -0
  171. package/dist/components/table/cellRenderers/CheckboxCellRenderer.stories.js.map +1 -0
  172. package/dist/components/table/cellRenderers/DefaultCellRenderer.stories.d.ts +55 -0
  173. package/dist/components/table/cellRenderers/DefaultCellRenderer.stories.d.ts.map +1 -0
  174. package/dist/components/table/cellRenderers/DefaultCellRenderer.stories.js +245 -0
  175. package/dist/components/table/cellRenderers/DefaultCellRenderer.stories.js.map +1 -0
  176. package/dist/components/table/cellRenderers/InlineTextCellRenderer.stories.d.ts +67 -0
  177. package/dist/components/table/cellRenderers/InlineTextCellRenderer.stories.d.ts.map +1 -0
  178. package/dist/components/table/cellRenderers/InlineTextCellRenderer.stories.js +221 -0
  179. package/dist/components/table/cellRenderers/InlineTextCellRenderer.stories.js.map +1 -0
  180. package/dist/components/table/cellRenderers/SelectDropdownCellRenderer.stories.d.ts +75 -0
  181. package/dist/components/table/cellRenderers/SelectDropdownCellRenderer.stories.d.ts.map +1 -0
  182. package/dist/components/table/cellRenderers/SelectDropdownCellRenderer.stories.js +270 -0
  183. package/dist/components/table/cellRenderers/SelectDropdownCellRenderer.stories.js.map +1 -0
  184. package/dist/components/table/columnFilters/BooleanFilter.stories.d.ts +57 -0
  185. package/dist/components/table/columnFilters/BooleanFilter.stories.d.ts.map +1 -0
  186. package/dist/components/table/columnFilters/BooleanFilter.stories.js +198 -0
  187. package/dist/components/table/columnFilters/BooleanFilter.stories.js.map +1 -0
  188. package/dist/components/table/columnFilters/TimeFilter.stories.d.ts +58 -0
  189. package/dist/components/table/columnFilters/TimeFilter.stories.d.ts.map +1 -0
  190. package/dist/components/table/columnFilters/TimeFilter.stories.js +207 -0
  191. package/dist/components/table/columnFilters/TimeFilter.stories.js.map +1 -0
  192. package/dist/components/table/pagination/PaginationPanel.stories.d.ts +113 -0
  193. package/dist/components/table/pagination/PaginationPanel.stories.d.ts.map +1 -0
  194. package/dist/components/table/pagination/PaginationPanel.stories.js +272 -0
  195. package/dist/components/table/pagination/PaginationPanel.stories.js.map +1 -0
  196. package/dist/components/table/tableControls/TableControls.stories.d.ts +151 -0
  197. package/dist/components/table/tableControls/TableControls.stories.d.ts.map +1 -0
  198. package/dist/components/table/tableControls/TableControls.stories.js +356 -0
  199. package/dist/components/table/tableControls/TableControls.stories.js.map +1 -0
  200. package/dist/components/table/tableControls/TableSettingsDropdown.d.ts +27 -1
  201. package/dist/components/table/tableControls/TableSettingsDropdown.d.ts.map +1 -1
  202. package/dist/components/table/tableControls/TableSettingsDropdown.js +53 -26
  203. package/dist/components/table/tableControls/TableSettingsDropdown.js.map +1 -1
  204. package/dist/components/table/tableControls/TableSettingsDropdown.test.d.ts +2 -0
  205. package/dist/components/table/tableControls/TableSettingsDropdown.test.d.ts.map +1 -0
  206. package/dist/components/table/tableControls/TableSettingsDropdown.test.js +178 -0
  207. package/dist/components/table/tableControls/TableSettingsDropdown.test.js.map +1 -0
  208. package/dist/components/tabs/Tabs.stories.d.ts +22 -4
  209. package/dist/components/tabs/Tabs.stories.d.ts.map +1 -1
  210. package/dist/components/tabs/Tabs.stories.js +398 -22
  211. package/dist/components/tabs/Tabs.stories.js.map +1 -1
  212. package/dist/components/tabs/TabsItem.stories.d.ts +54 -1
  213. package/dist/components/tabs/TabsItem.stories.d.ts.map +1 -1
  214. package/dist/components/tabs/TabsItem.stories.js +61 -9
  215. package/dist/components/tabs/TabsItem.stories.js.map +1 -1
  216. package/dist/components/toast/Toast.stories.d.ts +103 -10
  217. package/dist/components/toast/Toast.stories.d.ts.map +1 -1
  218. package/dist/components/toast/Toast.stories.js +409 -47
  219. package/dist/components/toast/Toast.stories.js.map +1 -1
  220. package/dist/components/toggle/Toggle.stories.d.ts +61 -46
  221. package/dist/components/toggle/Toggle.stories.d.ts.map +1 -1
  222. package/dist/components/toggle/Toggle.stories.js +311 -122
  223. package/dist/components/toggle/Toggle.stories.js.map +1 -1
  224. package/dist/components/tooltip/Tooltip.stories.d.ts +78 -6
  225. package/dist/components/tooltip/Tooltip.stories.d.ts.map +1 -1
  226. package/dist/components/tooltip/Tooltip.stories.js +413 -7
  227. package/dist/components/tooltip/Tooltip.stories.js.map +1 -1
  228. package/dist/components/tooltip/TooltipWrapper.stories.d.ts +71 -7
  229. package/dist/components/tooltip/TooltipWrapper.stories.d.ts.map +1 -1
  230. package/dist/components/tooltip/TooltipWrapper.stories.js +238 -10
  231. package/dist/components/tooltip/TooltipWrapper.stories.js.map +1 -1
  232. package/dist/index.css +27 -0
  233. package/dist/index.css.map +1 -1
  234. package/dist/index.d.ts +3 -2
  235. package/dist/index.d.ts.map +1 -1
  236. package/dist/index.js +3 -2
  237. package/dist/index.js.map +1 -1
  238. package/dist/utils/PopupParentContext.stories.d.ts +17 -0
  239. package/dist/utils/PopupParentContext.stories.d.ts.map +1 -0
  240. package/dist/utils/PopupParentContext.stories.js +266 -0
  241. package/dist/utils/PopupParentContext.stories.js.map +1 -0
  242. package/dist/utils/getDefaultPopupParent.d.ts.map +1 -1
  243. package/dist/utils/getDefaultPopupParent.js +6 -0
  244. package/dist/utils/getDefaultPopupParent.js.map +1 -1
  245. package/package.json +1 -1
  246. package/src/components/articleCard/ArticleCard.stories.tsx +524 -111
  247. package/src/components/avatar/Avatar.stories.tsx +504 -59
  248. package/src/components/avatarGroup/AvatarGroup.stories.tsx +977 -175
  249. package/src/components/banner/Banner.stories.tsx +7 -3
  250. package/src/components/card/Card.stories.tsx +466 -36
  251. package/src/components/combobox/Combobox.stories.tsx +867 -260
  252. package/src/components/datePicker/DatePicker.stories.tsx +777 -60
  253. package/src/components/dateTimePicker/DateTimePicker.stories.tsx +910 -132
  254. package/src/components/editableText/EditableText.stories.tsx +567 -91
  255. package/src/components/formField/FormField.test.tsx +6 -0
  256. package/src/components/formField/FormField.tsx +5 -0
  257. package/src/components/formField/fieldset/Fieldset.stories.tsx +761 -51
  258. package/src/components/formField/inputs/checkbox/CheckboxGroup.tsx +1 -1
  259. package/src/components/formField/inputs/checkbox/CheckboxInput.tsx +1 -1
  260. package/src/components/formField/inputs/colourPickerDropdown/ColourPickerDropdown.stories.tsx +504 -11
  261. package/src/components/formField/inputs/number/NumberInput.test.tsx +28 -0
  262. package/src/components/formField/inputs/number/NumberInput.tsx +15 -0
  263. package/src/components/formField/inputs/radio/RadioButtonGroup.tsx +17 -4
  264. package/src/components/formField/inputs/radio/RadioButtonInput.stories.tsx +71 -59
  265. package/src/components/formField/inputs/selectDropdown/SelectDropdown.stories.tsx +1079 -168
  266. package/src/components/formField/inputs/time/TimeInput.stories.tsx +1140 -104
  267. package/src/components/formField/label/Label.stories.tsx +317 -8
  268. package/src/components/icoText/IcoText.stories.tsx +442 -31
  269. package/src/components/kpiCard/KPICard.stories.tsx +475 -30
  270. package/src/components/kvpList/KVPList.stories.tsx +593 -26
  271. package/src/components/modal/Modal.stories.tsx +963 -26
  272. package/src/components/modal/modalManager/ModalManager.stories.tsx +612 -454
  273. package/src/components/pill/Pill.stories.tsx +11 -13
  274. package/src/components/pill/Pill.tsx +1 -0
  275. package/src/components/row/Row.stories.tsx +474 -58
  276. package/src/components/searchBar/SearchBar.stories.tsx +570 -38
  277. package/src/components/section/Section.stories.tsx +723 -70
  278. package/src/components/singleUser/SingleUser.stories.tsx +393 -34
  279. package/src/components/slideoverManager/SlideoverManager.stories.tsx +572 -342
  280. package/src/components/table/DSDefaultColDef.ts +25 -5
  281. package/src/components/table/Table.stories.tsx +504 -3
  282. package/src/components/table/Table.test.tsx +255 -0
  283. package/src/components/table/Table.tsx +15 -2
  284. package/src/components/table/TableFooter.stories.tsx +196 -0
  285. package/src/components/table/TableHeader.stories.tsx +251 -0
  286. package/src/components/table/cellEditors/DateCellEditor.stories.tsx +245 -0
  287. package/src/components/table/cellEditors/NumberCellEditor.tsx +83 -0
  288. package/src/components/table/cellEditors/numberCellEditor.scss +11 -0
  289. package/src/components/table/cellRenderers/BooleanCellRenderer.stories.tsx +278 -0
  290. package/src/components/table/cellRenderers/ButtonCellRenderer.stories.tsx +333 -0
  291. package/src/components/table/cellRenderers/CheckboxCellRenderer.stories.tsx +337 -0
  292. package/src/components/table/cellRenderers/CheckboxCellRenderer.tsx +5 -1
  293. package/src/components/table/cellRenderers/DefaultCellRenderer.stories.tsx +342 -0
  294. package/src/components/table/cellRenderers/InlineTextCellRenderer.stories.tsx +292 -0
  295. package/src/components/table/cellRenderers/SelectDropdownCellRenderer.stories.tsx +369 -0
  296. package/src/components/table/columnFilters/BooleanFilter.stories.tsx +268 -0
  297. package/src/components/table/columnFilters/TimeFilter.stories.tsx +281 -0
  298. package/src/components/table/pagination/PaginationPanel.stories.tsx +327 -0
  299. package/src/components/table/table.scss +11 -0
  300. package/src/components/table/tableControls/TableControls.stories.tsx +415 -0
  301. package/src/components/table/tableControls/TableSettingsDropdown.test.tsx +207 -0
  302. package/src/components/table/tableControls/TableSettingsDropdown.tsx +103 -39
  303. package/src/components/tabs/Tabs.stories.tsx +540 -60
  304. package/src/components/tabs/TabsItem.stories.tsx +82 -8
  305. package/src/components/toast/Toast.stories.tsx +539 -77
  306. package/src/components/toggle/Toggle.stories.tsx +371 -135
  307. package/src/components/tooltip/Tooltip.stories.tsx +606 -15
  308. package/src/components/tooltip/TooltipWrapper.stories.tsx +348 -12
  309. package/src/docs/Contributing.mdx +241 -0
  310. package/src/docs/UsingComponents.mdx +93 -0
  311. package/src/docs/Welcome.mdx +68 -0
  312. package/src/global.scss +7 -0
  313. package/src/index.scss +1 -0
  314. package/src/index.ts +3 -2
  315. package/src/utils/PopupParentContext.stories.tsx +367 -0
  316. package/src/utils/getDefaultPopupParent.ts +6 -0
  317. package/.ralph/storybook-upgrade/knowledge.md +0 -308
  318. package/.ralph/storybook-upgrade/prd.json +0 -777
  319. package/.ralph/storybook-upgrade/progress.md +0 -342
  320. package/src/components/table/TableWIP.mdx +0 -3
@@ -1,163 +1,734 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { formatNativeDateTimeInputValue } from '../datePicker/dateInputUtils';
3
- import { useEffect, useState } from 'react';
4
- import { fn } from 'storybook/test';
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';
5
5
  import { DateTimePicker } from './DateTimePicker';
6
- const timeOptions = ['09:00', '09:30', '10:00', '10:30', '11:00'];
6
+ // ---------------------------------------------------------------------------
7
+ // Docs page content
8
+ // ---------------------------------------------------------------------------
9
+ const DESCRIPTION_INTRO = [
10
+ 'DateTimePicker combines a date input with an embedded time input, keeping a single unified `Date`',
11
+ 'value. Built on [Radix UI Popover](https://www.radix-ui.com/primitives/docs/components/popover)',
12
+ 'and [react-day-picker](https://daypicker.dev/).',
13
+ '',
14
+ 'The calendar popup contains both the date picker and the time input, keeping date and time',
15
+ 'selection in a single interaction flow. When a date is picked from the calendar, the existing',
16
+ 'time is preserved. When a time is changed, the existing date is preserved.',
17
+ ].join('\n');
18
+ const USAGE_GUIDANCE = [
19
+ '### When to use',
20
+ '',
21
+ '- **Event scheduling** — lesson start times, parent evening appointments, report deadlines',
22
+ '- **Booking flows** — any scenario where both a date AND a specific time are required',
23
+ '- **Audit timestamps** — log entries, registration opens/closes with precise time',
24
+ '',
25
+ '---',
26
+ '',
27
+ '### When NOT to use',
28
+ '',
29
+ '| Situation | Use instead |',
30
+ '|---|---|',
31
+ '| Date only (no time needed) | `DatePicker` |',
32
+ '| Time only (no date needed) | `TimeInput` |',
33
+ '| Date range selection | Two `DatePicker` fields |',
34
+ '| Fixed time slots from a list | `DateTimePicker` with `timeOptions` |',
35
+ ].join('\n');
36
+ const DEVELOPER_NOTES = [
37
+ '### Critical usage patterns',
38
+ '',
39
+ '> **Portal rendering.** The calendar panel renders via a Radix portal — do **not** place inside',
40
+ '> a container with `overflow: hidden` or the calendar will be clipped.',
41
+ '',
42
+ '**`onChange` receives a `Date` object — never a string.** Format for display with',
43
+ '`date?.toLocaleDateString(\'en-GB\')` + time as needed. Do not assume ISO string format.',
44
+ '',
45
+ '**`value` and `defaultValue` are `Date` objects — not strings.** Month is zero-indexed:',
46
+ '`new Date(2026, 3, 14, 9, 0)` = 14 April 2026, 09:00. Passing a string is a type error.',
47
+ '',
48
+ '```tsx',
49
+ '// ✅ Correct',
50
+ 'value={new Date(2026, 3, 14, 9, 0)}',
51
+ '',
52
+ '// ❌ Wrong — type error',
53
+ 'value="2026-04-14T09:00"',
54
+ '```',
55
+ '',
56
+ '**Three display formats — choose based on your context:**',
57
+ '',
58
+ '| `displayFormat` | Input type | Placeholder hint | Example value |',
59
+ '|---|---|---|---|',
60
+ '| `"native"` (default) | `datetime-local` | `YYYY-MM-DDTHH:mm` | `2026-04-14T14:27` |',
61
+ '| `"default"` | `text` | `DD/MM/YYYY HH:mm` | `14/04/2026 14:27` |',
62
+ '| `"friendly"` | `text` | `Pick a date, HH:mm` | `April 14th, 2026 14:27` |',
63
+ '',
64
+ '**`timeOptions` switches the embedded time input to combobox mode.** Instead of a free-form',
65
+ 'time input, the user picks from a predefined list. Use for fixed appointment slots.',
66
+ '',
67
+ '**`hasError` is visual-only when used standalone.** It applies the red border but does NOT',
68
+ 'set `aria-invalid`. Always pair both when outside `<FormField>`:',
69
+ '',
70
+ '```tsx',
71
+ '<DateTimePicker hasError aria-invalid={true} aria-describedby="error-id" />',
72
+ '```',
73
+ '',
74
+ '**`granularity="second"` enables seconds** in both the `datetime-local` step attribute',
75
+ '(native mode) and the embedded time input.',
76
+ '',
77
+ '---',
78
+ '',
79
+ '### Accessibility',
80
+ '',
81
+ '- Always label via `<FormField>` or `aria-label` / `aria-labelledby`',
82
+ '- Pair `hasError` with `aria-invalid={true}` when used standalone — `<FormField>` handles both',
83
+ '- The calendar icon button has built-in screen reader text ("Open date and time picker")',
84
+ '- The embedded time input has `aria-label="Select time"`',
85
+ '- The calendar closes and returns focus to the date input on date selection or Escape',
86
+ '',
87
+ '---',
88
+ '',
89
+ '### TypeScript types',
90
+ '',
91
+ '```ts',
92
+ "import { DateTimePicker } from '@arbor-education/design-system.components';",
93
+ '',
94
+ '// Access via namespace:',
95
+ 'function MyField(props: DateTimePicker.Props) { ... }',
96
+ '',
97
+ '// Display format type:',
98
+ 'const format: DateTimePicker.DisplayFormat = "native"; // "native" | "default" | "friendly"',
99
+ '```',
100
+ '',
101
+ '| Type | Description |',
102
+ '|---|---|',
103
+ '| `DateTimePicker.Props` | Full props interface |',
104
+ '| `DateTimePicker.DisplayFormat` | `"native" \\| "default" \\| "friendly"` |',
105
+ ].join('\n');
106
+ const RELATED_COMPONENTS = [
107
+ '## Related components',
108
+ '',
109
+ '[DatePicker](?path=/docs/components-datepicker--docs) · [TimeInput](?path=/docs/components-formfield-inputs-timeinput--docs) · [FormField](?path=/docs/components-formfield--docs)',
110
+ ].join('\n');
111
+ const PROPS_INTRO = 'The preview below is wired to the **Controls** panel — tweak any prop to see the story update in real time.';
112
+ // ---------------------------------------------------------------------------
113
+ // Docs page component
114
+ // ---------------------------------------------------------------------------
115
+ function DateTimePickerDocsPage() {
116
+ 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 })] }));
117
+ }
118
+ // ---------------------------------------------------------------------------
119
+ // Meta
120
+ // ---------------------------------------------------------------------------
7
121
  const meta = {
8
122
  title: 'Components/DateTimePicker',
9
123
  component: DateTimePicker,
10
- decorators: [
11
- Story => (_jsx("div", { style: { maxWidth: '280px', width: '100%' }, children: _jsx(Story, {}) })),
12
- ],
124
+ tags: ['autodocs'],
13
125
  parameters: {
14
- layout: 'centered',
126
+ layout: 'padded',
15
127
  docs: {
16
- description: {
17
- component: '`DateTimePicker` combines a date field with the existing `TimeInput`, keeping a single combined `Date` value. `displayFormat="native"` (default) uses a `datetime-local` input with a decorative empty-state hint span (browsers do not reliably show placeholders on native date/time fields). `"default"` and `"friendly"` use a plain text input with locale-style formatting and a real HTML placeholder.',
18
- },
128
+ page: DateTimePickerDocsPage,
19
129
  },
20
130
  },
21
- tags: ['autodocs'],
22
- args: {
23
- onChange: fn(),
24
- },
25
131
  argTypes: {
26
- displayFormat: {
27
- control: 'inline-radio',
132
+ 'displayFormat': {
133
+ control: { type: 'inline-radio' },
28
134
  options: ['native', 'default', 'friendly'],
29
- description: '`native`: ISO `datetime-local` (default). `default` / `friendly`: plain text field using the same date patterns as `DatePicker` plus time.',
135
+ description: [
136
+ 'Controls input type and value format.',
137
+ '`"native"` (default): `datetime-local` input with `YYYY-MM-DDTHH:mm` hint.',
138
+ '`"default"`: text input with `DD/MM/YYYY HH:mm` format.',
139
+ '`"friendly"`: text input with `April 14th, 2026 14:27` format.',
140
+ ].join(' '),
141
+ table: {
142
+ type: { summary: '"native" | "default" | "friendly"' },
143
+ defaultValue: { summary: '"native"' },
144
+ },
30
145
  },
31
- granularity: {
32
- control: 'inline-radio',
146
+ 'granularity': {
147
+ control: { type: 'inline-radio' },
33
148
  options: ['minute', 'second'],
34
- description: 'Controls whether the native `datetime-local` input and embedded time input use minute or second precision.',
149
+ description: [
150
+ 'Controls time precision.',
151
+ '`"minute"` (default): HH:mm.',
152
+ '`"second"`: HH:mm:ss — sets `step={1}` on the datetime-local input and shows seconds in the embedded time input.',
153
+ ].join(' '),
154
+ table: {
155
+ type: { summary: '"minute" | "second"' },
156
+ defaultValue: { summary: '"minute"' },
157
+ },
35
158
  },
36
- timeOptions: {
37
- control: 'object',
38
- description: 'When provided, the embedded time input switches to list-backed time selection.',
159
+ 'placeholder': {
160
+ control: 'text',
161
+ description: [
162
+ 'Custom text for the empty-state hint overlay (native mode only).',
163
+ 'When set and the field is empty, the input becomes read-only — the calendar opens on click only, not Tab focus.',
164
+ ].join(' '),
165
+ table: {
166
+ type: { summary: 'string' },
167
+ defaultValue: { summary: 'undefined' },
168
+ },
39
169
  },
40
- value: {
170
+ 'onChange': {
41
171
  control: false,
42
- description: 'Controlled combined datetime value for app-level state.',
172
+ action: 'onChange',
173
+ description: [
174
+ 'Callback fired when the combined date+time changes.',
175
+ 'Receives a `Date` object, or `undefined` when cleared.',
176
+ 'Never receives a string.',
177
+ ].join(' '),
178
+ table: {
179
+ type: { summary: '(newDate?: Date) => void' },
180
+ },
43
181
  },
44
- defaultValue: {
182
+ 'hasError': {
183
+ control: 'boolean',
184
+ description: [
185
+ 'Applies error-state visual styling (red border).',
186
+ 'Does **not** set `aria-invalid` automatically when used standalone.',
187
+ 'Always pair with `aria-invalid={true}`. `<FormField>` handles both automatically.',
188
+ ].join(' '),
189
+ table: {
190
+ type: { summary: 'boolean' },
191
+ defaultValue: { summary: 'false' },
192
+ },
193
+ },
194
+ 'value': {
195
+ control: false,
196
+ description: [
197
+ 'Controlled combined date+time value.',
198
+ 'Must be a `Date` object — not a string.',
199
+ 'Pair with `onChange` to keep state in sync.',
200
+ ].join(' '),
201
+ table: {
202
+ type: { summary: 'Date' },
203
+ defaultValue: { summary: 'undefined' },
204
+ },
205
+ },
206
+ 'defaultValue': {
45
207
  control: false,
46
- description: 'Uncontrolled initial combined datetime value.',
208
+ description: [
209
+ 'Uncontrolled initial date+time.',
210
+ 'Must be a `Date` object (month is zero-indexed: `new Date(2026, 3, 14, 9, 0)` = 14 Apr 2026 09:00).',
211
+ 'Use when you only need the value on form submit.',
212
+ ].join(' '),
213
+ table: {
214
+ type: { summary: 'Date' },
215
+ defaultValue: { summary: 'undefined' },
216
+ },
47
217
  },
48
- onChange: {
49
- action: 'changed',
50
- description: 'Called with the next combined `Date` value, or `undefined` when the date becomes invalid/cleared.',
218
+ 'timeOptions': {
219
+ control: false,
220
+ description: [
221
+ 'Array of `TimeValue` strings to use as preset time slots.',
222
+ 'When provided, the embedded time input switches to **combobox mode** — the user picks from the list instead of typing freely.',
223
+ 'Ideal for fixed appointment slots like "09:00", "09:30", "10:00".',
224
+ ].join(' '),
225
+ table: {
226
+ type: { summary: 'TimeValue[]' },
227
+ defaultValue: { summary: 'undefined' },
228
+ },
229
+ },
230
+ 'searchType': {
231
+ control: { type: 'select' },
232
+ options: ['prefix', 'substring'],
233
+ description: [
234
+ '**Only used when `timeOptions` is provided.**',
235
+ 'Controls how the time combobox filters options as the user types.',
236
+ '`"prefix"` (default) matches from the start; `"substring"` matches anywhere.',
237
+ ].join(' '),
238
+ table: {
239
+ type: { summary: '"prefix" | "substring"' },
240
+ defaultValue: { summary: '"prefix"' },
241
+ },
51
242
  },
52
- placeholder: {
243
+ 'highlightStringMatches': {
244
+ control: 'boolean',
245
+ description: '**Only used when `timeOptions` is provided.** Bolds matched characters in the time dropdown.',
246
+ table: {
247
+ type: { summary: 'boolean' },
248
+ defaultValue: { summary: 'false' },
249
+ },
250
+ },
251
+ 'id': {
252
+ control: 'text',
253
+ description: 'HTML `id` for the date input element. Required when inside `<FormField>` so the label `htmlFor` can be linked.',
254
+ table: {
255
+ type: { summary: 'string' },
256
+ },
257
+ },
258
+ 'className': {
259
+ control: 'text',
260
+ description: 'Additional CSS class names on the root wrapper element.',
261
+ table: {
262
+ type: { summary: 'string' },
263
+ },
264
+ },
265
+ 'aria-invalid': {
266
+ control: 'boolean',
267
+ description: [
268
+ 'Marks the date input as invalid for screen readers.',
269
+ 'Must be set manually when used standalone — `<FormField>` sets this automatically when `errorText` is provided.',
270
+ ].join(' '),
271
+ table: {
272
+ type: { summary: 'boolean | "true" | "false"' },
273
+ },
274
+ },
275
+ 'aria-describedby': {
53
276
  control: 'text',
54
- description: 'Optional override for the field placeholder (defaults from `displayFormat` and `granularity`).',
277
+ description: 'ID of the element describing the field (e.g. an error message).',
278
+ table: {
279
+ type: { summary: 'string' },
280
+ },
55
281
  },
56
282
  },
57
283
  };
58
284
  export default meta;
59
- const ControlledDateTimePicker = ({ showCurrentValue = false, ...args }) => {
60
- const [value, setValue] = useState(args.value);
61
- useEffect(() => {
62
- setValue(args.value);
63
- }, [args.value]);
64
- return (_jsxs("div", { style: { display: 'grid', gap: 12, width: '100%', maxWidth: 320 }, children: [_jsx(DateTimePicker, { ...args, value: value, onChange: (nextValue) => {
65
- setValue(nextValue);
66
- args.onChange?.(nextValue);
67
- } }), showCurrentValue && (_jsxs("span", { children: ["Current value:", ' ', value ? formatNativeDateTimeInputValue(value, args.granularity) : 'None'] }))] }));
68
- };
69
- export const NativeTime = {
70
- args: {
71
- value: new Date(2026, 3, 14, 14, 27),
72
- granularity: 'minute',
285
+ // ---------------------------------------------------------------------------
286
+ // Helper: attach a per-story description
287
+ // ---------------------------------------------------------------------------
288
+ const withDescription = (story, description) => ({
289
+ ...story,
290
+ parameters: {
291
+ ...story.parameters,
292
+ docs: {
293
+ ...story.parameters?.docs,
294
+ description: {
295
+ story: description,
296
+ },
297
+ },
73
298
  },
74
- render: args => _jsx(ControlledDateTimePicker, { ...args }),
299
+ });
300
+ // ---------------------------------------------------------------------------
301
+ // Named template components for stateful stories
302
+ // ---------------------------------------------------------------------------
303
+ const APPOINTMENT_SLOTS = [
304
+ '09:00',
305
+ '09:30',
306
+ '10:00',
307
+ '10:30',
308
+ '11:00',
309
+ '11:30',
310
+ '14:00',
311
+ '14:30',
312
+ '15:00',
313
+ '15:30',
314
+ ];
315
+ const DisplayFormatDefaultTemplate = () => {
316
+ const [value, setValue] = useState(new Date(2026, 3, 14, 14, 27));
317
+ return (_jsxs("div", { style: { maxWidth: '320px' }, children: [_jsx(DateTimePicker, { displayFormat: "default", value: value, onChange: setValue, id: "format-default" }), _jsxs("p", { style: { margin: 'var(--spacing-small) 0 0', color: 'var(--color-grey-600)' }, children: ["Value:", ' ', _jsx("code", { children: value
318
+ ? `${value.toLocaleDateString('en-GB')} ${value.toLocaleTimeString('en-GB', { hour: '2-digit', minute: '2-digit' })}`
319
+ : '(none)' })] })] }));
75
320
  };
76
- export const TimeList = {
77
- args: {
78
- value: new Date(2026, 3, 14, 10, 0),
79
- timeOptions: [...timeOptions],
80
- },
81
- render: args => _jsx(ControlledDateTimePicker, { ...args }),
321
+ const DisplayFormatFriendlyTemplate = () => {
322
+ const [value, setValue] = useState(new Date(2026, 3, 14, 14, 27));
323
+ return (_jsxs("div", { style: { maxWidth: '320px' }, children: [_jsx(DateTimePicker, { displayFormat: "friendly", value: value, onChange: setValue, id: "format-friendly" }), _jsxs("p", { style: { margin: 'var(--spacing-small) 0 0', color: 'var(--color-grey-600)' }, children: ["Value:", ' ', _jsx("code", { children: value
324
+ ? `${value.toLocaleDateString('en-GB')} ${value.toLocaleTimeString('en-GB', { hour: '2-digit', minute: '2-digit' })}`
325
+ : '(none)' })] })] }));
82
326
  };
83
- export const ControlledValuePreview = {
84
- args: {
85
- value: new Date(2026, 3, 14, 14, 27),
86
- granularity: 'minute',
87
- },
88
- render: args => _jsx(ControlledDateTimePicker, { ...args, showCurrentValue: true }),
327
+ const WithTimeOptionsTemplate = () => {
328
+ const [value, setValue] = useState(new Date(2026, 3, 14, 9, 0));
329
+ return (_jsxs("div", { style: { maxWidth: '280px' }, children: [_jsx(DateTimePicker, { timeOptions: APPOINTMENT_SLOTS, value: value, onChange: setValue, id: "time-options" }), _jsxs("p", { style: { margin: 'var(--spacing-small) 0 0', color: 'var(--color-grey-600)' }, children: ["Appointment:", ' ', _jsx("code", { children: value
330
+ ? `${value.toLocaleDateString('en-GB')} ${value.toLocaleTimeString('en-GB', { hour: '2-digit', minute: '2-digit' })}`
331
+ : '(none)' })] })] }));
332
+ };
333
+ const GranularitySecondTemplate = () => {
334
+ const [value, setValue] = useState(undefined);
335
+ return (_jsxs("div", { style: { maxWidth: '280px' }, children: [_jsx(DateTimePicker, { granularity: "second", value: value, onChange: setValue, id: "granularity-second" }), _jsxs("p", { style: { margin: 'var(--spacing-small) 0 0', color: 'var(--color-grey-600)' }, children: ["Value (with seconds):", ' ', _jsx("code", { children: value
336
+ ? `${value.toLocaleDateString('en-GB')} ${value.toLocaleTimeString('en-GB')}`
337
+ : '(none)' })] })] }));
89
338
  };
90
- export const PlaceholderNativeMinuteHint = {
91
- name: 'Placeholder · native minute hint',
339
+ const ControlledWithDisplayTemplate = () => {
340
+ const [value, setValue] = useState(undefined);
341
+ return (_jsxs("div", { style: { maxWidth: '280px' }, children: [_jsx(DateTimePicker, { id: "controlled-display", onChange: setValue }), _jsxs("p", { style: { margin: 'var(--spacing-small) 0 0', color: 'var(--color-grey-600)' }, children: ["Selected:", ' ', _jsx("code", { children: value
342
+ ? `${value.toLocaleDateString('en-GB')} ${value.toLocaleTimeString('en-GB', { hour: '2-digit', minute: '2-digit' })}`
343
+ : '(none)' })] })] }));
344
+ };
345
+ const WithFormFieldTemplate = () => (_jsx("div", { style: { maxWidth: '320px' }, children: _jsx(FormField, { label: "Event start date and time", id: "ff-event-start", inputType: "dateTimePicker", fieldDescription: "Select the date and time the event will begin.", inputProps: {} }) }));
346
+ const WithFormFieldAndErrorTemplate = () => {
347
+ const [submitted, setSubmitted] = useState(false);
348
+ const [selectedDate, setSelectedDate] = useState(undefined);
349
+ const hasError = submitted && !selectedDate;
350
+ return (_jsxs("div", { style: { maxWidth: '320px', display: 'flex', flexDirection: 'column', gap: 'var(--spacing-large)' }, children: [_jsx(FormField, { label: "Lesson start date and time", id: "ff-lesson-start", inputType: "dateTimePicker", errorText: hasError ? 'Please select a date and time for the lesson.' : undefined, inputProps: {
351
+ 'onChange': setSelectedDate,
352
+ hasError,
353
+ 'aria-invalid': hasError ? true : undefined,
354
+ } }), _jsx("div", { children: _jsx("button", { type: "button", onClick: () => setSubmitted(true), style: { padding: 'var(--spacing-small) var(--spacing-medium)' }, children: "Submit" }) }), submitted && selectedDate && (_jsxs("p", { style: { margin: 0, color: 'var(--color-semantic-success-600)', fontSize: '0.875rem' }, children: ["Lesson scheduled for", ' ', selectedDate.toLocaleDateString('en-GB'), ' at ', selectedDate.toLocaleTimeString('en-GB', { hour: '2-digit', minute: '2-digit' }), "."] }))] }));
355
+ };
356
+ // ---------------------------------------------------------------------------
357
+ // Stories
358
+ // ---------------------------------------------------------------------------
359
+ export const Default = withDescription({
92
360
  args: {
93
- granularity: 'minute',
361
+ id: 'default-datetime',
94
362
  },
363
+ render: args => (_jsx("div", { style: { maxWidth: '280px' }, children: _jsx(DateTimePicker, { ...args }) })),
364
+ }, [
365
+ 'The interactive canvas — every prop is wired to the Controls panel.',
366
+ 'Default `displayFormat="native"` shows the `YYYY-MM-DDTHH:mm` hint overlay (a decorative span,',
367
+ 'because browsers do not reliably show `placeholder` on `datetime-local` inputs).',
368
+ 'Click the calendar icon to open the combined date+time picker.',
369
+ ].join(' '));
370
+ export const WithDefaultValue = withDescription({
371
+ render: () => (_jsx("div", { style: { maxWidth: '280px' }, children: _jsx(DateTimePicker, { id: "default-value-datetime", defaultValue: new Date(2026, 3, 14, 14, 27) }) })),
95
372
  parameters: {
373
+ controls: { disable: true },
96
374
  docs: {
97
- description: {
98
- story: 'Uncontrolled, no initial value — empty-state hint shows `YYYY-MM-DDTHH:mm` (decorative span for native mode).',
375
+ source: {
376
+ language: 'tsx',
377
+ code: `
378
+ import { DateTimePicker } from '@arbor-education/design-system.components';
379
+
380
+ function EditEventStart() {
381
+ // 14 April 2026, 14:27 — month is zero-indexed (Apr = 3)
382
+ const eventStart = new Date(2026, 3, 14, 14, 27);
383
+
384
+ return (
385
+ <DateTimePicker
386
+ id="event-start"
387
+ defaultValue={eventStart}
388
+ onChange={(date) => {
389
+ // date is Date | undefined — not a string
390
+ console.log('Updated to:', date?.toLocaleString('en-GB'));
391
+ }}
392
+ />
393
+ );
394
+ }
395
+ export default EditEventStart;
396
+ `.trim(),
99
397
  },
100
398
  },
101
399
  },
102
- };
103
- export const PlaceholderNativeSecondHint = {
104
- name: 'Placeholder · native second hint',
105
- args: {
106
- granularity: 'second',
400
+ }, [
401
+ 'Use `defaultValue` to pre-populate an uncontrolled DateTimePicker — for example when editing',
402
+ 'an existing event. The prop must be a `Date` object (month is zero-indexed: April = 3).',
403
+ ].join(' '));
404
+ export const GranularitySecond = withDescription({
405
+ render: () => _jsx(GranularitySecondTemplate, {}),
406
+ parameters: {
407
+ controls: { disable: true },
408
+ docs: {
409
+ source: {
410
+ language: 'tsx',
411
+ code: `
412
+ import { useState } from 'react';
413
+ import { DateTimePicker } from '@arbor-education/design-system.components';
414
+
415
+ function PreciseDateTimePickerExample() {
416
+ const [value, setValue] = useState<Date | undefined>(undefined);
417
+
418
+ return (
419
+ <DateTimePicker
420
+ granularity="second"
421
+ value={value}
422
+ onChange={setValue}
423
+ id="precise-datetime"
424
+ />
425
+ );
426
+ }
427
+ export default PreciseDateTimePickerExample;
428
+ `.trim(),
429
+ },
430
+ },
107
431
  },
432
+ }, [
433
+ '`granularity="second"` enables seconds in both the `datetime-local` native input (sets `step={1}`)',
434
+ 'and the embedded time input inside the calendar popup.',
435
+ 'Use for precise event logging, stopwatch-style entry, or when sub-minute accuracy matters.',
436
+ ].join(' '));
437
+ export const DisplayFormatDefault = withDescription({
438
+ render: () => _jsx(DisplayFormatDefaultTemplate, {}),
108
439
  parameters: {
440
+ controls: { disable: true },
109
441
  docs: {
110
- description: {
111
- story: 'Same as the minute story, but with `granularity="second"` so the hint includes seconds.',
442
+ source: {
443
+ language: 'tsx',
444
+ code: `
445
+ import { useState } from 'react';
446
+ import { DateTimePicker } from '@arbor-education/design-system.components';
447
+
448
+ function FormattedDateTimePickerExample() {
449
+ const [value, setValue] = useState<Date | undefined>(new Date(2026, 3, 14, 14, 27));
450
+
451
+ return (
452
+ <DateTimePicker
453
+ displayFormat="default"
454
+ value={value}
455
+ onChange={setValue}
456
+ id="event-datetime"
457
+ />
458
+ );
459
+ }
460
+ export default FormattedDateTimePickerExample;
461
+ `.trim(),
112
462
  },
113
463
  },
114
464
  },
115
- };
116
- export const DisplayFormatDefault = {
117
- name: 'Display format · default (text)',
118
- args: {
119
- displayFormat: 'default',
120
- value: new Date(2026, 3, 14, 14, 27),
121
- granularity: 'minute',
465
+ }, [
466
+ '`displayFormat="default"` uses a plain text input showing the value as `DD/MM/YYYY HH:mm`',
467
+ '(e.g. `14/04/2026 14:27`). The appearance is consistent across all browsers — unlike `"native"`,',
468
+ 'which uses the OS date/time widget and varies by platform.',
469
+ ].join(' '));
470
+ export const DisplayFormatFriendly = withDescription({
471
+ render: () => _jsx(DisplayFormatFriendlyTemplate, {}),
472
+ parameters: {
473
+ controls: { disable: true },
474
+ docs: {
475
+ source: {
476
+ language: 'tsx',
477
+ code: `
478
+ import { useState } from 'react';
479
+ import { DateTimePicker } from '@arbor-education/design-system.components';
480
+
481
+ function FriendlyDateTimePickerExample() {
482
+ const [value, setValue] = useState<Date | undefined>(new Date(2026, 3, 14, 14, 27));
483
+
484
+ return (
485
+ <DateTimePicker
486
+ displayFormat="friendly"
487
+ value={value}
488
+ onChange={setValue}
489
+ id="event-datetime"
490
+ />
491
+ );
492
+ }
493
+ export default FriendlyDateTimePickerExample;
494
+ `.trim(),
495
+ },
496
+ },
122
497
  },
123
- render: args => _jsx(ControlledDateTimePicker, { ...args }),
498
+ }, [
499
+ '`displayFormat="friendly"` shows the value as `April 14th, 2026 14:27` — human-readable and',
500
+ 'locale-style. Best for consumer-facing or less technical contexts where ISO-adjacent formats feel unfriendly.',
501
+ ].join(' '));
502
+ export const WithTimeOptions = withDescription({
503
+ render: () => _jsx(WithTimeOptionsTemplate, {}),
124
504
  parameters: {
505
+ controls: { disable: true },
125
506
  docs: {
126
- description: {
127
- story: 'Combined value is shown and edited as **15/04/2026 14:27** instead of the native ISO string.',
507
+ source: {
508
+ language: 'tsx',
509
+ code: `
510
+ import { useState } from 'react';
511
+ import { DateTimePicker, type TimeValue } from '@arbor-education/design-system.components';
512
+
513
+ const APPOINTMENT_SLOTS: TimeValue[] = [
514
+ '09:00', '09:30', '10:00', '10:30', '11:00', '11:30',
515
+ '14:00', '14:30', '15:00', '15:30',
516
+ ];
517
+
518
+ function AppointmentBookingExample() {
519
+ const [value, setValue] = useState<Date | undefined>(new Date(2026, 3, 14, 9, 0));
520
+
521
+ return (
522
+ <DateTimePicker
523
+ timeOptions={APPOINTMENT_SLOTS}
524
+ value={value}
525
+ onChange={setValue}
526
+ id="appointment-datetime"
527
+ />
528
+ );
529
+ }
530
+ export default AppointmentBookingExample;
531
+ `.trim(),
128
532
  },
129
533
  },
130
534
  },
131
- };
132
- export const DisplayFormatFriendly = {
133
- name: 'Display format · friendly (text)',
134
- args: {
135
- displayFormat: 'friendly',
136
- value: new Date(2026, 3, 14, 14, 27),
137
- granularity: 'minute',
535
+ }, [
536
+ '`timeOptions` switches the embedded time input from free-form entry to a combobox list.',
537
+ 'Ideal for appointment booking where only specific slots are valid — the user picks a date',
538
+ 'from the calendar, then picks a slot from the dropdown. Both stay in a single `Date` value.',
539
+ ].join(' '));
540
+ export const WithPlaceholder = withDescription({
541
+ render: () => (_jsx("div", { style: { maxWidth: '280px' }, children: _jsx(DateTimePicker, { id: "placeholder-datetime", placeholder: "Choose event start" }) })),
542
+ parameters: {
543
+ controls: { disable: true },
544
+ docs: {
545
+ source: {
546
+ language: 'tsx',
547
+ code: `
548
+ import { DateTimePicker } from '@arbor-education/design-system.components';
549
+
550
+ function GuidedDateTimePickerExample() {
551
+ return (
552
+ <DateTimePicker
553
+ id="event-start"
554
+ placeholder="Choose event start"
555
+ onChange={(date) => {
556
+ console.log('Event start:', date?.toLocaleString('en-GB'));
557
+ }}
558
+ />
559
+ );
560
+ }
561
+ export default GuidedDateTimePickerExample;
562
+ `.trim(),
563
+ },
564
+ },
138
565
  },
139
- render: args => _jsx(ControlledDateTimePicker, { ...args }),
566
+ }, [
567
+ '`placeholder` (native mode only) replaces the `YYYY-MM-DDTHH:mm` hint with custom copy.',
568
+ 'When set and the field is empty, the input becomes **read-only** and the calendar opens on',
569
+ '**click only** — Tab focus does not trigger it. Prevents accidental popup during keyboard navigation.',
570
+ ].join(' '));
571
+ export const ErrorState = withDescription({
572
+ render: () => (_jsxs("div", { style: { maxWidth: '280px' }, children: [_jsx(DateTimePicker, { id: "error-state-datetime", hasError: true, "aria-invalid": true, "aria-describedby": "error-state-datetime-msg" }), _jsx("p", { id: "error-state-datetime-msg", role: "alert", style: { margin: 'var(--spacing-xsmall) 0 0', color: 'var(--color-semantic-destructive-600)', fontSize: '0.875rem' }, children: "Please select a valid date and time." })] })),
140
573
  parameters: {
574
+ controls: { disable: true },
141
575
  docs: {
142
- description: {
143
- story: 'Combined value uses friendly wording, e.g. **April 14th, 2026 14:27**.',
576
+ source: {
577
+ language: 'tsx',
578
+ code: `
579
+ import { DateTimePicker } from '@arbor-education/design-system.components';
580
+
581
+ // Standalone — set both hasError (visual) and aria-invalid (screen reader) yourself.
582
+ // Inside <FormField> both are handled automatically when errorText is set.
583
+ function DateTimePickerWithError() {
584
+ return (
585
+ <>
586
+ <DateTimePicker
587
+ id="event-start"
588
+ hasError
589
+ aria-invalid={true}
590
+ aria-describedby="event-start-error"
591
+ />
592
+ <p id="event-start-error" role="alert">
593
+ Please select a valid date and time.
594
+ </p>
595
+ </>
596
+ );
597
+ }
598
+ export default DateTimePickerWithError;
599
+ `.trim(),
144
600
  },
145
601
  },
146
602
  },
147
- };
148
- export const PlaceholderCustomOverride = {
149
- name: 'Placeholder · custom copy',
150
- args: {
151
- granularity: 'minute',
152
- placeholder: 'Event start (local)',
603
+ }, [
604
+ 'The error state requires **both** `hasError` (visual red border) and `aria-invalid={true}`',
605
+ '(screen-reader signal). Use `aria-describedby` to link the input to the error message.',
606
+ 'When using `<FormField>`, set `errorText` and the component handles all three automatically.',
607
+ ].join(' '));
608
+ export const ControlledWithDisplay = withDescription({
609
+ render: () => _jsx(ControlledWithDisplayTemplate, {}),
610
+ parameters: {
611
+ controls: { disable: true },
612
+ docs: {
613
+ source: {
614
+ language: 'tsx',
615
+ code: `
616
+ import { useState } from 'react';
617
+ import { DateTimePicker } from '@arbor-education/design-system.components';
618
+
619
+ function ControlledDateTimePickerExample() {
620
+ const [value, setValue] = useState<Date | undefined>(undefined);
621
+
622
+ return (
623
+ <div>
624
+ <DateTimePicker
625
+ id="event-datetime"
626
+ onChange={setValue}
627
+ />
628
+ <p>
629
+ Selected:{' '}
630
+ <code>
631
+ {value
632
+ ? \`\${value.toLocaleDateString('en-GB')} \${value.toLocaleTimeString('en-GB', { hour: '2-digit', minute: '2-digit' })}\`
633
+ : '(none)'}
634
+ </code>
635
+ </p>
636
+ </div>
637
+ );
638
+ }
639
+ export default ControlledDateTimePickerExample;
640
+ `.trim(),
641
+ },
642
+ },
153
643
  },
644
+ }, [
645
+ '`onChange` receives a `Date` object (or `undefined`) — never a string.',
646
+ 'This controlled example stores the combined date+time in state and renders it below the field.',
647
+ 'Use `toLocaleDateString` + `toLocaleTimeString` (or `date-fns format`) for display.',
648
+ ].join(' '));
649
+ export const WithFormField = withDescription({
650
+ render: () => _jsx(WithFormFieldTemplate, {}),
154
651
  parameters: {
652
+ controls: { disable: true },
155
653
  docs: {
156
- description: {
157
- story: 'Pass `placeholder` to replace the default native empty-state hint.',
654
+ source: {
655
+ language: 'tsx',
656
+ code: `
657
+ import { FormField } from '@arbor-education/design-system.components';
658
+
659
+ function EventStartField() {
660
+ return (
661
+ <FormField
662
+ label="Event start date and time"
663
+ id="event-start"
664
+ inputType="dateTimePicker"
665
+ fieldDescription="Select the date and time the event will begin."
666
+ inputProps={{
667
+ onChange: (date) => {
668
+ // date is Date | undefined
669
+ console.log('Event start:', date?.toLocaleString('en-GB'));
670
+ },
671
+ }}
672
+ />
673
+ );
674
+ }
675
+ export default EventStartField;
676
+ `.trim(),
158
677
  },
159
678
  },
160
679
  },
161
- render: args => _jsx(ControlledDateTimePicker, { ...args }),
162
- };
680
+ }, [
681
+ 'The recommended form usage: `<FormField inputType="dateTimePicker">` provides the accessible',
682
+ 'label, description, and error layout. DateTimePicker props go in `inputProps`.',
683
+ 'The `label`, `fieldDescription`, and `errorText` props belong on `<FormField>` itself.',
684
+ ].join(' '));
685
+ export const WithFormFieldAndError = withDescription({
686
+ render: () => _jsx(WithFormFieldAndErrorTemplate, {}),
687
+ parameters: {
688
+ controls: { disable: true },
689
+ docs: {
690
+ source: {
691
+ language: 'tsx',
692
+ code: `
693
+ import { useState } from 'react';
694
+ import { FormField } from '@arbor-education/design-system.components';
695
+
696
+ function LessonSchedulerForm() {
697
+ const [submitted, setSubmitted] = useState(false);
698
+ const [selectedDate, setSelectedDate] = useState<Date | undefined>(undefined);
699
+ const hasError = submitted && !selectedDate;
700
+
701
+ return (
702
+ <div>
703
+ <FormField
704
+ label="Lesson start date and time"
705
+ id="lesson-start"
706
+ inputType="dateTimePicker"
707
+ errorText={
708
+ hasError
709
+ ? 'Please select a date and time for the lesson.'
710
+ : undefined
711
+ }
712
+ inputProps={{
713
+ onChange: setSelectedDate,
714
+ hasError,
715
+ 'aria-invalid': hasError ? true : undefined,
716
+ }}
717
+ />
718
+ <button type="button" onClick={() => setSubmitted(true)}>
719
+ Submit
720
+ </button>
721
+ </div>
722
+ );
723
+ }
724
+ export default LessonSchedulerForm;
725
+ `.trim(),
726
+ },
727
+ },
728
+ },
729
+ }, [
730
+ 'A submit-gated form: clicking Submit without a selection triggers the full error pattern.',
731
+ '`errorText` on `<FormField>` renders the message in the correct accessible layout,',
732
+ 'linked via `aria-describedby` automatically. Once a date+time is chosen, the error clears.',
733
+ ].join(' '));
163
734
  //# sourceMappingURL=DateTimePicker.stories.js.map