@arbor-education/design-system.components 4.7.0-alpha.0 → 4.7.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 (357) hide show
  1. package/.github/workflows/chromatic.yml +1 -1
  2. package/.nvmrc +1 -1
  3. package/CHANGELOG.md +36 -0
  4. package/dist/components/datePicker/DatePicker.d.ts +2 -0
  5. package/dist/components/datePicker/DatePicker.d.ts.map +1 -1
  6. package/dist/components/datePicker/DatePicker.js +2 -2
  7. package/dist/components/datePicker/DatePicker.js.map +1 -1
  8. package/dist/components/datePicker/DatePicker.stories.d.ts +18 -0
  9. package/dist/components/datePicker/DatePicker.stories.d.ts.map +1 -1
  10. package/dist/components/datePicker/DatePicker.stories.js +30 -8
  11. package/dist/components/datePicker/DatePicker.stories.js.map +1 -1
  12. package/dist/components/datePicker/DatePicker.test.js +9 -1
  13. package/dist/components/datePicker/DatePicker.test.js.map +1 -1
  14. package/dist/components/dateTimePicker/DateTimePicker.d.ts +2 -0
  15. package/dist/components/dateTimePicker/DateTimePicker.d.ts.map +1 -1
  16. package/dist/components/dateTimePicker/DateTimePicker.js +2 -2
  17. package/dist/components/dateTimePicker/DateTimePicker.js.map +1 -1
  18. package/dist/components/dateTimePicker/DateTimePicker.stories.d.ts +18 -0
  19. package/dist/components/dateTimePicker/DateTimePicker.stories.d.ts.map +1 -1
  20. package/dist/components/dateTimePicker/DateTimePicker.stories.js +32 -9
  21. package/dist/components/dateTimePicker/DateTimePicker.stories.js.map +1 -1
  22. package/dist/components/dateTimePicker/DateTimePicker.test.js +9 -1
  23. package/dist/components/dateTimePicker/DateTimePicker.test.js.map +1 -1
  24. package/dist/components/fileUpload/FileUpload.d.ts +12 -4
  25. package/dist/components/fileUpload/FileUpload.d.ts.map +1 -1
  26. package/dist/components/fileUpload/FileUpload.js +32 -7
  27. package/dist/components/fileUpload/FileUpload.js.map +1 -1
  28. package/dist/components/fileUpload/FileUpload.stories.d.ts +1 -0
  29. package/dist/components/fileUpload/FileUpload.stories.d.ts.map +1 -1
  30. package/dist/components/fileUpload/FileUpload.stories.js +59 -6
  31. package/dist/components/fileUpload/FileUpload.stories.js.map +1 -1
  32. package/dist/components/fileUpload/FileUpload.test.js +102 -1
  33. package/dist/components/fileUpload/FileUpload.test.js.map +1 -1
  34. package/dist/components/fileUpload/FileUploadLinkModal.d.ts +14 -0
  35. package/dist/components/fileUpload/FileUploadLinkModal.d.ts.map +1 -0
  36. package/dist/components/fileUpload/FileUploadLinkModal.js +61 -0
  37. package/dist/components/fileUpload/FileUploadLinkModal.js.map +1 -0
  38. package/dist/components/formField/inputs/text/TextInput.d.ts.map +1 -1
  39. package/dist/components/formField/inputs/text/TextInput.js +2 -2
  40. package/dist/components/formField/inputs/text/TextInput.js.map +1 -1
  41. package/dist/components/kpiCard/KPICard.stories.js +2 -2
  42. package/dist/components/kpiCard/KPICard.stories.js.map +1 -1
  43. package/dist/components/sideNavigation/SideNavigation.d.ts +5 -0
  44. package/dist/components/sideNavigation/SideNavigation.d.ts.map +1 -1
  45. package/dist/components/sideNavigation/SideNavigation.js +5 -4
  46. package/dist/components/sideNavigation/SideNavigation.js.map +1 -1
  47. package/dist/components/sideNavigation/SideNavigation.stories.d.ts.map +1 -1
  48. package/dist/components/sideNavigation/SideNavigation.stories.js +6 -2
  49. package/dist/components/sideNavigation/SideNavigation.stories.js.map +1 -1
  50. package/dist/components/sideNavigation/SideNavigation.test.js +13 -0
  51. package/dist/components/sideNavigation/SideNavigation.test.js.map +1 -1
  52. package/dist/components/sideNavigation/SideNavigationTitle.d.ts.map +1 -1
  53. package/dist/components/sideNavigation/SideNavigationTitle.js +1 -1
  54. package/dist/components/sideNavigation/SideNavigationTitle.js.map +1 -1
  55. package/dist/components/sidebar/Sidebar.stories.d.ts.map +1 -1
  56. package/dist/components/sidebar/Sidebar.stories.js +3 -11
  57. package/dist/components/sidebar/Sidebar.stories.js.map +1 -1
  58. package/dist/components/table/columnFilters/TimeFilter/TimeFilter.d.ts.map +1 -1
  59. package/dist/components/table/columnFilters/TimeFilter/TimeFilter.js +1 -1
  60. package/dist/components/table/columnFilters/TimeFilter/TimeFilter.js.map +1 -1
  61. package/dist/components/table/columnFilters/TimeFilter/TimeFilter.test.js +2 -2
  62. package/dist/components/table/columnFilters/filterResetButton/FilterResetButton.d.ts +1 -0
  63. package/dist/components/table/columnFilters/filterResetButton/FilterResetButton.d.ts.map +1 -1
  64. package/dist/components/table/columnFilters/filterResetButton/FilterResetButton.js +2 -2
  65. package/dist/components/table/columnFilters/filterResetButton/FilterResetButton.js.map +1 -1
  66. package/dist/components/table/pagination/PageSizeSelector.js +1 -1
  67. package/dist/components/table/pagination/PageSizeSelector.js.map +1 -1
  68. package/dist/components/table/pagination/Pagination.test.js +3 -3
  69. package/dist/components/table/pagination/Pagination.test.js.map +1 -1
  70. package/dist/index.css +20 -1
  71. package/dist/index.css.map +1 -1
  72. package/package.json +1 -1
  73. package/src/components/datePicker/DatePicker.stories.tsx +30 -3
  74. package/src/components/datePicker/DatePicker.test.tsx +15 -0
  75. package/src/components/datePicker/DatePicker.tsx +6 -0
  76. package/src/components/dateTimePicker/DateTimePicker.stories.tsx +32 -1
  77. package/src/components/dateTimePicker/DateTimePicker.test.tsx +15 -0
  78. package/src/components/dateTimePicker/DateTimePicker.tsx +6 -0
  79. package/src/components/fileUpload/FileUpload.stories.tsx +74 -6
  80. package/src/components/fileUpload/FileUpload.test.tsx +139 -1
  81. package/src/components/fileUpload/FileUpload.tsx +109 -14
  82. package/src/components/fileUpload/FileUploadLinkModal.tsx +109 -0
  83. package/src/components/fileUpload/fileUpload.scss +24 -1
  84. package/src/components/formField/inputs/text/TextInput.tsx +2 -0
  85. package/src/components/kpiCard/KPICard.stories.tsx +2 -2
  86. package/src/components/sideNavigation/SideNavigation.stories.tsx +6 -2
  87. package/src/components/sideNavigation/SideNavigation.test.tsx +21 -0
  88. package/src/components/sideNavigation/SideNavigation.tsx +19 -2
  89. package/src/components/sideNavigation/SideNavigationTitle.tsx +5 -3
  90. package/src/components/sidebar/Sidebar.stories.tsx +3 -16
  91. package/src/components/table/columnFilters/TimeFilter/TimeFilter.test.tsx +2 -2
  92. package/src/components/table/columnFilters/TimeFilter/TimeFilter.tsx +4 -1
  93. package/src/components/table/columnFilters/filterResetButton/FilterResetButton.tsx +3 -2
  94. package/src/components/table/pagination/PageSizeSelector.tsx +1 -1
  95. package/src/components/table/pagination/Pagination.test.tsx +3 -3
  96. package/.changeset/twenty-groups-wonder.md +0 -5
  97. package/.changeset/yellow-falcons-search.md +0 -5
  98. package/dist/components/button/Button.story.d.ts +0 -47
  99. package/dist/components/button/Button.story.d.ts.map +0 -1
  100. package/dist/components/button/Button.story.js +0 -123
  101. package/dist/components/button/Button.story.js.map +0 -1
  102. package/dist/components/button/ButtonUsageExample.d.ts +0 -2
  103. package/dist/components/button/ButtonUsageExample.d.ts.map +0 -1
  104. package/dist/components/button/ButtonUsageExample.js +0 -11
  105. package/dist/components/button/ButtonUsageExample.js.map +0 -1
  106. package/dist/components/combobox/useElementWidth.d.ts +0 -2
  107. package/dist/components/combobox/useElementWidth.d.ts.map +0 -1
  108. package/dist/components/combobox/useElementWidth.js +0 -31
  109. package/dist/components/combobox/useElementWidth.js.map +0 -1
  110. package/dist/components/combobox/useVisibleChips.d.ts +0 -21
  111. package/dist/components/combobox/useVisibleChips.d.ts.map +0 -1
  112. package/dist/components/combobox/useVisibleChips.js +0 -59
  113. package/dist/components/combobox/useVisibleChips.js.map +0 -1
  114. package/dist/components/combobox/useVisibleChips.test.d.ts +0 -2
  115. package/dist/components/combobox/useVisibleChips.test.d.ts.map +0 -1
  116. package/dist/components/combobox/useVisibleChips.test.js +0 -81
  117. package/dist/components/combobox/useVisibleChips.test.js.map +0 -1
  118. package/dist/components/heading/HeadingInnerContainer.d.ts +0 -5
  119. package/dist/components/heading/HeadingInnerContainer.d.ts.map +0 -1
  120. package/dist/components/heading/HeadingInnerContainer.js +0 -7
  121. package/dist/components/heading/HeadingInnerContainer.js.map +0 -1
  122. package/dist/components/icoText/IcoText.d.ts +0 -37
  123. package/dist/components/icoText/IcoText.d.ts.map +0 -1
  124. package/dist/components/icoText/IcoText.js +0 -29
  125. package/dist/components/icoText/IcoText.js.map +0 -1
  126. package/dist/components/icoText/IcoText.stories.d.ts +0 -60
  127. package/dist/components/icoText/IcoText.stories.d.ts.map +0 -1
  128. package/dist/components/icoText/IcoText.stories.js +0 -319
  129. package/dist/components/icoText/IcoText.stories.js.map +0 -1
  130. package/dist/components/icoText/IcoText.test.d.ts +0 -2
  131. package/dist/components/icoText/IcoText.test.d.ts.map +0 -1
  132. package/dist/components/icoText/IcoText.test.js +0 -27
  133. package/dist/components/icoText/IcoText.test.js.map +0 -1
  134. package/dist/components/sidebarNav/SidebarNav.d.ts +0 -46
  135. package/dist/components/sidebarNav/SidebarNav.d.ts.map +0 -1
  136. package/dist/components/sidebarNav/SidebarNav.js +0 -102
  137. package/dist/components/sidebarNav/SidebarNav.js.map +0 -1
  138. package/dist/components/sidebarNav/SidebarNav.stories.d.ts +0 -61
  139. package/dist/components/sidebarNav/SidebarNav.stories.d.ts.map +0 -1
  140. package/dist/components/sidebarNav/SidebarNav.stories.js +0 -253
  141. package/dist/components/sidebarNav/SidebarNav.stories.js.map +0 -1
  142. package/dist/components/sidebarNav/SidebarNav.test.d.ts +0 -2
  143. package/dist/components/sidebarNav/SidebarNav.test.d.ts.map +0 -1
  144. package/dist/components/sidebarNav/SidebarNav.test.js +0 -240
  145. package/dist/components/sidebarNav/SidebarNav.test.js.map +0 -1
  146. package/dist/components/sidebarNav/SidebarNavContext.d.ts +0 -13
  147. package/dist/components/sidebarNav/SidebarNavContext.d.ts.map +0 -1
  148. package/dist/components/sidebarNav/SidebarNavContext.js +0 -15
  149. package/dist/components/sidebarNav/SidebarNavContext.js.map +0 -1
  150. package/dist/components/sidebarNav/SidebarNavGroup.d.ts +0 -10
  151. package/dist/components/sidebarNav/SidebarNavGroup.d.ts.map +0 -1
  152. package/dist/components/sidebarNav/SidebarNavGroup.js +0 -16
  153. package/dist/components/sidebarNav/SidebarNavGroup.js.map +0 -1
  154. package/dist/components/sidebarNav/SidebarNavItem.d.ts +0 -32
  155. package/dist/components/sidebarNav/SidebarNavItem.d.ts.map +0 -1
  156. package/dist/components/sidebarNav/SidebarNavItem.js +0 -44
  157. package/dist/components/sidebarNav/SidebarNavItem.js.map +0 -1
  158. package/dist/components/sidebarNav/SidebarNavItemFavourite.d.ts +0 -8
  159. package/dist/components/sidebarNav/SidebarNavItemFavourite.d.ts.map +0 -1
  160. package/dist/components/sidebarNav/SidebarNavItemFavourite.js +0 -14
  161. package/dist/components/sidebarNav/SidebarNavItemFavourite.js.map +0 -1
  162. package/dist/components/sidebarNav/SidebarNavPanel.d.ts +0 -4
  163. package/dist/components/sidebarNav/SidebarNavPanel.d.ts.map +0 -1
  164. package/dist/components/sidebarNav/SidebarNavPanel.js +0 -9
  165. package/dist/components/sidebarNav/SidebarNavPanel.js.map +0 -1
  166. package/dist/components/sidebarNav/SidebarNavPanelNav.d.ts +0 -10
  167. package/dist/components/sidebarNav/SidebarNavPanelNav.d.ts.map +0 -1
  168. package/dist/components/sidebarNav/SidebarNavPanelNav.js +0 -21
  169. package/dist/components/sidebarNav/SidebarNavPanelNav.js.map +0 -1
  170. package/dist/components/sidebarNav/SidebarNavRail.d.ts +0 -6
  171. package/dist/components/sidebarNav/SidebarNavRail.d.ts.map +0 -1
  172. package/dist/components/sidebarNav/SidebarNavRail.js +0 -7
  173. package/dist/components/sidebarNav/SidebarNavRail.js.map +0 -1
  174. package/dist/components/sidebarNav/SidebarNavRailItem.d.ts +0 -10
  175. package/dist/components/sidebarNav/SidebarNavRailItem.d.ts.map +0 -1
  176. package/dist/components/sidebarNav/SidebarNavRailItem.js +0 -25
  177. package/dist/components/sidebarNav/SidebarNavRailItem.js.map +0 -1
  178. package/dist/components/sidebarNav/SidebarNavRailList.d.ts +0 -4
  179. package/dist/components/sidebarNav/SidebarNavRailList.d.ts.map +0 -1
  180. package/dist/components/sidebarNav/SidebarNavRailList.js +0 -7
  181. package/dist/components/sidebarNav/SidebarNavRailList.js.map +0 -1
  182. package/dist/components/sidebarNav/SidebarNavRailSlot.d.ts +0 -6
  183. package/dist/components/sidebarNav/SidebarNavRailSlot.d.ts.map +0 -1
  184. package/dist/components/sidebarNav/SidebarNavRailSlot.js +0 -7
  185. package/dist/components/sidebarNav/SidebarNavRailSlot.js.map +0 -1
  186. package/dist/components/sidebarNav/SidebarNavSeparator.d.ts +0 -6
  187. package/dist/components/sidebarNav/SidebarNavSeparator.d.ts.map +0 -1
  188. package/dist/components/sidebarNav/SidebarNavSeparator.js +0 -8
  189. package/dist/components/sidebarNav/SidebarNavSeparator.js.map +0 -1
  190. package/dist/components/sidebarNav/SidebarNavTitle.d.ts +0 -4
  191. package/dist/components/sidebarNav/SidebarNavTitle.d.ts.map +0 -1
  192. package/dist/components/sidebarNav/SidebarNavTitle.js +0 -7
  193. package/dist/components/sidebarNav/SidebarNavTitle.js.map +0 -1
  194. package/dist/components/sidebarNav/SidebarNavTooltip.d.ts +0 -7
  195. package/dist/components/sidebarNav/SidebarNavTooltip.d.ts.map +0 -1
  196. package/dist/components/sidebarNav/SidebarNavTooltip.js +0 -9
  197. package/dist/components/sidebarNav/SidebarNavTooltip.js.map +0 -1
  198. package/dist/components/sidebarNav/SidebarNavTrigger.d.ts +0 -8
  199. package/dist/components/sidebarNav/SidebarNavTrigger.d.ts.map +0 -1
  200. package/dist/components/sidebarNav/SidebarNavTrigger.js +0 -15
  201. package/dist/components/sidebarNav/SidebarNavTrigger.js.map +0 -1
  202. package/dist/components/sidebarNav/index.d.ts +0 -4
  203. package/dist/components/sidebarNav/index.d.ts.map +0 -1
  204. package/dist/components/sidebarNav/index.js +0 -3
  205. package/dist/components/sidebarNav/index.js.map +0 -1
  206. package/dist/components/sidebarNav/resolvePanelItemProps.d.ts +0 -4
  207. package/dist/components/sidebarNav/resolvePanelItemProps.d.ts.map +0 -1
  208. package/dist/components/sidebarNav/resolvePanelItemProps.js +0 -43
  209. package/dist/components/sidebarNav/resolvePanelItemProps.js.map +0 -1
  210. package/dist/components/sidebarNav/resolvePanelItemProps.test.d.ts +0 -2
  211. package/dist/components/sidebarNav/resolvePanelItemProps.test.d.ts.map +0 -1
  212. package/dist/components/sidebarNav/resolvePanelItemProps.test.js +0 -52
  213. package/dist/components/sidebarNav/resolvePanelItemProps.test.js.map +0 -1
  214. package/dist/components/sidebarNav/types.d.ts +0 -100
  215. package/dist/components/sidebarNav/types.d.ts.map +0 -1
  216. package/dist/components/sidebarNav/types.js +0 -4
  217. package/dist/components/sidebarNav/types.js.map +0 -1
  218. package/dist/components/sidebarNav/useControllableBoolean.d.ts +0 -9
  219. package/dist/components/sidebarNav/useControllableBoolean.d.ts.map +0 -1
  220. package/dist/components/sidebarNav/useControllableBoolean.js +0 -15
  221. package/dist/components/sidebarNav/useControllableBoolean.js.map +0 -1
  222. package/dist/components/slideover/Slideover.stories.d.ts +0 -10
  223. package/dist/components/slideover/Slideover.stories.d.ts.map +0 -1
  224. package/dist/components/slideover/Slideover.stories.js +0 -12
  225. package/dist/components/slideover/Slideover.stories.js.map +0 -1
  226. package/dist/components/table/BulkActionsDropdown.d.ts +0 -12
  227. package/dist/components/table/BulkActionsDropdown.d.ts.map +0 -1
  228. package/dist/components/table/BulkActionsDropdown.js +0 -11
  229. package/dist/components/table/BulkActionsDropdown.js.map +0 -1
  230. package/dist/components/table/HideColumnsDropdown.d.ts +0 -9
  231. package/dist/components/table/HideColumnsDropdown.d.ts.map +0 -1
  232. package/dist/components/table/HideColumnsDropdown.js +0 -33
  233. package/dist/components/table/HideColumnsDropdown.js.map +0 -1
  234. package/dist/components/table/cellRenderers/SelectDropdownCellEditor.d.ts +0 -8
  235. package/dist/components/table/cellRenderers/SelectDropdownCellEditor.d.ts.map +0 -1
  236. package/dist/components/table/cellRenderers/SelectDropdownCellEditor.js +0 -19
  237. package/dist/components/table/cellRenderers/SelectDropdownCellEditor.js.map +0 -1
  238. package/dist/components/table/hooks/useHistory.d.ts +0 -20
  239. package/dist/components/table/hooks/useHistory.d.ts.map +0 -1
  240. package/dist/components/table/hooks/useHistory.js +0 -73
  241. package/dist/components/table/hooks/useHistory.js.map +0 -1
  242. package/dist/components/table/hooks/useTableHistory.d.ts +0 -21
  243. package/dist/components/table/hooks/useTableHistory.d.ts.map +0 -1
  244. package/dist/components/table/hooks/useTableHistory.js +0 -88
  245. package/dist/components/table/hooks/useTableHistory.js.map +0 -1
  246. package/dist/components/table/pagination/TableSettingsDropdown.d.ts +0 -2
  247. package/dist/components/table/pagination/TableSettingsDropdown.d.ts.map +0 -1
  248. package/dist/components/table/pagination/TableSettingsDropdown.js +0 -42
  249. package/dist/components/table/pagination/TableSettingsDropdown.js.map +0 -1
  250. package/dist/components/table/tableHeader/TableHeader.d.ts +0 -14
  251. package/dist/components/table/tableHeader/TableHeader.d.ts.map +0 -1
  252. package/dist/components/table/tableHeader/TableHeader.js +0 -55
  253. package/dist/components/table/tableHeader/TableHeader.js.map +0 -1
  254. package/dist/components/table/tableHeader/index.d.ts +0 -2
  255. package/dist/components/table/tableHeader/index.d.ts.map +0 -1
  256. package/dist/components/table/tableHeader/index.js +0 -2
  257. package/dist/components/table/tableHeader/index.js.map +0 -1
  258. package/dist/components/table/tableTheme.d.ts +0 -2
  259. package/dist/components/table/tableTheme.d.ts.map +0 -1
  260. package/dist/components/table/tableTheme.js +0 -18
  261. package/dist/components/table/tableTheme.js.map +0 -1
  262. package/dist/components/table/types.d.ts +0 -169
  263. package/dist/components/table/types.d.ts.map +0 -1
  264. package/dist/components/table/types.js +0 -11
  265. package/dist/components/table/types.js.map +0 -1
  266. package/dist/components/table/utils/setAgGridLicenseKey.d.ts +0 -2
  267. package/dist/components/table/utils/setAgGridLicenseKey.d.ts.map +0 -1
  268. package/dist/components/table/utils/setAgGridLicenseKey.js +0 -11
  269. package/dist/components/table/utils/setAgGridLicenseKey.js.map +0 -1
  270. package/dist/layoutTest.stories.d.ts +0 -6
  271. package/dist/layoutTest.stories.d.ts.map +0 -1
  272. package/dist/layoutTest.stories.js +0 -9
  273. package/dist/layoutTest.stories.js.map +0 -1
  274. package/dist/pages/assessmentBuilderPage/AssessmentBuilderPage.d.ts +0 -39
  275. package/dist/pages/assessmentBuilderPage/AssessmentBuilderPage.d.ts.map +0 -1
  276. package/dist/pages/assessmentBuilderPage/AssessmentBuilderPage.js +0 -112
  277. package/dist/pages/assessmentBuilderPage/AssessmentBuilderPage.js.map +0 -1
  278. package/dist/pages/assessmentBuilderPage/AssessmentBuilderPage.stories.d.ts +0 -184
  279. package/dist/pages/assessmentBuilderPage/AssessmentBuilderPage.stories.d.ts.map +0 -1
  280. package/dist/pages/assessmentBuilderPage/AssessmentBuilderPage.stories.js +0 -317
  281. package/dist/pages/assessmentBuilderPage/AssessmentBuilderPage.stories.js.map +0 -1
  282. package/dist/pages/assessmentBuilderPage/AssessmentBuilderPage.test.d.ts +0 -2
  283. package/dist/pages/assessmentBuilderPage/AssessmentBuilderPage.test.d.ts.map +0 -1
  284. package/dist/pages/assessmentBuilderPage/AssessmentBuilderPage.test.js +0 -160
  285. package/dist/pages/assessmentBuilderPage/AssessmentBuilderPage.test.js.map +0 -1
  286. package/dist/pages/assessmentSettingsPage/AssessmentSettingsPage.d.ts +0 -11
  287. package/dist/pages/assessmentSettingsPage/AssessmentSettingsPage.d.ts.map +0 -1
  288. package/dist/pages/assessmentSettingsPage/AssessmentSettingsPage.js +0 -13
  289. package/dist/pages/assessmentSettingsPage/AssessmentSettingsPage.js.map +0 -1
  290. package/dist/pages/assessmentSettingsPage/AssessmentSettingsPage.stories.d.ts +0 -174
  291. package/dist/pages/assessmentSettingsPage/AssessmentSettingsPage.stories.d.ts.map +0 -1
  292. package/dist/pages/assessmentSettingsPage/AssessmentSettingsPage.stories.js +0 -212
  293. package/dist/pages/assessmentSettingsPage/AssessmentSettingsPage.stories.js.map +0 -1
  294. package/dist/pages/assessmentSettingsPage/AssessmentSettingsPage.test.d.ts +0 -2
  295. package/dist/pages/assessmentSettingsPage/AssessmentSettingsPage.test.d.ts.map +0 -1
  296. package/dist/pages/assessmentSettingsPage/AssessmentSettingsPage.test.js +0 -84
  297. package/dist/pages/assessmentSettingsPage/AssessmentSettingsPage.test.js.map +0 -1
  298. package/dist/pages/assessmentsPage/AssessmentsPage.d.ts +0 -16
  299. package/dist/pages/assessmentsPage/AssessmentsPage.d.ts.map +0 -1
  300. package/dist/pages/assessmentsPage/AssessmentsPage.js +0 -31
  301. package/dist/pages/assessmentsPage/AssessmentsPage.js.map +0 -1
  302. package/dist/pages/assessmentsPage/AssessmentsPage.stories.d.ts +0 -162
  303. package/dist/pages/assessmentsPage/AssessmentsPage.stories.d.ts.map +0 -1
  304. package/dist/pages/assessmentsPage/AssessmentsPage.stories.js +0 -290
  305. package/dist/pages/assessmentsPage/AssessmentsPage.stories.js.map +0 -1
  306. package/dist/pages/assessmentsPage/AssessmentsPage.test.d.ts +0 -2
  307. package/dist/pages/assessmentsPage/AssessmentsPage.test.d.ts.map +0 -1
  308. package/dist/pages/assessmentsPage/AssessmentsPage.test.js +0 -109
  309. package/dist/pages/assessmentsPage/AssessmentsPage.test.js.map +0 -1
  310. package/dist/pages/customReportWriterPage/CustomReportWriterPage.d.ts +0 -40
  311. package/dist/pages/customReportWriterPage/CustomReportWriterPage.d.ts.map +0 -1
  312. package/dist/pages/customReportWriterPage/CustomReportWriterPage.js +0 -156
  313. package/dist/pages/customReportWriterPage/CustomReportWriterPage.js.map +0 -1
  314. package/dist/pages/customReportWriterPage/CustomReportWriterPage.stories.d.ts +0 -239
  315. package/dist/pages/customReportWriterPage/CustomReportWriterPage.stories.d.ts.map +0 -1
  316. package/dist/pages/customReportWriterPage/CustomReportWriterPage.stories.js +0 -318
  317. package/dist/pages/customReportWriterPage/CustomReportWriterPage.stories.js.map +0 -1
  318. package/dist/pages/customReportWriterPage/CustomReportWriterPage.test.d.ts +0 -2
  319. package/dist/pages/customReportWriterPage/CustomReportWriterPage.test.d.ts.map +0 -1
  320. package/dist/pages/customReportWriterPage/CustomReportWriterPage.test.js +0 -183
  321. package/dist/pages/customReportWriterPage/CustomReportWriterPage.test.js.map +0 -1
  322. package/dist/pages/gradeSetsPage/GradeSetsPage.d.ts +0 -26
  323. package/dist/pages/gradeSetsPage/GradeSetsPage.d.ts.map +0 -1
  324. package/dist/pages/gradeSetsPage/GradeSetsPage.js +0 -33
  325. package/dist/pages/gradeSetsPage/GradeSetsPage.js.map +0 -1
  326. package/dist/pages/gradeSetsPage/GradeSetsPage.stories.d.ts +0 -176
  327. package/dist/pages/gradeSetsPage/GradeSetsPage.stories.d.ts.map +0 -1
  328. package/dist/pages/gradeSetsPage/GradeSetsPage.stories.js +0 -237
  329. package/dist/pages/gradeSetsPage/GradeSetsPage.stories.js.map +0 -1
  330. package/dist/pages/gradeSetsPage/GradeSetsPage.test.d.ts +0 -2
  331. package/dist/pages/gradeSetsPage/GradeSetsPage.test.d.ts.map +0 -1
  332. package/dist/pages/gradeSetsPage/GradeSetsPage.test.js +0 -91
  333. package/dist/pages/gradeSetsPage/GradeSetsPage.test.js.map +0 -1
  334. package/dist/pages/studentProfilePage/StudentProfilePage.d.ts +0 -48
  335. package/dist/pages/studentProfilePage/StudentProfilePage.d.ts.map +0 -1
  336. package/dist/pages/studentProfilePage/StudentProfilePage.js +0 -52
  337. package/dist/pages/studentProfilePage/StudentProfilePage.js.map +0 -1
  338. package/dist/pages/studentProfilePage/StudentProfilePage.stories.d.ts +0 -218
  339. package/dist/pages/studentProfilePage/StudentProfilePage.stories.d.ts.map +0 -1
  340. package/dist/pages/studentProfilePage/StudentProfilePage.stories.js +0 -273
  341. package/dist/pages/studentProfilePage/StudentProfilePage.stories.js.map +0 -1
  342. package/dist/pages/studentProfilePage/StudentProfilePage.test.d.ts +0 -2
  343. package/dist/pages/studentProfilePage/StudentProfilePage.test.d.ts.map +0 -1
  344. package/dist/pages/studentProfilePage/StudentProfilePage.test.js +0 -197
  345. package/dist/pages/studentProfilePage/StudentProfilePage.test.js.map +0 -1
  346. package/dist/utils/PopupParentContext.d.ts +0 -3
  347. package/dist/utils/PopupParentContext.d.ts.map +0 -1
  348. package/dist/utils/PopupParentContext.js +0 -6
  349. package/dist/utils/PopupParentContext.js.map +0 -1
  350. package/dist/utils/PopupParentContext.stories.d.ts +0 -17
  351. package/dist/utils/PopupParentContext.stories.d.ts.map +0 -1
  352. package/dist/utils/PopupParentContext.stories.js +0 -266
  353. package/dist/utils/PopupParentContext.stories.js.map +0 -1
  354. package/dist/utils/getDefaultPopupParent.d.ts +0 -2
  355. package/dist/utils/getDefaultPopupParent.d.ts.map +0 -1
  356. package/dist/utils/getDefaultPopupParent.js +0 -19
  357. package/dist/utils/getDefaultPopupParent.js.map +0 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arbor-education/design-system.components",
