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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (304) hide show
  1. package/.gather/skills/write-stories/SKILL.md +207 -271
  2. package/.storybook/preview.ts +5 -0
  3. package/CHANGELOG.md +23 -0
  4. package/README.md +8 -0
  5. package/component-library.md +144 -13
  6. package/dist/components/articleCard/ArticleCard.stories.d.ts +137 -11
  7. package/dist/components/articleCard/ArticleCard.stories.d.ts.map +1 -1
  8. package/dist/components/articleCard/ArticleCard.stories.js +358 -91
  9. package/dist/components/articleCard/ArticleCard.stories.js.map +1 -1
  10. package/dist/components/avatar/Avatar.stories.d.ts +6 -6
  11. package/dist/components/avatar/Avatar.stories.d.ts.map +1 -1
  12. package/dist/components/avatar/Avatar.stories.js +393 -49
  13. package/dist/components/avatar/Avatar.stories.js.map +1 -1
  14. package/dist/components/avatarGroup/AvatarGroup.stories.d.ts +9 -7
  15. package/dist/components/avatarGroup/AvatarGroup.stories.d.ts.map +1 -1
  16. package/dist/components/avatarGroup/AvatarGroup.stories.js +688 -65
  17. package/dist/components/avatarGroup/AvatarGroup.stories.js.map +1 -1
  18. package/dist/components/banner/Banner.stories.d.ts.map +1 -1
  19. package/dist/components/banner/Banner.stories.js +7 -3
  20. package/dist/components/banner/Banner.stories.js.map +1 -1
  21. package/dist/components/card/Card.stories.d.ts +105 -4
  22. package/dist/components/card/Card.stories.d.ts.map +1 -1
  23. package/dist/components/card/Card.stories.js +336 -18
  24. package/dist/components/card/Card.stories.js.map +1 -1
  25. package/dist/components/combobox/Combobox.stories.d.ts +134 -21
  26. package/dist/components/combobox/Combobox.stories.d.ts.map +1 -1
  27. package/dist/components/combobox/Combobox.stories.js +676 -175
  28. package/dist/components/combobox/Combobox.stories.js.map +1 -1
  29. package/dist/components/datePicker/DatePicker.stories.d.ts +119 -27
  30. package/dist/components/datePicker/DatePicker.stories.d.ts.map +1 -1
  31. package/dist/components/datePicker/DatePicker.stories.js +575 -47
  32. package/dist/components/datePicker/DatePicker.stories.js.map +1 -1
  33. package/dist/components/dateTimePicker/DateTimePicker.stories.d.ts +155 -39
  34. package/dist/components/dateTimePicker/DateTimePicker.stories.d.ts.map +1 -1
  35. package/dist/components/dateTimePicker/DateTimePicker.stories.js +674 -103
  36. package/dist/components/dateTimePicker/DateTimePicker.stories.js.map +1 -1
  37. package/dist/components/editableText/EditableText.stories.d.ts +53 -12
  38. package/dist/components/editableText/EditableText.stories.d.ts.map +1 -1
  39. package/dist/components/editableText/EditableText.stories.js +401 -64
  40. package/dist/components/editableText/EditableText.stories.js.map +1 -1
  41. package/dist/components/formField/FormField.d.ts +4 -0
  42. package/dist/components/formField/FormField.d.ts.map +1 -1
  43. package/dist/components/formField/FormField.js +2 -1
  44. package/dist/components/formField/FormField.js.map +1 -1
  45. package/dist/components/formField/FormField.test.js +5 -0
  46. package/dist/components/formField/FormField.test.js.map +1 -1
  47. package/dist/components/formField/fieldset/Fieldset.stories.d.ts +56 -4
  48. package/dist/components/formField/fieldset/Fieldset.stories.d.ts.map +1 -1
  49. package/dist/components/formField/fieldset/Fieldset.stories.js +534 -28
  50. package/dist/components/formField/fieldset/Fieldset.stories.js.map +1 -1
  51. package/dist/components/formField/inputs/checkbox/CheckboxGroup.d.ts +3 -1
  52. package/dist/components/formField/inputs/checkbox/CheckboxGroup.d.ts.map +1 -1
  53. package/dist/components/formField/inputs/checkbox/CheckboxInput.js +1 -1
  54. package/dist/components/formField/inputs/checkbox/CheckboxInput.js.map +1 -1
  55. package/dist/components/formField/inputs/colourPickerDropdown/ColourPickerDropdown.stories.d.ts +95 -1
  56. package/dist/components/formField/inputs/colourPickerDropdown/ColourPickerDropdown.stories.d.ts.map +1 -1
  57. package/dist/components/formField/inputs/colourPickerDropdown/ColourPickerDropdown.stories.js +386 -9
  58. package/dist/components/formField/inputs/colourPickerDropdown/ColourPickerDropdown.stories.js.map +1 -1
  59. package/dist/components/formField/inputs/radio/RadioButtonGroup.d.ts +6 -2
  60. package/dist/components/formField/inputs/radio/RadioButtonGroup.d.ts.map +1 -1
  61. package/dist/components/formField/inputs/radio/RadioButtonGroup.js.map +1 -1
  62. package/dist/components/formField/inputs/radio/RadioButtonInput.stories.d.ts.map +1 -1
  63. package/dist/components/formField/inputs/radio/RadioButtonInput.stories.js +61 -49
  64. package/dist/components/formField/inputs/radio/RadioButtonInput.stories.js.map +1 -1
  65. package/dist/components/formField/inputs/selectDropdown/SelectDropdown.stories.d.ts +188 -166
  66. package/dist/components/formField/inputs/selectDropdown/SelectDropdown.stories.d.ts.map +1 -1
  67. package/dist/components/formField/inputs/selectDropdown/SelectDropdown.stories.js +821 -160
  68. package/dist/components/formField/inputs/selectDropdown/SelectDropdown.stories.js.map +1 -1
  69. package/dist/components/formField/inputs/time/TimeInput.stories.d.ts +176 -22
  70. package/dist/components/formField/inputs/time/TimeInput.stories.d.ts.map +1 -1
  71. package/dist/components/formField/inputs/time/TimeInput.stories.js +851 -92
  72. package/dist/components/formField/inputs/time/TimeInput.stories.js.map +1 -1
  73. package/dist/components/formField/label/Label.stories.d.ts +54 -5
  74. package/dist/components/formField/label/Label.stories.d.ts.map +1 -1
  75. package/dist/components/formField/label/Label.stories.js +238 -4
  76. package/dist/components/formField/label/Label.stories.js.map +1 -1
  77. package/dist/components/icoText/IcoText.stories.d.ts +32 -6
  78. package/dist/components/icoText/IcoText.stories.d.ts.map +1 -1
  79. package/dist/components/icoText/IcoText.stories.js +309 -14
  80. package/dist/components/icoText/IcoText.stories.js.map +1 -1
  81. package/dist/components/kpiCard/KPICard.stories.d.ts +100 -2
  82. package/dist/components/kpiCard/KPICard.stories.d.ts.map +1 -1
  83. package/dist/components/kpiCard/KPICard.stories.js +354 -10
  84. package/dist/components/kpiCard/KPICard.stories.js.map +1 -1
  85. package/dist/components/kvpList/KVPList.stories.d.ts +57 -4
  86. package/dist/components/kvpList/KVPList.stories.d.ts.map +1 -1
  87. package/dist/components/kvpList/KVPList.stories.js +403 -10
  88. package/dist/components/kvpList/KVPList.stories.js.map +1 -1
  89. package/dist/components/modal/Modal.stories.d.ts +113 -9
  90. package/dist/components/modal/Modal.stories.d.ts.map +1 -1
  91. package/dist/components/modal/Modal.stories.js +633 -13
  92. package/dist/components/modal/Modal.stories.js.map +1 -1
  93. package/dist/components/modal/modalManager/ModalManager.stories.d.ts +34 -10
  94. package/dist/components/modal/modalManager/ModalManager.stories.d.ts.map +1 -1
  95. package/dist/components/modal/modalManager/ModalManager.stories.js +463 -85
  96. package/dist/components/modal/modalManager/ModalManager.stories.js.map +1 -1
  97. package/dist/components/pill/Pill.d.ts.map +1 -1
  98. package/dist/components/pill/Pill.js +1 -1
  99. package/dist/components/pill/Pill.js.map +1 -1
  100. package/dist/components/pill/Pill.stories.d.ts.map +1 -1
  101. package/dist/components/pill/Pill.stories.js +11 -13
  102. package/dist/components/pill/Pill.stories.js.map +1 -1
  103. package/dist/components/row/Row.stories.d.ts +1 -2
  104. package/dist/components/row/Row.stories.d.ts.map +1 -1
  105. package/dist/components/row/Row.stories.js +360 -50
  106. package/dist/components/row/Row.stories.js.map +1 -1
  107. package/dist/components/searchBar/SearchBar.stories.d.ts +52 -4
  108. package/dist/components/searchBar/SearchBar.stories.d.ts.map +1 -1
  109. package/dist/components/searchBar/SearchBar.stories.js +428 -36
  110. package/dist/components/searchBar/SearchBar.stories.js.map +1 -1
  111. package/dist/components/section/Section.stories.d.ts +11 -41
  112. package/dist/components/section/Section.stories.d.ts.map +1 -1
  113. package/dist/components/section/Section.stories.js +494 -56
  114. package/dist/components/section/Section.stories.js.map +1 -1
  115. package/dist/components/singleUser/SingleUser.stories.d.ts +5 -4
  116. package/dist/components/singleUser/SingleUser.stories.d.ts.map +1 -1
  117. package/dist/components/singleUser/SingleUser.stories.js +303 -31
  118. package/dist/components/singleUser/SingleUser.stories.js.map +1 -1
  119. package/dist/components/slideoverManager/SlideoverManager.stories.d.ts +32 -11
  120. package/dist/components/slideoverManager/SlideoverManager.stories.d.ts.map +1 -1
  121. package/dist/components/slideoverManager/SlideoverManager.stories.js +380 -84
  122. package/dist/components/slideoverManager/SlideoverManager.stories.js.map +1 -1
  123. package/dist/components/table/DSDefaultColDef.d.ts.map +1 -1
  124. package/dist/components/table/DSDefaultColDef.js +4 -3
  125. package/dist/components/table/DSDefaultColDef.js.map +1 -1
  126. package/dist/components/table/Table.d.ts +6 -1
  127. package/dist/components/table/Table.d.ts.map +1 -1
  128. package/dist/components/table/Table.js +8 -3
  129. package/dist/components/table/Table.js.map +1 -1
  130. package/dist/components/table/Table.stories.d.ts +3 -0
  131. package/dist/components/table/Table.stories.d.ts.map +1 -1
  132. package/dist/components/table/Table.stories.js +384 -5
  133. package/dist/components/table/Table.stories.js.map +1 -1
  134. package/dist/components/table/Table.test.js +30 -0
  135. package/dist/components/table/Table.test.js.map +1 -1
  136. package/dist/components/table/TableFooter.stories.d.ts +49 -0
  137. package/dist/components/table/TableFooter.stories.d.ts.map +1 -0
  138. package/dist/components/table/TableFooter.stories.js +137 -0
  139. package/dist/components/table/TableFooter.stories.js.map +1 -0
  140. package/dist/components/table/TableHeader.stories.d.ts +93 -0
  141. package/dist/components/table/TableHeader.stories.d.ts.map +1 -0
  142. package/dist/components/table/TableHeader.stories.js +176 -0
  143. package/dist/components/table/TableHeader.stories.js.map +1 -0
  144. package/dist/components/table/cellEditors/DateCellEditor.stories.d.ts +44 -0
  145. package/dist/components/table/cellEditors/DateCellEditor.stories.d.ts.map +1 -0
  146. package/dist/components/table/cellEditors/DateCellEditor.stories.js +186 -0
  147. package/dist/components/table/cellEditors/DateCellEditor.stories.js.map +1 -0
  148. package/dist/components/table/cellRenderers/BooleanCellRenderer.stories.d.ts +40 -0
  149. package/dist/components/table/cellRenderers/BooleanCellRenderer.stories.d.ts.map +1 -0
  150. package/dist/components/table/cellRenderers/BooleanCellRenderer.stories.js +209 -0
  151. package/dist/components/table/cellRenderers/BooleanCellRenderer.stories.js.map +1 -0
  152. package/dist/components/table/cellRenderers/ButtonCellRenderer.stories.d.ts +48 -0
  153. package/dist/components/table/cellRenderers/ButtonCellRenderer.stories.d.ts.map +1 -0
  154. package/dist/components/table/cellRenderers/ButtonCellRenderer.stories.js +244 -0
  155. package/dist/components/table/cellRenderers/ButtonCellRenderer.stories.js.map +1 -0
  156. package/dist/components/table/cellRenderers/CheckboxCellRenderer.d.ts.map +1 -1
  157. package/dist/components/table/cellRenderers/CheckboxCellRenderer.js +3 -1
  158. package/dist/components/table/cellRenderers/CheckboxCellRenderer.js.map +1 -1
  159. package/dist/components/table/cellRenderers/CheckboxCellRenderer.stories.d.ts +64 -0
  160. package/dist/components/table/cellRenderers/CheckboxCellRenderer.stories.d.ts.map +1 -0
  161. package/dist/components/table/cellRenderers/CheckboxCellRenderer.stories.js +241 -0
  162. package/dist/components/table/cellRenderers/CheckboxCellRenderer.stories.js.map +1 -0
  163. package/dist/components/table/cellRenderers/DefaultCellRenderer.stories.d.ts +55 -0
  164. package/dist/components/table/cellRenderers/DefaultCellRenderer.stories.d.ts.map +1 -0
  165. package/dist/components/table/cellRenderers/DefaultCellRenderer.stories.js +245 -0
  166. package/dist/components/table/cellRenderers/DefaultCellRenderer.stories.js.map +1 -0
  167. package/dist/components/table/cellRenderers/InlineTextCellRenderer.stories.d.ts +67 -0
  168. package/dist/components/table/cellRenderers/InlineTextCellRenderer.stories.d.ts.map +1 -0
  169. package/dist/components/table/cellRenderers/InlineTextCellRenderer.stories.js +221 -0
  170. package/dist/components/table/cellRenderers/InlineTextCellRenderer.stories.js.map +1 -0
  171. package/dist/components/table/cellRenderers/SelectDropdownCellRenderer.stories.d.ts +75 -0
  172. package/dist/components/table/cellRenderers/SelectDropdownCellRenderer.stories.d.ts.map +1 -0
  173. package/dist/components/table/cellRenderers/SelectDropdownCellRenderer.stories.js +270 -0
  174. package/dist/components/table/cellRenderers/SelectDropdownCellRenderer.stories.js.map +1 -0
  175. package/dist/components/table/columnFilters/BooleanFilter.stories.d.ts +57 -0
  176. package/dist/components/table/columnFilters/BooleanFilter.stories.d.ts.map +1 -0
  177. package/dist/components/table/columnFilters/BooleanFilter.stories.js +198 -0
  178. package/dist/components/table/columnFilters/BooleanFilter.stories.js.map +1 -0
  179. package/dist/components/table/columnFilters/TimeFilter.stories.d.ts +58 -0
  180. package/dist/components/table/columnFilters/TimeFilter.stories.d.ts.map +1 -0
  181. package/dist/components/table/columnFilters/TimeFilter.stories.js +207 -0
  182. package/dist/components/table/columnFilters/TimeFilter.stories.js.map +1 -0
  183. package/dist/components/table/pagination/PaginationPanel.stories.d.ts +113 -0
  184. package/dist/components/table/pagination/PaginationPanel.stories.d.ts.map +1 -0
  185. package/dist/components/table/pagination/PaginationPanel.stories.js +272 -0
  186. package/dist/components/table/pagination/PaginationPanel.stories.js.map +1 -0
  187. package/dist/components/table/tableControls/HideColumnsDropdown.d.ts.map +1 -1
  188. package/dist/components/table/tableControls/HideColumnsDropdown.js +9 -3
  189. package/dist/components/table/tableControls/HideColumnsDropdown.js.map +1 -1
  190. package/dist/components/table/tableControls/TableControls.stories.d.ts +151 -0
  191. package/dist/components/table/tableControls/TableControls.stories.d.ts.map +1 -0
  192. package/dist/components/table/tableControls/TableControls.stories.js +356 -0
  193. package/dist/components/table/tableControls/TableControls.stories.js.map +1 -0
  194. package/dist/components/table/tableControls/TableSettingsDropdown.d.ts +27 -1
  195. package/dist/components/table/tableControls/TableSettingsDropdown.d.ts.map +1 -1
  196. package/dist/components/table/tableControls/TableSettingsDropdown.js +53 -26
  197. package/dist/components/table/tableControls/TableSettingsDropdown.js.map +1 -1
  198. package/dist/components/table/tableControls/TableSettingsDropdown.test.d.ts +2 -0
  199. package/dist/components/table/tableControls/TableSettingsDropdown.test.d.ts.map +1 -0
  200. package/dist/components/table/tableControls/TableSettingsDropdown.test.js +178 -0
  201. package/dist/components/table/tableControls/TableSettingsDropdown.test.js.map +1 -0
  202. package/dist/components/tabs/Tabs.stories.d.ts +22 -4
  203. package/dist/components/tabs/Tabs.stories.d.ts.map +1 -1
  204. package/dist/components/tabs/Tabs.stories.js +398 -22
  205. package/dist/components/tabs/Tabs.stories.js.map +1 -1
  206. package/dist/components/tabs/TabsItem.stories.d.ts +54 -1
  207. package/dist/components/tabs/TabsItem.stories.d.ts.map +1 -1
  208. package/dist/components/tabs/TabsItem.stories.js +61 -9
  209. package/dist/components/tabs/TabsItem.stories.js.map +1 -1
  210. package/dist/components/toast/Toast.stories.d.ts +103 -10
  211. package/dist/components/toast/Toast.stories.d.ts.map +1 -1
  212. package/dist/components/toast/Toast.stories.js +409 -47
  213. package/dist/components/toast/Toast.stories.js.map +1 -1
  214. package/dist/components/toggle/Toggle.stories.d.ts +61 -46
  215. package/dist/components/toggle/Toggle.stories.d.ts.map +1 -1
  216. package/dist/components/toggle/Toggle.stories.js +311 -122
  217. package/dist/components/toggle/Toggle.stories.js.map +1 -1
  218. package/dist/components/tooltip/Tooltip.stories.d.ts +78 -6
  219. package/dist/components/tooltip/Tooltip.stories.d.ts.map +1 -1
  220. package/dist/components/tooltip/Tooltip.stories.js +413 -7
  221. package/dist/components/tooltip/Tooltip.stories.js.map +1 -1
  222. package/dist/components/tooltip/TooltipWrapper.stories.d.ts +71 -7
  223. package/dist/components/tooltip/TooltipWrapper.stories.d.ts.map +1 -1
  224. package/dist/components/tooltip/TooltipWrapper.stories.js +238 -10
  225. package/dist/components/tooltip/TooltipWrapper.stories.js.map +1 -1
  226. package/dist/index.css +8 -0
  227. package/dist/index.css.map +1 -1
  228. package/dist/utils/PopupParentContext.stories.d.ts +17 -0
  229. package/dist/utils/PopupParentContext.stories.d.ts.map +1 -0
  230. package/dist/utils/PopupParentContext.stories.js +266 -0
  231. package/dist/utils/PopupParentContext.stories.js.map +1 -0
  232. package/dist/utils/getDefaultPopupParent.d.ts.map +1 -1
  233. package/dist/utils/getDefaultPopupParent.js +6 -0
  234. package/dist/utils/getDefaultPopupParent.js.map +1 -1
  235. package/package.json +1 -1
  236. package/src/components/articleCard/ArticleCard.stories.tsx +524 -111
  237. package/src/components/avatar/Avatar.stories.tsx +504 -59
  238. package/src/components/avatarGroup/AvatarGroup.stories.tsx +977 -175
  239. package/src/components/banner/Banner.stories.tsx +7 -3
  240. package/src/components/card/Card.stories.tsx +466 -36
  241. package/src/components/combobox/Combobox.stories.tsx +867 -260
  242. package/src/components/datePicker/DatePicker.stories.tsx +777 -60
  243. package/src/components/dateTimePicker/DateTimePicker.stories.tsx +910 -132
  244. package/src/components/editableText/EditableText.stories.tsx +567 -91
  245. package/src/components/formField/FormField.test.tsx +6 -0
  246. package/src/components/formField/FormField.tsx +5 -0
  247. package/src/components/formField/fieldset/Fieldset.stories.tsx +761 -51
  248. package/src/components/formField/inputs/checkbox/CheckboxGroup.tsx +1 -1
  249. package/src/components/formField/inputs/checkbox/CheckboxInput.tsx +1 -1
  250. package/src/components/formField/inputs/colourPickerDropdown/ColourPickerDropdown.stories.tsx +504 -11
  251. package/src/components/formField/inputs/radio/RadioButtonGroup.tsx +17 -4
  252. package/src/components/formField/inputs/radio/RadioButtonInput.stories.tsx +71 -59
  253. package/src/components/formField/inputs/selectDropdown/SelectDropdown.stories.tsx +1079 -168
  254. package/src/components/formField/inputs/time/TimeInput.stories.tsx +1140 -104
  255. package/src/components/formField/label/Label.stories.tsx +317 -8
  256. package/src/components/icoText/IcoText.stories.tsx +442 -31
  257. package/src/components/kpiCard/KPICard.stories.tsx +475 -30
  258. package/src/components/kvpList/KVPList.stories.tsx +593 -26
  259. package/src/components/modal/Modal.stories.tsx +963 -26
  260. package/src/components/modal/modalManager/ModalManager.stories.tsx +612 -454
  261. package/src/components/pill/Pill.stories.tsx +11 -13
  262. package/src/components/pill/Pill.tsx +1 -0
  263. package/src/components/row/Row.stories.tsx +474 -58
  264. package/src/components/searchBar/SearchBar.stories.tsx +570 -38
  265. package/src/components/section/Section.stories.tsx +723 -70
  266. package/src/components/singleUser/SingleUser.stories.tsx +393 -34
  267. package/src/components/slideoverManager/SlideoverManager.stories.tsx +572 -342
  268. package/src/components/table/DSDefaultColDef.ts +25 -5
  269. package/src/components/table/Table.stories.tsx +460 -5
  270. package/src/components/table/Table.test.tsx +53 -0
  271. package/src/components/table/Table.tsx +9 -2
  272. package/src/components/table/TableFooter.stories.tsx +196 -0
  273. package/src/components/table/TableHeader.stories.tsx +251 -0
  274. package/src/components/table/cellEditors/DateCellEditor.stories.tsx +245 -0
  275. package/src/components/table/cellRenderers/BooleanCellRenderer.stories.tsx +278 -0
  276. package/src/components/table/cellRenderers/ButtonCellRenderer.stories.tsx +333 -0
  277. package/src/components/table/cellRenderers/CheckboxCellRenderer.stories.tsx +337 -0
  278. package/src/components/table/cellRenderers/CheckboxCellRenderer.tsx +5 -1
  279. package/src/components/table/cellRenderers/DefaultCellRenderer.stories.tsx +342 -0
  280. package/src/components/table/cellRenderers/InlineTextCellRenderer.stories.tsx +292 -0
  281. package/src/components/table/cellRenderers/SelectDropdownCellRenderer.stories.tsx +369 -0
  282. package/src/components/table/columnFilters/BooleanFilter.stories.tsx +268 -0
  283. package/src/components/table/columnFilters/TimeFilter.stories.tsx +281 -0
  284. package/src/components/table/pagination/PaginationPanel.stories.tsx +327 -0
  285. package/src/components/table/tableControls/HideColumnsDropdown.tsx +11 -2
  286. package/src/components/table/tableControls/TableControls.stories.tsx +415 -0
  287. package/src/components/table/tableControls/TableSettingsDropdown.test.tsx +207 -0
  288. package/src/components/table/tableControls/TableSettingsDropdown.tsx +103 -39
  289. package/src/components/tabs/Tabs.stories.tsx +540 -60
  290. package/src/components/tabs/TabsItem.stories.tsx +82 -8
  291. package/src/components/toast/Toast.stories.tsx +539 -77
  292. package/src/components/toggle/Toggle.stories.tsx +371 -135
  293. package/src/components/tooltip/Tooltip.stories.tsx +606 -15
  294. package/src/components/tooltip/TooltipWrapper.stories.tsx +348 -12
  295. package/src/docs/Contributing.mdx +241 -0
  296. package/src/docs/UsingComponents.mdx +93 -0
  297. package/src/docs/Welcome.mdx +68 -0
  298. package/src/global.scss +7 -0
  299. package/src/utils/PopupParentContext.stories.tsx +367 -0
  300. package/src/utils/getDefaultPopupParent.ts +6 -0
  301. package/.ralph/storybook-upgrade/knowledge.md +0 -308
  302. package/.ralph/storybook-upgrade/prd.json +0 -777
  303. package/.ralph/storybook-upgrade/progress.md +0 -342
  304. package/src/components/table/TableWIP.mdx +0 -3
