@arbor-education/design-system.components 0.25.8 → 1.0.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 (432) hide show
  1. package/.gather/instructions/project-overview.md +1 -1
  2. package/.github/CODEOWNERS +1 -0
  3. package/.snyk +3 -0
  4. package/CHANGELOG.md +20 -0
  5. package/CODEOWNERS +2 -1
  6. package/CONTRIBUTING.md +7 -5
  7. package/component-library.md +97 -3
  8. package/dist/components/datePicker/DatePicker.d.ts.map +1 -1
  9. package/dist/components/datePicker/DatePicker.js +8 -6
  10. package/dist/components/datePicker/DatePicker.js.map +1 -1
  11. package/dist/components/datePicker/DatePickerCalendar.d.ts +10 -0
  12. package/dist/components/datePicker/DatePickerCalendar.d.ts.map +1 -0
  13. package/dist/components/datePicker/DatePickerCalendar.js +9 -0
  14. package/dist/components/datePicker/DatePickerCalendar.js.map +1 -0
  15. package/dist/components/dateTimePicker/DateTimePicker.d.ts.map +1 -1
  16. package/dist/components/dateTimePicker/DateTimePicker.js +11 -8
  17. package/dist/components/dateTimePicker/DateTimePicker.js.map +1 -1
  18. package/dist/components/emptyState/EmptyState.d.ts +22 -0
  19. package/dist/components/emptyState/EmptyState.d.ts.map +1 -0
  20. package/dist/components/emptyState/EmptyState.js +11 -0
  21. package/dist/components/emptyState/EmptyState.js.map +1 -0
  22. package/dist/components/emptyState/EmptyState.stories.d.ts +117 -0
  23. package/dist/components/emptyState/EmptyState.stories.d.ts.map +1 -0
  24. package/dist/components/emptyState/EmptyState.stories.js +332 -0
  25. package/dist/components/emptyState/EmptyState.stories.js.map +1 -0
  26. package/dist/components/emptyState/EmptyState.test.d.ts +2 -0
  27. package/dist/components/emptyState/EmptyState.test.d.ts.map +1 -0
  28. package/dist/components/emptyState/EmptyState.test.js +80 -0
  29. package/dist/components/emptyState/EmptyState.test.js.map +1 -0
  30. package/dist/components/favourite/FavouriteButton.d.ts +18 -0
  31. package/dist/components/favourite/FavouriteButton.d.ts.map +1 -0
  32. package/dist/components/favourite/FavouriteButton.js +16 -0
  33. package/dist/components/favourite/FavouriteButton.js.map +1 -0
  34. package/dist/components/favourite/FavouriteButton.test.d.ts +2 -0
  35. package/dist/components/favourite/FavouriteButton.test.d.ts.map +1 -0
  36. package/dist/components/favourite/FavouriteButton.test.js +37 -0
  37. package/dist/components/favourite/FavouriteButton.test.js.map +1 -0
  38. package/dist/components/filterBar/FilterBar.d.ts +1 -1
  39. package/dist/components/filterBar/FilterBar.stories.d.ts +1 -1
  40. package/dist/components/icon/Icon.d.ts +1 -1
  41. package/dist/components/icon/Icon.d.ts.map +1 -1
  42. package/dist/components/icon/Icon.js +1 -1
  43. package/dist/components/icon/Icon.js.map +1 -1
  44. package/dist/components/pill/Pill.d.ts +3 -2
  45. package/dist/components/pill/Pill.d.ts.map +1 -1
  46. package/dist/components/pill/Pill.js +15 -9
  47. package/dist/components/pill/Pill.js.map +1 -1
  48. package/dist/components/pill/Pill.stories.d.ts +21 -3
  49. package/dist/components/pill/Pill.stories.d.ts.map +1 -1
  50. package/dist/components/pill/Pill.stories.js +275 -40
  51. package/dist/components/pill/Pill.stories.js.map +1 -1
  52. package/dist/components/pill/Pill.test.js +64 -3
  53. package/dist/components/pill/Pill.test.js.map +1 -1
  54. package/dist/components/sideNavigation/SideNavigation.d.ts +34 -0
  55. package/dist/components/sideNavigation/SideNavigation.d.ts.map +1 -0
  56. package/dist/components/sideNavigation/SideNavigation.js +30 -0
  57. package/dist/components/sideNavigation/SideNavigation.js.map +1 -0
  58. package/dist/components/sideNavigation/SideNavigation.stories.d.ts +72 -0
  59. package/dist/components/sideNavigation/SideNavigation.stories.d.ts.map +1 -0
  60. package/dist/components/sideNavigation/SideNavigation.stories.js +298 -0
  61. package/dist/components/sideNavigation/SideNavigation.stories.js.map +1 -0
  62. package/dist/components/sideNavigation/SideNavigation.test.d.ts +2 -0
  63. package/dist/components/sideNavigation/SideNavigation.test.d.ts.map +1 -0
  64. package/dist/components/sideNavigation/SideNavigation.test.js +74 -0
  65. package/dist/components/sideNavigation/SideNavigation.test.js.map +1 -0
  66. package/dist/components/sideNavigation/SideNavigationContext.d.ts +10 -0
  67. package/dist/components/sideNavigation/SideNavigationContext.d.ts.map +1 -0
  68. package/dist/components/sideNavigation/SideNavigationContext.js +15 -0
  69. package/dist/components/sideNavigation/SideNavigationContext.js.map +1 -0
  70. package/dist/components/{sidebarNav/SidebarNavGroup.d.ts → sideNavigation/SideNavigationGroup.d.ts} +3 -3
  71. package/dist/components/sideNavigation/SideNavigationGroup.d.ts.map +1 -0
  72. package/dist/components/sideNavigation/SideNavigationGroup.js +16 -0
  73. package/dist/components/sideNavigation/SideNavigationGroup.js.map +1 -0
  74. package/dist/components/sideNavigation/SideNavigationGroup.test.d.ts +2 -0
  75. package/dist/components/sideNavigation/SideNavigationGroup.test.d.ts.map +1 -0
  76. package/dist/components/sideNavigation/SideNavigationGroup.test.js +58 -0
  77. package/dist/components/sideNavigation/SideNavigationGroup.test.js.map +1 -0
  78. package/dist/components/{sidebarNav/SidebarNavItem.d.ts → sideNavigation/SideNavigationItem.d.ts} +11 -12
  79. package/dist/components/sideNavigation/SideNavigationItem.d.ts.map +1 -0
  80. package/dist/components/sideNavigation/SideNavigationItem.js +28 -0
  81. package/dist/components/sideNavigation/SideNavigationItem.js.map +1 -0
  82. package/dist/components/sideNavigation/SideNavigationItem.test.d.ts +2 -0
  83. package/dist/components/sideNavigation/SideNavigationItem.test.d.ts.map +1 -0
  84. package/dist/components/sideNavigation/SideNavigationItem.test.js +65 -0
  85. package/dist/components/sideNavigation/SideNavigationItem.test.js.map +1 -0
  86. package/dist/components/sideNavigation/SideNavigationItemFavourite.d.ts +8 -0
  87. package/dist/components/sideNavigation/SideNavigationItemFavourite.d.ts.map +1 -0
  88. package/dist/components/{sidebarNav/SidebarNavItemFavourite.js → sideNavigation/SideNavigationItemFavourite.js} +5 -5
  89. package/dist/components/sideNavigation/SideNavigationItemFavourite.js.map +1 -0
  90. package/dist/components/sideNavigation/SideNavigationItemFavourite.test.d.ts +2 -0
  91. package/dist/components/sideNavigation/SideNavigationItemFavourite.test.d.ts.map +1 -0
  92. package/dist/components/sideNavigation/SideNavigationItemFavourite.test.js +31 -0
  93. package/dist/components/sideNavigation/SideNavigationItemFavourite.test.js.map +1 -0
  94. package/dist/components/sideNavigation/SideNavigationTitle.d.ts +4 -0
  95. package/dist/components/sideNavigation/SideNavigationTitle.d.ts.map +1 -0
  96. package/dist/components/sideNavigation/SideNavigationTitle.js +7 -0
  97. package/dist/components/sideNavigation/SideNavigationTitle.js.map +1 -0
  98. package/dist/components/sideNavigation/SideNavigationTitle.test.d.ts +2 -0
  99. package/dist/components/sideNavigation/SideNavigationTitle.test.d.ts.map +1 -0
  100. package/dist/components/sideNavigation/SideNavigationTitle.test.js +22 -0
  101. package/dist/components/sideNavigation/SideNavigationTitle.test.js.map +1 -0
  102. package/dist/components/sideNavigation/SideNavigationTooltip.d.ts +7 -0
  103. package/dist/components/sideNavigation/SideNavigationTooltip.d.ts.map +1 -0
  104. package/dist/components/{sidebarNav/SidebarNavTooltip.js → sideNavigation/SideNavigationTooltip.js} +2 -2
  105. package/dist/components/sideNavigation/SideNavigationTooltip.js.map +1 -0
  106. package/dist/components/sideNavigation/SideNavigationTooltip.test.d.ts +2 -0
  107. package/dist/components/sideNavigation/SideNavigationTooltip.test.d.ts.map +1 -0
  108. package/dist/components/sideNavigation/SideNavigationTooltip.test.js +26 -0
  109. package/dist/components/sideNavigation/SideNavigationTooltip.test.js.map +1 -0
  110. package/dist/components/sideNavigation/resolvePanelItemProps.d.ts +4 -0
  111. package/dist/components/sideNavigation/resolvePanelItemProps.d.ts.map +1 -0
  112. package/dist/components/{sidebarNav → sideNavigation}/resolvePanelItemProps.js +5 -4
  113. package/dist/components/sideNavigation/resolvePanelItemProps.js.map +1 -0
  114. package/dist/components/sideNavigation/resolvePanelItemProps.test.d.ts.map +1 -0
  115. package/dist/components/{sidebarNav → sideNavigation}/resolvePanelItemProps.test.js +18 -4
  116. package/dist/components/sideNavigation/resolvePanelItemProps.test.js.map +1 -0
  117. package/dist/components/sideNavigation/types.d.ts +47 -0
  118. package/dist/components/sideNavigation/types.d.ts.map +1 -0
  119. package/dist/components/sideNavigation/types.js +2 -0
  120. package/dist/components/sideNavigation/types.js.map +1 -0
  121. package/dist/components/sidebar/Sidebar.d.ts +13 -0
  122. package/dist/components/sidebar/Sidebar.d.ts.map +1 -0
  123. package/dist/components/sidebar/Sidebar.js +9 -0
  124. package/dist/components/sidebar/Sidebar.js.map +1 -0
  125. package/dist/components/sidebar/Sidebar.stories.d.ts +48 -0
  126. package/dist/components/sidebar/Sidebar.stories.d.ts.map +1 -0
  127. package/dist/components/sidebar/Sidebar.stories.js +444 -0
  128. package/dist/components/sidebar/Sidebar.stories.js.map +1 -0
  129. package/dist/components/sidebar/Sidebar.test.d.ts +2 -0
  130. package/dist/components/sidebar/Sidebar.test.d.ts.map +1 -0
  131. package/dist/components/sidebar/Sidebar.test.js +81 -0
  132. package/dist/components/sidebar/Sidebar.test.js.map +1 -0
  133. package/dist/components/sidebar/SidebarItem.d.ts +7 -0
  134. package/dist/components/sidebar/SidebarItem.d.ts.map +1 -0
  135. package/dist/components/sidebar/SidebarItem.js +11 -0
  136. package/dist/components/sidebar/SidebarItem.js.map +1 -0
  137. package/dist/components/singleUser/SingleUser.d.ts +1 -1
  138. package/dist/components/tabs/TabsItem.stories.d.ts +2 -2
  139. package/dist/components/toast/Toast.d.ts +11 -2
  140. package/dist/components/toast/Toast.d.ts.map +1 -1
  141. package/dist/components/toast/Toast.js +5 -3
  142. package/dist/components/toast/Toast.js.map +1 -1
  143. package/dist/components/toast/Toast.stories.d.ts +15 -3
  144. package/dist/components/toast/Toast.stories.d.ts.map +1 -1
  145. package/dist/components/toast/Toast.stories.js +43 -41
  146. package/dist/components/toast/Toast.stories.js.map +1 -1
  147. package/dist/components/toast/Toast.test.js +19 -4
  148. package/dist/components/toast/Toast.test.js.map +1 -1
  149. package/dist/components/toast/ToastAction.d.ts +7 -0
  150. package/dist/components/toast/ToastAction.d.ts.map +1 -0
  151. package/dist/components/toast/ToastAction.js +9 -0
  152. package/dist/components/toast/ToastAction.js.map +1 -0
  153. package/dist/components/tooltip/TooltipWrapper.d.ts +1 -0
  154. package/dist/components/tooltip/TooltipWrapper.d.ts.map +1 -1
  155. package/dist/components/tooltip/TooltipWrapper.js +2 -2
  156. package/dist/components/tooltip/TooltipWrapper.js.map +1 -1
  157. package/dist/components/widget/Widget.d.ts +18 -0
  158. package/dist/components/widget/Widget.d.ts.map +1 -0
  159. package/dist/components/widget/Widget.js +51 -0
  160. package/dist/components/widget/Widget.js.map +1 -0
  161. package/dist/components/widget/Widget.stories.d.ts +128 -0
  162. package/dist/components/widget/Widget.stories.d.ts.map +1 -0
  163. package/dist/components/widget/Widget.stories.js +417 -0
  164. package/dist/components/widget/Widget.stories.js.map +1 -0
  165. package/dist/components/widget/Widget.test.d.ts +2 -0
  166. package/dist/components/widget/Widget.test.d.ts.map +1 -0
  167. package/dist/components/widget/Widget.test.js +110 -0
  168. package/dist/components/widget/Widget.test.js.map +1 -0
  169. package/dist/components/widget/WidgetBody.d.ts +6 -0
  170. package/dist/components/widget/WidgetBody.d.ts.map +1 -0
  171. package/dist/components/widget/WidgetBody.js +20 -0
  172. package/dist/components/widget/WidgetBody.js.map +1 -0
  173. package/dist/components/widget/WidgetContext.d.ts +20 -0
  174. package/dist/components/widget/WidgetContext.d.ts.map +1 -0
  175. package/dist/components/widget/WidgetContext.js +10 -0
  176. package/dist/components/widget/WidgetContext.js.map +1 -0
  177. package/dist/components/widget/WidgetHeader.d.ts +8 -0
  178. package/dist/components/widget/WidgetHeader.d.ts.map +1 -0
  179. package/dist/components/widget/WidgetHeader.js +56 -0
  180. package/dist/components/widget/WidgetHeader.js.map +1 -0
  181. package/dist/components/widget/types.d.ts +43 -0
  182. package/dist/components/widget/types.d.ts.map +1 -0
  183. package/dist/components/widget/types.js +2 -0
  184. package/dist/components/widget/types.js.map +1 -0
  185. package/dist/components/widgetCalendar/WidgetCalendar.d.ts +35 -0
  186. package/dist/components/widgetCalendar/WidgetCalendar.d.ts.map +1 -0
  187. package/dist/components/widgetCalendar/WidgetCalendar.js +22 -0
  188. package/dist/components/widgetCalendar/WidgetCalendar.js.map +1 -0
  189. package/dist/components/widgetCalendar/WidgetCalendar.stories.d.ts +60 -0
  190. package/dist/components/widgetCalendar/WidgetCalendar.stories.d.ts.map +1 -0
  191. package/dist/components/widgetCalendar/WidgetCalendar.stories.js +140 -0
  192. package/dist/components/widgetCalendar/WidgetCalendar.stories.js.map +1 -0
  193. package/dist/components/widgetCalendar/WidgetCalendar.test.d.ts +2 -0
  194. package/dist/components/widgetCalendar/WidgetCalendar.test.d.ts.map +1 -0
  195. package/dist/components/widgetCalendar/WidgetCalendar.test.js +88 -0
  196. package/dist/components/widgetCalendar/WidgetCalendar.test.js.map +1 -0
  197. package/dist/components/widgetCalendar/WidgetCalendarItem.d.ts +17 -0
  198. package/dist/components/widgetCalendar/WidgetCalendarItem.d.ts.map +1 -0
  199. package/dist/components/widgetCalendar/WidgetCalendarItem.js +25 -0
  200. package/dist/components/widgetCalendar/WidgetCalendarItem.js.map +1 -0
  201. package/dist/components/widgetFavourites/WidgetFavourites.d.ts +12 -0
  202. package/dist/components/widgetFavourites/WidgetFavourites.d.ts.map +1 -0
  203. package/dist/components/widgetFavourites/WidgetFavourites.js +23 -0
  204. package/dist/components/widgetFavourites/WidgetFavourites.js.map +1 -0
  205. package/dist/components/widgetFavourites/WidgetFavourites.stories.d.ts +84 -0
  206. package/dist/components/widgetFavourites/WidgetFavourites.stories.d.ts.map +1 -0
  207. package/dist/components/widgetFavourites/WidgetFavourites.stories.js +210 -0
  208. package/dist/components/widgetFavourites/WidgetFavourites.stories.js.map +1 -0
  209. package/dist/components/widgetFavourites/WidgetFavourites.test.d.ts +2 -0
  210. package/dist/components/widgetFavourites/WidgetFavourites.test.d.ts.map +1 -0
  211. package/dist/components/widgetFavourites/WidgetFavourites.test.js +67 -0
  212. package/dist/components/widgetFavourites/WidgetFavourites.test.js.map +1 -0
  213. package/dist/components/widgetFavourites/WidgetFavouritesItem.d.ts +3 -0
  214. package/dist/components/widgetFavourites/WidgetFavouritesItem.d.ts.map +1 -0
  215. package/dist/components/widgetFavourites/WidgetFavouritesItem.js +10 -0
  216. package/dist/components/widgetFavourites/WidgetFavouritesItem.js.map +1 -0
  217. package/dist/components/widgetFavourites/types.d.ts +25 -0
  218. package/dist/components/widgetFavourites/types.d.ts.map +1 -0
  219. package/dist/components/widgetFavourites/types.js +2 -0
  220. package/dist/components/widgetFavourites/types.js.map +1 -0
  221. package/dist/components/widgetNotifications/WidgetNotifications.d.ts +33 -0
  222. package/dist/components/widgetNotifications/WidgetNotifications.d.ts.map +1 -0
  223. package/dist/components/widgetNotifications/WidgetNotifications.js +18 -0
  224. package/dist/components/widgetNotifications/WidgetNotifications.js.map +1 -0
  225. package/dist/components/widgetNotifications/WidgetNotifications.stories.d.ts +60 -0
  226. package/dist/components/widgetNotifications/WidgetNotifications.stories.d.ts.map +1 -0
  227. package/dist/components/widgetNotifications/WidgetNotifications.stories.js +269 -0
  228. package/dist/components/widgetNotifications/WidgetNotifications.stories.js.map +1 -0
  229. package/dist/components/widgetNotifications/WidgetNotifications.test.d.ts +2 -0
  230. package/dist/components/widgetNotifications/WidgetNotifications.test.d.ts.map +1 -0
  231. package/dist/components/widgetNotifications/WidgetNotifications.test.js +79 -0
  232. package/dist/components/widgetNotifications/WidgetNotifications.test.js.map +1 -0
  233. package/dist/components/widgetNotifications/WidgetNotificationsItem.d.ts +14 -0
  234. package/dist/components/widgetNotifications/WidgetNotificationsItem.d.ts.map +1 -0
  235. package/dist/components/widgetNotifications/WidgetNotificationsItem.js +14 -0
  236. package/dist/components/widgetNotifications/WidgetNotificationsItem.js.map +1 -0
  237. package/dist/index.css +850 -205
  238. package/dist/index.css.map +1 -1
  239. package/dist/index.d.ts +15 -9
  240. package/dist/index.d.ts.map +1 -1
  241. package/dist/index.js +15 -8
  242. package/dist/index.js.map +1 -1
  243. package/dist/utils/favourites/favouriteConstants.d.ts +12 -0
  244. package/dist/utils/favourites/favouriteConstants.d.ts.map +1 -0
  245. package/dist/utils/favourites/favouriteConstants.js +16 -0
  246. package/dist/utils/favourites/favouriteConstants.js.map +1 -0
  247. package/dist/utils/favourites/favouriteConstants.test.d.ts +2 -0
  248. package/dist/utils/favourites/favouriteConstants.test.d.ts.map +1 -0
  249. package/dist/utils/favourites/favouriteConstants.test.js +12 -0
  250. package/dist/utils/favourites/favouriteConstants.test.js.map +1 -0
  251. package/dist/utils/hooks/useControllableBoolean.d.ts.map +1 -0
  252. package/dist/utils/hooks/useControllableBoolean.js.map +1 -0
  253. package/dist/utils/hooks/useControllableBoolean.test.d.ts +2 -0
  254. package/dist/utils/hooks/useControllableBoolean.test.d.ts.map +1 -0
  255. package/dist/utils/hooks/useControllableBoolean.test.js +34 -0
  256. package/dist/utils/hooks/useControllableBoolean.test.js.map +1 -0
  257. package/package.json +1 -1
  258. package/src/components/datePicker/DatePicker.tsx +30 -35
  259. package/src/components/datePicker/DatePickerCalendar.tsx +46 -0
  260. package/src/components/dateTimePicker/DateTimePicker.tsx +66 -38
  261. package/src/components/dateTimePicker/dateTimePicker.scss +0 -2
  262. package/src/components/emptyState/EmptyState.stories.tsx +487 -0
  263. package/src/components/emptyState/EmptyState.test.tsx +140 -0
  264. package/src/components/emptyState/EmptyState.tsx +84 -0
  265. package/src/components/emptyState/emptyState.scss +63 -0
  266. package/src/components/favourite/FavouriteButton.test.tsx +83 -0
  267. package/src/components/favourite/FavouriteButton.tsx +70 -0
  268. package/src/components/favourite/favouriteButton.scss +37 -0
  269. package/src/components/icon/Icon.tsx +2 -2
  270. package/src/components/link/link.scss +26 -0
  271. package/src/components/pill/Pill.stories.tsx +432 -41
  272. package/src/components/pill/Pill.test.tsx +74 -3
  273. package/src/components/pill/Pill.tsx +26 -11
  274. package/src/components/sideNavigation/SideNavigation.stories.tsx +578 -0
  275. package/src/components/sideNavigation/SideNavigation.test.tsx +109 -0
  276. package/src/components/sideNavigation/SideNavigation.tsx +98 -0
  277. package/src/components/sideNavigation/SideNavigationContext.tsx +25 -0
  278. package/src/components/sideNavigation/SideNavigationGroup.test.tsx +103 -0
  279. package/src/components/{sidebarNav/SidebarNavGroup.tsx → sideNavigation/SideNavigationGroup.tsx} +8 -8
  280. package/src/components/sideNavigation/SideNavigationItem.test.tsx +137 -0
  281. package/src/components/sideNavigation/SideNavigationItem.tsx +125 -0
  282. package/src/components/sideNavigation/SideNavigationItemFavourite.test.tsx +54 -0
  283. package/src/components/{sidebarNav/SidebarNavItemFavourite.tsx → sideNavigation/SideNavigationItemFavourite.tsx} +7 -7
  284. package/src/components/sideNavigation/SideNavigationTitle.test.tsx +23 -0
  285. package/src/components/sideNavigation/SideNavigationTitle.tsx +13 -0
  286. package/src/components/sideNavigation/SideNavigationTooltip.test.tsx +44 -0
  287. package/src/components/{sidebarNav/SidebarNavTooltip.tsx → sideNavigation/SideNavigationTooltip.tsx} +2 -2
  288. package/src/components/{sidebarNav → sideNavigation}/resolvePanelItemProps.test.ts +20 -4
  289. package/src/components/{sidebarNav → sideNavigation}/resolvePanelItemProps.ts +8 -7
  290. package/src/components/{sidebarNav/sidebarNav.scss → sideNavigation/sideNavigation.scss} +37 -136
  291. package/src/components/sideNavigation/types.ts +56 -0
  292. package/src/components/sidebar/Sidebar.stories.tsx +670 -0
  293. package/src/components/sidebar/Sidebar.test.tsx +155 -0
  294. package/src/components/sidebar/Sidebar.tsx +27 -0
  295. package/src/components/sidebar/SidebarItem.tsx +64 -0
  296. package/src/components/sidebar/sidebar.scss +65 -0
  297. package/src/components/toast/Toast.stories.tsx +75 -67
  298. package/src/components/toast/Toast.test.tsx +40 -10
  299. package/src/components/toast/Toast.tsx +19 -5
  300. package/src/components/toast/ToastAction.tsx +17 -0
  301. package/src/components/toast/toast.scss +37 -0
  302. package/src/components/tooltip/TooltipWrapper.tsx +3 -1
  303. package/src/components/widget/Widget.stories.tsx +600 -0
  304. package/src/components/widget/Widget.test.tsx +263 -0
  305. package/src/components/widget/Widget.tsx +102 -0
  306. package/src/components/widget/WidgetBody.tsx +66 -0
  307. package/src/components/widget/WidgetContext.tsx +31 -0
  308. package/src/components/widget/WidgetHeader.tsx +155 -0
  309. package/src/components/widget/types.ts +47 -0
  310. package/src/components/widget/widget.scss +218 -0
  311. package/src/components/widgetCalendar/WidgetCalendar.stories.tsx +241 -0
  312. package/src/components/widgetCalendar/WidgetCalendar.test.tsx +137 -0
  313. package/src/components/widgetCalendar/WidgetCalendar.tsx +105 -0
  314. package/src/components/widgetCalendar/WidgetCalendarItem.tsx +105 -0
  315. package/src/components/widgetCalendar/widgetCalendar.scss +124 -0
  316. package/src/components/widgetFavourites/WidgetFavourites.stories.tsx +341 -0
  317. package/src/components/widgetFavourites/WidgetFavourites.test.tsx +127 -0
  318. package/src/components/widgetFavourites/WidgetFavourites.tsx +83 -0
  319. package/src/components/widgetFavourites/WidgetFavouritesItem.tsx +37 -0
  320. package/src/components/widgetFavourites/types.ts +26 -0
  321. package/src/components/widgetFavourites/widgetFavourites.scss +84 -0
  322. package/src/components/widgetNotifications/WidgetNotifications.stories.tsx +417 -0
  323. package/src/components/widgetNotifications/WidgetNotifications.test.tsx +152 -0
  324. package/src/components/widgetNotifications/WidgetNotifications.tsx +112 -0
  325. package/src/components/widgetNotifications/WidgetNotificationsItem.tsx +88 -0
  326. package/src/components/widgetNotifications/widgetNotifications.scss +186 -0
  327. package/src/docs/Contributing.mdx +1 -1
  328. package/src/index.scss +9 -1
  329. package/src/index.ts +22 -18
  330. package/src/tokens.scss +12 -2
  331. package/src/utils/favourites/favouriteConstants.test.ts +19 -0
  332. package/src/utils/favourites/favouriteConstants.ts +21 -0
  333. package/src/utils/hooks/useControllableBoolean.test.ts +48 -0
  334. package/dist/components/sidebarNav/SidebarNav.d.ts +0 -46
  335. package/dist/components/sidebarNav/SidebarNav.d.ts.map +0 -1
  336. package/dist/components/sidebarNav/SidebarNav.js +0 -102
  337. package/dist/components/sidebarNav/SidebarNav.js.map +0 -1
  338. package/dist/components/sidebarNav/SidebarNav.stories.d.ts +0 -61
  339. package/dist/components/sidebarNav/SidebarNav.stories.d.ts.map +0 -1
  340. package/dist/components/sidebarNav/SidebarNav.stories.js +0 -253
  341. package/dist/components/sidebarNav/SidebarNav.stories.js.map +0 -1
  342. package/dist/components/sidebarNav/SidebarNav.test.d.ts +0 -2
  343. package/dist/components/sidebarNav/SidebarNav.test.d.ts.map +0 -1
  344. package/dist/components/sidebarNav/SidebarNav.test.js +0 -240
  345. package/dist/components/sidebarNav/SidebarNav.test.js.map +0 -1
  346. package/dist/components/sidebarNav/SidebarNavContext.d.ts +0 -13
  347. package/dist/components/sidebarNav/SidebarNavContext.d.ts.map +0 -1
  348. package/dist/components/sidebarNav/SidebarNavContext.js +0 -15
  349. package/dist/components/sidebarNav/SidebarNavContext.js.map +0 -1
  350. package/dist/components/sidebarNav/SidebarNavGroup.d.ts.map +0 -1
  351. package/dist/components/sidebarNav/SidebarNavGroup.js +0 -16
  352. package/dist/components/sidebarNav/SidebarNavGroup.js.map +0 -1
  353. package/dist/components/sidebarNav/SidebarNavItem.d.ts.map +0 -1
  354. package/dist/components/sidebarNav/SidebarNavItem.js +0 -44
  355. package/dist/components/sidebarNav/SidebarNavItem.js.map +0 -1
  356. package/dist/components/sidebarNav/SidebarNavItemFavourite.d.ts +0 -8
  357. package/dist/components/sidebarNav/SidebarNavItemFavourite.d.ts.map +0 -1
  358. package/dist/components/sidebarNav/SidebarNavItemFavourite.js.map +0 -1
  359. package/dist/components/sidebarNav/SidebarNavPanel.d.ts +0 -4
  360. package/dist/components/sidebarNav/SidebarNavPanel.d.ts.map +0 -1
  361. package/dist/components/sidebarNav/SidebarNavPanel.js +0 -9
  362. package/dist/components/sidebarNav/SidebarNavPanel.js.map +0 -1
  363. package/dist/components/sidebarNav/SidebarNavPanelNav.d.ts +0 -10
  364. package/dist/components/sidebarNav/SidebarNavPanelNav.d.ts.map +0 -1
  365. package/dist/components/sidebarNav/SidebarNavPanelNav.js +0 -21
  366. package/dist/components/sidebarNav/SidebarNavPanelNav.js.map +0 -1
  367. package/dist/components/sidebarNav/SidebarNavRail.d.ts +0 -6
  368. package/dist/components/sidebarNav/SidebarNavRail.d.ts.map +0 -1
  369. package/dist/components/sidebarNav/SidebarNavRail.js +0 -7
  370. package/dist/components/sidebarNav/SidebarNavRail.js.map +0 -1
  371. package/dist/components/sidebarNav/SidebarNavRailItem.d.ts +0 -10
  372. package/dist/components/sidebarNav/SidebarNavRailItem.d.ts.map +0 -1
  373. package/dist/components/sidebarNav/SidebarNavRailItem.js +0 -25
  374. package/dist/components/sidebarNav/SidebarNavRailItem.js.map +0 -1
  375. package/dist/components/sidebarNav/SidebarNavRailList.d.ts +0 -4
  376. package/dist/components/sidebarNav/SidebarNavRailList.d.ts.map +0 -1
  377. package/dist/components/sidebarNav/SidebarNavRailList.js +0 -7
  378. package/dist/components/sidebarNav/SidebarNavRailList.js.map +0 -1
  379. package/dist/components/sidebarNav/SidebarNavRailSlot.d.ts +0 -6
  380. package/dist/components/sidebarNav/SidebarNavRailSlot.d.ts.map +0 -1
  381. package/dist/components/sidebarNav/SidebarNavRailSlot.js +0 -7
  382. package/dist/components/sidebarNav/SidebarNavRailSlot.js.map +0 -1
  383. package/dist/components/sidebarNav/SidebarNavSeparator.d.ts +0 -6
  384. package/dist/components/sidebarNav/SidebarNavSeparator.d.ts.map +0 -1
  385. package/dist/components/sidebarNav/SidebarNavSeparator.js +0 -8
  386. package/dist/components/sidebarNav/SidebarNavSeparator.js.map +0 -1
  387. package/dist/components/sidebarNav/SidebarNavTitle.d.ts +0 -4
  388. package/dist/components/sidebarNav/SidebarNavTitle.d.ts.map +0 -1
  389. package/dist/components/sidebarNav/SidebarNavTitle.js +0 -7
  390. package/dist/components/sidebarNav/SidebarNavTitle.js.map +0 -1
  391. package/dist/components/sidebarNav/SidebarNavTooltip.d.ts +0 -7
  392. package/dist/components/sidebarNav/SidebarNavTooltip.d.ts.map +0 -1
  393. package/dist/components/sidebarNav/SidebarNavTooltip.js.map +0 -1
  394. package/dist/components/sidebarNav/SidebarNavTrigger.d.ts +0 -8
  395. package/dist/components/sidebarNav/SidebarNavTrigger.d.ts.map +0 -1
  396. package/dist/components/sidebarNav/SidebarNavTrigger.js +0 -15
  397. package/dist/components/sidebarNav/SidebarNavTrigger.js.map +0 -1
  398. package/dist/components/sidebarNav/index.d.ts +0 -4
  399. package/dist/components/sidebarNav/index.d.ts.map +0 -1
  400. package/dist/components/sidebarNav/index.js +0 -3
  401. package/dist/components/sidebarNav/index.js.map +0 -1
  402. package/dist/components/sidebarNav/resolvePanelItemProps.d.ts +0 -4
  403. package/dist/components/sidebarNav/resolvePanelItemProps.d.ts.map +0 -1
  404. package/dist/components/sidebarNav/resolvePanelItemProps.js.map +0 -1
  405. package/dist/components/sidebarNav/resolvePanelItemProps.test.d.ts.map +0 -1
  406. package/dist/components/sidebarNav/resolvePanelItemProps.test.js.map +0 -1
  407. package/dist/components/sidebarNav/types.d.ts +0 -100
  408. package/dist/components/sidebarNav/types.d.ts.map +0 -1
  409. package/dist/components/sidebarNav/types.js +0 -4
  410. package/dist/components/sidebarNav/types.js.map +0 -1
  411. package/dist/components/sidebarNav/useControllableBoolean.d.ts.map +0 -1
  412. package/dist/components/sidebarNav/useControllableBoolean.js.map +0 -1
  413. package/src/components/sidebarNav/SidebarNav.stories.tsx +0 -484
  414. package/src/components/sidebarNav/SidebarNav.test.tsx +0 -611
  415. package/src/components/sidebarNav/SidebarNav.tsx +0 -230
  416. package/src/components/sidebarNav/SidebarNavContext.tsx +0 -28
  417. package/src/components/sidebarNav/SidebarNavItem.tsx +0 -162
  418. package/src/components/sidebarNav/SidebarNavPanel.tsx +0 -20
  419. package/src/components/sidebarNav/SidebarNavPanelNav.tsx +0 -55
  420. package/src/components/sidebarNav/SidebarNavRail.tsx +0 -20
  421. package/src/components/sidebarNav/SidebarNavRailItem.tsx +0 -83
  422. package/src/components/sidebarNav/SidebarNavRailList.tsx +0 -11
  423. package/src/components/sidebarNav/SidebarNavRailSlot.tsx +0 -15
  424. package/src/components/sidebarNav/SidebarNavSeparator.tsx +0 -19
  425. package/src/components/sidebarNav/SidebarNavTitle.tsx +0 -13
  426. package/src/components/sidebarNav/SidebarNavTrigger.tsx +0 -52
  427. package/src/components/sidebarNav/index.ts +0 -6
  428. package/src/components/sidebarNav/types.ts +0 -126
  429. /package/dist/components/{sidebarNav → sideNavigation}/resolvePanelItemProps.test.d.ts +0 -0
  430. /package/dist/{components/sidebarNav → utils/hooks}/useControllableBoolean.d.ts +0 -0
  431. /package/dist/{components/sidebarNav → utils/hooks}/useControllableBoolean.js +0 -0
  432. /package/src/{components/sidebarNav → utils/hooks}/useControllableBoolean.ts +0 -0
