@arbor-education/design-system.components 0.25.9 → 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 (415) hide show
  1. package/.gather/instructions/project-overview.md +1 -1
  2. package/CHANGELOG.md +14 -0
  3. package/CONTRIBUTING.md +7 -5
  4. package/component-library.md +97 -3
  5. package/dist/components/emptyState/EmptyState.d.ts +22 -0
  6. package/dist/components/emptyState/EmptyState.d.ts.map +1 -0
  7. package/dist/components/emptyState/EmptyState.js +11 -0
  8. package/dist/components/emptyState/EmptyState.js.map +1 -0
  9. package/dist/components/emptyState/EmptyState.stories.d.ts +117 -0
  10. package/dist/components/emptyState/EmptyState.stories.d.ts.map +1 -0
  11. package/dist/components/emptyState/EmptyState.stories.js +332 -0
  12. package/dist/components/emptyState/EmptyState.stories.js.map +1 -0
  13. package/dist/components/emptyState/EmptyState.test.d.ts +2 -0
  14. package/dist/components/emptyState/EmptyState.test.d.ts.map +1 -0
  15. package/dist/components/emptyState/EmptyState.test.js +80 -0
  16. package/dist/components/emptyState/EmptyState.test.js.map +1 -0
  17. package/dist/components/favourite/FavouriteButton.d.ts +18 -0
  18. package/dist/components/favourite/FavouriteButton.d.ts.map +1 -0
  19. package/dist/components/favourite/FavouriteButton.js +16 -0
  20. package/dist/components/favourite/FavouriteButton.js.map +1 -0
  21. package/dist/components/favourite/FavouriteButton.test.d.ts +2 -0
  22. package/dist/components/favourite/FavouriteButton.test.d.ts.map +1 -0
  23. package/dist/components/favourite/FavouriteButton.test.js +37 -0
  24. package/dist/components/favourite/FavouriteButton.test.js.map +1 -0
  25. package/dist/components/filterBar/FilterBar.d.ts +1 -1
  26. package/dist/components/filterBar/FilterBar.stories.d.ts +1 -1
  27. package/dist/components/icon/Icon.d.ts +1 -1
  28. package/dist/components/icon/Icon.d.ts.map +1 -1
  29. package/dist/components/icon/Icon.js +1 -1
  30. package/dist/components/icon/Icon.js.map +1 -1
  31. package/dist/components/pill/Pill.d.ts +3 -2
  32. package/dist/components/pill/Pill.d.ts.map +1 -1
  33. package/dist/components/pill/Pill.js +15 -9
  34. package/dist/components/pill/Pill.js.map +1 -1
  35. package/dist/components/pill/Pill.stories.d.ts +21 -3
  36. package/dist/components/pill/Pill.stories.d.ts.map +1 -1
  37. package/dist/components/pill/Pill.stories.js +275 -40
  38. package/dist/components/pill/Pill.stories.js.map +1 -1
  39. package/dist/components/pill/Pill.test.js +64 -3
  40. package/dist/components/pill/Pill.test.js.map +1 -1
  41. package/dist/components/sideNavigation/SideNavigation.d.ts +34 -0
  42. package/dist/components/sideNavigation/SideNavigation.d.ts.map +1 -0
  43. package/dist/components/sideNavigation/SideNavigation.js +30 -0
  44. package/dist/components/sideNavigation/SideNavigation.js.map +1 -0
  45. package/dist/components/sideNavigation/SideNavigation.stories.d.ts +72 -0
  46. package/dist/components/sideNavigation/SideNavigation.stories.d.ts.map +1 -0
  47. package/dist/components/sideNavigation/SideNavigation.stories.js +298 -0
  48. package/dist/components/sideNavigation/SideNavigation.stories.js.map +1 -0
  49. package/dist/components/sideNavigation/SideNavigation.test.d.ts +2 -0
  50. package/dist/components/sideNavigation/SideNavigation.test.d.ts.map +1 -0
  51. package/dist/components/sideNavigation/SideNavigation.test.js +74 -0
  52. package/dist/components/sideNavigation/SideNavigation.test.js.map +1 -0
  53. package/dist/components/sideNavigation/SideNavigationContext.d.ts +10 -0
  54. package/dist/components/sideNavigation/SideNavigationContext.d.ts.map +1 -0
  55. package/dist/components/sideNavigation/SideNavigationContext.js +15 -0
  56. package/dist/components/sideNavigation/SideNavigationContext.js.map +1 -0
  57. package/dist/components/{sidebarNav/SidebarNavGroup.d.ts → sideNavigation/SideNavigationGroup.d.ts} +3 -3
  58. package/dist/components/sideNavigation/SideNavigationGroup.d.ts.map +1 -0
  59. package/dist/components/sideNavigation/SideNavigationGroup.js +16 -0
  60. package/dist/components/sideNavigation/SideNavigationGroup.js.map +1 -0
  61. package/dist/components/sideNavigation/SideNavigationGroup.test.d.ts +2 -0
  62. package/dist/components/sideNavigation/SideNavigationGroup.test.d.ts.map +1 -0
  63. package/dist/components/sideNavigation/SideNavigationGroup.test.js +58 -0
  64. package/dist/components/sideNavigation/SideNavigationGroup.test.js.map +1 -0
  65. package/dist/components/{sidebarNav/SidebarNavItem.d.ts → sideNavigation/SideNavigationItem.d.ts} +11 -12
  66. package/dist/components/sideNavigation/SideNavigationItem.d.ts.map +1 -0
  67. package/dist/components/sideNavigation/SideNavigationItem.js +28 -0
  68. package/dist/components/sideNavigation/SideNavigationItem.js.map +1 -0
  69. package/dist/components/sideNavigation/SideNavigationItem.test.d.ts +2 -0
  70. package/dist/components/sideNavigation/SideNavigationItem.test.d.ts.map +1 -0
  71. package/dist/components/sideNavigation/SideNavigationItem.test.js +65 -0
  72. package/dist/components/sideNavigation/SideNavigationItem.test.js.map +1 -0
  73. package/dist/components/sideNavigation/SideNavigationItemFavourite.d.ts +8 -0
  74. package/dist/components/sideNavigation/SideNavigationItemFavourite.d.ts.map +1 -0
  75. package/dist/components/{sidebarNav/SidebarNavItemFavourite.js → sideNavigation/SideNavigationItemFavourite.js} +5 -5
  76. package/dist/components/sideNavigation/SideNavigationItemFavourite.js.map +1 -0
  77. package/dist/components/sideNavigation/SideNavigationItemFavourite.test.d.ts +2 -0
  78. package/dist/components/sideNavigation/SideNavigationItemFavourite.test.d.ts.map +1 -0
  79. package/dist/components/sideNavigation/SideNavigationItemFavourite.test.js +31 -0
  80. package/dist/components/sideNavigation/SideNavigationItemFavourite.test.js.map +1 -0
  81. package/dist/components/sideNavigation/SideNavigationTitle.d.ts +4 -0
  82. package/dist/components/sideNavigation/SideNavigationTitle.d.ts.map +1 -0
  83. package/dist/components/sideNavigation/SideNavigationTitle.js +7 -0
  84. package/dist/components/sideNavigation/SideNavigationTitle.js.map +1 -0
  85. package/dist/components/sideNavigation/SideNavigationTitle.test.d.ts +2 -0
  86. package/dist/components/sideNavigation/SideNavigationTitle.test.d.ts.map +1 -0
  87. package/dist/components/sideNavigation/SideNavigationTitle.test.js +22 -0
  88. package/dist/components/sideNavigation/SideNavigationTitle.test.js.map +1 -0
  89. package/dist/components/sideNavigation/SideNavigationTooltip.d.ts +7 -0
  90. package/dist/components/sideNavigation/SideNavigationTooltip.d.ts.map +1 -0
  91. package/dist/components/{sidebarNav/SidebarNavTooltip.js → sideNavigation/SideNavigationTooltip.js} +2 -2
  92. package/dist/components/sideNavigation/SideNavigationTooltip.js.map +1 -0
  93. package/dist/components/sideNavigation/SideNavigationTooltip.test.d.ts +2 -0
  94. package/dist/components/sideNavigation/SideNavigationTooltip.test.d.ts.map +1 -0
  95. package/dist/components/sideNavigation/SideNavigationTooltip.test.js +26 -0
  96. package/dist/components/sideNavigation/SideNavigationTooltip.test.js.map +1 -0
  97. package/dist/components/sideNavigation/resolvePanelItemProps.d.ts +4 -0
  98. package/dist/components/sideNavigation/resolvePanelItemProps.d.ts.map +1 -0
  99. package/dist/components/{sidebarNav → sideNavigation}/resolvePanelItemProps.js +5 -4
  100. package/dist/components/sideNavigation/resolvePanelItemProps.js.map +1 -0
  101. package/dist/components/sideNavigation/resolvePanelItemProps.test.d.ts.map +1 -0
  102. package/dist/components/{sidebarNav → sideNavigation}/resolvePanelItemProps.test.js +18 -4
  103. package/dist/components/sideNavigation/resolvePanelItemProps.test.js.map +1 -0
  104. package/dist/components/sideNavigation/types.d.ts +47 -0
  105. package/dist/components/sideNavigation/types.d.ts.map +1 -0
  106. package/dist/components/sideNavigation/types.js +2 -0
  107. package/dist/components/sideNavigation/types.js.map +1 -0
  108. package/dist/components/sidebar/Sidebar.d.ts +13 -0
  109. package/dist/components/sidebar/Sidebar.d.ts.map +1 -0
  110. package/dist/components/sidebar/Sidebar.js +9 -0
  111. package/dist/components/sidebar/Sidebar.js.map +1 -0
  112. package/dist/components/sidebar/Sidebar.stories.d.ts +48 -0
  113. package/dist/components/sidebar/Sidebar.stories.d.ts.map +1 -0
  114. package/dist/components/sidebar/Sidebar.stories.js +444 -0
  115. package/dist/components/sidebar/Sidebar.stories.js.map +1 -0
  116. package/dist/components/sidebar/Sidebar.test.d.ts +2 -0
  117. package/dist/components/sidebar/Sidebar.test.d.ts.map +1 -0
  118. package/dist/components/sidebar/Sidebar.test.js +81 -0
  119. package/dist/components/sidebar/Sidebar.test.js.map +1 -0
  120. package/dist/components/sidebar/SidebarItem.d.ts +7 -0
  121. package/dist/components/sidebar/SidebarItem.d.ts.map +1 -0
  122. package/dist/components/sidebar/SidebarItem.js +11 -0
  123. package/dist/components/sidebar/SidebarItem.js.map +1 -0
  124. package/dist/components/singleUser/SingleUser.d.ts +1 -1
  125. package/dist/components/tabs/TabsItem.stories.d.ts +2 -2
  126. package/dist/components/toast/Toast.d.ts +11 -2
  127. package/dist/components/toast/Toast.d.ts.map +1 -1
  128. package/dist/components/toast/Toast.js +5 -3
  129. package/dist/components/toast/Toast.js.map +1 -1
  130. package/dist/components/toast/Toast.stories.d.ts +15 -3
  131. package/dist/components/toast/Toast.stories.d.ts.map +1 -1
  132. package/dist/components/toast/Toast.stories.js +43 -41
  133. package/dist/components/toast/Toast.stories.js.map +1 -1
  134. package/dist/components/toast/Toast.test.js +19 -4
  135. package/dist/components/toast/Toast.test.js.map +1 -1
  136. package/dist/components/toast/ToastAction.d.ts +7 -0
  137. package/dist/components/toast/ToastAction.d.ts.map +1 -0
  138. package/dist/components/toast/ToastAction.js +9 -0
  139. package/dist/components/toast/ToastAction.js.map +1 -0
  140. package/dist/components/tooltip/TooltipWrapper.d.ts +1 -0
  141. package/dist/components/tooltip/TooltipWrapper.d.ts.map +1 -1
  142. package/dist/components/tooltip/TooltipWrapper.js +2 -2
  143. package/dist/components/tooltip/TooltipWrapper.js.map +1 -1
  144. package/dist/components/widget/Widget.d.ts +18 -0
  145. package/dist/components/widget/Widget.d.ts.map +1 -0
  146. package/dist/components/widget/Widget.js +51 -0
  147. package/dist/components/widget/Widget.js.map +1 -0
  148. package/dist/components/widget/Widget.stories.d.ts +128 -0
  149. package/dist/components/widget/Widget.stories.d.ts.map +1 -0
  150. package/dist/components/widget/Widget.stories.js +417 -0
  151. package/dist/components/widget/Widget.stories.js.map +1 -0
  152. package/dist/components/widget/Widget.test.d.ts +2 -0
  153. package/dist/components/widget/Widget.test.d.ts.map +1 -0
  154. package/dist/components/widget/Widget.test.js +110 -0
  155. package/dist/components/widget/Widget.test.js.map +1 -0
  156. package/dist/components/widget/WidgetBody.d.ts +6 -0
  157. package/dist/components/widget/WidgetBody.d.ts.map +1 -0
  158. package/dist/components/widget/WidgetBody.js +20 -0
  159. package/dist/components/widget/WidgetBody.js.map +1 -0
  160. package/dist/components/widget/WidgetContext.d.ts +20 -0
  161. package/dist/components/widget/WidgetContext.d.ts.map +1 -0
  162. package/dist/components/widget/WidgetContext.js +10 -0
  163. package/dist/components/widget/WidgetContext.js.map +1 -0
  164. package/dist/components/widget/WidgetHeader.d.ts +8 -0
  165. package/dist/components/widget/WidgetHeader.d.ts.map +1 -0
  166. package/dist/components/widget/WidgetHeader.js +56 -0
  167. package/dist/components/widget/WidgetHeader.js.map +1 -0
  168. package/dist/components/widget/types.d.ts +43 -0
  169. package/dist/components/widget/types.d.ts.map +1 -0
  170. package/dist/components/widget/types.js +2 -0
  171. package/dist/components/widget/types.js.map +1 -0
  172. package/dist/components/widgetCalendar/WidgetCalendar.d.ts +35 -0
  173. package/dist/components/widgetCalendar/WidgetCalendar.d.ts.map +1 -0
  174. package/dist/components/widgetCalendar/WidgetCalendar.js +22 -0
  175. package/dist/components/widgetCalendar/WidgetCalendar.js.map +1 -0
  176. package/dist/components/widgetCalendar/WidgetCalendar.stories.d.ts +60 -0
  177. package/dist/components/widgetCalendar/WidgetCalendar.stories.d.ts.map +1 -0
  178. package/dist/components/widgetCalendar/WidgetCalendar.stories.js +140 -0
  179. package/dist/components/widgetCalendar/WidgetCalendar.stories.js.map +1 -0
  180. package/dist/components/widgetCalendar/WidgetCalendar.test.d.ts +2 -0
  181. package/dist/components/widgetCalendar/WidgetCalendar.test.d.ts.map +1 -0
  182. package/dist/components/widgetCalendar/WidgetCalendar.test.js +88 -0
  183. package/dist/components/widgetCalendar/WidgetCalendar.test.js.map +1 -0
  184. package/dist/components/widgetCalendar/WidgetCalendarItem.d.ts +17 -0
  185. package/dist/components/widgetCalendar/WidgetCalendarItem.d.ts.map +1 -0
  186. package/dist/components/widgetCalendar/WidgetCalendarItem.js +25 -0
  187. package/dist/components/widgetCalendar/WidgetCalendarItem.js.map +1 -0
  188. package/dist/components/widgetFavourites/WidgetFavourites.d.ts +12 -0
  189. package/dist/components/widgetFavourites/WidgetFavourites.d.ts.map +1 -0
  190. package/dist/components/widgetFavourites/WidgetFavourites.js +23 -0
  191. package/dist/components/widgetFavourites/WidgetFavourites.js.map +1 -0
  192. package/dist/components/widgetFavourites/WidgetFavourites.stories.d.ts +84 -0
  193. package/dist/components/widgetFavourites/WidgetFavourites.stories.d.ts.map +1 -0
  194. package/dist/components/widgetFavourites/WidgetFavourites.stories.js +210 -0
  195. package/dist/components/widgetFavourites/WidgetFavourites.stories.js.map +1 -0
  196. package/dist/components/widgetFavourites/WidgetFavourites.test.d.ts +2 -0
  197. package/dist/components/widgetFavourites/WidgetFavourites.test.d.ts.map +1 -0
  198. package/dist/components/widgetFavourites/WidgetFavourites.test.js +67 -0
  199. package/dist/components/widgetFavourites/WidgetFavourites.test.js.map +1 -0
  200. package/dist/components/widgetFavourites/WidgetFavouritesItem.d.ts +3 -0
  201. package/dist/components/widgetFavourites/WidgetFavouritesItem.d.ts.map +1 -0
  202. package/dist/components/widgetFavourites/WidgetFavouritesItem.js +10 -0
  203. package/dist/components/widgetFavourites/WidgetFavouritesItem.js.map +1 -0
  204. package/dist/components/widgetFavourites/types.d.ts +25 -0
  205. package/dist/components/widgetFavourites/types.d.ts.map +1 -0
  206. package/dist/components/widgetFavourites/types.js +2 -0
  207. package/dist/components/widgetFavourites/types.js.map +1 -0
  208. package/dist/components/widgetNotifications/WidgetNotifications.d.ts +33 -0
  209. package/dist/components/widgetNotifications/WidgetNotifications.d.ts.map +1 -0
  210. package/dist/components/widgetNotifications/WidgetNotifications.js +18 -0
  211. package/dist/components/widgetNotifications/WidgetNotifications.js.map +1 -0
  212. package/dist/components/widgetNotifications/WidgetNotifications.stories.d.ts +60 -0
  213. package/dist/components/widgetNotifications/WidgetNotifications.stories.d.ts.map +1 -0
  214. package/dist/components/widgetNotifications/WidgetNotifications.stories.js +269 -0
  215. package/dist/components/widgetNotifications/WidgetNotifications.stories.js.map +1 -0
  216. package/dist/components/widgetNotifications/WidgetNotifications.test.d.ts +2 -0
  217. package/dist/components/widgetNotifications/WidgetNotifications.test.d.ts.map +1 -0
  218. package/dist/components/widgetNotifications/WidgetNotifications.test.js +79 -0
  219. package/dist/components/widgetNotifications/WidgetNotifications.test.js.map +1 -0
  220. package/dist/components/widgetNotifications/WidgetNotificationsItem.d.ts +14 -0
  221. package/dist/components/widgetNotifications/WidgetNotificationsItem.d.ts.map +1 -0
  222. package/dist/components/widgetNotifications/WidgetNotificationsItem.js +14 -0
  223. package/dist/components/widgetNotifications/WidgetNotificationsItem.js.map +1 -0
  224. package/dist/index.css +756 -78
  225. package/dist/index.css.map +1 -1
  226. package/dist/index.d.ts +15 -9
  227. package/dist/index.d.ts.map +1 -1
  228. package/dist/index.js +15 -8
  229. package/dist/index.js.map +1 -1
  230. package/dist/utils/favourites/favouriteConstants.d.ts +12 -0
  231. package/dist/utils/favourites/favouriteConstants.d.ts.map +1 -0
  232. package/dist/utils/favourites/favouriteConstants.js +16 -0
  233. package/dist/utils/favourites/favouriteConstants.js.map +1 -0
  234. package/dist/utils/favourites/favouriteConstants.test.d.ts +2 -0
  235. package/dist/utils/favourites/favouriteConstants.test.d.ts.map +1 -0
  236. package/dist/utils/favourites/favouriteConstants.test.js +12 -0
  237. package/dist/utils/favourites/favouriteConstants.test.js.map +1 -0
  238. package/dist/utils/hooks/useControllableBoolean.d.ts.map +1 -0
  239. package/dist/utils/hooks/useControllableBoolean.js.map +1 -0
  240. package/dist/utils/hooks/useControllableBoolean.test.d.ts +2 -0
  241. package/dist/utils/hooks/useControllableBoolean.test.d.ts.map +1 -0
  242. package/dist/utils/hooks/useControllableBoolean.test.js +34 -0
  243. package/dist/utils/hooks/useControllableBoolean.test.js.map +1 -0
  244. package/package.json +1 -1
  245. package/src/components/emptyState/EmptyState.stories.tsx +487 -0
  246. package/src/components/emptyState/EmptyState.test.tsx +140 -0
  247. package/src/components/emptyState/EmptyState.tsx +84 -0
  248. package/src/components/emptyState/emptyState.scss +63 -0
  249. package/src/components/favourite/FavouriteButton.test.tsx +83 -0
  250. package/src/components/favourite/FavouriteButton.tsx +70 -0
  251. package/src/components/favourite/favouriteButton.scss +37 -0
  252. package/src/components/icon/Icon.tsx +2 -2
  253. package/src/components/link/link.scss +26 -0
  254. package/src/components/pill/Pill.stories.tsx +432 -41
  255. package/src/components/pill/Pill.test.tsx +74 -3
  256. package/src/components/pill/Pill.tsx +26 -11
  257. package/src/components/sideNavigation/SideNavigation.stories.tsx +578 -0
  258. package/src/components/sideNavigation/SideNavigation.test.tsx +109 -0
  259. package/src/components/sideNavigation/SideNavigation.tsx +98 -0
  260. package/src/components/sideNavigation/SideNavigationContext.tsx +25 -0
  261. package/src/components/sideNavigation/SideNavigationGroup.test.tsx +103 -0
  262. package/src/components/{sidebarNav/SidebarNavGroup.tsx → sideNavigation/SideNavigationGroup.tsx} +8 -8
  263. package/src/components/sideNavigation/SideNavigationItem.test.tsx +137 -0
  264. package/src/components/sideNavigation/SideNavigationItem.tsx +125 -0
  265. package/src/components/sideNavigation/SideNavigationItemFavourite.test.tsx +54 -0
  266. package/src/components/{sidebarNav/SidebarNavItemFavourite.tsx → sideNavigation/SideNavigationItemFavourite.tsx} +7 -7
  267. package/src/components/sideNavigation/SideNavigationTitle.test.tsx +23 -0
  268. package/src/components/sideNavigation/SideNavigationTitle.tsx +13 -0
  269. package/src/components/sideNavigation/SideNavigationTooltip.test.tsx +44 -0
  270. package/src/components/{sidebarNav/SidebarNavTooltip.tsx → sideNavigation/SideNavigationTooltip.tsx} +2 -2
  271. package/src/components/{sidebarNav → sideNavigation}/resolvePanelItemProps.test.ts +20 -4
  272. package/src/components/{sidebarNav → sideNavigation}/resolvePanelItemProps.ts +8 -7
  273. package/src/components/{sidebarNav/sidebarNav.scss → sideNavigation/sideNavigation.scss} +37 -136
  274. package/src/components/sideNavigation/types.ts +56 -0
  275. package/src/components/sidebar/Sidebar.stories.tsx +670 -0
  276. package/src/components/sidebar/Sidebar.test.tsx +155 -0
  277. package/src/components/sidebar/Sidebar.tsx +27 -0
  278. package/src/components/sidebar/SidebarItem.tsx +64 -0
  279. package/src/components/sidebar/sidebar.scss +65 -0
  280. package/src/components/toast/Toast.stories.tsx +75 -67
  281. package/src/components/toast/Toast.test.tsx +40 -10
  282. package/src/components/toast/Toast.tsx +19 -5
  283. package/src/components/toast/ToastAction.tsx +17 -0
  284. package/src/components/toast/toast.scss +37 -0
  285. package/src/components/tooltip/TooltipWrapper.tsx +3 -1
  286. package/src/components/widget/Widget.stories.tsx +600 -0
  287. package/src/components/widget/Widget.test.tsx +263 -0
  288. package/src/components/widget/Widget.tsx +102 -0
  289. package/src/components/widget/WidgetBody.tsx +66 -0
  290. package/src/components/widget/WidgetContext.tsx +31 -0
  291. package/src/components/widget/WidgetHeader.tsx +155 -0
  292. package/src/components/widget/types.ts +47 -0
  293. package/src/components/widget/widget.scss +218 -0
  294. package/src/components/widgetCalendar/WidgetCalendar.stories.tsx +241 -0
  295. package/src/components/widgetCalendar/WidgetCalendar.test.tsx +137 -0
  296. package/src/components/widgetCalendar/WidgetCalendar.tsx +105 -0
  297. package/src/components/widgetCalendar/WidgetCalendarItem.tsx +105 -0
  298. package/src/components/widgetCalendar/widgetCalendar.scss +124 -0
  299. package/src/components/widgetFavourites/WidgetFavourites.stories.tsx +341 -0
  300. package/src/components/widgetFavourites/WidgetFavourites.test.tsx +127 -0
  301. package/src/components/widgetFavourites/WidgetFavourites.tsx +83 -0
  302. package/src/components/widgetFavourites/WidgetFavouritesItem.tsx +37 -0
  303. package/src/components/widgetFavourites/types.ts +26 -0
  304. package/src/components/widgetFavourites/widgetFavourites.scss +84 -0
  305. package/src/components/widgetNotifications/WidgetNotifications.stories.tsx +417 -0
  306. package/src/components/widgetNotifications/WidgetNotifications.test.tsx +152 -0
  307. package/src/components/widgetNotifications/WidgetNotifications.tsx +112 -0
  308. package/src/components/widgetNotifications/WidgetNotificationsItem.tsx +88 -0
  309. package/src/components/widgetNotifications/widgetNotifications.scss +186 -0
  310. package/src/docs/Contributing.mdx +1 -1
  311. package/src/index.scss +9 -1
  312. package/src/index.ts +22 -18
  313. package/src/tokens.scss +12 -2
  314. package/src/utils/favourites/favouriteConstants.test.ts +19 -0
  315. package/src/utils/favourites/favouriteConstants.ts +21 -0
  316. package/src/utils/hooks/useControllableBoolean.test.ts +48 -0
  317. package/dist/components/sidebarNav/SidebarNav.d.ts +0 -46
  318. package/dist/components/sidebarNav/SidebarNav.d.ts.map +0 -1
  319. package/dist/components/sidebarNav/SidebarNav.js +0 -102
  320. package/dist/components/sidebarNav/SidebarNav.js.map +0 -1
  321. package/dist/components/sidebarNav/SidebarNav.stories.d.ts +0 -61
  322. package/dist/components/sidebarNav/SidebarNav.stories.d.ts.map +0 -1
  323. package/dist/components/sidebarNav/SidebarNav.stories.js +0 -253
  324. package/dist/components/sidebarNav/SidebarNav.stories.js.map +0 -1
  325. package/dist/components/sidebarNav/SidebarNav.test.d.ts +0 -2
  326. package/dist/components/sidebarNav/SidebarNav.test.d.ts.map +0 -1
  327. package/dist/components/sidebarNav/SidebarNav.test.js +0 -240
  328. package/dist/components/sidebarNav/SidebarNav.test.js.map +0 -1
  329. package/dist/components/sidebarNav/SidebarNavContext.d.ts +0 -13
  330. package/dist/components/sidebarNav/SidebarNavContext.d.ts.map +0 -1
  331. package/dist/components/sidebarNav/SidebarNavContext.js +0 -15
  332. package/dist/components/sidebarNav/SidebarNavContext.js.map +0 -1
  333. package/dist/components/sidebarNav/SidebarNavGroup.d.ts.map +0 -1
  334. package/dist/components/sidebarNav/SidebarNavGroup.js +0 -16
  335. package/dist/components/sidebarNav/SidebarNavGroup.js.map +0 -1
  336. package/dist/components/sidebarNav/SidebarNavItem.d.ts.map +0 -1
  337. package/dist/components/sidebarNav/SidebarNavItem.js +0 -44
  338. package/dist/components/sidebarNav/SidebarNavItem.js.map +0 -1
  339. package/dist/components/sidebarNav/SidebarNavItemFavourite.d.ts +0 -8
  340. package/dist/components/sidebarNav/SidebarNavItemFavourite.d.ts.map +0 -1
  341. package/dist/components/sidebarNav/SidebarNavItemFavourite.js.map +0 -1
  342. package/dist/components/sidebarNav/SidebarNavPanel.d.ts +0 -4
  343. package/dist/components/sidebarNav/SidebarNavPanel.d.ts.map +0 -1
  344. package/dist/components/sidebarNav/SidebarNavPanel.js +0 -9
  345. package/dist/components/sidebarNav/SidebarNavPanel.js.map +0 -1
  346. package/dist/components/sidebarNav/SidebarNavPanelNav.d.ts +0 -10
  347. package/dist/components/sidebarNav/SidebarNavPanelNav.d.ts.map +0 -1
  348. package/dist/components/sidebarNav/SidebarNavPanelNav.js +0 -21
  349. package/dist/components/sidebarNav/SidebarNavPanelNav.js.map +0 -1
  350. package/dist/components/sidebarNav/SidebarNavRail.d.ts +0 -6
  351. package/dist/components/sidebarNav/SidebarNavRail.d.ts.map +0 -1
  352. package/dist/components/sidebarNav/SidebarNavRail.js +0 -7
  353. package/dist/components/sidebarNav/SidebarNavRail.js.map +0 -1
  354. package/dist/components/sidebarNav/SidebarNavRailItem.d.ts +0 -10
  355. package/dist/components/sidebarNav/SidebarNavRailItem.d.ts.map +0 -1
  356. package/dist/components/sidebarNav/SidebarNavRailItem.js +0 -25
  357. package/dist/components/sidebarNav/SidebarNavRailItem.js.map +0 -1
  358. package/dist/components/sidebarNav/SidebarNavRailList.d.ts +0 -4
  359. package/dist/components/sidebarNav/SidebarNavRailList.d.ts.map +0 -1
  360. package/dist/components/sidebarNav/SidebarNavRailList.js +0 -7
  361. package/dist/components/sidebarNav/SidebarNavRailList.js.map +0 -1
  362. package/dist/components/sidebarNav/SidebarNavRailSlot.d.ts +0 -6
  363. package/dist/components/sidebarNav/SidebarNavRailSlot.d.ts.map +0 -1
  364. package/dist/components/sidebarNav/SidebarNavRailSlot.js +0 -7
  365. package/dist/components/sidebarNav/SidebarNavRailSlot.js.map +0 -1
  366. package/dist/components/sidebarNav/SidebarNavSeparator.d.ts +0 -6
  367. package/dist/components/sidebarNav/SidebarNavSeparator.d.ts.map +0 -1
  368. package/dist/components/sidebarNav/SidebarNavSeparator.js +0 -8
  369. package/dist/components/sidebarNav/SidebarNavSeparator.js.map +0 -1
  370. package/dist/components/sidebarNav/SidebarNavTitle.d.ts +0 -4
  371. package/dist/components/sidebarNav/SidebarNavTitle.d.ts.map +0 -1
  372. package/dist/components/sidebarNav/SidebarNavTitle.js +0 -7
  373. package/dist/components/sidebarNav/SidebarNavTitle.js.map +0 -1
  374. package/dist/components/sidebarNav/SidebarNavTooltip.d.ts +0 -7
  375. package/dist/components/sidebarNav/SidebarNavTooltip.d.ts.map +0 -1
  376. package/dist/components/sidebarNav/SidebarNavTooltip.js.map +0 -1
  377. package/dist/components/sidebarNav/SidebarNavTrigger.d.ts +0 -8
  378. package/dist/components/sidebarNav/SidebarNavTrigger.d.ts.map +0 -1
  379. package/dist/components/sidebarNav/SidebarNavTrigger.js +0 -15
  380. package/dist/components/sidebarNav/SidebarNavTrigger.js.map +0 -1
  381. package/dist/components/sidebarNav/index.d.ts +0 -4
  382. package/dist/components/sidebarNav/index.d.ts.map +0 -1
  383. package/dist/components/sidebarNav/index.js +0 -3
  384. package/dist/components/sidebarNav/index.js.map +0 -1
  385. package/dist/components/sidebarNav/resolvePanelItemProps.d.ts +0 -4
  386. package/dist/components/sidebarNav/resolvePanelItemProps.d.ts.map +0 -1
  387. package/dist/components/sidebarNav/resolvePanelItemProps.js.map +0 -1
  388. package/dist/components/sidebarNav/resolvePanelItemProps.test.d.ts.map +0 -1
  389. package/dist/components/sidebarNav/resolvePanelItemProps.test.js.map +0 -1
  390. package/dist/components/sidebarNav/types.d.ts +0 -100
  391. package/dist/components/sidebarNav/types.d.ts.map +0 -1
  392. package/dist/components/sidebarNav/types.js +0 -4
  393. package/dist/components/sidebarNav/types.js.map +0 -1
  394. package/dist/components/sidebarNav/useControllableBoolean.d.ts.map +0 -1
  395. package/dist/components/sidebarNav/useControllableBoolean.js.map +0 -1
  396. package/src/components/sidebarNav/SidebarNav.stories.tsx +0 -484
  397. package/src/components/sidebarNav/SidebarNav.test.tsx +0 -611
  398. package/src/components/sidebarNav/SidebarNav.tsx +0 -230
  399. package/src/components/sidebarNav/SidebarNavContext.tsx +0 -28
  400. package/src/components/sidebarNav/SidebarNavItem.tsx +0 -162
  401. package/src/components/sidebarNav/SidebarNavPanel.tsx +0 -20
  402. package/src/components/sidebarNav/SidebarNavPanelNav.tsx +0 -55
  403. package/src/components/sidebarNav/SidebarNavRail.tsx +0 -20
  404. package/src/components/sidebarNav/SidebarNavRailItem.tsx +0 -83
  405. package/src/components/sidebarNav/SidebarNavRailList.tsx +0 -11
  406. package/src/components/sidebarNav/SidebarNavRailSlot.tsx +0 -15
  407. package/src/components/sidebarNav/SidebarNavSeparator.tsx +0 -19
  408. package/src/components/sidebarNav/SidebarNavTitle.tsx +0 -13
  409. package/src/components/sidebarNav/SidebarNavTrigger.tsx +0 -52
  410. package/src/components/sidebarNav/index.ts +0 -6
  411. package/src/components/sidebarNav/types.ts +0 -126
  412. /package/dist/components/{sidebarNav → sideNavigation}/resolvePanelItemProps.test.d.ts +0 -0
  413. /package/dist/{components/sidebarNav → utils/hooks}/useControllableBoolean.d.ts +0 -0
  414. /package/dist/{components/sidebarNav → utils/hooks}/useControllableBoolean.js +0 -0
  415. /package/src/{components/sidebarNav → utils/hooks}/useControllableBoolean.ts +0 -0