@@ -1,40 +1,228 @@
1
- import { useEffect, useState } from 'react';
2
1
  import type { Meta, StoryObj } from '@storybook/react-vite';
2
+ import {
3
+ Controls,
4
+ Heading as DocHeading,
5
+ Markdown,
6
+ Primary as DocPrimary,
7
+ Stories,
8
+ Subtitle,
9
+ Title,
10
+ } from '@storybook/addon-docs/blocks';
11
+ import { useEffect, useState } from 'react';
3
12
  import { SearchBar } from './SearchBar';
4
13
 
14
+ // ---------------------------------------------------------------------------
15
+ // Docs page content
16
+ // ---------------------------------------------------------------------------
17
+
18
+ const DESCRIPTION_INTRO = [
19
+ 'SearchBar is a space-efficient search input that starts collapsed — showing only a search icon or a',
20
+ 'short label — and expands into a full text input when clicked. It is designed for use in navigation',
21
+ 'bars, table headers, and other space-constrained contexts where search is a secondary action.',
22
+ ].join('\n');
23
+
24
+ const USAGE_GUIDANCE = [
25
+ '### When to use',
26
+ '',
27
+ '- **Table headers** — filter a data table without dominating the header layout',
28
+ '- **Navigation bars** — compact global search that expands on demand',
29
+ '- **Space-constrained sidebars** — secondary search that should not distract from primary content',
30
+ '- **Any context where search is optional** — consumers can click to open or ignore it entirely',
31
+ '',
32
+ '---',
33
+ '',
34
+ '### When NOT to use',
35
+ '',
36
+ '| Situation | Use instead |',
37
+ '|---|---|',
38
+ '| Search is the primary action on the page | A full-width `TextInput` with a visible label — always-visible inputs reduce cognitive load when search is the main task |',
39
+ '| Search drives a list of suggested results | [`Combobox`](?path=/docs/components-combobox--docs) — it handles filtering + suggestions + keyboard selection |',
40
+ '| You need a labelled form field with validation | [`FormField`](?path=/docs/components-formfield--docs) — SearchBar has no label or error state |',
41
+ '| The input must always be visible | Set `alwaysOpen` to `true` — or use a plain `TextInput` if you also need a label |',
42
+ ].join('\n');
43
+
44
+ const DEVELOPER_NOTES = [
45
+ '### Critical usage patterns',
46
+ '',
47
+ '**`searchValue` and `setSearchValue` are a controlled pair.** Always pass both. Passing `searchValue`',
48
+ 'without `setSearchValue` creates a frozen input — the user can see the value but typing has no effect.',
49
+ 'Passing `setSearchValue` without `searchValue` will work but leaves state management up to an',
50
+ 'anonymous internal variable, which makes reset and restore difficult.',
51
+ '',
52
+ '**`alwaysOpen` removes the clear button entirely.** When `alwaysOpen` is `true`, the component never',
53
+ 'shows the collapsed state AND never renders the clear (×) button. There is no built-in way for the',
54
+ 'user to clear the input. If you use `alwaysOpen`, add an external "Reset" button or clear link in your',
55
+ 'UI and call `setSearchValue(\'\')` programmatically.',
56
+ '',
57
+ '**Non-empty `searchValue` forces the expanded state on mount.** If your page restores filter state',
58
+ 'from a URL parameter or localStorage, passing a pre-filled `searchValue` will start the component',
59
+ 'expanded — no flash of the collapsed button. This is intentional behaviour, not a side-effect.',
60
+ '',
61
+ '**`placeholderText` and `hoverText` label the COLLAPSED BUTTON, not the `<input>`.** They appear',
62
+ 'only in the inactive state. The expanded `<input>` has no visible placeholder — it uses',
63
+ '`aria-label="Search input"` for accessibility.',
64
+ '',
65
+ '**`hoverText` swaps in on hover of the collapsed button.** When the user mouses away, the component',
66
+ 'reverts to `placeholderText` (or an icon-only state if `placeholderText` is not set). This is a',
67
+ 'hover-only affordance — there is no keyboard-equivalent of the hover swap.',
68
+ '',
69
+ '**Theming.** SearchBar is designed for brand-coloured and dark backgrounds. Its default background,',
70
+ 'icon, and text colours use the `--search-global-*` token family. If you are placing it on a white or',
71
+ 'light-grey background, override these tokens to ensure sufficient contrast:',
72
+ '',
73
+ '| Token | Controls |',
74
+ '|---|---|',
75
+ '| `--search-global-default-color-background` | Resting background |',
76
+ '| `--search-global-hover-color-background` | Hover background |',
77
+ '| `--search-global-focus-color-background` | Expanded/focused background |',
78
+ '| `--search-global-default-color-icon` | Search icon colour at rest |',
79
+ '| `--search-global-hover-color-icon` | Search icon colour on hover |',
80
+ '| `--search-global-focus-color-icon` | Search icon colour when active |',
81
+ '| `--search-global-default-color-text` | Placeholder and label text colour |',
82
+ '| `--search-global-radius` | Border radius of the component |',
83
+ '',
84
+ '---',
85
+ '',
86
+ '### Accessibility',
87
+ '',
88
+ '- The collapsed state renders a `<button>` — fully keyboard-operable and screen-reader-accessible',
89
+ '- The expanded `<input>` has `aria-label="Search input"` — do not add a visible `<label>` externally',
90
+ '- The clear button has `aria-label="Clear search"` and responds to `Enter` and `Space` in addition to click',
91
+ '- The search icon in the expanded state has `screenReaderText="Search icon"` rendered as a visually hidden span',
92
+ '- There is no `role="search"` landmark on the component — wrap in `<form role="search">` if you need a search landmark',
93
+ ].join('\n');
94
+
95
+ const RELATED_COMPONENTS = [
96
+ '## Related components',
97
+ '',
98
+ '[Combobox](?path=/docs/components-combobox--docs) · [Table](?path=/docs/components-table--docs) · [FormField](?path=/docs/components-formfield--docs)',
99
+ ].join('\n');
100
+
101
+ const PROPS_INTRO = 'The preview below is wired to the **Controls** panel — tweak any prop to see the story update in real time.';
102
+
103
+ function SearchBarDocsPage() {
104
+ return (
105
+ <>
106
+ <Title />
107
+ <Subtitle />
108
+ <Markdown>{DESCRIPTION_INTRO}</Markdown>
109
+ <DocHeading>Interactive example</DocHeading>
110
+ <Markdown>{PROPS_INTRO}</Markdown>
111
+ <DocPrimary />
112
+ <Controls />
113
+ <DocHeading>Usage guidance</DocHeading>
114
+ <Markdown>{USAGE_GUIDANCE}</Markdown>
115
+ <DocHeading>Developer notes</DocHeading>
116
+ <Markdown>{DEVELOPER_NOTES}</Markdown>
117
+ <DocHeading>Examples</DocHeading>
118
+ <Stories title="" />
119
+ <Markdown>{RELATED_COMPONENTS}</Markdown>
120
+ </>
121
+ );
122
+ }
123
+
124
+ // ---------------------------------------------------------------------------
125
+ // Meta
126
+ // ---------------------------------------------------------------------------
127
+
5
128
  const meta = {
6
129
  title: 'Components/SearchBar',
7
130
  component: SearchBar,
131
+ tags: ['autodocs'],
8
132
  parameters: {
9
133
  layout: 'padded',
134
+ docs: {
135
+ page: SearchBarDocsPage,
136
+ },
10
137
  },
11
- tags: ['autodocs'],
12
138
  argTypes: {
13
139
  searchValue: {
14
140
  control: 'text',
15
- description: 'The current search input value',
141
+ description: [
142
+ 'The controlled value of the search input.',
143
+ 'If non-empty on mount, the component starts in the expanded state.',
144
+ 'Always pair with `setSearchValue`.',
145
+ ].join(' '),
146
+ table: {
147
+ type: { summary: 'string' },
148
+ defaultValue: { summary: 'undefined' },
149
+ },
16
150
  },
17
151
  setSearchValue: {
18
- action: 'search value changed',
19
- description: 'Callback fired when the search input changes',
152
+ control: false,
153
+ action: 'setSearchValue',
154
+ description: [
155
+ 'Callback fired when the search input changes and when the clear button is clicked.',
156
+ 'Always pair with `searchValue`. Omitting this creates a frozen, non-interactive input.',
157
+ ].join(' '),
158
+ table: {
159
+ type: { summary: '(searchValue: string) => void' },
160
+ defaultValue: { summary: 'undefined' },
161
+ },
20
162
  },
21
163
  placeholderText: {
22
164
  control: 'text',
23
- description: 'Text displayed in the inactive/collapsed state',
165
+ description: [
166
+ 'Label text displayed inside the collapsed (inactive) button beside the search icon.',
167
+ 'This is NOT the HTML input placeholder — it labels the collapsed button state only.',
168
+ 'When absent, the collapsed button shows an icon only.',
169
+ ].join(' '),
170
+ table: {
171
+ type: { summary: 'string' },
172
+ defaultValue: { summary: 'undefined' },
173
+ },
24
174
  },
25
175
  hoverText: {
26
176
  control: 'text',
27
- description: 'Text displayed when hovering the inactive state',
177
+ description: [
178
+ 'Text shown in the collapsed button when the user hovers over it.',
179
+ 'Replaces `placeholderText` on hover and reverts on mouse-leave.',
180
+ 'Only active in the collapsed (inactive) state — has no effect when expanded.',
181
+ ].join(' '),
182
+ table: {
183
+ type: { summary: 'string' },
184
+ defaultValue: { summary: 'undefined' },
185
+ },
28
186
  },
29
187
  alwaysOpen: {
30
188
  control: 'boolean',
31
- description: 'Whether the search bar should be always open',
189
+ description: [
190
+ 'When `true`, the component renders permanently expanded and the clear (×) button is hidden.',
191
+ 'The collapsed button state is never shown. The consumer is responsible for clearing the',
192
+ 'search value externally — there is no built-in clear affordance.',
193
+ ].join(' '),
194
+ table: {
195
+ type: { summary: 'boolean' },
196
+ defaultValue: { summary: 'false' },
197
+ },
32
198
  },
33
199
  },
34
200
  } satisfies Meta<typeof SearchBar>;