@@ -0,0 +1,155 @@
1
+ import '@testing-library/jest-dom/vitest';
2
+ import { render, screen } from '@testing-library/react';
3
+ import userEvent from '@testing-library/user-event';
4
+ import { Icon } from 'Components/icon/Icon';
5
+ import { Separator } from 'Components/separator/Separator';
6
+ import { describe, expect, test, vi } from 'vitest';
7
+ import { Sidebar } from './Sidebar.js';
8
+ import { SidebarItem } from './SidebarItem.js';
9
+
10
+ describe('Sidebar', () => {
11
+ test('renders a nav element with default aria-label', () => {
12
+ render(<Sidebar />);
13
+ expect(screen.getByRole('navigation', { name: 'Sidebar' })).toBeInTheDocument();
14
+ });
15
+
16
+ test('renders a nav element with a custom ariaLabel', () => {
17
+ render(<Sidebar ariaLabel="App navigation" />);
18
+ expect(screen.getByRole('navigation', { name: 'App navigation' })).toBeInTheDocument();
19
+ });
20
+
21
+ test('applies data-testid to the nav', () => {
22
+ render(<Sidebar />);
23
+ expect(screen.getByTestId('sidebar')).toBeInTheDocument();
24
+ });
25
+ });
26
+
27
+ describe('SidebarItem', () => {
28
+ test('renders an anchor when href is provided', () => {
29
+ render(
30
+ <Sidebar>
31
+ <SidebarItem href="/home" aria-label="Home">
32
+ <Icon name="menu" size={24} />
33
+ </SidebarItem>
34
+ </Sidebar>,
35
+ );
36
+ const link = screen.getByRole('link', { name: 'Home' });
37
+ expect(link).toBeInTheDocument();
38
+ expect(link).toHaveAttribute('href', '/home');
39
+ });
40
+
41
+ test('renders a button when no href is provided', () => {
42
+ render(
43
+ <Sidebar>
44
+ <SidebarItem aria-label="Menu" onClick={vi.fn()}>
45
+ <Icon name="menu" size={24} />
46
+ </SidebarItem>
47
+ </Sidebar>,
48
+ );
49
+ const button = screen.getByRole('button', { name: 'Menu' });
50
+ expect(button).toBeInTheDocument();
51
+ expect(button.tagName).toBe('BUTTON');
52
+ });
53
+
54
+ test('applies selected class and aria-current when current is true on a link', () => {
55
+ render(
56
+ <Sidebar>
57
+ <SidebarItem href="/home" aria-label="Home" selected>
58
+ <Icon name="menu" size={24} />
59
+ </SidebarItem>
60
+ </Sidebar>,
61
+ );
62
+ const link = screen.getByRole('link', { name: 'Home' });
63
+ expect(link).toHaveClass('ds-sidebar__item--selected');
64
+ expect(link).toHaveAttribute('aria-current', 'true');
65
+ });
66
+
67
+ test('applies selected class on a button when selected is true', () => {
68
+ render(
69
+ <Sidebar>
70
+ <SidebarItem aria-label="Menu" selected onClick={vi.fn()}>
71
+ <Icon name="menu" size={24} />
72
+ </SidebarItem>
73
+ </Sidebar>,
74
+ );
75
+ const button = screen.getByRole('button', { name: 'Menu' });
76
+ expect(button).toHaveClass('ds-sidebar__item--selected');
77
+ expect(button).toHaveAttribute('aria-current', 'true');
78
+ });
79
+
80
+ test('does not apply selected class or aria-current when current is false', () => {
81
+ render(
82
+ <Sidebar>
83
+ <SidebarItem href="/home" aria-label="Home">
84
+ <Icon name="menu" size={24} />
85
+ </SidebarItem>
86
+ </Sidebar>,
87
+ );
88
+ const link = screen.getByRole('link', { name: 'Home' });
89
+ expect(link).not.toHaveClass('ds-sidebar__item--selected');
90
+ expect(link).toHaveAttribute('aria-current', 'false');
91
+ });
92
+
93
+ test('calls onClick when the button is clicked', async () => {
94
+ const onClick = vi.fn();
95
+ render(
96
+ <Sidebar>
97
+ <SidebarItem aria-label="Menu" onClick={onClick}>
98
+ <Icon name="menu" size={24} />
99
+ </SidebarItem>
100
+ </Sidebar>,
101
+ );
102
+ await userEvent.click(screen.getByRole('button', { name: 'Menu' }));
103
+ expect(onClick).toHaveBeenCalledOnce();
104
+ });
105
+
106
+ test('passes through extra HTML attributes to the button', () => {
107
+ render(
108
+ <Sidebar>
109
+ <SidebarItem
110
+ aria-label="Toggle nav"
111
+ aria-expanded={false}
112
+ aria-controls="nav-panel"
113
+ onClick={vi.fn()}
114
+ >
115
+ <Icon name="menu" size={24} />
116
+ </SidebarItem>
117
+ </Sidebar>,
118
+ );
119
+ const button = screen.getByRole('button', { name: 'Toggle nav' });
120
+ expect(button).toHaveAttribute('aria-expanded', 'false');
121
+ expect(button).toHaveAttribute('aria-controls', 'nav-panel');
122
+ });
123
+
124
+ test('applies data-testid to the rendered element', () => {
125
+ render(
126
+ <Sidebar>
127
+ <SidebarItem href="/home" aria-label="Home" data-testid="home-item">
128
+ <Icon name="menu" size={24} />
129
+ </SidebarItem>
130
+ </Sidebar>,
131
+ );
132
+ expect(screen.getByTestId('home-item')).toBeInTheDocument();
133
+ });
134
+ });
135
+
136
+ describe('Sidebar + SidebarItem + Separator composition', () => {
137
+ test('renders items and a separator together', () => {
138
+ render(
139
+ <Sidebar ariaLabel="App navigation">
140
+ <SidebarItem href="/home" aria-label="Home">
141
+ <Icon name="menu" size={24} />
142
+ </SidebarItem>
143
+ <Separator orientation="horizontal" decorative />
144
+ <SidebarItem href="/calendar" aria-label="Calendar">
145
+ <Icon name="date" size={24} />
146
+ </SidebarItem>
147
+ </Sidebar>,
148
+ );
149
+
150
+ expect(screen.getByRole('navigation', { name: 'App navigation' })).toBeInTheDocument();
151
+ expect(screen.getByRole('link', { name: 'Home' })).toBeInTheDocument();
152
+ expect(screen.getByRole('link', { name: 'Calendar' })).toBeInTheDocument();
153
+ expect(document.querySelector('.ds-separator')).toBeInTheDocument();
154
+ });
155
+ });
@@ -0,0 +1,27 @@
1
+ import classNames from 'classnames';
2
+ import { SidebarItem, type SidebarItemProps } from './SidebarItem.js';
3
+
4
+ export type SidebarProps = {
5
+ ariaLabel?: string;
6
+ } & React.HTMLAttributes<HTMLDivElement>;
7
+
8
+ export const Sidebar = (props: SidebarProps) => {
9
+ const { ariaLabel = 'Sidebar', className, children, ...rest } = props;
10
+ return (
11
+ <nav
12
+ aria-label={ariaLabel}
13
+ className={classNames('ds-sidebar', className)}
14
+ data-testid="sidebar"
15
+ {...rest}
16
+ >
17
+ {children}
18
+ </nav>
19
+ );
20
+ };
21
+
22
+ Sidebar.Item = SidebarItem;
23
+
24
+ export namespace Sidebar {
25
+ export type Props = SidebarProps;
26
+ export type ItemProps = SidebarItemProps;
27
+ }
@@ -0,0 +1,64 @@
1
+ import classNames from 'classnames';
2
+ import { Tooltip } from 'Components/tooltip/Tooltip.js';
3
+
4
+ export type SidebarItemProps = {
5
+ 'href'?: string;
6
+ 'selected'?: boolean;
7
+ 'data-testid'?: string;
8
+ } & React.HTMLAttributes<HTMLElement>;
9
+
10
+ export const SidebarItem = (props: SidebarItemProps) => {
11
+ const {
12
+ href,
13
+ onClick,
14
+ selected,
15
+ 'aria-label': ariaLabel,
16
+ 'data-testid': dataTestId,
17
+ className,
18
+ children,
19
+ ...rest
20
+ } = props;
21
+
22
+ const itemClassName = classNames(
23
+ 'ds-sidebar__item',
24
+ { 'ds-sidebar__item--selected': Boolean(selected) },
25
+ className,
26
+ );
27
+
28
+ return (
29
+ <Tooltip>
30
+ <Tooltip.Trigger>
31
+ {href
32
+ ? (
33
+ <a
34
+ href={href}
35
+ className={itemClassName}
36
+ aria-current={selected ? 'true' : 'false'}
37
+ aria-label={ariaLabel}
38
+ onClick={onClick}
39
+ data-testid={dataTestId}
40
+ {...rest}
41
+ >
42
+ {children}
43
+ </a>
44
+ )
45
+ : (
46
+ <button
47
+ type="button"
48
+ className={itemClassName}
49
+ aria-current={selected ? 'true' : 'false'}
50
+ aria-label={ariaLabel}
51
+ onClick={onClick}
52
+ data-testid={dataTestId}
53
+ {...rest}
54
+ >
55
+ {children}
56
+ </button>
57
+ )}
58
+ </Tooltip.Trigger>
59
+ <Tooltip.Content side="right">
60
+ {ariaLabel}
61
+ </Tooltip.Content>
62
+ </Tooltip>
63
+ );
64
+ };
@@ -0,0 +1,65 @@
1
+ .ds-sidebar {
2
+ margin: 0;
3
+ width: var(--sidebar-nav-rail-width);
4
+ display: flex;
5
+ flex-direction: column;
6
+ align-items: center;
7
+ padding: var(--spacing-small);
8
+ gap: var(--spacing-large);
9
+ box-sizing: border-box;
10
+ border-right: var(--border-weight) solid var(--page-base-color-border);
11
+ }
12
+
13
+ .ds-sidebar__item {
14
+ position: relative;
15
+ display: inline-flex;
16
+ align-items: center;
17
+ justify-content: center;
18
+ width: var(--sidebar-nav-rail-item-size);
19
+ height: var(--sidebar-nav-rail-item-size);
20
+ background: transparent;
21
+ border: 0;
22
+ padding: 0;
23
+ border-radius: var(--border-radius-round);
24
+ color: var(--sidebar-nav-rail-item-color-icon);
25
+ cursor: pointer;
26
+ text-decoration: none;
27
+
28
+ &:hover,
29
+ &:active,
30
+ &[data-state='delayed-open'],
31
+ &[data-state='instant-open'],
32
+ &[data-state='open'] {
33
+ color: var(--sidebar-nav-rail-item-color-icon-hover);
34
+ background: var(--sidebar-nav-rail-item-color-background-hover);
35
+ }
36
+
37
+ &:focus {
38
+ outline: none;
39
+ }
40
+
41
+ &:focus-visible {
42
+ outline: none;
43
+ box-shadow: 0 0 0 var(--focus-border) var(--focus-color-focus);
44
+ }
45
+ }
46
+
47
+ .ds-sidebar__item--selected {
48
+ color: var(--sidebar-nav-rail-item-color-icon-selected);
49
+ background: var(--sidebar-nav-rail-item-color-background-selected);
50
+
51
+ &:hover,
52
+ &:active,
53
+ &[data-state='delayed-open'],
54
+ &[data-state='instant-open'],
55
+ &[data-state='open'] {
56
+ color: var(--sidebar-nav-rail-item-color-icon-selected);
57
+ background: var(--sidebar-nav-rail-item-color-background-selected);
58
+ }
59
+ }
60
+
61
+ .ds-sidebar__item .ds-badge {
62
+ position: absolute;
63
+ top: var(--spacing-xsmall);
64
+ right: var(--spacing-xsmall);
65
+ }
@@ -10,8 +10,9 @@ import {
10
10
  Subtitle,
11
11
  Title,
12
12
  } from '@storybook/addon-docs/blocks';
