@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,49 +1,303 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { Button } from '../../../button/Button';
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 { TimeInput } from './TimeInput';
6
- const timeOptions = ['09:00', '09:30', '10:00', '10:30', '11:00'];
6
+ // ---------------------------------------------------------------------------
7
+ // Docs page content
8
+ // ---------------------------------------------------------------------------
9
+ const DESCRIPTION_INTRO = [
10
+ 'TimeInput is a flexible time-entry field that operates in **two distinct modes** depending on',
11
+ 'whether the `options` prop is supplied.',
12
+ '',
13
+ '**Native mode** (no `options`) renders a browser-native `<input type="time">` decorated with a',
14
+ 'clock icon. The browser handles the picker UI, spin-button keyboard navigation, and locale-aware',
15
+ 'display. This mode supports `min`, `max`, `granularity`, `className`, a forwarded `ref`, and the',
16
+ 'native `onChange` event.',
17
+ '',
18
+ '**Combobox mode** (`options` provided) replaces the native input with a searchable Combobox.',
19
+ 'The user can type to filter or click to select from a predefined list of time values. This mode',
20
+ 'supports `searchType` and `highlightStringMatches`, but ignores `min`, `max`, `granularity`,',
21
+ '`className`, `ref`, and `onChange`.',
22
+ '',
23
+ 'Both modes share: `value`, `defaultValue`, `onValueChange`, `hasError`, `disabled`, `id`, `name`,',
24
+ '`placeholder`, and ARIA attributes.',
25
+ ].join('\n');
26
+ const USAGE_GUIDANCE = [
27
+ '### When to use',
28
+ '',
29
+ '- **Any valid time** — use native mode (the default). The browser enforces valid time entry and',
30
+ ' supports `min`/`max` bounds for school hours (e.g. `min="08:00" max="17:00"`).',
31
+ '- **Fixed set of allowed times** — use combobox mode with `options`. Ideal for lesson periods,',
32
+ ' appointment slots, or any scenario where only specific times are valid.',
33
+ '- **Inside a form** — always wrap in `<FormField inputType="time">` so label, error text, and',
34
+ ' ARIA attributes are wired up automatically.',
35
+ '',
36
+ '---',
37
+ '',
38
+ '### When NOT to use',
39
+ '',
40
+ '| Situation | Use instead |',
41
+ '|---|---|',
42
+ '| Date + time combined | Separate `DateInput` and `TimeInput` fields |',
43
+ '| Free-form duration entry | `TextInput` with format hint |',
44
+ '| More than ~50 time options | Native mode with `min`/`max` bounds |',
45
+ '| Status or category selection | `SelectDropdown` with meaningful labels |',
46
+ ].join('\n');
47
+ const DEVELOPER_NOTES = [
48
+ '### Critical usage patterns',
49
+ '',
50
+ '**Prop availability varies by mode.** Props that are native-only are silently ignored in combobox',
51
+ 'mode, and vice versa:',
52
+ '',
53
+ '| Prop | Native mode | Combobox mode |',
54
+ '|---|---|---|',
55
+ '| `granularity` | Sets `step={1}` for second-level precision | **Ignored** |',
56
+ '| `min` / `max` | Restricts picker range | **Ignored** — filter `options` array instead |',
57
+ '| `className` | Applied to the wrapper `<div>` | **Ignored** |',
58
+ '| `ref` (forwarded) | Attached to the `<input>` element | **Ignored** |',
59
+ '| `onChange` | Native `ChangeEvent<HTMLInputElement>` | **Ignored** — use `onValueChange` |',
60
+ '| `searchType` | **Ignored** | Controls `prefix`/`substring` matching |',
61
+ '| `highlightStringMatches` | **Ignored** | Bolds matched characters in dropdown |',
62
+ '| `onValueChange` | Fires with the string value | Fires with the string value |',
63
+ '',
64
+ '**`aria-invalid` is NOT automatic.** When using TimeInput standalone (outside FormField),',
65
+ '`hasError` only applies visual error styling. It does NOT set `aria-invalid`:',
66
+ '',
67
+ '```tsx',
68
+ '// Standalone — set aria-invalid yourself:',
69
+ '<TimeInput hasError aria-invalid={true} aria-label="Start time" />',
70
+ '',
71
+ '// Inside FormField — aria-invalid is set automatically when errorText is present:',
72
+ '<FormField label="Start time" id="start" inputType="time" errorText="Required." inputProps={{}} />',
73
+ '```',
74
+ '',
75
+ '**`onValueChange` is the preferred callback** — it works in both modes and receives the plain',
76
+ 'string value (`HH:MM` or `HH:MM:SS`). The native `onChange` event is only available in native',
77
+ 'mode and is not forwarded in combobox mode.',
78
+ '',
79
+ '**`TimeValue` is format-shaped, not strictly validated.** The TypeScript type',
80
+ '`` `${string}:${string}` `` accepts `"99:99"` without complaint. The browser rejects it at render',
81
+ 'time for `<input type="time">`, but the component itself does no validation.',
82
+ '',
83
+ '---',
84
+ '',
85
+ '### Accessibility',
86
+ '',
87
+ '- Always provide a visible label via `<FormField>` or `aria-label` / `aria-labelledby`.',
88
+ '- In native mode, the browser\'s built-in time picker is fully keyboard-navigable (arrow keys',
89
+ ' cycle through the hour/minute/second segments; AM/PM toggles with A/P).',
90
+ '- In combobox mode, the Combobox component implements the ARIA combobox pattern',
91
+ ' (`role="combobox"`, `aria-expanded`, `aria-activedescendant`).',
92
+ '- The clock icon is decorative and carries `aria-hidden="true"`.',
93
+ '- Pair `hasError` with `aria-invalid={true}` when used standalone — one is visual, the other',
94
+ ' is for screen readers. `<FormField>` handles this automatically when `errorText` is set.',
95
+ '',
96
+ '---',
97
+ '',
98
+ '### TypeScript types',
99
+ '',
100
+ '```ts',
101
+ "import { TimeInput, type TimeValue, type TimeGranularity } from '@arbor-education/design-system.components';",
102
+ '',
103
+ '// TimeValue: HH:MM or HH:MM:SS (format-shaped, not strictly validated)',
104
+ 'type TimeValue = `${string}:${string}` | `${string}:${string}:${string}`;',
105
+ "type TimeGranularity = 'minute' | 'second';",
106
+ '',
107
+ '// Access types via the namespace:',
108
+ 'function MyField(props: TimeInput.Props) { ... }',
109
+ '```',
110
+ ].join('\n');
111
+ const RELATED_COMPONENTS = [
112
+ '## Related components',
113
+ '',
114
+ '[FormField](?path=/docs/components-formfield--docs) · [DateInput](?path=/docs/components-formfield-inputs-dateinput--docs) · [Combobox](?path=/docs/components-combobox--docs)',
115
+ ].join('\n');
116
+ const PROPS_INTRO = 'The preview below is wired to the **Controls** panel — tweak any prop to see the story update in real time.';
117
+ // ---------------------------------------------------------------------------
118
+ // Docs page component
119
+ // ---------------------------------------------------------------------------
120
+ function TimeInputDocsPage() {
121
+ 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 })] }));
122
+ }
123
+ // ---------------------------------------------------------------------------
124
+ // Meta
125
+ // ---------------------------------------------------------------------------
7
126
  const meta = {
8
127
  title: 'Components/FormField/Inputs/TimeInput',
9
128
  component: TimeInput,
129
+ tags: ['autodocs'],
10
130
  parameters: {
11
- layout: 'centered',
131
+ layout: 'padded',
12
132
  docs: {
13
- description: {
14
- component: '`TimeInput` supports both native time entry and a combobox-backed time list. When you pass `value`, treat it as a controlled component and update it in `onValueChange`; use `defaultValue` for uncontrolled usage.',
15
- },
133
+ page: TimeInputDocsPage,
16
134
  },
17
135
  },
18
- tags: ['autodocs'],
19
- args: {
20
- onValueChange: fn(),
21
- },
22
136
  argTypes: {
23
- granularity: {
24
- control: 'inline-radio',
25
- options: ['minute', 'second'],
26
- description: 'Controls whether the native time input works in minute or second increments.',
137
+ 'value': {
138
+ description: [
139
+ 'Controlled value in `HH:MM` or `HH:MM:SS` format.',
140
+ 'Pair with `onValueChange` to keep state in sync.',
141
+ 'If `undefined`, the component manages its own state (uncontrolled).',
142
+ ].join(' '),
143
+ control: 'text',
144
+ table: {
145
+ type: { summary: "TimeValue | ''" },
146
+ defaultValue: { summary: 'undefined' },
147
+ },
148
+ },
149
+ 'defaultValue': {
150
+ description: [
151
+ 'Uncontrolled initial value in `HH:MM` or `HH:MM:SS` format.',
152
+ 'Use when you only need the value on form submit, not on every change.',
153
+ ].join(' '),
154
+ control: 'text',
155
+ table: {
156
+ type: { summary: "TimeValue | ''" },
157
+ defaultValue: { summary: "''" },
158
+ },
27
159
  },
28
- options: {
160
+ 'onValueChange': {
161
+ description: [
162
+ 'Callback fired with the plain string value on every change.',
163
+ 'Works in **both** native and combobox modes.',
164
+ 'Prefer this over the native `onChange` event for cross-mode compatibility.',
165
+ ].join(' '),
166
+ action: 'onValueChange',
167
+ control: false,
168
+ table: {
169
+ type: { summary: '(value: string) => void' },
170
+ },
171
+ },
172
+ 'options': {
173
+ description: [
174
+ 'Array of `TimeValue` strings to display as selectable options.',
175
+ 'Providing this prop switches to **combobox mode** — the native `<input type="time">` is',
176
+ 'replaced by a searchable Combobox. Omit to use the browser-native time picker.',
177
+ ].join(' '),
29
178
  control: 'object',
30
- description: 'When provided, `TimeInput` switches from native `input[type="time"]` to the combobox-backed time list mode.',
179
+ table: {
180
+ type: { summary: 'TimeValue[]' },
181
+ defaultValue: { summary: 'undefined' },
182
+ },
31
183
  },
32
- value: {
184
+ 'granularity': {
185
+ description: [
186
+ '**Native mode only.** Controls the precision of the time input.',
187
+ '`"second"` sets `step={1}` so the seconds segment is visible.',
188
+ 'Silently ignored when `options` is provided.',
189
+ ].join(' '),
190
+ control: { type: 'select' },
191
+ options: ['minute', 'second'],
192
+ table: {
193
+ type: { summary: "'minute' | 'second'" },
194
+ defaultValue: { summary: "'minute'" },
195
+ },
196
+ },
197
+ 'searchType': {
198
+ description: [
199
+ '**Combobox mode only.** Controls how options are filtered as the user types.',
200
+ '`"prefix"` (default) matches from the start of the string;',
201
+ '`"substring"` matches anywhere in the string.',
202
+ 'Silently ignored in native mode.',
203
+ ].join(' '),
204
+ control: { type: 'select' },
205
+ options: ['prefix', 'substring'],
206
+ table: {
207
+ type: { summary: "'prefix' | 'substring'" },
208
+ defaultValue: { summary: "'prefix'" },
209
+ },
210
+ },
211
+ 'highlightStringMatches': {
212
+ description: [
213
+ '**Combobox mode only.** When `true`, matched characters in dropdown options are bolded.',
214
+ 'Silently ignored in native mode.',
215
+ ].join(' '),
216
+ control: 'boolean',
217
+ table: {
218
+ type: { summary: 'boolean' },
219
+ defaultValue: { summary: 'false' },
220
+ },
221
+ },
222
+ 'hasError': {
223
+ description: [
224
+ 'Applies error-state visual styling (red border).',
225
+ 'Does **not** set `aria-invalid` automatically when used standalone outside FormField.',
226
+ 'Always pair with `aria-invalid={true}` for screen-reader coverage.',
227
+ ].join(' '),
228
+ control: 'boolean',
229
+ table: {
230
+ type: { summary: 'boolean' },
231
+ defaultValue: { summary: 'false' },
232
+ },
233
+ },
234
+ 'disabled': {
235
+ description: 'Disables the input. The current value remains visible.',
236
+ control: 'boolean',
237
+ table: {
238
+ type: { summary: 'boolean' },
239
+ defaultValue: { summary: 'false' },
240
+ },
241
+ },
242
+ 'id': {
243
+ description: [
244
+ 'HTML `id` for the input element.',
245
+ 'Required when inside FormField so the `<label>` `htmlFor` can be linked.',
246
+ ].join(' '),
33
247
  control: 'text',
34
- description: 'Controlled value. If you provide this, update it from `onValueChange` in your app or story state.',
248
+ table: {
249
+ type: { summary: 'string' },
250
+ },
35
251
  },
36
- defaultValue: {
252
+ 'name': {
253
+ description: 'HTML `name` attribute for native form submission.',
37
254
  control: 'text',
38
- description: 'Uncontrolled initial value. Use this when you want the component to manage its own state.',
255
+ table: {
256
+ type: { summary: 'string' },
257
+ },
39
258
  },
40
- onValueChange: {
41
- action: 'value changed',
42
- description: 'Called with the next string value (`HH:MM` or `HH:MM:SS`).',
259
+ 'placeholder': {
260
+ description: 'Placeholder text shown when no value is selected. Most useful in combobox mode.',
261
+ control: 'text',
262
+ table: {
263
+ type: { summary: 'string' },
264
+ },
265
+ },
266
+ 'aria-label': {
267
+ description: [
268
+ 'Accessible label for the input.',
269
+ 'Required when the component is used standalone without a visible `<label>`.',
270
+ ].join(' '),
271
+ control: 'text',
272
+ table: {
273
+ type: { summary: 'string' },
274
+ },
275
+ },
276
+ 'aria-invalid': {
277
+ description: [
278
+ 'Marks the input as invalid for screen readers.',
279
+ 'Must be set manually when used standalone — FormField sets this automatically',
280
+ 'when `errorText` is provided.',
281
+ ].join(' '),
282
+ control: 'boolean',
283
+ table: {
284
+ type: { summary: 'boolean' },
285
+ defaultValue: { summary: 'false' },
286
+ },
287
+ },
288
+ 'aria-describedby': {
289
+ description: 'ID of the element that describes the field (e.g. an error or hint message).',
290
+ control: 'text',
291
+ table: {
292
+ type: { summary: 'string' },
293
+ },
43
294
  },
44
295
  },
45
296
  };
46
297
  export default meta;
298
+ // ---------------------------------------------------------------------------
299
+ // Helper: attach a per-story description
300
+ // ---------------------------------------------------------------------------
47
301
  const withDescription = (story, description) => ({
48
302
  ...story,
49
303
  parameters: {
@@ -56,77 +310,582 @@ const withDescription = (story, description) => ({
56
310
  },
57
311
  },
58
312
  });
59
- const ControlledTimeInput = (args) => {
60
- const [value, setValue] = useState(args.value ?? args.defaultValue ?? '');
61
- useEffect(() => {
62
- setValue(args.value ?? args.defaultValue ?? '');
63
- }, [args.defaultValue, args.value]);
64
- return (_jsx(TimeInput, { ...args, value: value, onValueChange: (nextValue) => {
65
- setValue(nextValue);
66
- args.onValueChange?.(nextValue);
67
- } }));
313
+ // ---------------------------------------------------------------------------
314
+ // Lesson period options (shared across combobox stories)
315
+ // ---------------------------------------------------------------------------
316
+ const LESSON_PERIOD_OPTIONS = [
317
+ '09:00',
318
+ '09:45',
319
+ '10:30',
320
+ '11:15',
321
+ '13:00',
322
+ '13:45',
323
+ '14:30',
324
+ '15:15',
325
+ '16:00',
326
+ ];
327
+ // ---------------------------------------------------------------------------
328
+ // Named template components for stateful stories
329
+ // ---------------------------------------------------------------------------
330
+ const ControlledTemplate = () => {
331
+ const [value, setValue] = useState('09:30');
332
+ return (_jsxs("div", { style: { display: 'flex', flexDirection: 'column', gap: 'var(--spacing-small)', maxWidth: '280px' }, children: [_jsx(TimeInput, { value: value, onValueChange: v => setValue(v), id: "controlled-time", name: "start-time" }), _jsxs("p", { style: { margin: 0, color: 'var(--color-grey-600)' }, children: ["Selected:", ' ', _jsx("code", { children: value || '(none)' })] })] }));
333
+ };
334
+ const GranularitySecondTemplate = () => {
335
+ const [value, setValue] = useState('');
336
+ return (_jsxs("div", { style: { display: 'flex', flexDirection: 'column', gap: 'var(--spacing-small)', maxWidth: '280px' }, children: [_jsx(TimeInput, { granularity: "second", value: value, onValueChange: v => setValue(v), id: "second-granularity", name: "precise-time" }), _jsxs("p", { style: { margin: 0, color: 'var(--color-grey-600)' }, children: ["Selected:", ' ', _jsx("code", { children: value || '(none)' })] })] }));
68
337
  };
69
- const NativeValidationDemo = (args) => {
70
- const [value, setValue] = useState(args.value ?? '');
71
- useEffect(() => {
72
- setValue(args.value ?? '');
73
- }, [args.value]);
74
- return (_jsxs("form", { style: { display: 'grid', gap: 12, width: 280 }, onSubmit: (event) => {
75
- event.preventDefault();
76
- const form = event.currentTarget;
77
- if (!form.reportValidity()) {
78
- return;
79
- }
80
- }, children: [_jsx(TimeInput, { ...args, value: value, onValueChange: (nextValue) => {
81
- setValue(nextValue);
82
- args.onValueChange?.(nextValue);
83
- } }), _jsx(Button, { variant: "primary", size: "M", type: "submit", children: "Submit" })] }));
338
+ const WithMinMaxBoundsTemplate = () => {
339
+ const [value, setValue] = useState('');
340
+ return (_jsxs("div", { style: { display: 'flex', flexDirection: 'column', gap: 'var(--spacing-small)', maxWidth: '280px' }, children: [_jsx(TimeInput, { min: "08:00", max: "17:00", value: value, onValueChange: v => setValue(v), id: "bounded-time", name: "school-time", "aria-label": "School hours time" }), _jsx("p", { style: { margin: 0, color: 'var(--color-grey-600)' }, children: "Restricted to school hours: 08:00 \u2013 17:00" })] }));
84
341
  };
85
- export const NativeMinute = withDescription({
342
+ const WithOptionsTemplate = () => {
343
+ const [value, setValue] = useState('');
344
+ return (_jsxs("div", { style: { display: 'flex', flexDirection: 'column', gap: 'var(--spacing-small)', maxWidth: '280px' }, children: [_jsx(TimeInput, { options: LESSON_PERIOD_OPTIONS, value: value, onValueChange: v => setValue(v), id: "lesson-period", name: "lesson-start", placeholder: "Select lesson period...", "aria-label": "Lesson period" }), _jsxs("p", { style: { margin: 0, color: 'var(--color-grey-600)' }, children: ["Selected:", ' ', _jsx("code", { children: value || '(none)' })] })] }));
345
+ };
346
+ const SubstringSearchTemplate = () => {
347
+ const [value, setValue] = useState('');
348
+ return (_jsxs("div", { style: { display: 'flex', flexDirection: 'column', gap: 'var(--spacing-small)', maxWidth: '280px' }, children: [_jsx(TimeInput, { options: LESSON_PERIOD_OPTIONS, searchType: "substring", value: value, onValueChange: v => setValue(v), id: "substring-time", name: "lesson-substring", placeholder: "Type to search...", "aria-label": "Lesson period" }), _jsxs("p", { style: { margin: 0, color: 'var(--color-grey-600)' }, children: ["Try typing", ' ', _jsx("code", { children: "45" }), ' ', "\u2014 matches any period containing \"45\"."] })] }));
349
+ };
350
+ const HighlightedMatchesTemplate = () => {
351
+ const [value, setValue] = useState('');
352
+ return (_jsxs("div", { style: { display: 'flex', flexDirection: 'column', gap: 'var(--spacing-small)', maxWidth: '280px' }, children: [_jsx(TimeInput, { options: LESSON_PERIOD_OPTIONS, searchType: "substring", highlightStringMatches: true, value: value, onValueChange: v => setValue(v), id: "highlighted-time", name: "lesson-highlighted", placeholder: "Type to filter...", "aria-label": "Lesson period" }), _jsx("p", { style: { margin: 0, color: 'var(--color-grey-600)' }, children: "Matched characters are bolded in the dropdown list." })] }));
353
+ };
354
+ const ControlledComboboxTemplate = () => {
355
+ const [value, setValue] = useState('13:00');
356
+ return (_jsxs("div", { style: { display: 'flex', flexDirection: 'column', gap: 'var(--spacing-small)', maxWidth: '280px' }, children: [_jsx(TimeInput, { options: LESSON_PERIOD_OPTIONS, value: value, onValueChange: v => setValue(v), id: "controlled-combobox", name: "lesson-controlled", placeholder: "Select lesson period...", "aria-label": "Lesson period" }), _jsxs("p", { style: { margin: 0, color: 'var(--color-grey-600)' }, children: ["Selected:", ' ', _jsx("code", { children: value || '(none)' })] })] }));
357
+ };
358
+ // ---------------------------------------------------------------------------
359
+ // FormField template components
360
+ // ---------------------------------------------------------------------------
361
+ const InFormFieldTemplate = () => (_jsx("div", { style: { maxWidth: '320px' }, children: _jsx(FormField, { label: "Lesson start time", id: "ff-start-time", inputType: "time", inputProps: { name: 'lesson-start', defaultValue: '09:00' } }) }));
362
+ const InFormFieldWithErrorTemplate = () => (_jsx("div", { style: { maxWidth: '320px' }, children: _jsx(FormField, { label: "Registration start time", id: "ff-error-time", inputType: "time", errorText: "Please enter a valid registration time.", inputProps: { name: 'registration-time' } }) }));
363
+ const InFormFieldComboboxTemplate = () => {
364
+ const [value, setValue] = useState('');
365
+ return (_jsx("div", { style: { maxWidth: '320px' }, children: _jsx(FormField, { label: "Lesson period", id: "ff-lesson-period", inputType: "time", inputProps: {
366
+ name: 'lesson-period',
367
+ options: LESSON_PERIOD_OPTIONS,
368
+ value,
369
+ onValueChange: v => setValue(v),
370
+ placeholder: 'Select a lesson period...',
371
+ } }) }));
372
+ };
373
+ // ---------------------------------------------------------------------------
374
+ // Stories — Native mode
375
+ // ---------------------------------------------------------------------------
376
+ export const Default = {
86
377
  args: {
87
- value: '14:30',
88
- granularity: 'minute',
378
+ 'id': 'default-time',
379
+ 'name': 'default-time',
380
+ 'aria-label': 'Start time',
89
381
  },
90
- render: args => _jsx(ControlledTimeInput, { ...args }),
91
- }, 'Controlled native time input in minute mode. This story keeps local state on purpose to demonstrate the correct `value` + `onValueChange` integration pattern.');
92
- export const NativeSecond = withDescription({
93
- args: {
94
- value: '14:30:15',
95
- granularity: 'second',
382
+ render: args => (_jsx("div", { style: { maxWidth: '280px' }, children: _jsx(TimeInput, { ...args }) })),
383
+ };
384
+ export const Controlled = withDescription({
385
+ parameters: {
386
+ controls: { disable: true },
387
+ docs: {
388
+ source: {
389
+ language: 'tsx',
390
+ code: `
391
+ import { useState } from 'react';
392
+ import { TimeInput, type TimeValue } from '@arbor-education/design-system.components';
393
+
394
+ function ControlledTimeInputExample() {
395
+ const [value, setValue] = useState<TimeValue | ''>('09:30');
396
+
397
+ return (
398
+ <div style={{ display: 'flex', flexDirection: 'column', gap: 'var(--spacing-small)', maxWidth: '280px' }}>
399
+ <TimeInput
400
+ value={value}
401
+ onValueChange={v => setValue(v as TimeValue | '')}
402
+ id="start-time"
403
+ name="start-time"
404
+ aria-label="Start time"
405
+ />
406
+ <p style={{ margin: 0, color: 'var(--color-grey-600)' }}>
407
+ Selected: <code>{value || '(none)'}</code>
408
+ </p>
409
+ </div>
410
+ );
411
+ }
412
+
413
+ export default ControlledTimeInputExample;
414
+ `.trim(),
415
+ },
416
+ },
96
417
  },
97
- render: args => _jsx(ControlledTimeInput, { ...args }),
98
- }, 'Controlled native time input in second mode, showing the same parent-managed state pattern with `HH:MM:SS` values.');
99
- export const NativeWithBounds = withDescription({
100
- args: {
101
- value: '08:30',
102
- granularity: 'minute',
103
- min: '09:00',
104
- max: '17:30',
105
- },
106
- render: args => _jsx(NativeValidationDemo, { ...args }),
107
- }, 'Demonstrates the browser-native min/max validation flow. Enter a time outside the allowed range and submit the form to let the browser show its own native validation message.');
108
- export const TimeList = withDescription({
109
- args: {
110
- options: [...timeOptions],
111
- value: '10:00',
112
- placeholder: 'Select time',
418
+ render: () => _jsx(ControlledTemplate, {}),
419
+ }, 'A controlled TimeInput keeps the value in React state via `onValueChange`. This is the recommended pattern for forms where you need to read, validate, or derive other values from the selected time.');
420
+ export const Uncontrolled = withDescription({
421
+ parameters: {
422
+ controls: { disable: true },
423
+ docs: {
424
+ source: {
425
+ language: 'tsx',
426
+ code: `
427
+ import { TimeInput } from '@arbor-education/design-system.components';
428
+
429
+ function UncontrolledTimeInputExample() {
430
+ return (
431
+ <TimeInput
432
+ defaultValue="08:45"
433
+ id="uncontrolled-time"
434
+ name="registration-time"
435
+ aria-label="Registration time"
436
+ />
437
+ );
438
+ }
439
+
440
+ export default UncontrolledTimeInputExample;
441
+ `.trim(),
442
+ },
443
+ },
113
444
  },
114
- render: args => _jsx(ControlledTimeInput, { ...args }),
115
- }, 'Controlled time-list mode using the combobox-backed variant. Selecting a time updates the local story state the same way an app component would.');
116
- export const TimeListHighlightedMatches = withDescription({
117
- args: {
118
- options: [...timeOptions],
119
- value: '',
120
- placeholder: 'Search times',
121
- searchType: 'substring',
122
- highlightStringMatches: true,
123
- },
124
- render: args => _jsx(ControlledTimeInput, { ...args }),
125
- }, 'Combobox-backed time-list mode with substring matching and highlighted text so consumers can review how search behaves when users type partial time fragments such as `30`.');
126
- export const UncontrolledNative = withDescription({
127
- args: {
128
- defaultValue: '09:30',
129
- granularity: 'minute',
445
+ render: () => (_jsx("div", { style: { maxWidth: '280px' }, children: _jsx(TimeInput, { defaultValue: "08:45", id: "uncontrolled-time", name: "registration-time", "aria-label": "Registration time" }) })),
446
+ }, 'An uncontrolled TimeInput uses `defaultValue` to set the initial time. React does not track subsequent changes — the current value is read via a form `FormData` or a forwarded `ref`. Suitable for simple forms where you only need the value on submit.');
447
+ export const GranularitySecond = withDescription({
448
+ parameters: {
449
+ controls: { disable: true },
450
+ docs: {
451
+ source: {
452
+ language: 'tsx',
453
+ code: `
454
+ import { useState } from 'react';
455
+ import { TimeInput, type TimeValue } from '@arbor-education/design-system.components';
456
+
457
+ function PreciseTimeInputExample() {
458
+ const [value, setValue] = useState<TimeValue | ''>('');
459
+
460
+ return (
461
+ <TimeInput
462
+ granularity="second"
463
+ value={value}
464
+ onValueChange={v => setValue(v as TimeValue | '')}
465
+ id="precise-time"
466
+ name="precise-time"
467
+ aria-label="Precise time"
468
+ />
469
+ );
470
+ }
471
+
472
+ export default PreciseTimeInputExample;
473
+ `.trim(),
474
+ },
475
+ },
476
+ },
477
+ render: () => _jsx(GranularitySecondTemplate, {}),
478
+ }, 'Setting `granularity="second"` adds a seconds segment to the native time input (`step={1}`). Use this for stopwatch-style timing or precise event logging. **Native mode only** — silently ignored when `options` is provided.');
479
+ export const WithMinMaxBounds = withDescription({
480
+ parameters: {
481
+ controls: { disable: true },
482
+ docs: {
483
+ source: {
484
+ language: 'tsx',
485
+ code: `
486
+ import { TimeInput, type TimeValue } from '@arbor-education/design-system.components';
487
+
488
+ function BoundedTimeInputExample() {
489
+ return (
490
+ <TimeInput
491
+ min="08:00"
492
+ max="17:00"
493
+ id="school-time"
494
+ name="school-time"
495
+ aria-label="School hours time"
496
+ />
497
+ );
498
+ }
499
+
500
+ export default BoundedTimeInputExample;
501
+ `.trim(),
502
+ },
503
+ },
504
+ },
505
+ render: () => _jsx(WithMinMaxBoundsTemplate, {}),
506
+ }, 'The `min` and `max` props restrict the selectable range in the native browser picker to school hours. **Native mode only** — in combobox mode, restrict available times by filtering the `options` array instead.');
507
+ export const WithError = withDescription({
508
+ parameters: {
509
+ controls: { disable: true },
510
+ docs: {
511
+ source: {
512
+ language: 'tsx',
513
+ code: `
514
+ import { TimeInput } from '@arbor-education/design-system.components';
515
+
516
+ // Standalone usage — you MUST set aria-invalid yourself.
517
+ // Inside <FormField> this is handled automatically when errorText is present.
518
+ function ErrorTimeInputExample() {
519
+ return (
520
+ <TimeInput
521
+ hasError
522
+ aria-invalid={true}
523
+ aria-label="End time"
524
+ id="end-time"
525
+ name="end-time"
526
+ />
527
+ );
528
+ }
529
+
530
+ export default ErrorTimeInputExample;
531
+ `.trim(),
532
+ },
533
+ },
534
+ },
535
+ render: () => (_jsx("div", { style: { maxWidth: '280px' }, children: _jsx(TimeInput, { hasError: true, "aria-invalid": true, "aria-label": "End time", id: "end-time-error-story", name: "end-time" }) })),
536
+ }, '`hasError` applies error-state visual styling. **Important:** it does NOT automatically set `aria-invalid` when used standalone — add that attribute yourself for screen-reader coverage. Inside `<FormField>`, both are set automatically when `errorText` is provided.');
537
+ export const Disabled = withDescription({
538
+ parameters: {
539
+ controls: { disable: true },
540
+ docs: {
541
+ source: {
542
+ language: 'tsx',
543
+ code: `
544
+ import { TimeInput } from '@arbor-education/design-system.components';
545
+
546
+ function DisabledTimeInputExample() {
547
+ return (
548
+ <TimeInput
549
+ disabled
550
+ value="09:00"
551
+ aria-label="Lesson start time"
552
+ id="disabled-time"
553
+ name="disabled-time"
554
+ />
555
+ );
556
+ }
557
+
558
+ export default DisabledTimeInputExample;
559
+ `.trim(),
560
+ },
561
+ },
562
+ },
563
+ render: () => (_jsx("div", { style: { maxWidth: '280px' }, children: _jsx(TimeInput, { disabled: true, value: "09:00", "aria-label": "Lesson start time", id: "disabled-time-story", name: "disabled-time" }) })),
564
+ }, 'A disabled TimeInput prevents user interaction. The current value remains visible, so users can still read the scheduled time. Pass `disabled` via `inputProps` when using inside `<FormField>`.');
565
+ // ---------------------------------------------------------------------------
566
+ // Stories — Combobox mode
567
+ // ---------------------------------------------------------------------------
568
+ export const WithOptions = withDescription({
569
+ parameters: {
570
+ controls: { disable: true },
571
+ docs: {
572
+ source: {
573
+ language: 'tsx',
574
+ code: `
575
+ import { useState } from 'react';
576
+ import { TimeInput, type TimeValue } from '@arbor-education/design-system.components';
577
+
578
+ const LESSON_PERIODS: TimeValue[] = [
579
+ '09:00', '09:45', '10:30', '11:15',
580
+ '13:00', '13:45', '14:30', '15:15', '16:00',
581
+ ];
582
+
583
+ function LessonPeriodPickerExample() {
584
+ const [value, setValue] = useState<TimeValue | ''>('');
585
+
586
+ return (
587
+ <TimeInput
588
+ options={LESSON_PERIODS}
589
+ value={value}
590
+ onValueChange={v => setValue(v as TimeValue | '')}
591
+ id="lesson-period"
592
+ name="lesson-start"
593
+ placeholder="Select lesson period..."
594
+ aria-label="Lesson period"
595
+ />
596
+ );
597
+ }
598
+
599
+ export default LessonPeriodPickerExample;
600
+ `.trim(),
601
+ },
602
+ },
603
+ },
604
+ render: () => _jsx(WithOptionsTemplate, {}),
605
+ }, 'Providing an `options` array switches the component to **combobox mode**. The user can type to filter or click to select from the predefined list. Ideal for scheduling interfaces where only specific time slots are valid.');
606
+ export const SubstringSearch = withDescription({
607
+ parameters: {
608
+ controls: { disable: true },
609
+ docs: {
610
+ source: {
611
+ language: 'tsx',
612
+ code: `
613
+ import { useState } from 'react';
614
+ import { TimeInput, type TimeValue } from '@arbor-education/design-system.components';
615
+
616
+ const LESSON_PERIODS: TimeValue[] = [
617
+ '09:00', '09:45', '10:30', '11:15',
618
+ '13:00', '13:45', '14:30', '15:15', '16:00',
619
+ ];
620
+
621
+ function SubstringSearchPickerExample() {
622
+ const [value, setValue] = useState<TimeValue | ''>('');
623
+
624
+ return (
625
+ <TimeInput
626
+ options={LESSON_PERIODS}
627
+ searchType="substring"
628
+ value={value}
629
+ onValueChange={v => setValue(v as TimeValue | '')}
630
+ id="lesson-substring"
631
+ name="lesson-substring"
632
+ placeholder="Type to search..."
633
+ aria-label="Lesson period"
634
+ />
635
+ );
636
+ }
637
+
638
+ export default SubstringSearchPickerExample;
639
+ `.trim(),
640
+ },
641
+ },
642
+ },
643
+ render: () => _jsx(SubstringSearchTemplate, {}),
644
+ }, '`searchType="substring"` matches anywhere in the option string, not just the start. Try typing `"45"` to match `09:45`, `13:45`, and `14:45`. **Combobox mode only** — silently ignored in native mode.');
645
+ export const HighlightedMatches = withDescription({
646
+ parameters: {
647
+ controls: { disable: true },
648
+ docs: {
649
+ source: {
650
+ language: 'tsx',
651
+ code: `
652
+ import { useState } from 'react';
653
+ import { TimeInput, type TimeValue } from '@arbor-education/design-system.components';
654
+
655
+ const LESSON_PERIODS: TimeValue[] = [
656
+ '09:00', '09:45', '10:30', '11:15',
657
+ '13:00', '13:45', '14:30', '15:15', '16:00',
658
+ ];
659
+
660
+ function HighlightedMatchesPickerExample() {
661
+ const [value, setValue] = useState<TimeValue | ''>('');
662
+
663
+ return (
664
+ <TimeInput
665
+ options={LESSON_PERIODS}
666
+ searchType="substring"
667
+ highlightStringMatches
668
+ value={value}
669
+ onValueChange={v => setValue(v as TimeValue | '')}
670
+ id="lesson-highlighted"
671
+ name="lesson-highlighted"
672
+ placeholder="Type to filter..."
673
+ aria-label="Lesson period"
674
+ />
675
+ );
676
+ }
677
+
678
+ export default HighlightedMatchesPickerExample;
679
+ `.trim(),
680
+ },
681
+ },
682
+ },
683
+ render: () => _jsx(HighlightedMatchesTemplate, {}),
684
+ }, '`highlightStringMatches` bolds the matched characters in each dropdown option. Combine with `searchType="substring"` for maximum clarity when filtering. **Combobox mode only.**');
685
+ export const ComboboxWithError = withDescription({
686
+ parameters: {
687
+ controls: { disable: true },
688
+ docs: {
689
+ source: {
690
+ language: 'tsx',
691
+ code: `
692
+ import { TimeInput, type TimeValue } from '@arbor-education/design-system.components';
693
+
694
+ const LESSON_PERIODS: TimeValue[] = ['09:00', '09:45', '10:30', '11:15'];
695
+
696
+ function ErrorComboboxTimeInputExample() {
697
+ return (
698
+ <TimeInput
699
+ options={LESSON_PERIODS}
700
+ hasError
701
+ aria-invalid={true}
702
+ aria-label="Lesson period"
703
+ id="lesson-error"
704
+ name="lesson-error"
705
+ placeholder="Select a lesson period..."
706
+ />
707
+ );
708
+ }
709
+
710
+ export default ErrorComboboxTimeInputExample;
711
+ `.trim(),
712
+ },
713
+ },
714
+ },
715
+ render: () => (_jsx("div", { style: { maxWidth: '280px' }, children: _jsx(TimeInput, { options: LESSON_PERIOD_OPTIONS, hasError: true, "aria-invalid": true, "aria-label": "Lesson period", id: "combobox-error-story", name: "lesson-error", placeholder: "Select a lesson period..." }) })),
716
+ }, 'Combobox mode supports `hasError` for error-state styling. As with native mode, `aria-invalid` must be set manually when used outside `<FormField>`.');
717
+ export const ComboboxDisabled = withDescription({
718
+ parameters: {
719
+ controls: { disable: true },
720
+ docs: {
721
+ source: {
722
+ language: 'tsx',
723
+ code: `
724
+ import { TimeInput, type TimeValue } from '@arbor-education/design-system.components';
725
+
726
+ const LESSON_PERIODS: TimeValue[] = ['09:00', '09:45', '10:30', '11:15'];
727
+
728
+ function DisabledComboboxTimeInputExample() {
729
+ return (
730
+ <TimeInput
731
+ options={LESSON_PERIODS}
732
+ disabled
733
+ value="09:00"
734
+ aria-label="Lesson period"
735
+ id="lesson-disabled"
736
+ name="lesson-disabled"
737
+ />
738
+ );
739
+ }
740
+
741
+ export default DisabledComboboxTimeInputExample;
742
+ `.trim(),
743
+ },
744
+ },
745
+ },
746
+ render: () => (_jsx("div", { style: { maxWidth: '280px' }, children: _jsx(TimeInput, { options: LESSON_PERIOD_OPTIONS, disabled: true, value: "09:00", "aria-label": "Lesson period", id: "combobox-disabled-story", name: "lesson-disabled" }) })),
747
+ }, 'Combobox mode respects the `disabled` prop. The trigger button is non-interactive and visually muted, but the selected value remains readable.');
748
+ export const ControlledCombobox = withDescription({
749
+ parameters: {
750
+ controls: { disable: true },
751
+ docs: {
752
+ source: {
753
+ language: 'tsx',
754
+ code: `
755
+ import { useState } from 'react';
756
+ import { TimeInput, type TimeValue } from '@arbor-education/design-system.components';
757
+
758
+ const LESSON_PERIODS: TimeValue[] = [
759
+ '09:00', '09:45', '10:30', '11:15',
760
+ '13:00', '13:45', '14:30', '15:15', '16:00',
761
+ ];
762
+
763
+ function ControlledComboboxTimeInputExample() {
764
+ const [value, setValue] = useState<TimeValue | ''>('13:00');
765
+
766
+ return (
767
+ <TimeInput
768
+ options={LESSON_PERIODS}
769
+ value={value}
770
+ onValueChange={v => setValue(v as TimeValue | '')}
771
+ id="lesson-controlled"
772
+ name="lesson-controlled"
773
+ placeholder="Select lesson period..."
774
+ aria-label="Lesson period"
775
+ />
776
+ );
777
+ }
778
+
779
+ export default ControlledComboboxTimeInputExample;
780
+ `.trim(),
781
+ },
782
+ },
783
+ },
784
+ render: () => _jsx(ControlledComboboxTemplate, {}),
785
+ }, 'A controlled combobox TimeInput pre-selects the afternoon session ("13:00") via the `value` prop. `onValueChange` keeps React state in sync whenever the user picks a different period.');
786
+ // ---------------------------------------------------------------------------
787
+ // Stories — FormField integration
788
+ // ---------------------------------------------------------------------------
789
+ export const InFormField = withDescription({
790
+ parameters: {
791
+ controls: { disable: true },
792
+ docs: {
793
+ source: {
794
+ language: 'tsx',
795
+ code: `
796
+ import { FormField } from '@arbor-education/design-system.components';
797
+
798
+ function InFormFieldExample() {
799
+ return (
800
+ <div style={{ maxWidth: '320px' }}>
801
+ <FormField
802
+ label="Lesson start time"
803
+ id="lesson-start"
804
+ inputType="time"
805
+ inputProps={{ name: 'lesson-start', defaultValue: '09:00' }}
806
+ />
807
+ </div>
808
+ );
809
+ }
810
+
811
+ export default InFormFieldExample;
812
+ `.trim(),
813
+ },
814
+ },
815
+ },
816
+ render: () => _jsx(InFormFieldTemplate, {}),
817
+ }, 'The recommended usage pattern — `<FormField inputType="time">` wires the accessible `<label>`, `aria-describedby`, `hasError`, and `aria-invalid` automatically. No manual prop threading required.');
818
+ export const InFormFieldWithError = withDescription({
819
+ parameters: {
820
+ controls: { disable: true },
821
+ docs: {
822
+ source: {
823
+ language: 'tsx',
824
+ code: `
825
+ import { FormField } from '@arbor-education/design-system.components';
826
+
827
+ function InFormFieldWithErrorExample() {
828
+ return (
829
+ <div style={{ maxWidth: '320px' }}>
830
+ <FormField
831
+ label="Registration start time"
832
+ id="registration-time"
833
+ inputType="time"
834
+ errorText="Please enter a valid registration time."
835
+ inputProps={{ name: 'registration-time' }}
836
+ />
837
+ </div>
838
+ );
839
+ }
840
+
841
+ export default InFormFieldWithErrorExample;
842
+ `.trim(),
843
+ },
844
+ },
845
+ },
846
+ render: () => _jsx(InFormFieldWithErrorTemplate, {}),
847
+ }, 'Setting `errorText` on `<FormField>` automatically applies error styling to the input, renders the error message, sets `aria-invalid`, and links `aria-describedby` to the error element. No extra props needed on `TimeInput` itself.');
848
+ export const InFormFieldCombobox = withDescription({
849
+ parameters: {
850
+ controls: { disable: true },
851
+ docs: {
852
+ source: {
853
+ language: 'tsx',
854
+ code: `
855
+ import { useState } from 'react';
856
+ import { FormField, type TimeValue } from '@arbor-education/design-system.components';
857
+
858
+ const LESSON_PERIODS: TimeValue[] = [
859
+ '09:00', '09:45', '10:30', '11:15',
860
+ '13:00', '13:45', '14:30', '15:15', '16:00',
861
+ ];
862
+
863
+ function InFormFieldComboboxExample() {
864
+ const [value, setValue] = useState<TimeValue | ''>('');
865
+
866
+ return (
867
+ <div style={{ maxWidth: '320px' }}>
868
+ <FormField
869
+ label="Lesson period"
870
+ id="lesson-period"
871
+ inputType="time"
872
+ inputProps={{
873
+ name: 'lesson-period',
874
+ options: LESSON_PERIODS,
875
+ value,
876
+ onValueChange: v => setValue(v as TimeValue | ''),
877
+ placeholder: 'Select a lesson period...',
878
+ }}
879
+ />
880
+ </div>
881
+ );
882
+ }
883
+
884
+ export default InFormFieldComboboxExample;
885
+ `.trim(),
886
+ },
887
+ },
130
888
  },
131
- }, 'Uncontrolled native usage using `defaultValue`. This is useful when the parent does not need to drive the current value after initial render.');
889
+ render: () => _jsx(InFormFieldComboboxTemplate, {}),
890
+ }, 'Combobox mode works seamlessly inside `<FormField>`. Pass `options` (and any combobox-mode props) via `inputProps`. The label, ARIA wiring, and error handling are all handled identically to native mode.');
132
891
  //# sourceMappingURL=TimeInput.stories.js.map