35
201
 
36
202
  export default meta;
37
- type Story = StoryObj<typeof meta>;
203
+ type Story = StoryObj<typeof SearchBar>;
204
+
205
+ // ---------------------------------------------------------------------------
206
+ // Helper: attach a per-story description to docs
207
+ // ---------------------------------------------------------------------------
208
+
209
+ const withDescription = (story: Story, description: string): Story => ({
210
+ ...story,
211
+ parameters: {
212
+ ...story.parameters,
213
+ docs: {
214
+ ...story.parameters?.docs,
215
+ description: {
216
+ story: description,
217
+ },
218
+ },
219
+ },
220
+ });
221
+
222
+ // ---------------------------------------------------------------------------
223
+ // Template components — all stories need local state because SearchBar is
224
+ // a fully controlled component. Without useState the input is frozen.
225
+ // ---------------------------------------------------------------------------
38
226
 
39
227
  const InteractiveSearchBar = (args: React.ComponentProps<typeof SearchBar>) => {
40
228
  const [value, setValue] = useState(args.searchValue ?? '');
@@ -44,43 +232,387 @@ const InteractiveSearchBar = (args: React.ComponentProps<typeof SearchBar>) => {
44
232
  return <SearchBar {...args} searchValue={value} setSearchValue={setValue} />;
45
233
  };
46
234
 
47
- export const Default: Story = {
48
- render: args => <InteractiveSearchBar {...args} />,
235
+ const WithPlaceholderTextTemplate = () => {
236
+ const [value, setValue] = useState('');
237
+ return (
238
+ <div style={{ background: 'var(--color-brand-600)', padding: 'var(--spacing-xlarge)' }}>
239
+ <SearchBar
240
+ searchValue={value}
241
+ setSearchValue={setValue}
242
+ placeholderText="Search pupils"
243
+ />
244
+ </div>
245
+ );
49
246
  };
50
247
 
51
- export const WithPlaceholder: Story = {
52
- render: args => <InteractiveSearchBar {...args} />,
53
- args: {
54
- placeholderText: 'Search...',
55
- },
248
+ const WithHoverTextTemplate = () => {
249
+ const [value, setValue] = useState('');
250
+ return (
251
+ <div style={{ background: 'var(--color-brand-600)', padding: 'var(--spacing-xlarge)' }}>
252
+ <SearchBar
253
+ searchValue={value}
254
+ setSearchValue={setValue}
255
+ placeholderText="Search pupils"
256
+ hoverText="Search all pupils and staff"
257
+ />
258
+ </div>
259
+ );
56
260
  };
57
261
 
58
- export const WithHoverText: Story = {
59
- render: args => <InteractiveSearchBar {...args} />,
60
- args: {
61
- placeholderText: 'Search...',
62
- hoverText: 'Start searching',
63
- },
262
+ const WithHoverTextOnlyTemplate = () => {
263
+ const [value, setValue] = useState('');
264
+ return (
265
+ <div style={{ background: 'var(--color-brand-600)', padding: 'var(--spacing-xlarge)' }}>
266
+ <SearchBar
267
+ searchValue={value}
268
+ setSearchValue={setValue}
269
+ hoverText="Search pupils"
270
+ />
271
+ </div>
272
+ );
64
273
  };
65
274
 
66
- export const WithHoverTextNoPlaceholder: Story = {
67
- render: args => <InteractiveSearchBar {...args} />,
68
- args: {
69
- hoverText: 'Start searching',
70
- },
275
+ const WithSearchValueTemplate = () => {
276
+ const [value, setValue] = useState('Emily Johnson');
277
+ return (
278
+ <div style={{ background: 'var(--color-brand-600)', padding: 'var(--spacing-xlarge)' }}>
279
+ <SearchBar
280
+ searchValue={value}
281
+ setSearchValue={setValue}
282
+ placeholderText="Search pupils"
283
+ />
284
+ </div>
285
+ );
71
286
  };
72
287
 
73
- export const WithSearchValue: Story = {
74
- render: args => <InteractiveSearchBar {...args} />,
75
- args: {
76
- searchValue: 'Jacob Black',
77
- placeholderText: 'Search...',
78
- },
288
+ const AlwaysOpenTemplate = () => {
289
+ const [value, setValue] = useState('');
290
+ return (
291
+ <div style={{ background: 'var(--color-brand-600)', padding: 'var(--spacing-xlarge)', display: 'flex', flexDirection: 'column', gap: 'var(--spacing-large)' }}>
292
+ <SearchBar
293
+ searchValue={value}
294
+ setSearchValue={setValue}
295
+ alwaysOpen
296
+ />
297
+ <p className="ds-text" style={{ margin: 0, color: 'var(--color-mono-white)', fontSize: '0.875rem' }}>
298
+ Note: no clear button — the × icon is absent when alwaysOpen is true.
299
+ </p>
300
+ </div>
301
+ );
79
302
  };
80
303
 
81
- export const AlwaysOpen: Story = {
82
- render: args => <InteractiveSearchBar {...args} />,
83
- args: {
84
- alwaysOpen: true,
85
- },
304
+ const AlwaysOpenWithValueTemplate = () => {
305
+ const [value, setValue] = useState('Year 8');
306
+ return (
307
+ <div style={{ background: 'var(--color-brand-600)', padding: 'var(--spacing-xlarge)', display: 'flex', flexDirection: 'column', gap: 'var(--spacing-large)' }}>
308
+ <SearchBar
309
+ searchValue={value}
310
+ setSearchValue={setValue}
311
+ alwaysOpen
312
+ />
313
+ <p className="ds-text" style={{ margin: 0, color: 'var(--color-mono-white)', fontSize: '0.875rem' }}>
314
+ Pre-filled value with no clear button — the consumer must provide external clearing.
315
+ </p>
316
+ </div>
317
+ );
318
+ };
319
+
320
+ const ControlledByParentTemplate = () => {
321
+ const [value, setValue] = useState('');
322
+ return (
323
+ <div style={{ background: 'var(--color-brand-600)', padding: 'var(--spacing-xlarge)', display: 'flex', flexDirection: 'column', gap: 'var(--spacing-large)' }}>
324
+ <SearchBar
325
+ searchValue={value}
326
+ setSearchValue={setValue}
327
+ placeholderText="Search reports"
328
+ />
329
+ <p className="ds-text" style={{ margin: 0, color: 'var(--color-mono-white)', fontSize: '0.875rem' }}>
330
+ {value
331
+ ? `Active filter: "${value}" — ${value.length} character${value.length === 1 ? '' : 's'}`
332
+ : 'No active filter — click the search icon and start typing'}
333
+ </p>
334
+ </div>
335
+ );
86
336
  };
337
+
338
+ // ---------------------------------------------------------------------------
339
+ // Stories
340
+ // ---------------------------------------------------------------------------
341
+
342
+ export const Default: Story = withDescription(
343
+ {
344
+ args: {
345
+ placeholderText: 'Search',
346
+ alwaysOpen: false,
347
+ },
348
+ render: args => (
349
+ <div style={{ background: 'var(--color-brand-600)', padding: 'var(--spacing-xlarge)' }}>
350
+ <InteractiveSearchBar {...args} />
351
+ </div>
352
+ ),
353
+ },
354
+ [
355
+ 'The interactive canvas — every prop is wired to the Controls panel below.',
356
+ 'Click the search icon to expand the input. The story background is brand-coloured to reflect the',
357
+ 'real-world context (navigation bars, table headers) where SearchBar is designed to be used.',
358
+ ].join(' '),
359
+ );
360
+
361
+ export const WithPlaceholderText: Story = withDescription(
362
+ {
363
+ render: WithPlaceholderTextTemplate,
364
+ parameters: {
365
+ controls: { disable: true },
366
+ docs: {
367
+ source: {
368
+ language: 'tsx',
369
+ code: `
370
+ import { useState } from 'react';
371
+ import { SearchBar } from '@arbor-education/design-system.components';
372
+
373
+ function PupilSearchExample() {
374
+ const [value, setValue] = useState('');
375
+ return (
376
+ <SearchBar
377
+ searchValue={value}
378
+ setSearchValue={setValue}
379
+ placeholderText="Search pupils"
380
+ />
381
+ );
382
+ }
383
+ export default PupilSearchExample;
384
+ `.trim(),
385
+ },
386
+ },
387
+ },
388
+ },
389
+ [
390
+ '`placeholderText` adds a label beside the search icon in the collapsed state — making the affordance',
391
+ 'explicit without taking up much space. Click the label to expand the full input.',
392
+ 'Note: this is a label on the collapsed button, not the HTML `placeholder` attribute on the input.',
393
+ ].join(' '),
394
+ );
395
+
396
+ export const WithHoverText: Story = withDescription(
397
+ {
398
+ render: WithHoverTextTemplate,
399
+ parameters: {
400
+ controls: { disable: true },
401
+ docs: {
402
+ source: {
403
+ language: 'tsx',
404
+ code: `
405
+ import { useState } from 'react';
406
+ import { SearchBar } from '@arbor-education/design-system.components';
407
+
408
+ function PupilSearchWithHoverExample() {
409
+ const [value, setValue] = useState('');
410
+ return (
411
+ <SearchBar
412
+ searchValue={value}
413
+ setSearchValue={setValue}
414
+ placeholderText="Search pupils"
415
+ hoverText="Search all pupils and staff"
416
+ />
417
+ );
418
+ }
419
+ export default PupilSearchWithHoverExample;
420
+ `.trim(),
421
+ },
422
+ },
423
+ },
424
+ },
425
+ [
426
+ 'When both `placeholderText` and `hoverText` are set, the label swaps to `hoverText` on mouse-enter',
427
+ 'and reverts to `placeholderText` on mouse-leave. Hover the icon to see the swap.',
428
+ 'This is a hover-only affordance — keyboard users always see `placeholderText`.',
429
+ ].join(' '),
430
+ );
431
+
432
+ export const WithHoverTextOnly: Story = withDescription(
433
+ {
434
+ render: WithHoverTextOnlyTemplate,
435
+ parameters: {
436
+ controls: { disable: true },
437
+ docs: {
438
+ source: {
439
+ language: 'tsx',
440
+ code: `
441
+ import { useState } from 'react';
442
+ import { SearchBar } from '@arbor-education/design-system.components';
443
+
444
+ function IconOnlySearchExample() {
445
+ const [value, setValue] = useState('');
446
+ return (
447
+ <SearchBar
448
+ searchValue={value}
449
+ setSearchValue={setValue}
450
+ hoverText="Search pupils"
451
+ />
452
+ );
453
+ }
454
+ export default IconOnlySearchExample;
455
+ `.trim(),
456
+ },
457
+ },
458
+ },
459
+ },
460
+ [
461
+ 'When `placeholderText` is absent but `hoverText` is set, the button shows an icon only at rest.',
462
+ 'Hovering reveals the label text. This pattern works well in very narrow navigation bars where',
463
+ 'every pixel counts — the icon provides the affordance at rest and hover confirms the action.',
464
+ ].join(' '),
465
+ );
466
+
467
+ export const WithSearchValue: Story = withDescription(
468
+ {
469
+ render: WithSearchValueTemplate,
470
+ parameters: {
471
+ controls: { disable: true },
472
+ docs: {
473
+ source: {
474
+ language: 'tsx',
475
+ code: `
476
+ import { useState } from 'react';
477
+ import { SearchBar } from '@arbor-education/design-system.components';
478
+
479
+ // Restore a previously entered search value from URL params or session storage.
480
+ // Passing a non-empty searchValue starts the component in the expanded state.
481
+ function RestoredSearchExample() {
482
+ const [value, setValue] = useState('Emily Johnson');
483
+ return (
484
+ <SearchBar
485
+ searchValue={value}
486
+ setSearchValue={setValue}
487
+ placeholderText="Search pupils"
488
+ />
489
+ );
490
+ }
491
+ export default RestoredSearchExample;
492
+ `.trim(),
493
+ },
494
+ },
495
+ },
496
+ },
497
+ [
498
+ 'Passing a non-empty `searchValue` starts the component expanded — no flash of the collapsed button.',
499
+ 'This is the correct pattern for restoring filter state from URL parameters or local storage.',
500
+ 'The clear (×) button is visible and clears both the value and collapses the input.',
501
+ ].join(' '),
502
+ );
503
+
504
+ export const AlwaysOpen: Story = withDescription(
505
+ {
506
+ render: AlwaysOpenTemplate,
507
+ parameters: {
508
+ controls: { disable: true },
509
+ docs: {
510
+ source: {
511
+ language: 'tsx',
512
+ code: `
513
+ import { useState } from 'react';
514
+ import { SearchBar } from '@arbor-education/design-system.components';
515
+
516
+ // alwaysOpen=true: permanently expanded, no clear button.
517
+ // Wire up an external reset mechanism if users need to clear.
518
+ function PersistentSearchExample() {
519
+ const [value, setValue] = useState('');
520
+ return (
521
+ <SearchBar
522
+ searchValue={value}
523
+ setSearchValue={setValue}
524
+ alwaysOpen
525
+ />
526
+ );
527
+ }
528
+ export default PersistentSearchExample;
529
+ `.trim(),
530
+ },
531
+ },
532
+ },
533
+ },
534
+ [
535
+ '`alwaysOpen` keeps the input permanently expanded and **removes the clear (×) button entirely**.',
536
+ 'The collapsed button state is never shown. Use this in sidebars or fixed layouts where search is',
537
+ 'always visible. Because there is no built-in clear affordance, add an external "Reset" button or',
538
+ 'call `setSearchValue(\'\')` from your own UI when the user wants to clear.',
539
+ ].join(' '),
540
+ );
541
+
542
+ export const AlwaysOpenWithValue: Story = withDescription(
543
+ {
544
+ render: AlwaysOpenWithValueTemplate,
545
+ parameters: {
546
+ controls: { disable: true },
547
+ docs: {
548
+ source: {
549
+ language: 'tsx',
550
+ code: `
551
+ import { useState } from 'react';
552
+ import { SearchBar } from '@arbor-education/design-system.components';
553
+
554
+ // alwaysOpen + pre-filled value: expanded with no collapse or clear affordance.
555
+ // The consumer is responsible for all value management.
556
+ function PersistentFilteredSearchExample() {
557
+ const [value, setValue] = useState('Year 8');
558
+ return (
559
+ <SearchBar
560
+ searchValue={value}
561
+ setSearchValue={setValue}
562
+ alwaysOpen
563
+ />
564
+ );
565
+ }
566
+ export default PersistentFilteredSearchExample;
567
+ `.trim(),
568
+ },
569
+ },
570
+ },
571
+ },
572
+ [
573
+ '`alwaysOpen` combined with a pre-filled `searchValue` — the component starts expanded with a value',
574
+ 'and no way for the user to clear it through the component itself.',
575
+ 'This combination is appropriate when an external "Reset filters" action exists in the parent UI.',
576
+ ].join(' '),
577
+ );
578
+
579
+ export const ControlledByParent: Story = withDescription(
580
+ {
581
+ render: ControlledByParentTemplate,
582
+ parameters: {
583
+ controls: { disable: true },
584
+ docs: {
585
+ source: {
586
+ language: 'tsx',
587
+ code: `
588
+ import { useState } from 'react';
589
+ import { SearchBar } from '@arbor-education/design-system.components';
590
+
591
+ // Always provide both searchValue and setSearchValue together.
592
+ // The parent owns the state — SearchBar is a fully controlled component.
593
+ function ReportSearchExample() {
594
+ const [value, setValue] = useState('');
595
+ return (
596
+ <div>
597
+ <SearchBar
598
+ searchValue={value}
599
+ setSearchValue={setValue}
600
+ placeholderText="Search reports"
601
+ />
602
+ {value && <p>Filtering by: "{value}"</p>}
603
+ </div>
604
+ );
605
+ }
606
+ export default ReportSearchExample;
607
+ `.trim(),
608
+ },
609
+ },
610
+ },
611
+ },
612
+ [
613
+ 'The controlled pattern: the parent component owns `searchValue` state and passes both `searchValue`',
614
+ 'and `setSearchValue` to SearchBar. The live value is displayed below — showing how a table filter,',
615
+ 'URL parameter, or analytics event would respond to the search.',
616
+ 'Click the search icon, type, and watch the status text update on every keystroke.',
617
+ ].join(' '),
618
+ );