13
- import { Toast, type ToastProps } from './Toast.js';
13
+ import { Toast } from './Toast.js';
14
14
  import { Button } from 'Components/button/Button';
15
+ import { getFavouriteRemovedToastMessage } from 'Utils/favourites/favouriteConstants';
15
16
 
16
17
  // ---------------------------------------------------------------------------
17
18
  // Docs page content
@@ -52,7 +53,7 @@ const USAGE_GUIDANCE = [
52
53
  '- **Acceptable: 2 lines.** A little more context is fine — "Assessment period saved. Pupils can now submit work."',
53
54
  '- **Never: 3+ lines.** If you need that much copy, use a Banner instead.',
54
55
  '- Use sentence case. No trailing punctuation on single sentences (optional on two-sentence messages).',
55
- '- Do NOT include action links or buttons inside the toast the close button is the only interaction.',
56
+ '- Pass action controls via `toastActions`use `Toast.Action` for safe-to-ignore undo patterns',
56
57
  ].join('\n');
57
58
 
58
59
  const DEVELOPER_NOTES = [
@@ -93,9 +94,7 @@ const DEVELOPER_NOTES = [
93
94
  ' return (',
94
95
  ' <>',
95
96
  ' <Button onClick={handleSave}>Save</Button>',
96
- ' <Toast open={open} onOpenChange={setOpen} variant="success">',
97
- ' Assessment period saved',
98
- ' </Toast>',
97
+ ' <Toast open={open} onOpenChange={setOpen} variant="success" toastContent="Assessment period saved" />',
99
98
  ' </>',
100
99
  ' );',
101
100
  '}',
@@ -114,7 +113,7 @@ const DEVELOPER_NOTES = [
114
113
  '',
115
114
  '- Radix renders `role="status"` (for `type="background"`, the default) or `role="alert"`',
116
115
  ' (for `type="foreground"`). Do NOT add `aria-live` yourself — Radix handles this.',
117
- '- `children` is rendered as `Toast.Title` — it is always required for screen reader announcement.',
116
+ '- `toastContent` is rendered as `Toast.Title` — it is always required for screen reader announcement.',
118
117
  '- `type="foreground"` causes the toast to be announced immediately, interrupting the user.',
119
118
  ' Use only for genuinely urgent messages. Default `type="background"` announces at the next pause.',
120
119
  '',
@@ -139,6 +138,8 @@ const DEVELOPER_NOTES = [
139
138
  '',
140
139
  'type ToastProps = RadixToast.ToastProps & {',
141
140
  ' variant?: ToastVariant; // default: "information"',
141
+ ' toastContent: ReactNode;',
142
+ ' toastActions?: ReactNode;',
142
143
  '};',
143
144
  '```',
144
145
  ].join('\n');
@@ -184,6 +185,10 @@ const meta = {
184
185
  title: 'Components/Toast',
185
186
  component: Toast,
186
187
  tags: ['autodocs'],
188
+ args: {
189
+ variant: 'information',
190
+ toastContent: 'Attendance report generated',
191
+ },
187
192
  parameters: {
188
193
  layout: 'centered',
189
194
  docs: {
@@ -200,7 +205,7 @@ const meta = {
200
205
  defaultValue: { summary: "'information'" },
201
206
  },
202
207
  },
203
- children: {
208
+ toastContent: {
204
209
  control: 'text',
205
210
  description: [
206
211
  'The toast message. Rendered as `Toast.Title` — always required for screen reader announcement.',
@@ -288,7 +293,7 @@ const ToastStoryWrapper = ({ children }: { children: React.ReactNode }) => (
288
293
  </Toast.Provider>
289
294
  );
290
295
 
291
- const DefaultTemplate = (args: ToastProps) => {
296
+ const DefaultTemplate = (args: Toast.Props) => {
292
297
  const [open, setOpen] = useState(false);
293
298
  return (
294
299
  <ToastStoryWrapper>
@@ -312,18 +317,10 @@ const AllVariantsTemplate = () => {
312
317
  <Button onClick={() => setWarningOpen(true)}>Warning</Button>
313
318
  <Button onClick={() => setDangerOpen(true)}>Danger</Button>
314
319
  </div>
315
- <Toast variant="information" open={infoOpen} onOpenChange={setInfoOpen}>
316
- Attendance report generated
317
- </Toast>
318
- <Toast variant="success" open={successOpen} onOpenChange={setSuccessOpen}>
319
- Assessment period saved
320
- </Toast>
321
- <Toast variant="warning" open={warningOpen} onOpenChange={setWarningOpen}>
322
- Session expires in 5 minutes
323
- </Toast>
324
- <Toast variant="danger" open={dangerOpen} onOpenChange={setDangerOpen}>
325
- Failed to save changes — please try again
326
- </Toast>
320
+ <Toast variant="information" open={infoOpen} onOpenChange={setInfoOpen} toastContent="Attendance report generated" />
321
+ <Toast variant="success" open={successOpen} onOpenChange={setSuccessOpen} toastContent="Assessment period saved" />
322
+ <Toast variant="warning" open={warningOpen} onOpenChange={setWarningOpen} toastContent="Session expires in 5 minutes" />
323
+ <Toast variant="danger" open={dangerOpen} onOpenChange={setDangerOpen} toastContent="Failed to save changes — please try again" />
327
324
  </ToastStoryWrapper>
328
325
  );
329
326
  };
@@ -355,15 +352,9 @@ const ContentGuidelinesTemplate = () => {
355
352
  </span>
356
353
  </div>
357
354
  </div>
358
- <Toast variant="success" open={idealOpen} onOpenChange={setIdealOpen}>
359
- Assessment period saved
360
- </Toast>
361
- <Toast variant="success" open={acceptableOpen} onOpenChange={setAcceptableOpen}>
362
- Assessment period saved. Pupils can now submit work for grading.
363
- </Toast>
364
- <Toast variant="information" open={tooLongOpen} onOpenChange={setTooLongOpen}>
365
- Assessment period saved. Pupils can now submit work for grading. Remember to notify your department heads and check the term dates align with your reporting schedule before the deadline.
366
- </Toast>
355
+ <Toast variant="success" open={idealOpen} onOpenChange={setIdealOpen} toastContent="Assessment period saved" />
356
+ <Toast variant="success" open={acceptableOpen} onOpenChange={setAcceptableOpen} toastContent="Assessment period saved. Pupils can now submit work for grading." />
357
+ <Toast variant="information" open={tooLongOpen} onOpenChange={setTooLongOpen} toastContent="Assessment period saved. Pupils can now submit work for grading. Remember to notify your department heads and check the term dates align with your reporting schedule before the deadline." />
367
358
  </ToastStoryWrapper>
368
359
  );
369
360
  };
@@ -378,12 +369,35 @@ const WithCustomDurationTemplate = () => {
378
369
  <Button onClick={() => setShortOpen(true)}>Short (2 s)</Button>
379
370
  <Button onClick={() => setLongOpen(true)}>Long (10 s)</Button>
380
371
  </div>
381
- <Toast variant="information" open={shortOpen} onOpenChange={setShortOpen} duration={2000}>
382
- Dismissed in 2 seconds
383
- </Toast>
384
- <Toast variant="information" open={longOpen} onOpenChange={setLongOpen} duration={10000}>
385
- Dismissed in 10 seconds — use for important but non-urgent messages
386
- </Toast>
372
+ <Toast variant="information" open={shortOpen} onOpenChange={setShortOpen} duration={2000} toastContent="Dismissed in 2 seconds" />
373
+ <Toast variant="information" open={longOpen} onOpenChange={setLongOpen} duration={10000} toastContent="Dismissed in 10 seconds — use for important but non-urgent messages" />
374
+ </ToastStoryWrapper>
375
+ );
376
+ };
377
+
378
+ const WithActionTemplate = () => {
379
+ const [open, setOpen] = useState(false);
380
+
381
+ const handleUndo = () => {
382
+ setOpen(false);
383
+ };
384
+
385
+ return (
386
+ <ToastStoryWrapper>
387
+ <Button onClick={() => setOpen(true)}>Remove favourite</Button>
388
+ <Toast
389
+ open={open}
390
+ onOpenChange={setOpen}
391
+ type="foreground"
392
+ duration={10000}
393
+ variant="information"
394
+ toastContent={getFavouriteRemovedToastMessage('Daily Attendance')}
395
+ toastActions={(
396
+ <Toast.Action altText="Undo removal" onClick={handleUndo} asChild>
397
+ <Button variant="text-link" type="button" hasHorizontalPadding={false}>Undo</Button>
398
+ </Toast.Action>
399
+ )}
400
+ />
387
401
  </ToastStoryWrapper>
388
402
  );
389
403
  };
@@ -396,7 +410,7 @@ export const Default: Story = withDescription(
396
410
  {
397
411
  args: {
398
412
  variant: 'information',
399
- children: 'Attendance report generated',
413
+ toastContent: 'Attendance report generated',
400
414
  },
401
415
  render: args => <DefaultTemplate {...args} />,
402
416
  parameters: {
@@ -414,9 +428,7 @@ function ToastDemo() {
414
428
  return (
415
429
  <Toast.Provider>
416
430
  <Button onClick={() => setOpen(true)}>Show toast</Button>
417
- <Toast variant="information" open={open} onOpenChange={setOpen}>
418
- Attendance report generated
419
- </Toast>
431
+ <Toast variant="information" open={open} onOpenChange={setOpen} toastContent="Attendance report generated" />
420
432
  <Toast.Viewport />
421
433
  </Toast.Provider>
422
434
  );
@@ -430,7 +442,7 @@ export default ToastDemo;
430
442
  [
431
443
  'The default `information` toast. Click the button to trigger it — it auto-dismisses after 5 seconds',
432
444
  'or can be closed manually with the X button.',
433
- 'Tweak the **variant** and **children** controls to preview other variants.',
445
+ 'Tweak the **variant** and **toastContent** controls to preview other variants.',
434
446
  ].join(' '),
435
447
  );
436
448
 
@@ -459,18 +471,10 @@ function AllVariantsDemo() {
459
471
  <Button onClick={() => setWarningOpen(true)}>Warning</Button>
460
472
  <Button onClick={() => setDangerOpen(true)}>Danger</Button>
461
473
 
462
- <Toast variant="information" open={infoOpen} onOpenChange={setInfoOpen}>
463
- Attendance report generated
464
- </Toast>
465
- <Toast variant="success" open={successOpen} onOpenChange={setSuccessOpen}>
466
- Assessment period saved
467
- </Toast>
468
- <Toast variant="warning" open={warningOpen} onOpenChange={setWarningOpen}>
469
- Session expires in 5 minutes
470
- </Toast>
471
- <Toast variant="danger" open={dangerOpen} onOpenChange={setDangerOpen}>
472
- Failed to save changes — please try again
473
- </Toast>
474
+ <Toast variant="information" open={infoOpen} onOpenChange={setInfoOpen} toastContent="Attendance report generated" />
475
+ <Toast variant="success" open={successOpen} onOpenChange={setSuccessOpen} toastContent="Assessment period saved" />
476
+ <Toast variant="warning" open={warningOpen} onOpenChange={setWarningOpen} toastContent="Session expires in 5 minutes" />
477
+ <Toast variant="danger" open={dangerOpen} onOpenChange={setDangerOpen} toastContent="Failed to save changes — please try again" />
474
478
  <Toast.Viewport />
475
479
  </Toast.Provider>
476
480
  );
@@ -501,21 +505,13 @@ export const ContentGuidelines: Story = withDescription(
501
505
  language: 'tsx',
502
506
  code: `
503
507
  // ✅ Ideal — 1 line, past tense, no trailing punctuation
504
- <Toast variant="success" open={open} onOpenChange={setOpen}>
505
- Assessment period saved
506
- </Toast>
508
+ <Toast variant="success" open={open} onOpenChange={setOpen} toastContent="Assessment period saved" />
507
509
 
508
510
  // ✅ Acceptable — 2 lines with brief context
509
- <Toast variant="success" open={open} onOpenChange={setOpen}>
510
- Assessment period saved. Pupils can now submit work for grading.
511
- </Toast>
511
+ <Toast variant="success" open={open} onOpenChange={setOpen} toastContent="Assessment period saved. Pupils can now submit work for grading." />
512
512
 
513
513
  // ❌ Too long — switch to a Banner instead
514
- <Toast variant="information" open={open} onOpenChange={setOpen}>
515
- Assessment period saved. Pupils can now submit work for grading.
516
- Remember to notify your department heads and check the term dates
517
- align with your reporting schedule before the deadline.
518
- </Toast>
514
+ <Toast variant="information" open={open} onOpenChange={setOpen} toastContent="Assessment period saved. Pupils can now submit work for grading. Remember to notify your department heads and check the term dates align with your reporting schedule before the deadline." />
519
515
  `.trim(),
520
516
  },
521
517
  },
@@ -549,12 +545,10 @@ function CustomDurationDemo() {
549
545
  <Button onClick={() => setOpen(true)}>Show toast</Button>
550
546
 
551
547
  {/* Per-toast duration overrides the provider default (5000ms) */}
552
- <Toast variant="information" open={open} onOpenChange={setOpen} duration={10000}>
553
- This stays visible for 10 seconds
554
- </Toast>
548
+ <Toast variant="information" open={open} onOpenChange={setOpen} duration={10000} toastContent="This stays visible for 10 seconds" />
555
549
 
556
550
  {/* Pass Infinity to keep the toast open until manually dismissed */}
557
- {/* <Toast duration={Infinity}>Persistent until closed</Toast> */}
551
+ {/* <Toast duration={Infinity} toastContent="Persistent until closed" /> */}
558
552
 
559
553
  <Toast.Viewport />
560
554
  </Toast.Provider>
@@ -573,3 +567,17 @@ export default CustomDurationDemo;
573
567
  'Pass `duration={Infinity}` to keep the toast open until the user explicitly dismisses it.',
574
568
  ].join(' '),
575
569
  );
570
+
571
+ export const WithAction: Story = withDescription(
572
+ {
573
+ render: WithActionTemplate,
574
+ parameters: {
575
+ controls: { disable: true },
576
+ },
577
+ },
578
+ [
579
+ 'Use `toastActions` with `Toast.Action` for safe-to-ignore undo patterns — mirroring the Radix toast undo example.',
580
+ 'Set `type="foreground"` and a longer `duration` so keyboard users have time to act.',
581
+ 'Pass `altText` for screen reader users who need an alternative way to trigger the action.',
582
+ ].join(' '),
583
+ );
@@ -8,9 +8,7 @@ describe('Toast', () => {
8
8
  const renderToast = (props = {}) => {
9
9
  return render(
10
10
  <Toast.Provider>
11
- <Toast open={true} {...props}>
12
- Test message
13
- </Toast>
11
+ <Toast open={true} toastContent="Test message" {...props} />
14
12
  <Toast.Viewport />
15
13
  </Toast.Provider>,
16
14
  );
@@ -74,20 +72,54 @@ describe('Toast', () => {
74
72
 
75
73
  render(
76
74
  <Toast.Provider>
77
- <Toast open={true} onOpenChange={onOpenChange}>
78
- Test message
79
- </Toast>
75
+ <Toast open={true} toastContent="Test message" onOpenChange={onOpenChange} />
80
76
  <Toast.Viewport />
81
77
  </Toast.Provider>,
82
78
  );
83
79
 
84
- const closeButton = screen.getByRole('button');
80
+ const closeButton = screen.getByRole('button', { name: 'Close' });
85
81
  await user.click(closeButton);
86
82
 
87
83
  expect(onOpenChange).toHaveBeenCalled();
88
84
  });
89
85
  });
90
86
 
87
+ describe('Toast.Action', () => {
88
+ it('renders action inline with title and calls handler when clicked', async () => {
89
+ const user = userEvent.setup();
90
+ const onAction = vi.fn();
91
+
92
+ const { container } = render(
93
+ <Toast.Provider>
94
+ <Toast
95
+ open={true}
96
+ type="foreground"
97
+ duration={10000}
98
+ toastContent="Item removed"
99
+ toastActions={(
100
+ <Toast.Action altText="Undo removal" onClick={onAction}>
101
+ Undo
102
+ </Toast.Action>
103
+ )}
104
+ />
105
+ <Toast.Viewport />
106
+ </Toast.Provider>,
107
+ );
108
+
109
+ expect(screen.getByText('Item removed')).toBeInTheDocument();
110
+ expect(container.querySelector('.ds-toast__message-row')).toBeInTheDocument();
111
+ expect(container.querySelector('.ds-toast__title')?.parentElement).toHaveClass('ds-toast__message-row');
112
+
113
+ await user.click(screen.getByRole('button', { name: 'Undo' }));
114
+
115
+ expect(onAction).toHaveBeenCalled();
116
+ });
117
+
118
+ it('exposes Action component', () => {
119
+ expect(Toast.Action).toBeDefined();
120
+ });
121
+ });
122
+
91
123
  describe('Provider and Viewport', () => {
92
124
  it('exposes Provider component', () => {
93
125
  expect(Toast.Provider).toBeDefined();
@@ -112,9 +144,7 @@ describe('Toast', () => {
112
144
  renderToast();
113
145
  const { container } = render(
114
146
  <Toast.Provider>
115
- <Toast open={true} variant="success">
116
- Success message
117
- </Toast>
147
+ <Toast open={true} variant="success" toastContent="Success message" />
118
148
  <Toast.Viewport />
119
149
  </Toast.Provider>,
120
150
  );