3
- "version": "4.7.0-alpha.0",
3
+ "version": "4.7.0",
4
4
  "description": "The component library for the design system (the baby)",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",
@@ -312,6 +312,20 @@ const meta = {
312
312
  type: { summary: 'string' },
313
313
  },
314
314
  },
315
+ 'aria-label': {
316
+ control: 'text',
317
+ description: 'Accessible name for the input when used outside `<FormField>`. Use when there is no visible label.',
318
+ table: {
319
+ type: { summary: 'string' },
320
+ },
321
+ },
322
+ 'aria-labelledby': {
323
+ control: 'text',
324
+ description: 'ID of a visible element that names the input. Prefer this over `aria-label` when a visible label exists.',
325
+ table: {
326
+ type: { summary: 'string' },
327
+ },
328
+ },
315
329
  },
316
330
  } satisfies Meta<typeof DatePicker>;
317
331
 
@@ -345,6 +359,7 @@ const ControlledWithDisplayTemplate = () => {
345
359
  <div style={{ maxWidth: '280px' }}>
346
360
  <DatePicker
347
361
  id="controlled-display"
362
+ aria-label="Event date"
348
363
  onChange={setSelectedDate}
349
364
  />
350
365
  <p style={{ margin: 'var(--spacing-small) 0 0', color: 'var(--color-grey-600)' }}>
@@ -385,6 +400,7 @@ const ControlledWithValidationTemplate = () => {
385
400
  <div style={{ maxWidth: '280px' }}>
386
401
  <DatePicker
387
402
  id="validated-date"
403
+ aria-label="Event date"
388
404
  onChange={handleChange}
389
405
  hasError={!!error}
390
406
  aria-invalid={error ? true : undefined}
@@ -468,7 +484,8 @@ const WithFormFieldAndErrorTemplate = () => {
468
484
  export const Default: Story = withDescription(
469
485
  {
470
486
  args: {
471
- id: 'default-date',
487
+ 'id': 'default-date',
488
+ 'aria-label': 'Start date',
472
489
  },
473
490
  render: args => (
474
491
  <div style={{ maxWidth: '280px' }}>
@@ -486,8 +503,9 @@ export const Default: Story = withDescription(
486
503
  export const FriendlyFormat: Story = withDescription(
487
504
  {
488
505
  args: {
489
- displayFormat: 'friendly',
490
- id: 'friendly-date',
506
+ 'displayFormat': 'friendly',
507
+ 'id': 'friendly-date',
508
+ 'aria-label': 'Event date',
491
509
  },
492
510
  render: args => (
493
511
  <div style={{ maxWidth: '280px' }}>
@@ -506,6 +524,7 @@ function FriendlyFormatExample() {
506
524
  return (
507
525
  <DatePicker
508
526
  id="event-date"
527
+ aria-label="Event date"
509
528
  displayFormat="friendly"
510
529
  onChange={(date) => {
511
530
  // date is Date | undefined — not a string
@@ -530,6 +549,7 @@ export const WithDefaultValue: Story = withDescription(
530
549
  {/* September 1 2024 — month is zero-indexed (Jan = 0, Sep = 8) */}
531
550
  <DatePicker
532
551
  id="default-value-date"
552
+ aria-label="Enrolment date"
533
553
  defaultValue={new Date(2024, 8, 1)}
534
554
  />
535
555
  </div>
@@ -549,6 +569,7 @@ function EditEnrollmentDate() {
549
569
  return (
550
570
  <DatePicker
551
571
  id="enrollment-date"
572
+ aria-label="Enrolment date"
552
573
  defaultValue={currentEnrollmentDate}
553
574
  onChange={(date) => {
554
575
  // date is Date | undefined — not a string
@@ -576,6 +597,7 @@ export const WithPlaceholder: Story = withDescription(
576
597
  <div style={{ maxWidth: '280px' }}>
577
598
  <DatePicker
578
599
  id="placeholder-date"
600
+ aria-label="Term start date"
579
601
  placeholder="Choose term start date"
580
602
  />
581
603
  </div>
@@ -592,6 +614,7 @@ function TermStartDatePicker() {
592
614
  return (
593
615
  <DatePicker
594
616
  id="term-start-date"
617
+ aria-label="Term start date"
595
618
  placeholder="Choose term start date"
596
619
  onChange={(date) => {
597
620
  console.log('Term start:', date?.toLocaleDateString('en-GB'));
@@ -618,6 +641,7 @@ export const ErrorState: Story = withDescription(
618
641
  <div style={{ maxWidth: '280px' }}>
619
642
  <DatePicker
620
643
  id="error-state-date"
644
+ aria-label="Enrolment date"
621
645
  hasError
622
646
  aria-invalid={true}
623
647
  aria-describedby="error-state-msg"
@@ -646,6 +670,7 @@ function EnrollmentDateWithError() {
646
670
  <>
647
671
  <DatePicker
648
672
  id="enrollment-date"
673
+ aria-label="Enrolment date"
649
674
  hasError
650
675
  aria-invalid={true}
651
676
  aria-describedby="enrollment-date-error"
@@ -688,6 +713,7 @@ function ControlledDatePickerExample() {
688
713
  <div>
689
714
  <DatePicker
690
715
  id="event-date"
716
+ aria-label="Event date"
691
717
  onChange={setSelectedDate}
692
718
  />
693
719
  <p>
@@ -749,6 +775,7 @@ function FutureDatePicker() {
749
775
  <div>
750
776
  <DatePicker
751
777
  id="event-date"
778
+ aria-label="Event date"
752
779
  onChange={handleChange}
753
780
  hasError={!!error}
754
781
  aria-invalid={error ? true : undefined}
@@ -21,6 +21,21 @@ describe('DatePicker', () => {
21
21
  expect(screen.getByRole('button', { name: 'Open date picker' })).toBeInTheDocument();
22
22
  });
23
23
 
24
+ test('names the input with aria-label', () => {
25
+ const { container } = render(<DatePicker aria-label="Start date" />);
26
+ expect(screen.getByLabelText('Start date')).toBe(container.querySelector('input[type="date"]'));
27
+ });
28
+
29
+ test('names the input with aria-labelledby', () => {
30
+ const { container } = render(
31
+ <>
32
+ <span id="start-date-label">Start date</span>
33
+ <DatePicker aria-labelledby="start-date-label" />
34
+ </>,
35
+ );
36
+ expect(screen.getByLabelText('Start date')).toBe(container.querySelector('input[type="date"]'));
37
+ });
38
+
24
39
  test('day picker is hidden by default', () => {
25
40
  render(<DatePicker />);
26
41
  expect(screen.queryByRole('application')).not.toBeInTheDocument();
@@ -37,6 +37,8 @@ export type DatePickerProps = {
37
37
  'value'?: Date;
38
38
  'defaultValue'?: Date;
39
39
  'autoFocus'?: boolean;
40
+ 'aria-label'?: string;
41
+ 'aria-labelledby'?: string;
40
42
  };
41
43
 
42
44
  export const DatePicker = (props: DatePickerProps) => {
@@ -48,6 +50,8 @@ export const DatePicker = (props: DatePickerProps) => {
48
50
  hasError,
49
51
  'aria-describedby': ariaDescribedBy,
50
52
  'aria-invalid': ariaInvalid,
53
+ 'aria-label': ariaLabel,
54
+ 'aria-labelledby': ariaLabelledBy,
51
55
  value,
52
56
  defaultValue,
53
57
  placeholder,
@@ -183,6 +187,8 @@ export const DatePicker = (props: DatePickerProps) => {
183
187
  hasError={hasError}
184
188
  aria-describedby={ariaDescribedBy}
185
189
  aria-invalid={ariaInvalid}
190
+ aria-label={ariaLabel}
191
+ aria-labelledby={ariaLabelledBy}
186
192
  className="ds-date-picker__input"
187
193
  autoFocus={autoFocus}
188
194
  />
@@ -315,6 +315,20 @@ const meta = {
315
315
  type: { summary: 'string' },
316
316
  },
317
317
  },
318
+ 'aria-label': {
319
+ control: 'text',
320
+ description: 'Accessible name for the input when used outside `<FormField>`. Use when there is no visible label.',
321
+ table: {
322
+ type: { summary: 'string' },
323
+ },
324
+ },
325
+ 'aria-labelledby': {
326
+ control: 'text',
327
+ description: 'ID of a visible element that names the input. Prefer this over `aria-label` when a visible label exists.',
328
+ table: {
329
+ type: { summary: 'string' },
330
+ },
331
+ },
318
332
  },
319
333
  } satisfies Meta<typeof DateTimePicker>;
320
334
 
@@ -364,6 +378,7 @@ const DisplayFormatDefaultTemplate = () => {
364
378
  value={value}
365
379
  onChange={setValue}
366
380
  id="format-default"
381
+ aria-label="Event start date and time"
367
382
  />
368
383
  <p style={{ margin: 'var(--spacing-small) 0 0', color: 'var(--color-grey-600)' }}>
369
384
  Value:
@@ -387,6 +402,7 @@ const DisplayFormatFriendlyTemplate = () => {
387
402
  value={value}
388
403
  onChange={setValue}
389
404
  id="format-friendly"
405
+ aria-label="Event start date and time"
390
406
  />
391
407
  <p style={{ margin: 'var(--spacing-small) 0 0', color: 'var(--color-grey-600)' }}>
392
408
  Value:
@@ -410,6 +426,7 @@ const WithTimeOptionsTemplate = () => {
410
426
  value={value}
411
427
  onChange={setValue}
412
428
  id="time-options"
429
+ aria-label="Appointment date and time"
413
430
  />
414
431
  <p style={{ margin: 'var(--spacing-small) 0 0', color: 'var(--color-grey-600)' }}>
415
432
  Appointment:
@@ -433,6 +450,7 @@ const GranularitySecondTemplate = () => {
433
450
  value={value}
434
451
  onChange={setValue}
435
452
  id="granularity-second"
453
+ aria-label="Precise date and time"
436
454
  />
437
455
  <p style={{ margin: 'var(--spacing-small) 0 0', color: 'var(--color-grey-600)' }}>
438
456
  Value (with seconds):
@@ -453,6 +471,7 @@ const ControlledWithDisplayTemplate = () => {
453
471
  <div style={{ maxWidth: '280px' }}>
454
472
  <DateTimePicker
455
473
  id="controlled-display"
474
+ aria-label="Event date and time"
456
475
  onChange={setValue}
457
476
  />
458
477
  <p style={{ margin: 'var(--spacing-small) 0 0', color: 'var(--color-grey-600)' }}>
@@ -528,7 +547,8 @@ const WithFormFieldAndErrorTemplate = () => {
528
547
  export const Default: Story = withDescription(
529
548
  {
530
549
  args: {
531
- id: 'default-datetime',
550
+ 'id': 'default-datetime',
551
+ 'aria-label': 'Event start date and time',
532
552
  },
533
553
  render: args => (
534
554
  <div style={{ maxWidth: '280px' }}>
@@ -551,6 +571,7 @@ export const WithDefaultValue: Story = withDescription(
551
571
  {/* 14 April 2026, 14:27 — month is zero-indexed (Apr = 3) */}
552
572
  <DateTimePicker
553
573
  id="default-value-datetime"
574
+ aria-label="Event start date and time"
554
575
  defaultValue={new Date(2026, 3, 14, 14, 27)}
555
576
  />
556
577
  </div>
@@ -570,6 +591,7 @@ function EditEventStart() {
570
591
  return (
571
592
  <DateTimePicker
572
593
  id="event-start"
594
+ aria-label="Event start date and time"
573
595
  defaultValue={eventStart}
574
596
  onChange={(date) => {
575
597
  // date is Date | undefined — not a string
@@ -611,6 +633,7 @@ function PreciseDateTimePickerExample() {
611
633
  value={value}
612
634
  onChange={setValue}
613
635
  id="precise-datetime"
636
+ aria-label="Precise date and time"
614
637
  />
615
638
  );
616
639
  }
@@ -648,6 +671,7 @@ function FormattedDateTimePickerExample() {
648
671
  value={value}
649
672
  onChange={setValue}
650
673
  id="event-datetime"
674
+ aria-label="Event start date and time"
651
675
  />
652
676
  );
653
677
  }
@@ -685,6 +709,7 @@ function FriendlyDateTimePickerExample() {
685
709
  value={value}
686
710
  onChange={setValue}
687
711
  id="event-datetime"
712
+ aria-label="Event start date and time"
688
713
  />
689
714
  );
690
715
  }
@@ -726,6 +751,7 @@ function AppointmentBookingExample() {
726
751
  value={value}
727
752
  onChange={setValue}
728
753
  id="appointment-datetime"
754
+ aria-label="Appointment date and time"
729
755
  />
730
756
  );
731
757
  }
@@ -748,6 +774,7 @@ export const WithPlaceholder: Story = withDescription(
748
774
  <div style={{ maxWidth: '280px' }}>
749
775
  <DateTimePicker
750
776
  id="placeholder-datetime"
777
+ aria-label="Event start date and time"
751
778
  placeholder="Choose event start"
752
779
  />
753
780
  </div>
@@ -764,6 +791,7 @@ function GuidedDateTimePickerExample() {
764
791
  return (
765
792
  <DateTimePicker
766
793
  id="event-start"
794
+ aria-label="Event start date and time"
767
795
  placeholder="Choose event start"
768
796
  onChange={(date) => {
769
797
  console.log('Event start:', date?.toLocaleString('en-GB'));
@@ -790,6 +818,7 @@ export const ErrorState: Story = withDescription(
790
818
  <div style={{ maxWidth: '280px' }}>
791
819
  <DateTimePicker
792
820
  id="error-state-datetime"
821
+ aria-label="Event start date and time"
793
822
  hasError
794
823
  aria-invalid={true}
795
824
  aria-describedby="error-state-datetime-msg"
@@ -818,6 +847,7 @@ function DateTimePickerWithError() {
818
847
  <>
819
848
  <DateTimePicker
820
849
  id="event-start"
850
+ aria-label="Event start date and time"
821
851
  hasError
822
852
  aria-invalid={true}
823
853
  aria-describedby="event-start-error"
@@ -860,6 +890,7 @@ function ControlledDateTimePickerExample() {
860
890
  <div>
861
891
  <DateTimePicker
862
892
  id="event-datetime"
893
+ aria-label="Event date and time"
863
894
  onChange={setValue}
864
895
  />
865
896
  <p>
@@ -27,6 +27,21 @@ describe('DateTimePicker', () => {
27
27
  expect(screen.getByRole('button', { name: 'Open date and time picker' })).toBeInTheDocument();
28
28
  });
29
29
 
30
+ test('names the input with aria-label', () => {
31
+ const { container } = render(<DateTimePicker aria-label="Start date" />);
32
+ expect(screen.getByLabelText('Start date')).toBe(container.querySelector('input[type="datetime-local"]'));
33
+ });
34
+
35
+ test('names the input with aria-labelledby', () => {
36
+ const { container } = render(
37
+ <>
38
+ <span id="start-date-label">Start date</span>
39
+ <DateTimePicker aria-labelledby="start-date-label" />
40
+ </>,
41
+ );
42
+ expect(screen.getByLabelText('Start date')).toBe(container.querySelector('input[type="datetime-local"]'));
43
+ });
44
+
30
45
  test('uses a native datetime-local input with minute stepping by default', () => {
31
46
  const { container } = render(<DateTimePicker />);
32
47
  const input = container.querySelector('input[type="datetime-local"]');
@@ -41,6 +41,8 @@ export type DateTimePickerProps = {
41
41
  'hasError'?: boolean;
42
42
  'aria-describedby'?: string;
43
43
  'aria-invalid'?: boolean;
44
+ 'aria-label'?: string;
45
+ 'aria-labelledby'?: string;
44
46
  'value'?: Date;
45
47
  'defaultValue'?: Date;
46
48
  'granularity'?: TimeGranularity;
@@ -59,6 +61,8 @@ export const DateTimePicker = (props: DateTimePickerProps) => {
59
61
  hasError,
60
62
  'aria-describedby': ariaDescribedBy,
61
63
  'aria-invalid': ariaInvalid,
64
+ 'aria-label': ariaLabel,
65
+ 'aria-labelledby': ariaLabelledBy,
62
66
  value,
63
67
  defaultValue,
64
68
  displayFormat = 'native',
@@ -258,6 +262,8 @@ export const DateTimePicker = (props: DateTimePickerProps) => {
258
262
  hasError={hasError}
259
263
  aria-describedby={ariaDescribedBy}
260
264
  aria-invalid={ariaInvalid}
265
+ aria-label={ariaLabel}
266
+ aria-labelledby={ariaLabelledBy}
261
267
  className="ds-date-time-picker__input"
262
268
  />
263
269
  {showFieldHint
@@ -12,6 +12,7 @@ import { fn } from 'storybook/test';
12
12
  import { useEffect, useRef, useState } from 'react';
13
13
  import { Button } from 'Components/button/Button';
14
14
  import { FileUpload } from './FileUpload.js';
15
+ import type { FileUploadLink } from './FileUpload.js';
15
16
  import type { FileUploadProps } from './FileUpload.js';
16
17
 
17
18
  // ─── Content strings ──────────────────────────────────────────────────────────
@@ -88,8 +89,12 @@ it is exported you can inline the shape or declare your own local type:
88
89
  type UploadedFile = {
89
90
  fileName: string;
90
91
  icon?: string; // any IconName from the design system Icon component
91
- fileType: string; // e.g. 'PDF', 'XLSX', 'DOCX'
92
- fileSize: string; // human-readable, e.g. '1.2 MB'
92
+ fileType?: string; // e.g. 'PDF', 'XLSX', 'DOCX'
93
+ fileSize?: string; // human-readable, e.g. '1.2 MB'
94
+ href?: string; // renders the row as a link; fileName becomes the link name
95
+ linkProps?: object; // extra anchor attributes (target/rel default to new tab, noopener)
96
+ renderLink?: (args: WidgetRenderLinkArgs) => ReactElement; // e.g. router links
97
+ onEdit?: (link: { url: string; name: string }) => void; // link rows only
93
98
  onDownload?: () => void;
94
99
  onDelete: () => void;
95
100
  progressPercentage?: number; // 0–100; hidden at exactly 100
@@ -103,7 +108,7 @@ const RELATED_COMPONENTS = `
103
108
 
104
109
  - [Progress](?path=/docs/components-progress--docs) — the progress bar used
105
110
  inside each file row to indicate upload completion.
106
- - [Button](?path=/docs/components-button--docs) — the "click to upload",
111
+ - [Button](?path=/docs/components-button--docs) — the "Browse files",
107
112
  download, and delete buttons inside the component.
108
113
  - [Card](?path=/docs/components-card--docs) — the visual wrapper for both the
109
114
  dropzone area and the file list.
@@ -174,13 +179,13 @@ const meta = {
174
179
  dropzoneContent: {
175
180
  description:
176
181
  'Custom content rendered inside the dropzone area. Defaults to a '
177
- + '"Drag and drop, or click to upload" paragraph. Pass a `ReactNode` to '
182
+ + '"Drag and drop, add a link or" paragraph with a "Browse files" button. Pass a `ReactNode` to '
178
183
  + 'replace this with your own copy or layout.',
179
184
  control: false,
180
185
  table: {
181
186
  type: { summary: 'ReactNode' },
182
187
  defaultValue: {
183
- summary: '<p>Drag and drop, or <Button variant="text-link">click to upload</Button></p>',
188
+ summary: '<p>Drag and drop, add a link or</p> + <Button variant="secondary">Browse files</Button>',
184
189
  },
185
190
  },
186
191
  },
@@ -325,6 +330,69 @@ export default UploadWithFilesExample;
325
330
 
326
331
  // ─────────────────────────────────────────────────────────────────────────────
327
332
 
333
+ export const WithLinks: Story = withDescription(
334
+ {
335
+ render: () => {
336
+ const [links, setLinks] = useState<FileUploadLink[]>([
337
+ { name: 'Pastoral policy', url: 'https://example.com/pastoral-policy' },
338
+ ]);
339
+ return (
340
+ <div style={{ maxWidth: '560px', padding: 'var(--spacing-large)' }}>
341
+ <FileUpload
342
+ uploadFiles={noop}
343
+ onAddLink={link => setLinks(previous => [...previous, link])}
344
+ uploadedFiles={[
345
+ {
346
+ fileName: 'spring-term-report-2024.pdf',
347
+ fileType: 'PDF',
348
+ fileSize: '1.2 MB',
349
+ onDownload: noop,
350
+ onDelete: noop,
351
+ },
352
+ ...links.map((link, index) => ({
353
+ fileName: link.name,
354
+ href: link.url,
355
+ onEdit: (updated: FileUploadLink) =>
356
+ setLinks(previous => previous.map((item, i) => (i === index ? updated : item))),
357
+ onDelete: () => setLinks(previous => previous.filter((_, i) => i !== index)),
358
+ })),
359
+ ]}
360
+ />
361
+ </div>
362
+ );
363
+ },
364
+ parameters: {
365
+ controls: { disable: true },
366
+ docs: {
367
+ source: {
368
+ language: 'tsx',
369
+ code: `
370
+ <FileUpload
371
+ uploadFiles={(files) => { /* start upload */ }}
372
+ onAddLink={({ url, name }) => { /* save the new link */ }}
373
+ uploadedFiles={[
374
+ { fileName: 'report.pdf', fileType: 'PDF', fileSize: '1.2 MB', onDelete: () => {} },
375
+ {
376
+ fileName: 'Pastoral policy',
377
+ href: 'https://example.com/pastoral-policy',
378
+ onEdit: ({ url, name }) => { /* save the edited link */ },
379
+ onDelete: () => {},
380
+ },
381
+ ]}
382
+ />
383
+ `.trim(),
384
+ },
385
+ },
386
+ },
387
+ },
388
+ 'A mixed list of file rows and link rows. Passing `onAddLink` turns "add a link" into '
389
+ + 'a trigger for a modal form (name and URL). A row with `href` renders as a link that '
390
+ + 'opens in a new tab, with copy and delete actions; adding `onEdit` shows an edit action '
391
+ + 'that opens the same modal pre-filled.',
392
+ );
393
+
394
+ // ─────────────────────────────────────────────────────────────────────────────
395
+
328
396
  export const SingleFile: Story = withDescription(
329
397
  {
330
398
  render: () => (
@@ -721,7 +789,7 @@ export default CustomDropzoneExample;
721
789
  },
722
790
  },
723
791
  'Pass a `ReactNode` to `dropzoneContent` to replace the default '
724
- + '"Drag and drop, or click to upload" copy. This is useful when you need '
792
+ + 'default "Drag and drop" copy. This is useful when you need '
725
793
  + 'context-specific instructions or a branded call-to-action button.',
726
794
  );
727
795
 
@@ -15,7 +15,13 @@ describe('FileUpload', () => {
15
15
  describe('dropzone', () => {
16
16
  test('renders default dropzone content', () => {
17
17
  render(<FileUpload uploadedFiles={[]} uploadFiles={vi.fn()} />);
18
- expect(screen.getByText('click to upload')).toBeInTheDocument();
18
+ expect(screen.getByRole('button', { name: 'Browse files' })).toBeInTheDocument();
19
+ expect(screen.getByText('Drag and drop or')).toBeInTheDocument();
20
+ });
21
+
22
+ test('does not render the add a link trigger without onAddLink', () => {
23
+ render(<FileUpload uploadedFiles={[]} uploadFiles={vi.fn()} />);
24
+ expect(screen.queryByRole('button', { name: 'add a link' })).not.toBeInTheDocument();
19
25
  });
20
26
 
21
27
  test('renders custom dropzoneContent', () => {
@@ -169,4 +175,136 @@ describe('FileUpload', () => {
169
175
  expect(container.firstChild).toHaveClass('ds-file-upload', 'my-custom-class');
170
176
  });
171
177
  });
178
+ describe('link rows', () => {
179
+ const linkRow = {
180
+ fileName: 'Pastoral policy',
181
+ href: 'https://example.com/policy',
182
+ onDelete: vi.fn(),
183
+ };
184
+
185
+ test('renders a link row as an anchor opening in a new tab', () => {
186
+ render(<FileUpload uploadedFiles={[linkRow]} uploadFiles={vi.fn()} />);
187
+ const anchor = screen.getByRole('link', { name: 'Pastoral policy' });
188
+ expect(anchor).toHaveAttribute('href', 'https://example.com/policy');
189
+ expect(anchor).toHaveAttribute('target', '_blank');
190
+ expect(anchor).toHaveAttribute('rel', 'noopener noreferrer');
191
+ expect(screen.getByText('https://example.com/policy')).toBeInTheDocument();
192
+ });
193
+
194
+ test('merges linkProps onto the anchor', () => {
195
+ render(
196
+ <FileUpload
197
+ uploadedFiles={[{ ...linkRow, linkProps: { target: '_self' } }]}
198
+ uploadFiles={vi.fn()}
199
+ />,
200
+ );
201
+ expect(screen.getByRole('link', { name: 'Pastoral policy' })).toHaveAttribute('target', '_self');
202
+ });
203
+
204
+ test('uses renderLink when provided', () => {
205
+ render(
206
+ <FileUpload
207
+ uploadedFiles={[{
208
+ ...linkRow,
209
+ renderLink: ({ href, className, children }) => (
210
+ <a href={href} className={className} data-custom="true">{children}</a>
211
+ ),
212
+ }]}
213
+ uploadFiles={vi.fn()}
214
+ />,
215
+ );
216
+ expect(screen.getByRole('link', { name: 'Pastoral policy' })).toHaveAttribute('data-custom', 'true');
217
+ });
218
+
219
+ test('does not render a link for rows without href', () => {
220
+ render(<FileUpload uploadedFiles={[baseFile]} uploadFiles={vi.fn()} />);
221
+ expect(screen.queryByRole('link')).not.toBeInTheDocument();
222
+ expect(screen.queryByRole('button', { name: 'Edit link' })).not.toBeInTheDocument();
223
+ });
224
+
225
+ test('calls onDelete without triggering other actions', async () => {
226
+ const onDelete = vi.fn();
227
+ const onEdit = vi.fn();
228
+ render(
229
+ <FileUpload uploadedFiles={[{ ...linkRow, onDelete, onEdit }]} uploadFiles={vi.fn()} />,
230
+ );
231
+ await userEvent.click(screen.getByRole('button', { name: 'Delete link' }));
232
+ expect(onDelete).toHaveBeenCalledOnce();
233
+ expect(onEdit).not.toHaveBeenCalled();
234
+ });
235
+
236
+ test('copies the link to the clipboard', async () => {
237
+ const user = userEvent.setup();
238
+ render(<FileUpload uploadedFiles={[linkRow]} uploadFiles={vi.fn()} />);
239
+ await user.click(screen.getByRole('button', { name: 'Copy link' }));
240
+ expect(await navigator.clipboard.readText()).toBe('https://example.com/policy');
241
+ });
242
+ });
243
+
244
+ describe('adding a link', () => {
245
+ test('opens the form from the add a link trigger and submits trimmed values', async () => {
246
+ const onAddLink = vi.fn();
247
+ const uploadFiles = vi.fn();
248
+ render(<FileUpload uploadedFiles={[]} uploadFiles={uploadFiles} onAddLink={onAddLink} />);
249
+
250
+ await userEvent.click(screen.getByRole('button', { name: 'add a link' }));
251
+ expect(screen.getByRole('dialog', { name: 'Add URL attachment' })).toBeInTheDocument();
252
+ await userEvent.type(screen.getByLabelText('Name of link'), ' Policy ');
253
+ await userEvent.type(screen.getByLabelText('Paste your link URL here'), ' https://example.com ');
254
+ await userEvent.click(screen.getByRole('button', { name: 'Add' }));
255
+
256
+ expect(onAddLink).toHaveBeenCalledWith({ name: 'Policy', url: 'https://example.com' });
257
+ expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
258
+ });
259
+
260
+ test('shows validation errors and does not submit invalid values', async () => {
261
+ const onAddLink = vi.fn();
262
+ render(<FileUpload uploadedFiles={[]} uploadFiles={vi.fn()} onAddLink={onAddLink} />);
263
+
264
+ await userEvent.click(screen.getByRole('button', { name: 'add a link' }));
265
+ await userEvent.click(screen.getByRole('button', { name: 'Add' }));
266
+ expect(screen.getByText('Name is required.')).toBeInTheDocument();
267
+ expect(screen.getByText('URL is required.')).toBeInTheDocument();
268
+
269
+ await userEvent.type(screen.getByLabelText('Name of link'), 'Policy');
270
+ await userEvent.type(screen.getByLabelText('Paste your link URL here'), 'ftp://example.com');
271
+ await userEvent.click(screen.getByRole('button', { name: 'Add' }));
272
+ expect(screen.getByText('Enter a valid URL starting with http:// or https://.')).toBeInTheDocument();
273
+ expect(onAddLink).not.toHaveBeenCalled();
274
+ });
275
+
276
+ test('closes the form on cancel', async () => {
277
+ render(<FileUpload uploadedFiles={[]} uploadFiles={vi.fn()} onAddLink={vi.fn()} />);
278
+ await userEvent.click(screen.getByRole('button', { name: 'add a link' }));
279
+ await userEvent.click(screen.getByRole('button', { name: 'Cancel' }));
280
+ expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
281
+ });
282
+ });
283
+
284
+ describe('editing a link', () => {
285
+ test('opens a pre-filled form and calls onEdit with the updated link', async () => {
286
+ const onEdit = vi.fn();
287
+ render(
288
+ <FileUpload
289
+ uploadFiles={vi.fn()}
290
+ uploadedFiles={[{
291
+ fileName: 'Policy',
292
+ href: 'https://example.com',
293
+ onEdit,
294
+ onDelete: vi.fn(),
295
+ }]}
296
+ />,
297
+ );
298
+
299
+ await userEvent.click(screen.getByRole('button', { name: 'Edit link' }));
300
+ expect(screen.getByLabelText('Name of link')).toHaveValue('Policy');
301
+ expect(screen.getByLabelText('Paste your link URL here')).toHaveValue('https://example.com');
302
+
303
+ await userEvent.clear(screen.getByLabelText('Name of link'));
304
+ await userEvent.type(screen.getByLabelText('Name of link'), 'Updated');
305
+ await userEvent.click(screen.getByRole('button', { name: 'Update' }));
306
+
307
+ expect(onEdit).toHaveBeenCalledWith({ name: 'Updated', url: 'https://example.com' });
308
+ });
309
+ });
172
310
  });