@oneuptime/common 9.4.7 → 9.4.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (304) hide show
  1. package/Models/DatabaseModels/Alert.ts +76 -0
  2. package/Models/DatabaseModels/AlertEpisode.ts +1201 -0
  3. package/Models/DatabaseModels/AlertEpisodeFeed.ts +529 -0
  4. package/Models/DatabaseModels/AlertEpisodeInternalNote.ts +455 -0
  5. package/Models/DatabaseModels/AlertEpisodeMember.ts +586 -0
  6. package/Models/DatabaseModels/AlertEpisodeOwnerTeam.ts +421 -0
  7. package/Models/DatabaseModels/AlertEpisodeOwnerUser.ts +419 -0
  8. package/Models/DatabaseModels/AlertEpisodeStateTimeline.ts +523 -0
  9. package/Models/DatabaseModels/AlertFeed.ts +1 -0
  10. package/Models/DatabaseModels/AlertGroupingRule.ts +1432 -0
  11. package/Models/DatabaseModels/Index.ts +18 -0
  12. package/Models/DatabaseModels/OnCallDutyPolicyExecutionLog.ts +70 -0
  13. package/Models/DatabaseModels/StatusPageDomain.ts +2 -0
  14. package/Models/DatabaseModels/WorkspaceNotificationLog.ts +57 -0
  15. package/Server/API/SlackAPI.ts +21 -0
  16. package/Server/Infrastructure/Postgres/SchemaMigrations/1768938069147-MigrationName.ts +751 -0
  17. package/Server/Infrastructure/Postgres/SchemaMigrations/1769125561322-MigrationName.ts +41 -0
  18. package/Server/Infrastructure/Postgres/SchemaMigrations/1769170578688-MigrationName.ts +29 -0
  19. package/Server/Infrastructure/Postgres/SchemaMigrations/1769172358833-MigrationName.ts +177 -0
  20. package/Server/Infrastructure/Postgres/SchemaMigrations/1769176450526-MigrationName.ts +71 -0
  21. package/Server/Infrastructure/Postgres/SchemaMigrations/1769190495840-MigrationName.ts +35 -0
  22. package/Server/Infrastructure/Postgres/SchemaMigrations/1769199303656-MigrationName.ts +29 -0
  23. package/Server/Infrastructure/Postgres/SchemaMigrations/1769202898645-MigrationName.ts +29 -0
  24. package/Server/Infrastructure/Postgres/SchemaMigrations/1769428619414-MigrationName.ts +35 -0
  25. package/Server/Infrastructure/Postgres/SchemaMigrations/1769428821686-MigrationName.ts +47 -0
  26. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +20 -0
  27. package/Server/Services/AlertEpisodeFeedService.ts +94 -0
  28. package/Server/Services/AlertEpisodeInternalNoteService.ts +71 -0
  29. package/Server/Services/AlertEpisodeMemberService.ts +267 -0
  30. package/Server/Services/AlertEpisodeOwnerTeamService.ts +10 -0
  31. package/Server/Services/AlertEpisodeOwnerUserService.ts +10 -0
  32. package/Server/Services/AlertEpisodeService.ts +988 -0
  33. package/Server/Services/AlertEpisodeStateTimelineService.ts +557 -0
  34. package/Server/Services/AlertGroupingEngineService.ts +1120 -0
  35. package/Server/Services/AlertGroupingRuleService.ts +14 -0
  36. package/Server/Services/AlertService.ts +12 -0
  37. package/Server/Services/CallService.ts +2 -0
  38. package/Server/Services/Index.ts +21 -0
  39. package/Server/Services/MailService.ts +5 -0
  40. package/Server/Services/OnCallDutyPolicyService.ts +5 -0
  41. package/Server/Services/SmsService.ts +2 -0
  42. package/Server/Services/UserNotificationSettingService.ts +23 -0
  43. package/Server/Services/WhatsAppService.ts +5 -0
  44. package/Server/Services/WorkspaceNotificationRuleService.ts +26 -0
  45. package/Server/Utils/AnalyticsDatabase/Statement.ts +6 -2
  46. package/Server/Utils/WhatsAppTemplateUtil.ts +13 -0
  47. package/Server/Utils/Workspace/MicrosoftTeams/Actions/ActionTypes.ts +18 -0
  48. package/Server/Utils/Workspace/MicrosoftTeams/Actions/AlertEpisode.ts +689 -0
  49. package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +16 -0
  50. package/Server/Utils/Workspace/Slack/Actions/ActionTypes.ts +11 -0
  51. package/Server/Utils/Workspace/Slack/Actions/AlertEpisode.ts +915 -0
  52. package/Server/Utils/Workspace/Slack/Messages/AlertEpisode.ts +120 -0
  53. package/Server/Utils/Workspace/WorkspaceMessages/AlertEpisode.ts +74 -0
  54. package/Tests/Server/Services/AlertEpisodeMemberService.test.ts +200 -0
  55. package/Tests/Server/Services/AlertEpisodeService.test.ts +240 -0
  56. package/Tests/Server/Services/AlertGroupingEngineService.test.ts +542 -0
  57. package/Tests/Server/Services/AlertGroupingRuleService.test.ts +383 -0
  58. package/Tests/Server/Utils/AnalyticsDatabase/StatementGenerator.test.ts +1 -1
  59. package/Tests/UI/Components/Input.test.tsx +1 -1
  60. package/Tests/UI/Components/TextArea.test.tsx +2 -2
  61. package/Types/BaseDatabase/SortOrder.ts +9 -0
  62. package/Types/Email/EmailTemplateType.ts +5 -0
  63. package/Types/NotificationRule/NotificationRuleType.ts +1 -0
  64. package/Types/NotificationSetting/NotificationSettingEventType.ts +7 -0
  65. package/Types/Permission.ts +309 -0
  66. package/Types/UserNotification/UserNotificationEventType.ts +1 -0
  67. package/Types/WhatsApp/WhatsAppTemplates.ts +20 -0
  68. package/Types/Workspace/NotificationRules/EventType.ts +1 -0
  69. package/Types/Workspace/NotificationRules/NotificationRuleCondition.ts +32 -3
  70. package/UI/Components/Accordion/Accordion.tsx +20 -2
  71. package/UI/Components/Alerts/Alert.tsx +1 -0
  72. package/UI/Components/Button/Button.tsx +29 -0
  73. package/UI/Components/CardSelect/CardSelect.tsx +5 -1
  74. package/UI/Components/Checkbox/Checkbox.tsx +7 -3
  75. package/UI/Components/ColorCircle/ColorCircle.tsx +2 -0
  76. package/UI/Components/ColorViewer/ColorViewer.tsx +19 -3
  77. package/UI/Components/CopyableButton/CopyableButton.tsx +22 -5
  78. package/UI/Components/Detail/Detail.tsx +1 -1
  79. package/UI/Components/Dropdown/Dropdown.tsx +14 -1
  80. package/UI/Components/Forms/Fields/FormField.tsx +28 -0
  81. package/UI/Components/FullPageModal/FullPageModal.tsx +35 -4
  82. package/UI/Components/Input/Input.tsx +14 -2
  83. package/UI/Components/Link/Link.tsx +1 -0
  84. package/UI/Components/Loader/Loader.tsx +8 -2
  85. package/UI/Components/Markdown.tsx/MarkdownViewer.tsx +76 -1
  86. package/UI/Components/Modal/Modal.tsx +47 -3
  87. package/UI/Components/ModelTable/BaseModelTable.tsx +42 -1
  88. package/UI/Components/MoreMenu/MoreMenu.tsx +84 -2
  89. package/UI/Components/OrderedStatesList/OrderedStatesList.tsx +30 -8
  90. package/UI/Components/Pagination/Pagination.tsx +113 -8
  91. package/UI/Components/ProgressBar/ProgressBar.tsx +12 -2
  92. package/UI/Components/Radio/Radio.tsx +21 -3
  93. package/UI/Components/SideMenu/CountModelSideMenuItem.tsx +54 -27
  94. package/UI/Components/StatusBubble/StatusBubble.tsx +7 -2
  95. package/UI/Components/Table/TableHeader.tsx +20 -3
  96. package/UI/Components/Tabs/Tab.tsx +16 -1
  97. package/UI/Components/Tabs/Tabs.tsx +12 -1
  98. package/UI/Components/TextArea/TextArea.tsx +12 -2
  99. package/UI/Components/Toggle/Toggle.tsx +14 -3
  100. package/UI/Components/Tooltip/Tooltip.tsx +11 -1
  101. package/UI/Components/TopAlert/TopAlert.tsx +2 -0
  102. package/build/dist/Models/DatabaseModels/Alert.js +77 -0
  103. package/build/dist/Models/DatabaseModels/Alert.js.map +1 -1
  104. package/build/dist/Models/DatabaseModels/AlertEpisode.js +1225 -0
  105. package/build/dist/Models/DatabaseModels/AlertEpisode.js.map +1 -0
  106. package/build/dist/Models/DatabaseModels/AlertEpisodeFeed.js +553 -0
  107. package/build/dist/Models/DatabaseModels/AlertEpisodeFeed.js.map +1 -0
  108. package/build/dist/Models/DatabaseModels/AlertEpisodeInternalNote.js +467 -0
  109. package/build/dist/Models/DatabaseModels/AlertEpisodeInternalNote.js.map +1 -0
  110. package/build/dist/Models/DatabaseModels/AlertEpisodeMember.js +607 -0
  111. package/build/dist/Models/DatabaseModels/AlertEpisodeMember.js.map +1 -0
  112. package/build/dist/Models/DatabaseModels/AlertEpisodeOwnerTeam.js +437 -0
  113. package/build/dist/Models/DatabaseModels/AlertEpisodeOwnerTeam.js.map +1 -0
  114. package/build/dist/Models/DatabaseModels/AlertEpisodeOwnerUser.js +436 -0
  115. package/build/dist/Models/DatabaseModels/AlertEpisodeOwnerUser.js.map +1 -0
  116. package/build/dist/Models/DatabaseModels/AlertEpisodeStateTimeline.js +546 -0
  117. package/build/dist/Models/DatabaseModels/AlertEpisodeStateTimeline.js.map +1 -0
  118. package/build/dist/Models/DatabaseModels/AlertFeed.js +1 -0
  119. package/build/dist/Models/DatabaseModels/AlertFeed.js.map +1 -1
  120. package/build/dist/Models/DatabaseModels/AlertGroupingRule.js +1437 -0
  121. package/build/dist/Models/DatabaseModels/AlertGroupingRule.js.map +1 -0
  122. package/build/dist/Models/DatabaseModels/Index.js +16 -0
  123. package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
  124. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyExecutionLog.js +69 -0
  125. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyExecutionLog.js.map +1 -1
  126. package/build/dist/Models/DatabaseModels/StatusPageDomain.js +2 -0
  127. package/build/dist/Models/DatabaseModels/StatusPageDomain.js.map +1 -1
  128. package/build/dist/Models/DatabaseModels/WorkspaceNotificationLog.js +58 -0
  129. package/build/dist/Models/DatabaseModels/WorkspaceNotificationLog.js.map +1 -1
  130. package/build/dist/Server/API/SlackAPI.js +18 -0
  131. package/build/dist/Server/API/SlackAPI.js.map +1 -1
  132. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1768938069147-MigrationName.js +266 -0
  133. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1768938069147-MigrationName.js.map +1 -0
  134. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1769125561322-MigrationName.js +20 -0
  135. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1769125561322-MigrationName.js.map +1 -0
  136. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1769170578688-MigrationName.js +16 -0
  137. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1769170578688-MigrationName.js.map +1 -0
  138. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1769172358833-MigrationName.js +68 -0
  139. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1769172358833-MigrationName.js.map +1 -0
  140. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1769176450526-MigrationName.js +30 -0
  141. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1769176450526-MigrationName.js.map +1 -0
  142. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1769190495840-MigrationName.js +18 -0
  143. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1769190495840-MigrationName.js.map +1 -0
  144. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1769199303656-MigrationName.js +16 -0
  145. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1769199303656-MigrationName.js.map +1 -0
  146. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1769202898645-MigrationName.js +16 -0
  147. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1769202898645-MigrationName.js.map +1 -0
  148. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1769428619414-MigrationName.js +18 -0
  149. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1769428619414-MigrationName.js.map +1 -0
  150. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1769428821686-MigrationName.js +22 -0
  151. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1769428821686-MigrationName.js.map +1 -0
  152. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +20 -0
  153. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  154. package/build/dist/Server/Services/AlertEpisodeFeedService.js +83 -0
  155. package/build/dist/Server/Services/AlertEpisodeFeedService.js.map +1 -0
  156. package/build/dist/Server/Services/AlertEpisodeInternalNoteService.js +70 -0
  157. package/build/dist/Server/Services/AlertEpisodeInternalNoteService.js.map +1 -0
  158. package/build/dist/Server/Services/AlertEpisodeMemberService.js +256 -0
  159. package/build/dist/Server/Services/AlertEpisodeMemberService.js.map +1 -0
  160. package/build/dist/Server/Services/AlertEpisodeOwnerTeamService.js +9 -0
  161. package/build/dist/Server/Services/AlertEpisodeOwnerTeamService.js.map +1 -0
  162. package/build/dist/Server/Services/AlertEpisodeOwnerUserService.js +9 -0
  163. package/build/dist/Server/Services/AlertEpisodeOwnerUserService.js.map +1 -0
  164. package/build/dist/Server/Services/AlertEpisodeService.js +885 -0
  165. package/build/dist/Server/Services/AlertEpisodeService.js.map +1 -0
  166. package/build/dist/Server/Services/AlertEpisodeStateTimelineService.js +494 -0
  167. package/build/dist/Server/Services/AlertEpisodeStateTimelineService.js.map +1 -0
  168. package/build/dist/Server/Services/AlertGroupingEngineService.js +893 -0
  169. package/build/dist/Server/Services/AlertGroupingEngineService.js.map +1 -0
  170. package/build/dist/Server/Services/AlertGroupingRuleService.js +13 -0
  171. package/build/dist/Server/Services/AlertGroupingRuleService.js.map +1 -0
  172. package/build/dist/Server/Services/AlertService.js +11 -0
  173. package/build/dist/Server/Services/AlertService.js.map +1 -1
  174. package/build/dist/Server/Services/CallService.js +11 -10
  175. package/build/dist/Server/Services/CallService.js.map +1 -1
  176. package/build/dist/Server/Services/Index.js +18 -0
  177. package/build/dist/Server/Services/Index.js.map +1 -1
  178. package/build/dist/Server/Services/MailService.js +3 -0
  179. package/build/dist/Server/Services/MailService.js.map +1 -1
  180. package/build/dist/Server/Services/OnCallDutyPolicyService.js +3 -0
  181. package/build/dist/Server/Services/OnCallDutyPolicyService.js.map +1 -1
  182. package/build/dist/Server/Services/SmsService.js +11 -10
  183. package/build/dist/Server/Services/SmsService.js.map +1 -1
  184. package/build/dist/Server/Services/UserNotificationSettingService.js +9 -0
  185. package/build/dist/Server/Services/UserNotificationSettingService.js.map +1 -1
  186. package/build/dist/Server/Services/WhatsAppService.js +3 -0
  187. package/build/dist/Server/Services/WhatsAppService.js.map +1 -1
  188. package/build/dist/Server/Services/WorkspaceNotificationRuleService.js +25 -0
  189. package/build/dist/Server/Services/WorkspaceNotificationRuleService.js.map +1 -1
  190. package/build/dist/Server/Utils/AnalyticsDatabase/Statement.js +4 -2
  191. package/build/dist/Server/Utils/AnalyticsDatabase/Statement.js.map +1 -1
  192. package/build/dist/Server/Utils/WhatsAppTemplateUtil.js +8 -0
  193. package/build/dist/Server/Utils/WhatsAppTemplateUtil.js.map +1 -1
  194. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/ActionTypes.js +17 -0
  195. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/ActionTypes.js.map +1 -1
  196. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/AlertEpisode.js +545 -0
  197. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/AlertEpisode.js.map +1 -0
  198. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +13 -0
  199. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
  200. package/build/dist/Server/Utils/Workspace/Slack/Actions/ActionTypes.js +10 -0
  201. package/build/dist/Server/Utils/Workspace/Slack/Actions/ActionTypes.js.map +1 -1
  202. package/build/dist/Server/Utils/Workspace/Slack/Actions/AlertEpisode.js +651 -0
  203. package/build/dist/Server/Utils/Workspace/Slack/Actions/AlertEpisode.js.map +1 -0
  204. package/build/dist/Server/Utils/Workspace/Slack/Messages/AlertEpisode.js +100 -0
  205. package/build/dist/Server/Utils/Workspace/Slack/Messages/AlertEpisode.js.map +1 -0
  206. package/build/dist/Server/Utils/Workspace/WorkspaceMessages/AlertEpisode.js +70 -0
  207. package/build/dist/Server/Utils/Workspace/WorkspaceMessages/AlertEpisode.js.map +1 -0
  208. package/build/dist/Tests/Server/Services/AlertEpisodeMemberService.test.js +165 -0
  209. package/build/dist/Tests/Server/Services/AlertEpisodeMemberService.test.js.map +1 -0
  210. package/build/dist/Tests/Server/Services/AlertEpisodeService.test.js +193 -0
  211. package/build/dist/Tests/Server/Services/AlertEpisodeService.test.js.map +1 -0
  212. package/build/dist/Tests/Server/Services/AlertGroupingEngineService.test.js +412 -0
  213. package/build/dist/Tests/Server/Services/AlertGroupingEngineService.test.js.map +1 -0
  214. package/build/dist/Tests/Server/Services/AlertGroupingRuleService.test.js +308 -0
  215. package/build/dist/Tests/Server/Services/AlertGroupingRuleService.test.js.map +1 -0
  216. package/build/dist/Tests/Server/Utils/AnalyticsDatabase/StatementGenerator.test.js +1 -1
  217. package/build/dist/Tests/Server/Utils/AnalyticsDatabase/StatementGenerator.test.js.map +1 -1
  218. package/build/dist/Tests/UI/Components/Input.test.js +1 -1
  219. package/build/dist/Tests/UI/Components/Input.test.js.map +1 -1
  220. package/build/dist/Tests/UI/Components/TextArea.test.js +2 -2
  221. package/build/dist/Tests/UI/Components/TextArea.test.js.map +1 -1
  222. package/build/dist/Types/BaseDatabase/SortOrder.js +5 -0
  223. package/build/dist/Types/BaseDatabase/SortOrder.js.map +1 -1
  224. package/build/dist/Types/Email/EmailTemplateType.js +4 -0
  225. package/build/dist/Types/Email/EmailTemplateType.js.map +1 -1
  226. package/build/dist/Types/NotificationRule/NotificationRuleType.js +1 -0
  227. package/build/dist/Types/NotificationRule/NotificationRuleType.js.map +1 -1
  228. package/build/dist/Types/NotificationSetting/NotificationSettingEventType.js +5 -0
  229. package/build/dist/Types/NotificationSetting/NotificationSettingEventType.js.map +1 -1
  230. package/build/dist/Types/Permission.js +264 -0
  231. package/build/dist/Types/Permission.js.map +1 -1
  232. package/build/dist/Types/UserNotification/UserNotificationEventType.js +1 -0
  233. package/build/dist/Types/UserNotification/UserNotificationEventType.js.map +1 -1
  234. package/build/dist/Types/WhatsApp/WhatsAppTemplates.js +12 -0
  235. package/build/dist/Types/WhatsApp/WhatsAppTemplates.js.map +1 -1
  236. package/build/dist/Types/Workspace/NotificationRules/EventType.js +1 -0
  237. package/build/dist/Types/Workspace/NotificationRules/EventType.js.map +1 -1
  238. package/build/dist/Types/Workspace/NotificationRules/NotificationRuleCondition.js +28 -3
  239. package/build/dist/Types/Workspace/NotificationRules/NotificationRuleCondition.js.map +1 -1
  240. package/build/dist/UI/Components/Accordion/Accordion.js +10 -3
  241. package/build/dist/UI/Components/Accordion/Accordion.js.map +1 -1
  242. package/build/dist/UI/Components/Alerts/Alert.js +1 -1
  243. package/build/dist/UI/Components/Alerts/Alert.js.map +1 -1
  244. package/build/dist/UI/Components/Button/Button.js +8 -2
  245. package/build/dist/UI/Components/Button/Button.js.map +1 -1
  246. package/build/dist/UI/Components/CardSelect/CardSelect.js +1 -1
  247. package/build/dist/UI/Components/CardSelect/CardSelect.js.map +1 -1
  248. package/build/dist/UI/Components/Checkbox/Checkbox.js +2 -2
  249. package/build/dist/UI/Components/Checkbox/Checkbox.js.map +1 -1
  250. package/build/dist/UI/Components/ColorCircle/ColorCircle.js +1 -1
  251. package/build/dist/UI/Components/ColorCircle/ColorCircle.js.map +1 -1
  252. package/build/dist/UI/Components/ColorViewer/ColorViewer.js +12 -3
  253. package/build/dist/UI/Components/ColorViewer/ColorViewer.js.map +1 -1
  254. package/build/dist/UI/Components/CopyableButton/CopyableButton.js +12 -5
  255. package/build/dist/UI/Components/CopyableButton/CopyableButton.js.map +1 -1
  256. package/build/dist/UI/Components/Detail/Detail.js +1 -1
  257. package/build/dist/UI/Components/Detail/Detail.js.map +1 -1
  258. package/build/dist/UI/Components/Dropdown/Dropdown.js +5 -3
  259. package/build/dist/UI/Components/Dropdown/Dropdown.js.map +1 -1
  260. package/build/dist/UI/Components/Forms/Fields/FormField.js +19 -1
  261. package/build/dist/UI/Components/Forms/Fields/FormField.js.map +1 -1
  262. package/build/dist/UI/Components/FullPageModal/FullPageModal.js +24 -5
  263. package/build/dist/UI/Components/FullPageModal/FullPageModal.js.map +1 -1
  264. package/build/dist/UI/Components/Input/Input.js +3 -3
  265. package/build/dist/UI/Components/Input/Input.js.map +1 -1
  266. package/build/dist/UI/Components/Link/Link.js +1 -1
  267. package/build/dist/UI/Components/Link/Link.js.map +1 -1
  268. package/build/dist/UI/Components/Loader/Loader.js +6 -4
  269. package/build/dist/UI/Components/Loader/Loader.js.map +1 -1
  270. package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js +56 -3
  271. package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js.map +1 -1
  272. package/build/dist/UI/Components/Modal/Modal.js +28 -3
  273. package/build/dist/UI/Components/Modal/Modal.js.map +1 -1
  274. package/build/dist/UI/Components/ModelTable/BaseModelTable.js +23 -1
  275. package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
  276. package/build/dist/UI/Components/MoreMenu/MoreMenu.js +67 -6
  277. package/build/dist/UI/Components/MoreMenu/MoreMenu.js.map +1 -1
  278. package/build/dist/UI/Components/OrderedStatesList/OrderedStatesList.js +14 -3
  279. package/build/dist/UI/Components/OrderedStatesList/OrderedStatesList.js.map +1 -1
  280. package/build/dist/UI/Components/Pagination/Pagination.js +69 -13
  281. package/build/dist/UI/Components/Pagination/Pagination.js.map +1 -1
  282. package/build/dist/UI/Components/ProgressBar/ProgressBar.js +2 -2
  283. package/build/dist/UI/Components/ProgressBar/ProgressBar.js.map +1 -1
  284. package/build/dist/UI/Components/Radio/Radio.js +8 -5
  285. package/build/dist/UI/Components/Radio/Radio.js.map +1 -1
  286. package/build/dist/UI/Components/SideMenu/CountModelSideMenuItem.js +23 -4
  287. package/build/dist/UI/Components/SideMenu/CountModelSideMenuItem.js.map +1 -1
  288. package/build/dist/UI/Components/StatusBubble/StatusBubble.js +2 -2
  289. package/build/dist/UI/Components/StatusBubble/StatusBubble.js.map +1 -1
  290. package/build/dist/UI/Components/Table/TableHeader.js +12 -4
  291. package/build/dist/UI/Components/Table/TableHeader.js.map +1 -1
  292. package/build/dist/UI/Components/Tabs/Tab.js +8 -1
  293. package/build/dist/UI/Components/Tabs/Tab.js.map +1 -1
  294. package/build/dist/UI/Components/Tabs/Tabs.js +4 -3
  295. package/build/dist/UI/Components/Tabs/Tabs.js.map +1 -1
  296. package/build/dist/UI/Components/TextArea/TextArea.js +3 -3
  297. package/build/dist/UI/Components/TextArea/TextArea.js.map +1 -1
  298. package/build/dist/UI/Components/Toggle/Toggle.js +7 -4
  299. package/build/dist/UI/Components/Toggle/Toggle.js.map +1 -1
  300. package/build/dist/UI/Components/Tooltip/Tooltip.js +4 -1
  301. package/build/dist/UI/Components/Tooltip/Tooltip.js.map +1 -1
  302. package/build/dist/UI/Components/TopAlert/TopAlert.js +1 -1
  303. package/build/dist/UI/Components/TopAlert/TopAlert.js.map +1 -1
  304. package/package.json +2 -1