@@ -0,0 +1,263 @@
1
+ import '@testing-library/jest-dom/vitest';
2
+ import { fireEvent, render, screen } from '@testing-library/react';
3
+ import { EmptyState } from 'Components/emptyState/EmptyState';
4
+ import { Icon } from 'Components/icon/Icon';
5
+ import { describe, expect, test, vi } from 'vitest';
6
+ import { Widget } from './Widget.js';
7
+
8
+ describe('Widget', () => {
9
+ test('throws when used outside Widget context', () => {
10
+ expect(() => render(<Widget.Header>Title</Widget.Header>)).toThrow(
11
+ 'Widget compound components must be used within <Widget>.',
12
+ );
13
+ });
14
+
15
+ test('renders static header without link or chevron', () => {
16
+ const { container } = render(
17
+ <Widget>
18
+ <Widget.Header>Alerts (0)</Widget.Header>
19
+ <Widget.Body />
20
+ </Widget>,
21
+ );
22
+
23
+ expect(screen.getByRole('heading', { level: 3, name: 'Alerts (0)' })).toBeInTheDocument();
24
+ expect(container.querySelector('a')).not.toBeInTheDocument();
25
+ expect(container.querySelector('.ds-widget__header-chevron')).not.toBeInTheDocument();
26
+ });
27
+
28
+ test('renders slotEnd in header end region', () => {
29
+ render(
30
+ <Widget>
31
+ <Widget.Header slotEnd={<button type="button">Mark all as read</button>}>
32
+ Notifications
33
+ </Widget.Header>
34
+ <Widget.Body />
35
+ </Widget>,
36
+ );
37
+
38
+ expect(screen.getByRole('button', { name: 'Mark all as read' })).toBeInTheDocument();
39
+ expect(screen.getByRole('button', { name: 'Mark all as read' }).closest('.ds-widget__header-end')).toBeInTheDocument();
40
+ });
41
+
42
+ test('renders linked header with anchor and chevron', () => {
43
+ const { container } = render(
44
+ <Widget href="/alerts">
45
+ <Widget.Header>Alerts (2)</Widget.Header>
46
+ <Widget.Body />
47
+ </Widget>,
48
+ );
49
+
50
+ const link = screen.getByRole('link', { name: 'Alerts (2)' });
51
+ const header = container.querySelector('.ds-widget__header');
52
+
53
+ expect(link).toHaveAttribute('href', '/alerts');
54
+ expect(header).toHaveClass('ds-widget__header--linked');
55
+ expect(container.querySelector('.ds-widget__header-chevron')).toBeInTheDocument();
56
+ expect(link.querySelector('.ds-widget__header-chevron')).not.toBeInTheDocument();
57
+ expect(container.querySelector('.ds-widget__header-end .ds-widget__header-chevron')).toBeInTheDocument();
58
+ });
59
+
60
+ test('uses renderLink when provided', () => {
61
+ const renderLink = vi.fn(({ href, className, children }) => (
62
+ <a href={href} className={className} data-testid="custom-link">{children}</a>
63
+ ));
64
+
65
+ render(
66
+ <Widget href="/alerts" renderLink={renderLink}>
67
+ <Widget.Header>Alerts (2)</Widget.Header>
68
+ <Widget.Body />
69
+ </Widget>,
70
+ );
71
+
72
+ expect(renderLink).toHaveBeenCalledWith(
73
+ expect.objectContaining({
74
+ href: '/alerts',
75
+ className: 'ds-widget__header-link',
76
+ }),
77
+ );
78
+ expect(screen.getByTestId('custom-link')).toBeInTheDocument();
79
+ });
80
+
81
+ test('renders tooltip trigger as sibling of heading in heading row', () => {
82
+ const { container } = render(
83
+ <Widget tooltip="Your starred items appear here">
84
+ <Widget.Header>Favourites</Widget.Header>
85
+ <Widget.Body />
86
+ </Widget>,
87
+ );
88
+
89
+ const headingRow = container.querySelector('.ds-widget__header-heading-row');
90
+
91
+ expect(headingRow).toBeInTheDocument();
92
+ expect(headingRow?.querySelector('.ds-widget__header-heading')).toBeInTheDocument();
93
+ expect(headingRow?.querySelector('.ds-widget__header-tooltip-trigger')).toBeInTheDocument();
94
+ expect(screen.getByRole('heading', { name: 'Favourites' }).closest('button')).not.toBeInTheDocument();
95
+ });
96
+
97
+ test('collapsible header toggles body visibility', () => {
98
+ render(
99
+ <Widget collapsible defaultExpanded={false}>
100
+ <Widget.Header>Attendance</Widget.Header>
101
+ <Widget.Body><p>Body content</p></Widget.Body>
102
+ </Widget>,
103
+ );
104
+
105
+ expect(screen.queryByText('Body content')).not.toBeInTheDocument();
106
+ expect(screen.getByRole('heading', { level: 3, name: 'Attendance' })).toBeInTheDocument();
107
+
108
+ const toggle = screen.getByRole('button', { name: 'Attendance' });
109
+
110
+ expect(toggle.querySelector('h3')).toBeNull();
111
+ expect(toggle).toHaveAttribute('aria-expanded', 'false');
112
+
113
+ fireEvent.click(toggle);
114
+
115
+ expect(toggle).toHaveAttribute('aria-expanded', 'true');
116
+ expect(screen.getByText('Body content')).toBeInTheDocument();
117
+ });
118
+
119
+ test('collapsible chevron rotates when expanded', () => {
120
+ const { container } = render(
121
+ <Widget collapsible defaultExpanded>
122
+ <Widget.Header>Attendance</Widget.Header>
123
+ <Widget.Body><p>Body content</p></Widget.Body>
124
+ </Widget>,
125
+ );
126
+
127
+ expect(container.querySelector('.ds-widget__header-toggle[aria-expanded="true"] .ds-widget__header-chevron')).toBeInTheDocument();
128
+ });
129
+
130
+ test('throws in dev when href and collapsible are both set', () => {
131
+ expect(() => render(
132
+ <Widget
133
+ {...({
134
+ href: '/alerts',
135
+ collapsible: true,
136
+ } as unknown as Widget.Props)}
137
+ >
138
+ <Widget.Header>Alerts</Widget.Header>
139
+ <Widget.Body />
140
+ </Widget>,
141
+ )).toThrow('Widget cannot have both href and collapsible. Use one or the other.');
142
+ });
143
+
144
+ test('renders empty state with h4 heading nested under h3 widget header', () => {
145
+ render(
146
+ <Widget
147
+ isEmpty
148
+ emptyState={(
149
+ <EmptyState
150
+ heading="All clear!"
151
+ subHeading="There are no alerts for you right now."
152
+ />
153
+ )}
154
+ >
155
+ <Widget.Header>Alerts (0)</Widget.Header>
156
+ <Widget.Body />
157
+ </Widget>,
158
+ );
159
+
160
+ expect(screen.getByRole('heading', { level: 3, name: 'Alerts (0)' })).toBeInTheDocument();
161
+ expect(screen.getByRole('heading', { level: 4, name: 'All clear!' })).toBeInTheDocument();
162
+ });
163
+
164
+ test('renders empty state when isEmpty is true', () => {
165
+ render(
166
+ <Widget
167
+ isEmpty
168
+ emptyState={(
169
+ <EmptyState
170
+ heading="All clear!"
171
+ subHeading="There are no alerts for you right now."
172
+ />
173
+ )}
174
+ >
175
+ <Widget.Header>Alerts (0)</Widget.Header>
176
+ <Widget.Body />
177
+ </Widget>,
178
+ );
179
+
180
+ expect(screen.getByRole('heading', { name: 'All clear!' })).toBeInTheDocument();
181
+ });
182
+
183
+ test('sets aria-busy on body when isLoading is true', () => {
184
+ render(
185
+ <Widget isLoading loadingLabel="Loading alerts">
186
+ <Widget.Header>Alerts</Widget.Header>
187
+ <Widget.Body><p>Body content</p></Widget.Body>
188
+ </Widget>,
189
+ );
190
+
191
+ expect(screen.getByRole('status', { name: 'Loading alerts' })).toBeInTheDocument();
192
+ expect(screen.queryByText('Body content')).not.toBeInTheDocument();
193
+ expect(document.querySelector('.ds-widget__body')).toHaveAttribute('aria-busy', 'true');
194
+ });
195
+
196
+ test('sets aria-live on body when live is true', () => {
197
+ const { container } = render(
198
+ <Widget live isEmpty emptyState={<EmptyState heading="Empty" />}>
199
+ <Widget.Header>Alerts</Widget.Header>
200
+ <Widget.Body />
201
+ </Widget>,
202
+ );
203
+
204
+ expect(container.querySelector('.ds-widget__body[aria-live="polite"]')).toBeInTheDocument();
205
+ });
206
+
207
+ test('linked header keeps tooltip outside link wrapper', () => {
208
+ render(
209
+ <Widget href="/alerts" tooltip="About alerts">
210
+ <Widget.Header>Alerts (2)</Widget.Header>
211
+ <Widget.Body />
212
+ </Widget>,
213
+ );
214
+
215
+ const link = screen.getByRole('link', { name: 'Alerts (2)' });
216
+ const tooltipButton = screen.getByRole('button', { name: 'About alerts' });
217
+
218
+ expect(link.contains(tooltipButton)).toBe(false);
219
+ });
220
+
221
+ test('applies floating modifier when isFloating is true', () => {
222
+ const { container } = render(
223
+ <Widget isFloating>
224
+ <Widget.Header>Favourites</Widget.Header>
225
+ <Widget.Body />
226
+ </Widget>,
227
+ );
228
+
229
+ expect(container.querySelector('.ds-widget.ds-widget--floating')).toBeInTheDocument();
230
+ });
231
+
232
+ test('does not apply floating modifier by default', () => {
233
+ const { container } = render(
234
+ <Widget>
235
+ <Widget.Header>Favourites</Widget.Header>
236
+ <Widget.Body />
237
+ </Widget>,
238
+ );
239
+
240
+ expect(container.querySelector('.ds-widget--floating')).not.toBeInTheDocument();
241
+ });
242
+
243
+ test('renders empty state with illustration inside widget', () => {
244
+ render(
245
+ <Widget
246
+ isEmpty
247
+ emptyState={(
248
+ <EmptyState
249
+ illustration={<Icon name="circle-help" size={24} />}
250
+ heading="All clear!"
251
+ subHeading="There are no alerts for you right now."
252
+ />
253
+ )}
254
+ >
255
+ <Widget.Header>Alerts (0)</Widget.Header>
256
+ <Widget.Body />
257
+ </Widget>,
258
+ );
259
+
260
+ expect(screen.getByRole('region')).toBeInTheDocument();
261
+ expect(screen.getByRole('heading', { name: 'All clear!' })).toBeInTheDocument();
262
+ });
263
+ });
@@ -0,0 +1,102 @@
1
+ import classNames from 'classnames';
2
+ import { Card } from 'Components/card/Card';
3
+ import { useId } from 'react';
4
+ import { useControllableBoolean } from 'Utils/hooks/useControllableBoolean';
5
+ import type {
6
+ WidgetCollapsibleProps,
7
+ WidgetLinkedProps,
8
+ WidgetProps,
9
+ WidgetRenderLinkArgs,
10
+ WidgetStaticProps,
11
+ } from './types.js';
12
+ import type { WidgetBodyProps } from './WidgetBody.js';
13
+ import { WidgetBody } from './WidgetBody.js';
14
+ import { WidgetContext } from './WidgetContext.js';
15
+ import type { WidgetHeaderProps } from './WidgetHeader.js';
16
+ import { WidgetHeader } from './WidgetHeader.js';
17
+
18
+ const HREF_COLLAPSIBLE_ERROR = 'Widget cannot have both href and collapsible. Use one or the other.';
19
+
20
+ function assertValidWidgetMode(props: WidgetProps): void {
21
+ const href = 'href' in props ? props.href : undefined;
22
+ const collapsible = props.collapsible === true;
23
+
24
+ if (href && collapsible) {
25
+ if (process.env.NODE_ENV !== 'production') {
26
+ throw new Error(HREF_COLLAPSIBLE_ERROR);
27
+ }
28
+
29
+ console.error(HREF_COLLAPSIBLE_ERROR);
30
+ }
31
+ }
32
+
33
+ export const Widget = (props: WidgetProps): React.JSX.Element => {
34
+ assertValidWidgetMode(props);
35
+
36
+ const {
37
+ children,
38
+ className,
39
+ tooltip,
40
+ isFloating = false,
41
+ isLoading = false,
42
+ isEmpty = false,
43
+ emptyState,
44
+ loadingLabel = 'Loading',
45
+ live = false,
46
+ expanded,
47
+ defaultExpanded = true,
48
+ onExpandedChange,
49
+ } = props;
50
+
51
+ const href = 'href' in props ? props.href : undefined;
52
+ const renderLink = 'renderLink' in props ? props.renderLink : undefined;
53
+ const linkProps = 'linkProps' in props ? props.linkProps : undefined;
54
+ const collapsible = props.collapsible === true;
55
+
56
+ const bodyId = useId();
57
+ const { value: isExpanded, set: setExpanded } = useControllableBoolean({
58
+ value: expanded,
59
+ defaultValue: defaultExpanded,
60
+ onChange: onExpandedChange,
61
+ });
62
+
63
+ return (
64
+ <WidgetContext.Provider
65
+ value={{
66
+ bodyId,
67
+ expanded: isExpanded,
68
+ setExpanded,
69
+ collapsible,
70
+ href,
71
+ renderLink,
72
+ linkProps,
73
+ tooltip,
74
+ isLoading,
75
+ isEmpty,
76
+ emptyState,
77
+ loadingLabel,
78
+ live,
79
+ }}
80
+ >
81
+ <Card
82
+ spacing="dense"
83
+ className={classNames('ds-widget', { 'ds-widget--floating': isFloating }, className)}
84
+ >
85
+ {children}
86
+ </Card>
87
+ </WidgetContext.Provider>
88
+ );
89
+ };
90
+
91
+ Widget.Header = WidgetHeader;
92
+ Widget.Body = WidgetBody;
93
+
94
+ export namespace Widget {
95
+ export type Props = WidgetProps;
96
+ export type HeaderProps = WidgetHeaderProps;
97
+ export type BodyProps = WidgetBodyProps;
98
+ export type RenderLinkArgs = WidgetRenderLinkArgs;
99
+ export type LinkedProps = WidgetLinkedProps;
100
+ export type CollapsibleProps = WidgetCollapsibleProps;
101
+ export type StaticProps = WidgetStaticProps;
102
+ }
@@ -0,0 +1,66 @@
1
+ import classNames from 'classnames';
2
+ import { Icon } from 'Components/icon/Icon';
3
+ import { useWidgetContext } from './WidgetContext.js';
4
+
5
+ export type WidgetBodyProps = {
6
+ children?: React.ReactNode;
7
+ className?: string;
8
+ };
9
+
10
+ export const WidgetBody = (props: WidgetBodyProps): React.JSX.Element => {
11
+ const {
12
+ children,
13
+ className,
14
+ } = props;
15
+
16
+ const {
17
+ bodyId,
18
+ expanded,
19
+ collapsible,
20
+ isLoading,
21
+ isEmpty,
22
+ emptyState,
23
+ loadingLabel,
24
+ live,
25
+ } = useWidgetContext();
26
+
27
+ const isCollapsed = collapsible && !expanded;
28
+
29
+ let content: React.ReactNode = children;
30
+
31
+ if (!isCollapsed) {
32
+ if (isLoading) {
33
+ content = (
34
+ <div
35
+ role="status"
36
+ className="ds-widget__loading"
37
+ aria-label={loadingLabel}
38
+ >
39
+ <span className="ds-widget__loading-spinner" aria-hidden="true">
40
+ <Icon name="loader" size={16} />
41
+ </span>
42
+ </div>
43
+ );
44
+ }
45
+ else if (isEmpty) {
46
+ content = emptyState ?? null;
47
+ }
48
+ }
49
+
50
+ return (
51
+ <div
52
+ id={bodyId}
53
+ className={classNames(
54
+ 'ds-widget__body',
55
+ isCollapsed && 'ds-widget__body--collapsed',
56
+ className,
57
+ )}
58
+ hidden={isCollapsed || undefined}
59
+ aria-hidden={isCollapsed || undefined}
60
+ aria-live={!isCollapsed && live ? 'polite' : undefined}
61
+ aria-busy={!isCollapsed && isLoading ? true : undefined}
62
+ >
63
+ {isCollapsed ? null : content}
64
+ </div>
65
+ );
66
+ };
@@ -0,0 +1,31 @@
1
+ import { createContext, useContext } from 'react';
2
+ import type { AnchorHTMLAttributes } from 'react';
3
+ import type { Widget } from './Widget.js';
4
+
5
+ export type WidgetContextValue = {
6
+ bodyId: string;
7
+ expanded: boolean;
8
+ setExpanded: (expanded: boolean) => void;
9
+ collapsible: boolean;
10
+ href?: string;
11
+ renderLink?: (args: Widget.RenderLinkArgs) => React.ReactElement;
12
+ linkProps?: Omit<AnchorHTMLAttributes<HTMLAnchorElement>, 'href' | 'className' | 'children'>;
13
+ tooltip?: string;
14
+ isLoading: boolean;
15
+ isEmpty: boolean;
16
+ emptyState?: React.ReactNode;
17
+ loadingLabel: string;
18
+ live: boolean;
19
+ };
20
+
21
+ export const WidgetContext = createContext<WidgetContextValue | null>(null);
22
+
23
+ export function useWidgetContext(): WidgetContextValue {
24
+ const context = useContext(WidgetContext);
25
+
26
+ if (!context) {
27
+ throw new Error('Widget compound components must be used within <Widget>.');
28
+ }
29
+
30
+ return context;
31
+ }
@@ -0,0 +1,155 @@
1
+ import classNames from 'classnames';
2
+ import { Heading } from 'Components/heading/Heading';
3
+ import { Icon } from 'Components/icon/Icon';
4
+ import { TooltipWrapper } from 'Components/tooltip/TooltipWrapper';
5
+ import type { MouseEventHandler } from 'react';
6
+ import { ENTER_KEY, SPACE_KEY } from 'Utils/keyboardConstants';
7
+ import { useWidgetContext } from './WidgetContext.js';
8
+
9
+ export type WidgetHeaderProps = {
10
+ children: React.ReactNode;
11
+ slotStart?: React.ReactNode;
12
+ slotEnd?: React.ReactNode;
13
+ className?: string;
14
+ };
15
+
16
+ export const WidgetHeader = (props: WidgetHeaderProps): React.JSX.Element => {
17
+ const {
18
+ children,
19
+ slotStart,
20
+ slotEnd,
21
+ className,
22
+ } = props;
23
+
24
+ const {
25
+ bodyId,
26
+ expanded,
27
+ setExpanded,
28
+ collapsible,
29
+ href,
30
+ renderLink,
31
+ linkProps,
32
+ tooltip,
33
+ } = useWidgetContext();
34
+
35
+ const isLinked = Boolean(href);
36
+
37
+ const headingNode = (
38
+ <Heading level={3} className="ds-widget__header-heading">
39
+ {children}
40
+ </Heading>
41
+ );
42
+
43
+ const chevronNode = (
44
+ <Icon
45
+ name="chevron-right"
46
+ size={16}
47
+ className="ds-widget__header-chevron"
48
+ />
49
+ );
50
+
51
+ const tooltipNode = tooltip
52
+ ? (
53
+ <TooltipWrapper tooltipContent={tooltip}>
54
+ <button
55
+ type="button"
56
+ className="ds-widget__header-tooltip-trigger remove-default-button-styles"
57
+ aria-label={tooltip}
58
+ >
59
+ <Icon name="info" size={16} />
60
+ </button>
61
+ </TooltipWrapper>
62
+ )
63
+ : null;
64
+
65
+ const toggleCollapsedState = () => {
66
+ if (collapsible) {
67
+ setExpanded(!expanded);
68
+ }
69
+ };
70
+
71
+ const handleToggleKeyDown = (event: React.KeyboardEvent<HTMLElement>) => {
72
+ if ([SPACE_KEY, ENTER_KEY].includes(event.key)) {
73
+ event.preventDefault();
74
+ toggleCollapsedState();
75
+ }
76
+ };
77
+
78
+ const linkClassName = 'ds-widget__header-link';
79
+
80
+ let headingControl: React.ReactNode;
81
+
82
+ if (collapsible) {
83
+ headingControl = (
84
+ <Heading level={3} className="ds-widget__header-heading">
85
+ <button
86
+ type="button"
87
+ className={classNames('ds-widget__header-toggle', 'remove-default-button-styles')}
88
+ aria-expanded={expanded}
89
+ aria-controls={bodyId}
90
+ onClick={toggleCollapsedState}
91
+ onKeyDown={handleToggleKeyDown}
92
+ >
93
+ <Heading.InnerContainer>{children}</Heading.InnerContainer>
94
+ {chevronNode}
95
+ </button>
96
+ </Heading>
97
+ );
98
+ }
99
+ else if (isLinked) {
100
+ const handleLinkClick: MouseEventHandler<HTMLElement> = (event) => {
101
+ linkProps?.onClick?.(event as React.MouseEvent<HTMLAnchorElement>);
102
+ };
103
+
104
+ headingControl = renderLink
105
+ ? renderLink({
106
+ href: href!,
107
+ className: linkClassName,
108
+ children: headingNode,
109
+ linkProps,
110
+ onClick: handleLinkClick,
111
+ })
112
+ : (
113
+ <a
114
+ href={href}
115
+ className={linkClassName}
116
+ {...linkProps}
117
+ onClick={handleLinkClick}
118
+ >
119
+ {headingNode}
120
+ </a>
121
+ );
122
+ }
123
+ else {
124
+ headingControl = headingNode;
125
+ }
126
+
127
+ const headingRow = (
128
+ <div className="ds-widget__header-heading-row">
129
+ {headingControl}
130
+ {tooltipNode}
131
+ </div>
132
+ );
133
+
134
+ return (
135
+ <div
136
+ className={classNames('ds-widget__header', {
137
+ 'ds-widget__header--linked': isLinked,
138
+ 'ds-widget__header--collapsible': collapsible,
139
+ }, className)}
140
+ >
141
+ <div className="ds-widget__header-title-group">
142
+ {slotStart && (
143
+ <span className="ds-widget__header-slot-start">{slotStart}</span>
144
+ )}
145
+ {headingRow}
146
+ </div>
147
+ {(isLinked || slotEnd) && (
148
+ <span className="ds-widget__header-end">
149
+ {slotEnd}
150
+ {isLinked && chevronNode}
151
+ </span>
152
+ )}
153
+ </div>
154
+ );
155
+ };
@@ -0,0 +1,47 @@
1
+ import type { AnchorHTMLAttributes } from 'react';
2
+
3
+ export type WidgetRenderLinkArgs = {
4
+ href: string;
5
+ className: string;
6
+ children: React.ReactNode;
7
+ onClick?: React.MouseEventHandler;
8
+ linkProps?: Omit<AnchorHTMLAttributes<HTMLAnchorElement>, 'href' | 'className' | 'children'>;
9
+ };
10
+
11
+ type WidgetBaseProps = {
12
+ children: React.ReactNode;
13
+ className?: string;
14
+ tooltip?: string;
15
+ isFloating?: boolean;
16
+ isLoading?: boolean;
17
+ isEmpty?: boolean;
18
+ emptyState?: React.ReactNode;
19
+ loadingLabel?: string;
20
+ live?: boolean;
21
+ expanded?: boolean;
22
+ defaultExpanded?: boolean;
23
+ onExpandedChange?: (expanded: boolean) => void;
24
+ };
25
+
26
+ export type WidgetLinkedProps = WidgetBaseProps & {
27
+ href: string;
28
+ renderLink?: (args: WidgetRenderLinkArgs) => React.ReactElement;
29
+ linkProps?: Omit<AnchorHTMLAttributes<HTMLAnchorElement>, 'href' | 'className' | 'children'>;
30
+ collapsible?: false;
31
+ };
32
+
33
+ export type WidgetCollapsibleProps = WidgetBaseProps & {
34
+ href?: never;
35
+ renderLink?: never;
36
+ linkProps?: never;
37
+ collapsible: true;
38
+ };
39
+
40
+ export type WidgetStaticProps = WidgetBaseProps & {
41
+ href?: never;
42
+ renderLink?: never;
43
+ linkProps?: never;
44
+ collapsible?: false;
45
+ };
46
+
47
+ export type WidgetProps = WidgetLinkedProps | WidgetCollapsibleProps | WidgetStaticProps;