@@ -1,8 +1,11 @@
1
1
  import React, {
2
2
  forwardRef,
3
3
  ReactElement,
4
+ useCallback,
4
5
  useEffect,
6
+ useId,
5
7
  useImperativeHandle,
8
+ useRef,
6
9
  useState,
7
10
  } from "react";
8
11
  import IconProp from "../../../Types/Icon/IconProp";
@@ -20,8 +23,14 @@ const MoreMenu: React.ForwardRefExoticComponent<
20
23
  ComponentProps & React.RefAttributes<unknown>
21
24
  > = forwardRef(
22
25
  (props: ComponentProps, componentRef: React.ForwardedRef<unknown>) => {
26
+ const uniqueId: string = useId();
27
+ const menuId: string = `menu-${uniqueId}`;
28
+ const buttonId: string = `menu-button-${uniqueId}`;
23
29
  const { ref, isComponentVisible, setIsComponentVisible } =
24
30
  useComponentOutsideClick(false);
31
+ const [focusedIndex, setFocusedIndex] = useState<number>(-1);
32
+ const menuItemRefs: React.MutableRefObject<(HTMLDivElement | null)[]> =
33
+ useRef<(HTMLDivElement | null)[]>([]);
25
34
 
26
35
  useImperativeHandle(componentRef, () => {
27
36
  return {
@@ -41,18 +50,75 @@ const MoreMenu: React.ForwardRefExoticComponent<
41
50
 
42
51
  useEffect(() => {
43
52
  setDropdownVisible(isComponentVisible);
53
+ if (isComponentVisible) {
54
+ setFocusedIndex(0);
55
+ } else {
56
+ setFocusedIndex(-1);
57
+ }
44
58
  }, [isComponentVisible]);
45
59
 
60
+ useEffect(() => {
61
+ if (focusedIndex >= 0 && menuItemRefs.current[focusedIndex]) {
62
+ menuItemRefs.current[focusedIndex]?.focus();
63
+ }
64
+ }, [focusedIndex]);
65
+
66
+ const handleKeyDown: (event: React.KeyboardEvent) => void = useCallback(
67
+ (event: React.KeyboardEvent): void => {
68
+ if (!isComponentVisible) {
69
+ return;
70
+ }
71
+
72
+ const itemCount: number = props.children.length;
73
+
74
+ switch (event.key) {
75
+ case "Escape":
76
+ event.preventDefault();
77
+ setIsComponentVisible(false);
78
+ break;
79
+ case "ArrowDown":
80
+ event.preventDefault();
81
+ setFocusedIndex((prev: number) => {
82
+ return (prev + 1) % itemCount;
83
+ });
84
+ break;
85
+ case "ArrowUp":
86
+ event.preventDefault();
87
+ setFocusedIndex((prev: number) => {
88
+ return (prev - 1 + itemCount) % itemCount;
89
+ });
90
+ break;
91
+ case "Home":
92
+ event.preventDefault();
93
+ setFocusedIndex(0);
94
+ break;
95
+ case "End":
96
+ event.preventDefault();
97
+ setFocusedIndex(itemCount - 1);
98
+ break;
99
+ }
100
+ },
101
+ [isComponentVisible, props.children.length, setIsComponentVisible],
102
+ );
103
+
46
104
  return (
47
- <div className="relative inline-block text-left">
105
+ <div
106
+ className="relative inline-block text-left"
107
+ onKeyDown={handleKeyDown}
108
+ >
48
109
  {!props.elementToBeShownInsteadOfButton && (
49
110
  <Button
111
+ id={buttonId}
50
112
  icon={props.menuIcon || IconProp.More}
51
113
  title={props.text || ""}
52
114
  buttonStyle={ButtonStyleType.OUTLINE}
53
115
  onClick={() => {
54
116
  setIsComponentVisible(!isDropdownVisible);
55
117
  }}
118
+ ariaLabel={props.text || "More options"}
119
+ ariaExpanded={isComponentVisible}
120
+ ariaHaspopup="menu"
121
+ ariaControls={isComponentVisible ? menuId : undefined}
56
122
  />
57
123
  )}
58
124
 
@@ -63,21 +129,37 @@ const MoreMenu: React.ForwardRefExoticComponent<
63
129
  {isComponentVisible && (
64
130
  <div
65
131
  ref={ref}
132
+ id={menuId}
66
133
  className="absolute right-0 z-10 mt-2 w-56 origin-top-right divide-y divide-gray-100 rounded-md bg-white shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none"
67
134
  role="menu"
68
135
  aria-orientation="vertical"
69
- aria-labelledby="menu-button"
136
+ aria-labelledby={buttonId}
70
137
  >
71
138
  {props.children.map((child: ReactElement, index: number) => {
72
139
  return (
73
140
  <div
74
141
  key={index}
142
+ ref={(el: HTMLDivElement | null) => {
143
+ menuItemRefs.current[index] = el;
144
+ }}
75
145
  role="menuitem"
146
+ tabIndex={focusedIndex === index ? 0 : -1}
76
147
  onClick={() => {
77
148
  if (isComponentVisible) {
78
149
  setIsComponentVisible(false);
79
150
  }
80
151
  }}
152
+ onKeyDown={(e: React.KeyboardEvent) => {
153
+ if (e.key === "Enter" || e.key === " ") {
154
+ e.preventDefault();
155
+ setIsComponentVisible(false);
156
+ // Trigger child click
157
+ const clickEvent: MouseEvent = new MouseEvent("click", {
158
+ bubbles: true,
159
+ });
160
+ e.currentTarget.dispatchEvent(clickEvent);
161
+ }
162
+ }}
81
163
  >
82
164
  {child}
83
165
  </div>
@@ -48,14 +48,36 @@ const OrderedStatesList: OrderedStatesListFunction = <T extends GenericObject>(
48
48
 
49
49
  if (props.data.length === 0) {
50
50
  return (
51
- <ErrorMessage
52
- message={
53
- props.noItemsMessage
54
- ? props.noItemsMessage
55
- : `No ${props.singularLabel.toLocaleLowerCase()}`
56
- }
57
- onRefreshClick={props.onRefreshClick}
58
- />
51
+ <div className="text-center">
52
+ {/* Only show the no items message if there's no create button */}
53
+ {!props.onCreateNewItem && (
54
+ <ErrorMessage
55
+ message={
56
+ props.noItemsMessage
57
+ ? props.noItemsMessage
58
+ : `No ${props.singularLabel.toLocaleLowerCase()}`
59
+ }
60
+ onRefreshClick={props.onRefreshClick}
61
+ />
62
+ )}
63
+ {props.onCreateNewItem && (
64
+ <div className="my-10">
65
+ <div
66
+ className="m-auto inline-flex items-center cursor-pointer text-gray-400 hover:bg-gray-50 border hover:text-gray-600 rounded-full border-gray-300 p-5"
67
+ onClick={() => {
68
+ if (props.onCreateNewItem) {
69
+ props.onCreateNewItem(1);
70
+ }
71
+ }}
72
+ >
73
+ <Icon icon={IconProp.Add} className="h-5 w-5" />
74
+ <span className="text-sm ml-2">
75
+ Add New {props.singularLabel}
76
+ </span>
77
+ </div>
78
+ </div>
79
+ )}
80
+ </div>
59
81
  );
60
82
  }
61
83
 
@@ -67,9 +67,10 @@ const Pagination: FunctionComponent<ComponentProps> = (
67
67
  useState<boolean>(false);
68
68
 
69
69
  return (
70
- <div
70
+ <nav
71
71
  className="flex items-center justify-between border-t border-gray-200 bg-white px-4"
72
72
  data-testid={props.dataTestId}
73
+ aria-label={`Pagination for ${props.pluralLabel}`}
73
74
  >
74
75
  {/* Desktop layout: Description on left, all controls on right */}
75
76
  <div className="hidden md:block">
@@ -92,7 +93,7 @@ const Pagination: FunctionComponent<ComponentProps> = (
92
93
 
93
94
  {/* Desktop layout: All controls together on right */}
94
95
  <div className="hidden md:flex">
95
- <nav className="inline-flex -space-x-px rounded-md shadow-sm">
96
+ <div className="inline-flex -space-x-px rounded-md shadow-sm">
96
97
  <div className="my-2">
97
98
  <Button
98
99
  dataTestId="show-pagination-modal-button"
@@ -100,14 +101,19 @@ const Pagination: FunctionComponent<ComponentProps> = (
100
101
  buttonSize={ButtonSize.ExtraSmall}
101
102
  icon={IconProp.AdjustmentHorizontal}
102
103
  buttonStyle={ButtonStyleType.ICON_LIGHT}
104
+ ariaLabel="Open pagination settings"
103
105
  onClick={() => {
104
106
  setShowPaginationModel(true);
105
107
  }}
106
108
  />
107
109
  </div>
108
110
 
109
- <ul className="py-3">
111
+ <ul className="py-3" role="list">
110
112
  <li
113
+ role="button"
114
+ tabIndex={isPreviousDisabled ? -1 : 0}
115
+ aria-disabled={isPreviousDisabled}
116
+ aria-label="Go to previous page"
111
117
  onClick={() => {
112
118
  let currentPageNumber: number = props.currentPageNumber;
113
119
 
@@ -122,6 +128,24 @@ const Pagination: FunctionComponent<ComponentProps> = (
122
128
  );
123
129
  }
124
130
  }}
131
+ onKeyDown={(e: React.KeyboardEvent) => {
132
+ if (
133
+ (e.key === "Enter" || e.key === " ") &&
134
+ !isPreviousDisabled
135
+ ) {
136
+ e.preventDefault();
137
+ let currentPageNumber: number = props.currentPageNumber;
138
+ if (typeof currentPageNumber === "string") {
139
+ currentPageNumber = parseInt(currentPageNumber);
140
+ }
141
+ if (props.onNavigateToPage) {
142
+ props.onNavigateToPage(
143
+ currentPageNumber - 1,
144
+ props.itemsOnPage,
145
+ );
146
+ }
147
+ }
148
+ }}
125
149
  className={` inline-flex items-center rounded-l-md border border-gray-300 bg-white px-2 py-2 text-sm font-medium text-gray-500 ${
126
150
  isPreviousDisabled
127
151
  ? "bg-gray-100"
@@ -131,6 +155,10 @@ const Pagination: FunctionComponent<ComponentProps> = (
131
155
  <span className="page-link">Previous</span>
132
156
  </li>
133
157
  <li
158
+ role="button"
159
+ tabIndex={isCurrentPageButtonDisabled ? -1 : 0}
160
+ aria-current="page"
161
+ aria-label={`Page ${props.currentPageNumber}, click to change page`}
134
162
  data-testid="current-page-link"
135
163
  className={` z-10 inline-flex items-center border border-x-0 border-gray-300 hover:bg-gray-50 px-4 py-2 text-sm font-medium text-text-600 cursor-pointer ${
136
164
  isCurrentPageButtonDisabled ? "bg-gray-100" : ""
@@ -138,10 +166,20 @@ const Pagination: FunctionComponent<ComponentProps> = (
138
166
  onClick={() => {
139
167
  setShowPaginationModel(true);
140
168
  }}
169
+ onKeyDown={(e: React.KeyboardEvent) => {
170
+ if (e.key === "Enter" || e.key === " ") {
171
+ e.preventDefault();
172
+ setShowPaginationModel(true);
173
+ }
174
+ }}
141
175
  >
142
176
  <span>{props.currentPageNumber}</span>
143
177
  </li>
144
178
  <li
179
+ role="button"
180
+ tabIndex={isNextDisabled ? -1 : 0}
181
+ aria-disabled={isNextDisabled}
182
+ aria-label="Go to next page"
145
183
  onClick={() => {
146
184
  let currentPageNumber: number = props.currentPageNumber;
147
185
 
@@ -156,6 +194,21 @@ const Pagination: FunctionComponent<ComponentProps> = (
156
194
  );
157
195
  }
158
196
  }}
197
+ onKeyDown={(e: React.KeyboardEvent) => {
198
+ if ((e.key === "Enter" || e.key === " ") && !isNextDisabled) {
199
+ e.preventDefault();
200
+ let currentPageNumber: number = props.currentPageNumber;
201
+ if (typeof currentPageNumber === "string") {
202
+ currentPageNumber = parseInt(currentPageNumber);
203
+ }
204
+ if (props.onNavigateToPage) {
205
+ props.onNavigateToPage(
206
+ currentPageNumber + 1,
207
+ props.itemsOnPage,
208
+ );
209
+ }
210
+ }
211
+ }}
159
212
  className={` inline-flex items-center rounded-r-md border border-gray-300 bg-white px-2 py-2 text-sm font-medium text-gray-500 ${
160
213
  isNextDisabled
161
214
  ? "bg-gray-100"
@@ -165,7 +218,7 @@ const Pagination: FunctionComponent<ComponentProps> = (
165
218
  <span>Next</span>
166
219
  </li>
167
220
  </ul>
168
- </nav>
221
+ </div>
169
222
  </div>
170
223
 
171
224
  {/* Mobile layout: Navigate button on left, pagination controls on right */}
@@ -176,6 +229,7 @@ const Pagination: FunctionComponent<ComponentProps> = (
176
229
  buttonSize={ButtonSize.ExtraSmall}
177
230
  icon={IconProp.AdjustmentHorizontal}
178
231
  buttonStyle={ButtonStyleType.ICON_LIGHT}
232
+ ariaLabel="Open pagination settings"
179
233
  onClick={() => {
180
234
  setShowPaginationModel(true);
181
235
  }}
@@ -183,9 +237,13 @@ const Pagination: FunctionComponent<ComponentProps> = (
183
237
  </div>
184
238
 
185
239
  <div className="md:hidden">
186
- <nav className="inline-flex -space-x-px rounded-md shadow-sm">
187
- <ul>
240
+ <div className="inline-flex -space-x-px rounded-md shadow-sm">
241
+ <ul role="list">
188
242
  <li
243
+ role="button"
244
+ tabIndex={isPreviousDisabled ? -1 : 0}
245
+ aria-disabled={isPreviousDisabled}
246
+ aria-label="Go to previous page"
189
247
  onClick={() => {
190
248
  let currentPageNumber: number = props.currentPageNumber;
191
249
 
@@ -200,6 +258,24 @@ const Pagination: FunctionComponent<ComponentProps> = (
200
258
  );
201
259
  }
202
260
  }}
261
+ onKeyDown={(e: React.KeyboardEvent) => {
262
+ if (
263
+ (e.key === "Enter" || e.key === " ") &&
264
+ !isPreviousDisabled
265
+ ) {
266
+ e.preventDefault();
267
+ let currentPageNumber: number = props.currentPageNumber;
268
+ if (typeof currentPageNumber === "string") {
269
+ currentPageNumber = parseInt(currentPageNumber);
270
+ }
271
+ if (props.onNavigateToPage) {
272
+ props.onNavigateToPage(
273
+ currentPageNumber - 1,
274
+ props.itemsOnPage,
275
+ );
276
+ }
277
+ }
278
+ }}
203
279
  className={` inline-flex items-center rounded-l-md border border-gray-300 bg-white px-2 py-2 text-sm font-medium text-gray-500 ${
204
280
  isPreviousDisabled
205
281
  ? "bg-gray-100"
@@ -209,6 +285,10 @@ const Pagination: FunctionComponent<ComponentProps> = (
209
285
  <span className="page-link">Previous</span>
210
286
  </li>
211
287
  <li
288
+ role="button"
289
+ tabIndex={isCurrentPageButtonDisabled ? -1 : 0}
290
+ aria-current="page"
291
+ aria-label={`Page ${props.currentPageNumber}, click to change page`}
212
292
  data-testid="current-page-link-mobile"
213
293
  className={` z-10 inline-flex items-center border border-x-0 border-gray-300 hover:bg-gray-50 px-4 py-2 text-sm font-medium text-text-600 cursor-pointer ${
214
294
  isCurrentPageButtonDisabled ? "bg-gray-100" : ""
@@ -216,10 +296,20 @@ const Pagination: FunctionComponent<ComponentProps> = (
216
296
  onClick={() => {
217
297
  setShowPaginationModel(true);
218
298
  }}
299
+ onKeyDown={(e: React.KeyboardEvent) => {
300
+ if (e.key === "Enter" || e.key === " ") {
301
+ e.preventDefault();
302
+ setShowPaginationModel(true);
303
+ }
304
+ }}
219
305
  >
220
306
  <span>{props.currentPageNumber}</span>
221
307
  </li>
222
308
  <li
309
+ role="button"
310
+ tabIndex={isNextDisabled ? -1 : 0}
311
+ aria-disabled={isNextDisabled}
312
+ aria-label="Go to next page"
223
313
  onClick={() => {
224
314
  let currentPageNumber: number = props.currentPageNumber;
225
315
 
@@ -234,6 +324,21 @@ const Pagination: FunctionComponent<ComponentProps> = (
234
324
  );
235
325
  }
236
326
  }}
327
+ onKeyDown={(e: React.KeyboardEvent) => {
328
+ if ((e.key === "Enter" || e.key === " ") && !isNextDisabled) {
329
+ e.preventDefault();
330
+ let currentPageNumber: number = props.currentPageNumber;
331
+ if (typeof currentPageNumber === "string") {
332
+ currentPageNumber = parseInt(currentPageNumber);
333
+ }
334
+ if (props.onNavigateToPage) {
335
+ props.onNavigateToPage(
336
+ currentPageNumber + 1,
337
+ props.itemsOnPage,
338
+ );
339
+ }
340
+ }
341
+ }}
237
342
  className={` inline-flex items-center rounded-r-md border border-gray-300 bg-white px-2 py-2 text-sm font-medium text-gray-500 ${
238
343
  isNextDisabled
239
344
  ? "bg-gray-100"
@@ -243,7 +348,7 @@ const Pagination: FunctionComponent<ComponentProps> = (
243
348
  <span>Next</span>
244
349
  </li>
245
350
  </ul>
246
- </nav>
351
+ </div>
247
352
  </div>
248
353
 
249
354
  {showPaginationModel && (
@@ -317,7 +422,7 @@ const Pagination: FunctionComponent<ComponentProps> = (
317
422
  }}
318
423
  />
319
424
  )}
320
- </div>
425
+ </nav>
321
426
  );
322
427
  };
323
428
 
@@ -50,13 +50,23 @@ const ProgressBar: FunctionComponent<ComponentProps> = (
50
50
  }
51
51
 
52
52
  return (
53
- <div className={`w-full ${progressBarSize} mb-4 bg-gray-200 rounded-full`}>
53
+ <div
54
+ className={`w-full ${progressBarSize} mb-4 bg-gray-200 rounded-full`}
55
+ role="progressbar"
56
+ aria-valuenow={percent}
57
+ aria-valuemin={0}
58
+ aria-valuemax={100}
59
+ aria-label={`Progress: ${props.count} of ${props.totalCount} ${props.suffix} (${percent}%)`}
60
+ >
54
61
  <div
55
62
  data-testid="progress-bar"
56
63
  className={`${progressBarSize} bg-indigo-600 rounded-full `}
57
64
  style={{ width: percent + "%" }}
58
65
  ></div>
59
- <div className="text-sm text-gray-400 mt-1 flex justify-between">
66
+ <div
67
+ className="text-sm text-gray-400 mt-1 flex justify-between"
68
+ aria-hidden="true"
69
+ >
60
70
  <div data-testid="progress-bar-count">
61
71
  {props.count} {props.suffix}
62
72
  </div>
@@ -4,6 +4,7 @@ import React, {
4
4
  FunctionComponent,
5
5
  ReactElement,
6
6
  useEffect,
7
+ useId,
7
8
  useState,
8
9
  } from "react";
9
10
 
@@ -22,11 +23,14 @@ export interface ComponentProps {
22
23
  tabIndex?: number | undefined;
23
24
  error?: string | undefined;
24
25
  dataTestId?: string | undefined;
26
+ ariaLabel?: string | undefined;
25
27
  }
26
28
 
27
29
  const Radio: FunctionComponent<ComponentProps> = (
28
30
  props: ComponentProps,
29
31
  ): ReactElement => {
32
+ const uniqueId: string = useId();
33
+ const errorId: string = `radio-error-${uniqueId}`;
30
34
  const [value, setValue] = useState<RadioValue | undefined>(
31
35
  props.initialValue || props.value || undefined,
32
36
  );
@@ -41,14 +45,20 @@ const Radio: FunctionComponent<ComponentProps> = (
41
45
  <div
42
46
  className={`mt-2 space-y-2 ${props.className}`}
43
47
  data-testid={props.dataTestId}
48
+ role="radiogroup"
49
+ aria-label={props.ariaLabel}
50
+ aria-invalid={props.error ? "true" : undefined}
51
+ aria-describedby={props.error ? errorId : undefined}
44
52
  >
45
53
  {props.options.map((option: RadioOption, index: number) => {
54
+ const optionId: string = `${groupName}-option-${index}`;
46
55
  return (
47
56
  <div key={index} className="flex items-center gap-x-3">
48
57
  <input
58
+ id={optionId}
49
59
  tabIndex={props.tabIndex}
50
60
  checked={value === option.value}
51
- onClick={() => {
61
+ onChange={() => {
52
62
  setValue(option.value);
53
63
  if (props.onChange) {
54
64
  props.onChange(option.value);
@@ -64,7 +74,10 @@ const Radio: FunctionComponent<ComponentProps> = (
64
74
  type="radio"
65
75
  className="h-4 w-4 border-gray-300 text-indigo-600 focus:ring-indigo-600"
66
76
  />
67
- <label className="block text-sm font-medium leading-6 text-gray-900">
77
+ <label
78
+ htmlFor={optionId}
79
+ className="block text-sm font-medium leading-6 text-gray-900"
80
+ >
68
81
  {option.label}
69
82
  </label>
70
83
  </div>
@@ -72,7 +85,12 @@ const Radio: FunctionComponent<ComponentProps> = (
72
85
  })}
73
86
 
74
87
  {props.error && (
75
- <p data-testid="error-message" className="mt-1 text-sm text-red-400">
88
+ <p
89
+ id={errorId}
90
+ data-testid="error-message"
91
+ className="mt-1 text-sm text-red-400"
92
+ role="alert"
93
+ >
76
94
  {props.error}
77
95
  </p>
78
96
  )}
@@ -7,7 +7,10 @@ import BaseModel from "../../../Models/DatabaseModels/DatabaseBaseModel/Database
7
7
  import { PromiseVoidFunction } from "../../../Types/FunctionTypes";
8
8
  import IconProp from "../../../Types/Icon/IconProp";
9
9
  import Link from "../../../Types/Link";
10
- import React, { ReactElement, useEffect, useState } from "react";
10
+ import React, { ReactElement, useEffect, useState, useCallback } from "react";
11
+ import GlobalEvents from "../../Utils/GlobalEvents";
12
+
13
+ export const REFRESH_SIDEBAR_COUNT_EVENT: string = "REFRESH_SIDEBAR_COUNTS";
11
14
 
12
15
  export interface ComponentProps<TBaseModel extends BaseModel> {
13
16
  link: Link;
@@ -29,43 +32,67 @@ const CountModelSideMenuItem: <TBaseModel extends BaseModel>(
29
32
  const [error, setError] = useState<string>("");
30
33
  const [count, setCount] = useState<number>(0);
31
34
 
32
- const fetchCount: PromiseVoidFunction = async (): Promise<void> => {
33
- if (!props.modelType) {
34
- return;
35
- }
35
+ const fetchCount: PromiseVoidFunction =
36
+ useCallback(async (): Promise<void> => {
37
+ if (!props.modelType) {
38
+ return;
39
+ }
36
40
 
37
- if (!props.countQuery) {
38
- return;
39
- }
41
+ if (!props.countQuery) {
42
+ return;
43
+ }
40
44
 
41
- setError("");
42
- setIsLoading(true);
45
+ setError("");
46
+ setIsLoading(true);
43
47
 
44
- if (props.onCountFetchInit) {
45
- props.onCountFetchInit();
46
- }
48
+ if (props.onCountFetchInit) {
49
+ props.onCountFetchInit();
50
+ }
47
51
 
48
- try {
49
- const count: number = await ModelAPI.count<BaseModel>({
50
- modelType: props.modelType,
51
- query: props.countQuery,
52
- requestOptions: props.requestOptions,
53
- });
52
+ try {
53
+ const count: number = await ModelAPI.count<BaseModel>({
54
+ modelType: props.modelType,
55
+ query: props.countQuery,
56
+ requestOptions: props.requestOptions,
57
+ });
54
58
 
55
- setCount(count);
56
- setError("");
57
- } catch (err) {
58
- setError(API.getFriendlyMessage(err));
59
- }
59
+ setCount(count);
60
+ setError("");
61
+ } catch (err) {
62
+ setError(API.getFriendlyMessage(err));
63
+ }
60
64
 
61
- setIsLoading(false);
62
- };
65
+ setIsLoading(false);
66
+ }, [
67
+ props.modelType,
68
+ props.countQuery,
69
+ props.requestOptions,
70
+ props.onCountFetchInit,
71
+ ]);
63
72
 
64
73
  useEffect(() => {
65
74
  fetchCount().catch((err: Error) => {
66
75
  setError(API.getFriendlyMessage(err));
67
76
  });
68
- }, [props.countQuery]);
77
+ }, [fetchCount]);
78
+
79
+ // Listen for global refresh events
80
+ useEffect(() => {
81
+ const handleRefresh: () => void = (): void => {
82
+ fetchCount().catch((err: Error) => {
83
+ setError(API.getFriendlyMessage(err));
84
+ });
85
+ };
86
+
87
+ GlobalEvents.addEventListener(REFRESH_SIDEBAR_COUNT_EVENT, handleRefresh);
88
+
89
+ return () => {
90
+ GlobalEvents.removeEventListener(
91
+ REFRESH_SIDEBAR_COUNT_EVENT,
92
+ handleRefresh,
93
+ );
94
+ };
95
+ }, [fetchCount]);
69
96
 
70
97
  return (
71
98
  <SideMenuItem
@@ -17,8 +17,13 @@ const Statusbubble: FunctionComponent<ComponentProps> = (
17
17
  : Black.toString();
18
18
 
19
19
  return (
20
- <div className="flex" style={props.style}>
21
- <div className="-mr-2 ml-5">
20
+ <div
21
+ className="flex"
22
+ style={props.style}
23
+ role="status"
24
+ aria-label={`Status: ${props.text}`}
25
+ >
26
+ <div className="-mr-2 ml-5" aria-hidden="true">
22
27
  <span className="relative -left-1 -translate-x-full top-1/2 -translate-y-1/2 flex h-3.5 w-3.5">
23
28
  <span
24
29
  className={`${