@oneuptime/common 11.7.2 → 11.7.4

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 (414) hide show
  1. package/Models/AnalyticsModels/KubernetesCostAllocation.ts +103 -0
  2. package/Models/DatabaseModels/AlertEpisodeStateTimeline.ts +1 -0
  3. package/Models/DatabaseModels/AlertStateTimeline.ts +1 -0
  4. package/Models/DatabaseModels/IncidentEpisodeStateTimeline.ts +1 -0
  5. package/Models/DatabaseModels/IncidentStateTimeline.ts +1 -0
  6. package/Models/DatabaseModels/Index.ts +5 -0
  7. package/Models/DatabaseModels/LogDropFilter.ts +89 -0
  8. package/Models/DatabaseModels/NetworkDeviceDiscoveryScan.ts +13 -3
  9. package/Models/DatabaseModels/RecommendationDismissal.ts +463 -0
  10. package/Models/DatabaseModels/ScheduledMaintenanceStateTimeline.ts +1 -0
  11. package/Models/DatabaseModels/StatusPageGroup.ts +95 -0
  12. package/Models/DatabaseModels/TableView.ts +40 -0
  13. package/Models/DatabaseModels/TraceDropFilter.ts +89 -0
  14. package/Models/DatabaseModels/WorkflowVariable.ts +5 -1
  15. package/Server/API/AlertAPI.ts +32 -17
  16. package/Server/API/BaseAPI.ts +31 -2
  17. package/Server/API/BaseAnalyticsAPI.ts +36 -2
  18. package/Server/API/CommonAPI.ts +57 -0
  19. package/Server/API/IncidentAPI.ts +60 -33
  20. package/Server/API/IncidentEpisodeAPI.ts +31 -16
  21. package/Server/API/MonitorAPI.ts +30 -0
  22. package/Server/API/MonitorTemplateAPI.ts +8 -0
  23. package/Server/API/ProbeAPI.ts +196 -15
  24. package/Server/API/ProjectAPI.ts +79 -1
  25. package/Server/API/ScheduledMaintenanceAPI.ts +33 -18
  26. package/Server/API/StatusPageAPI.ts +74 -37
  27. package/Server/API/WorkspaceNotificationRuleAPI.ts +34 -4
  28. package/Server/Infrastructure/Postgres/SchemaMigrations/1785241000000-AddColumnsToTableView.ts +13 -0
  29. package/Server/Infrastructure/Postgres/SchemaMigrations/1785320000000-RepairCrossProjectIncidentReferences.ts +527 -0
  30. package/Server/Infrastructure/Postgres/SchemaMigrations/1785329453269-AddParentGroupToStatusPageGroup.ts +31 -0
  31. package/Server/Infrastructure/Postgres/SchemaMigrations/1785397674289-AddRecommendationDismissalTable.ts +55 -0
  32. package/Server/Infrastructure/Postgres/SchemaMigrations/1785405581596-AddDroppedCountersToDropFilters.ts +53 -0
  33. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +10 -0
  34. package/Server/Services/AlertEpisodeService.ts +47 -0
  35. package/Server/Services/AlertService.ts +125 -0
  36. package/Server/Services/BillingService.ts +98 -0
  37. package/Server/Services/DatabaseService.ts +184 -20
  38. package/Server/Services/IncidentEpisodeService.ts +47 -0
  39. package/Server/Services/IncidentService.ts +123 -0
  40. package/Server/Services/IncidentTemplateService.ts +123 -1
  41. package/Server/Services/Index.ts +2 -0
  42. package/Server/Services/LogDropFilterService.ts +133 -0
  43. package/Server/Services/MonitorProbeService.ts +94 -4
  44. package/Server/Services/MonitorService.ts +48 -43
  45. package/Server/Services/NetworkDeviceDiscoveryScanService.ts +70 -0
  46. package/Server/Services/ProbeService.ts +66 -0
  47. package/Server/Services/ProjectService.ts +131 -7
  48. package/Server/Services/RecommendationDismissalService.ts +52 -0
  49. package/Server/Services/RunbookAgentJobService.ts +68 -7
  50. package/Server/Services/ScheduledMaintenanceService.ts +103 -0
  51. package/Server/Services/ScheduledMaintenanceTemplateService.ts +90 -0
  52. package/Server/Services/StatusPageGroupService.ts +186 -0
  53. package/Server/Services/TraceDropFilterService.ts +133 -0
  54. package/Server/Types/Workflow/Components/API/Delete.ts +12 -0
  55. package/Server/Types/Workflow/Components/API/Get.ts +12 -0
  56. package/Server/Types/Workflow/Components/API/Patch.ts +12 -0
  57. package/Server/Types/Workflow/Components/API/Post.ts +12 -0
  58. package/Server/Types/Workflow/Components/API/Put.ts +13 -0
  59. package/Server/Utils/Database/ProjectScopedReferenceValidator.ts +212 -0
  60. package/Server/Utils/DropFilterValidation.ts +127 -0
  61. package/Server/Utils/Errors.ts +5 -0
  62. package/Server/Utils/Monitor/MonitorAlert.ts +28 -1
  63. package/Server/Utils/Monitor/MonitorIncident.ts +31 -2
  64. package/Server/Utils/Telemetry/AppMetrics.ts +31 -0
  65. package/Tests/Models/AnalyticsModels/KubernetesCostAllocation.test.ts +16 -0
  66. package/Tests/Models/DatabaseModels/MonitorProbeColumnAccessControl.test.ts +196 -0
  67. package/Tests/Models/RecommendationDismissalModel.test.ts +269 -0
  68. package/Tests/Server/API/BaseAPIUpdatePayloadValidation.test.ts +320 -0
  69. package/Tests/Server/API/BaseAnalyticsAPIUpdatePayloadValidation.test.ts +293 -0
  70. package/Tests/Server/API/CommonAPIAuthGuard.test.ts +164 -0
  71. package/Tests/Server/API/ProbeAPI.test.ts +422 -53
  72. package/Tests/Server/API/ProjectAPI.test.ts +180 -0
  73. package/Tests/Server/API/ProjectAPIExtendTrialBillingDisabled.test.ts +138 -0
  74. package/Tests/Server/API/ProjectScopedCustomRouteAuth.test.ts +458 -0
  75. package/Tests/Server/Infrastructure/ContainerBootConfiguration.test.ts +359 -0
  76. package/Tests/Server/Infrastructure/ProcessSignalDelivery.test.ts +427 -0
  77. package/Tests/Server/Services/BillingServiceExtendTrial.test.ts +439 -0
  78. package/Tests/Server/Services/CrossProjectReferenceWriteGuard.test.ts +810 -0
  79. package/Tests/Server/Services/DatabaseServiceAtomicAddToColumns.test.ts +269 -0
  80. package/Tests/Server/Services/DatabaseServiceEmptyUpdate.test.ts +277 -0
  81. package/Tests/Server/Services/DatabaseServiceSortColumnSelect.test.ts +201 -0
  82. package/Tests/Server/Services/DiscoveryScanClaimHookFreeSafety.test.ts +81 -23
  83. package/Tests/Server/Services/DropFilterSaveValidation.test.ts +398 -0
  84. package/Tests/Server/Services/MonitorProbeDeleteHooks.test.ts +280 -0
  85. package/Tests/Server/Services/MonitorProbeServiceProbeTenancy.test.ts +304 -0
  86. package/Tests/Server/Services/NetworkDeviceDiscoveryScanService.test.ts +262 -0
  87. package/Tests/Server/Services/ProjectServiceExtendTrial.test.ts +614 -0
  88. package/Tests/Server/Services/RecommendationDismissalService.test.ts +306 -0
  89. package/Tests/Server/Services/RepairCrossProjectIncidentReferencesMigration.test.ts +528 -0
  90. package/Tests/Server/Services/StatusPageGroupNesting.test.ts +395 -0
  91. package/Tests/Server/TestingUtils/Services/BillingServiceHelper.ts +26 -21
  92. package/Tests/Server/Types/Database/Permissions/WorkflowVariableColumnPermission.test.ts +227 -0
  93. package/Tests/Server/Types/Workflow/Components/ApiComponentErrorPort.test.ts +285 -0
  94. package/Tests/Server/Utils/Database/ProjectScopedReferenceValidator.test.ts +463 -0
  95. package/Tests/Server/Utils/DropFilterValidation.test.ts +269 -0
  96. package/Tests/Server/Utils/Telemetry/SpanUtil.test.ts +94 -0
  97. package/Tests/Types/DateUserTimezone.test.ts +52 -0
  98. package/Tests/Types/JSONFunctions.test.ts +103 -1
  99. package/Tests/Types/Monitor/KubernetesAlertTemplates.test.ts +177 -9
  100. package/Tests/Types/Monitor/MonitorStepDnsMonitor.test.ts +105 -0
  101. package/Tests/Types/Monitor/MonitorStepDnssecMonitor.test.ts +131 -0
  102. package/Tests/Types/Monitor/MonitorStepDomainMonitor.test.ts +60 -0
  103. package/Tests/Types/Monitor/MonitorStepExternalStatusPageMonitor.test.ts +82 -0
  104. package/Tests/Types/Monitor/MonitorStepInfrastructureMonitors.test.ts +268 -0
  105. package/Tests/Types/Monitor/MonitorStepMetricMonitor.test.ts +386 -0
  106. package/Tests/Types/Monitor/MonitorStepProfileMonitor.test.ts +185 -0
  107. package/Tests/Types/Monitor/MonitorStepSnmpMonitor.test.ts +156 -0
  108. package/Tests/Types/Monitor/Recommendation/MonitorRecommendationCatalog.test.ts +371 -0
  109. package/Tests/Types/Monitor/Recommendation/MonitorRecommendationCoverage.test.ts +901 -0
  110. package/Tests/Types/Monitor/Recommendation/MonitorRecommendationNotificationMode.test.ts +607 -0
  111. package/Tests/Types/Monitor/Recommendation/MonitorRecommendationSeverityMapper.test.ts +754 -0
  112. package/Tests/Types/Monitor/Recommendation/MonitorRecommendationUtil.test.ts +960 -0
  113. package/Tests/Types/RollingTime/RollingTimeUtil.test.ts +186 -0
  114. package/Tests/Types/Runbook/RunbookStepTimeout.test.ts +410 -0
  115. package/Tests/Types/Telemetry/DropFilterSampling.test.ts +241 -0
  116. package/Tests/Types/Workspace/NotificationRuleConditionUtil.test.ts +353 -0
  117. package/Tests/Types/Workspace/NotificationRuleUtil.test.ts +566 -0
  118. package/Tests/UI/Components/CodeEditor.test.tsx +174 -0
  119. package/Tests/UI/Components/ErrorBoundary.test.tsx +555 -24
  120. package/Tests/UI/Components/InfoCard.test.tsx +358 -0
  121. package/Tests/UI/Components/LogTimeRangePicker.test.tsx +138 -0
  122. package/Tests/UI/Components/Modal.test.tsx +43 -5
  123. package/Tests/UI/Components/ModelTable/BaseModelTableColumnCustomization.test.tsx +679 -0
  124. package/Tests/UI/Components/ModelTable/BaseModelTableSearchLabels.test.tsx +397 -0
  125. package/Tests/UI/Components/ModelTable/ColumnCustomizationModal.test.tsx +999 -0
  126. package/Tests/UI/Components/ModelTable/ColumnPreference.test.ts +1678 -0
  127. package/Tests/UI/Components/ModelTable/CustomFieldColumns.test.tsx +1094 -0
  128. package/Tests/UI/Components/Runbook/StepTimeoutInput.test.tsx +386 -0
  129. package/Tests/UI/Components/StatusPage/ResourceGroupSection.test.tsx +671 -0
  130. package/Tests/UI/Components/TelemetryTimeRangePicker.test.tsx +404 -0
  131. package/Tests/UI/Utils/DropdownAlignment.test.ts +306 -0
  132. package/Tests/UI/Utils/TableFilterUrlState.test.ts +292 -0
  133. package/Tests/Utils/Monitor/MonitorSummaryProbeUtil.test.ts +263 -0
  134. package/Tests/Utils/NetworkDiscovery/ScanTargetUtil.test.ts +583 -0
  135. package/Tests/Utils/StatusPage/GroupNestingLayout.test.ts +1072 -0
  136. package/Tests/Utils/StatusPage/GroupTree.test.ts +583 -0
  137. package/Tests/Utils/StatusPage/ResourceUptime.test.ts +411 -0
  138. package/Tests/Utils/UserPreferences.test.ts +563 -0
  139. package/Types/Date.ts +15 -6
  140. package/Types/JSONFunctions.ts +7 -2
  141. package/Types/Kubernetes/KubernetesCostIngest.ts +37 -1
  142. package/Types/Monitor/KubernetesAlertTemplates.ts +205 -0
  143. package/Types/Monitor/MonitorStep.ts +10 -1
  144. package/Types/Monitor/MonitorStepMetricMonitor.ts +46 -1
  145. package/Types/Monitor/Recommendation/MonitorRecommendationCatalog.ts +413 -0
  146. package/Types/Monitor/Recommendation/MonitorRecommendationSeverityMapper.ts +168 -0
  147. package/Types/Monitor/Recommendation/MonitorRecommendationTypes.ts +175 -0
  148. package/Types/Monitor/Recommendation/MonitorRecommendationUtil.ts +743 -0
  149. package/Types/Permission.ts +45 -0
  150. package/Types/Recommendation/RecommendationType.ts +20 -0
  151. package/Types/Runbook/RunbookExecutionDeadline.ts +129 -0
  152. package/Types/Runbook/RunbookStepTimeout.ts +202 -0
  153. package/Types/Telemetry/DropFilterSampling.ts +92 -0
  154. package/UI/Components/AI/GenerateFromAIModal.tsx +0 -2
  155. package/UI/Components/Accordion/Accordion.tsx +10 -2
  156. package/UI/Components/BulkUpdate/BulkLabelActions.tsx +0 -1
  157. package/UI/Components/CodeEditor/CodeEditor.tsx +34 -20
  158. package/UI/Components/Date/RangeStartAndEndDateView.tsx +0 -1
  159. package/UI/Components/EditionLabel/EditionLabel.tsx +1 -27
  160. package/UI/Components/Error/PageError.tsx +0 -4
  161. package/UI/Components/ErrorBoundary.tsx +252 -15
  162. package/UI/Components/InfoCard/InfoCard.tsx +58 -1
  163. package/UI/Components/LogsViewer/components/LogTimeRangePicker.tsx +34 -1
  164. package/UI/Components/MasterPage/MasterPage.tsx +65 -0
  165. package/UI/Components/Modal/Modal.tsx +16 -60
  166. package/UI/Components/ModelFormModal/ModelFormModal.tsx +0 -8
  167. package/UI/Components/ModelTable/BaseModelTable.tsx +503 -71
  168. package/UI/Components/ModelTable/Column.ts +17 -0
  169. package/UI/Components/ModelTable/ColumnCustomizationModal.tsx +523 -0
  170. package/UI/Components/ModelTable/ColumnPreference.ts +482 -0
  171. package/UI/Components/ModelTable/CustomFieldColumns.tsx +326 -0
  172. package/UI/Components/ModelTable/TableView.tsx +24 -2
  173. package/UI/Components/ModelTable/useCustomFieldColumns.ts +150 -0
  174. package/UI/Components/Runbook/StepTimeoutInput.tsx +173 -0
  175. package/UI/Components/SideMenu/SideMenu.tsx +24 -4
  176. package/UI/Components/StatusPage/ResourceGroupSection.tsx +231 -0
  177. package/UI/Components/Table/Table.tsx +14 -1
  178. package/UI/Components/Table/TableRow.tsx +180 -175
  179. package/UI/Components/Table/Types/Column.ts +2 -0
  180. package/UI/Components/TelemetryViewer/components/TelemetryTimeRangePicker.tsx +34 -1
  181. package/UI/Utils/DropdownAlignment.ts +162 -0
  182. package/UI/Utils/TableFilterUrlState.ts +59 -20
  183. package/Utils/API.ts +106 -2
  184. package/Utils/Monitor/MonitorSummaryProbeUtil.ts +130 -0
  185. package/Utils/NetworkDiscovery/ScanTargetUtil.ts +434 -0
  186. package/Utils/StatusPage/GroupNestingLayout.ts +343 -0
  187. package/Utils/StatusPage/GroupTree.ts +307 -0
  188. package/Utils/StatusPage/ResourceUptime.ts +90 -17
  189. package/Utils/UserPreferences.ts +53 -0
  190. package/build/dist/Models/AnalyticsModels/KubernetesCostAllocation.js +86 -0
  191. package/build/dist/Models/AnalyticsModels/KubernetesCostAllocation.js.map +1 -1
  192. package/build/dist/Models/DatabaseModels/AlertEpisodeStateTimeline.js +1 -0
  193. package/build/dist/Models/DatabaseModels/AlertEpisodeStateTimeline.js.map +1 -1
  194. package/build/dist/Models/DatabaseModels/AlertStateTimeline.js +1 -0
  195. package/build/dist/Models/DatabaseModels/AlertStateTimeline.js.map +1 -1
  196. package/build/dist/Models/DatabaseModels/IncidentEpisodeStateTimeline.js +1 -0
  197. package/build/dist/Models/DatabaseModels/IncidentEpisodeStateTimeline.js.map +1 -1
  198. package/build/dist/Models/DatabaseModels/IncidentStateTimeline.js +1 -0
  199. package/build/dist/Models/DatabaseModels/IncidentStateTimeline.js.map +1 -1
  200. package/build/dist/Models/DatabaseModels/Index.js +2 -0
  201. package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
  202. package/build/dist/Models/DatabaseModels/LogDropFilter.js +91 -0
  203. package/build/dist/Models/DatabaseModels/LogDropFilter.js.map +1 -1
  204. package/build/dist/Models/DatabaseModels/NetworkDeviceDiscoveryScan.js +14 -4
  205. package/build/dist/Models/DatabaseModels/NetworkDeviceDiscoveryScan.js.map +1 -1
  206. package/build/dist/Models/DatabaseModels/RecommendationDismissal.js +484 -0
  207. package/build/dist/Models/DatabaseModels/RecommendationDismissal.js.map +1 -0
  208. package/build/dist/Models/DatabaseModels/ScheduledMaintenanceStateTimeline.js +1 -0
  209. package/build/dist/Models/DatabaseModels/ScheduledMaintenanceStateTimeline.js.map +1 -1
  210. package/build/dist/Models/DatabaseModels/StatusPageGroup.js +94 -0
  211. package/build/dist/Models/DatabaseModels/StatusPageGroup.js.map +1 -1
  212. package/build/dist/Models/DatabaseModels/TableView.js +40 -0
  213. package/build/dist/Models/DatabaseModels/TableView.js.map +1 -1
  214. package/build/dist/Models/DatabaseModels/TraceDropFilter.js +91 -0
  215. package/build/dist/Models/DatabaseModels/TraceDropFilter.js.map +1 -1
  216. package/build/dist/Models/DatabaseModels/WorkflowVariable.js +5 -1
  217. package/build/dist/Models/DatabaseModels/WorkflowVariable.js.map +1 -1
  218. package/build/dist/Server/API/AlertAPI.js +22 -13
  219. package/build/dist/Server/API/AlertAPI.js.map +1 -1
  220. package/build/dist/Server/API/BaseAPI.js +22 -1
  221. package/build/dist/Server/API/BaseAPI.js.map +1 -1
  222. package/build/dist/Server/API/BaseAnalyticsAPI.js +24 -1
  223. package/build/dist/Server/API/BaseAnalyticsAPI.js.map +1 -1
  224. package/build/dist/Server/API/CommonAPI.js +42 -0
  225. package/build/dist/Server/API/CommonAPI.js.map +1 -1
  226. package/build/dist/Server/API/IncidentAPI.js +43 -26
  227. package/build/dist/Server/API/IncidentAPI.js.map +1 -1
  228. package/build/dist/Server/API/IncidentEpisodeAPI.js +21 -12
  229. package/build/dist/Server/API/IncidentEpisodeAPI.js.map +1 -1
  230. package/build/dist/Server/API/MonitorAPI.js +23 -0
  231. package/build/dist/Server/API/MonitorAPI.js.map +1 -1
  232. package/build/dist/Server/API/MonitorTemplateAPI.js +4 -0
  233. package/build/dist/Server/API/MonitorTemplateAPI.js.map +1 -1
  234. package/build/dist/Server/API/ProbeAPI.js +131 -15
  235. package/build/dist/Server/API/ProbeAPI.js.map +1 -1
  236. package/build/dist/Server/API/ProjectAPI.js +62 -3
  237. package/build/dist/Server/API/ProjectAPI.js.map +1 -1
  238. package/build/dist/Server/API/ScheduledMaintenanceAPI.js +23 -14
  239. package/build/dist/Server/API/ScheduledMaintenanceAPI.js.map +1 -1
  240. package/build/dist/Server/API/StatusPageAPI.js +61 -33
  241. package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
  242. package/build/dist/Server/API/WorkspaceNotificationRuleAPI.js +26 -2
  243. package/build/dist/Server/API/WorkspaceNotificationRuleAPI.js.map +1 -1
  244. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785241000000-AddColumnsToTableView.js +12 -0
  245. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785241000000-AddColumnsToTableView.js.map +1 -0
  246. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785320000000-RepairCrossProjectIncidentReferences.js +397 -0
  247. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785320000000-RepairCrossProjectIncidentReferences.js.map +1 -0
  248. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785329453269-AddParentGroupToStatusPageGroup.js +16 -0
  249. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785329453269-AddParentGroupToStatusPageGroup.js.map +1 -0
  250. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785397674289-AddRecommendationDismissalTable.js +30 -0
  251. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785397674289-AddRecommendationDismissalTable.js.map +1 -0
  252. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785405581596-AddDroppedCountersToDropFilters.js +34 -0
  253. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785405581596-AddDroppedCountersToDropFilters.js.map +1 -0
  254. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +10 -0
  255. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  256. package/build/dist/Server/Services/AlertEpisodeService.js +39 -0
  257. package/build/dist/Server/Services/AlertEpisodeService.js.map +1 -1
  258. package/build/dist/Server/Services/AlertService.js +95 -0
  259. package/build/dist/Server/Services/AlertService.js.map +1 -1
  260. package/build/dist/Server/Services/BillingService.js +71 -0
  261. package/build/dist/Server/Services/BillingService.js.map +1 -1
  262. package/build/dist/Server/Services/DatabaseService.js +140 -17
  263. package/build/dist/Server/Services/DatabaseService.js.map +1 -1
  264. package/build/dist/Server/Services/IncidentEpisodeService.js +39 -0
  265. package/build/dist/Server/Services/IncidentEpisodeService.js.map +1 -1
  266. package/build/dist/Server/Services/IncidentService.js +100 -0
  267. package/build/dist/Server/Services/IncidentService.js.map +1 -1
  268. package/build/dist/Server/Services/IncidentTemplateService.js +97 -0
  269. package/build/dist/Server/Services/IncidentTemplateService.js.map +1 -1
  270. package/build/dist/Server/Services/Index.js +2 -0
  271. package/build/dist/Server/Services/Index.js.map +1 -1
  272. package/build/dist/Server/Services/LogDropFilterService.js +93 -0
  273. package/build/dist/Server/Services/LogDropFilterService.js.map +1 -1
  274. package/build/dist/Server/Services/MonitorProbeService.js +73 -5
  275. package/build/dist/Server/Services/MonitorProbeService.js.map +1 -1
  276. package/build/dist/Server/Services/MonitorService.js +34 -39
  277. package/build/dist/Server/Services/MonitorService.js.map +1 -1
  278. package/build/dist/Server/Services/NetworkDeviceDiscoveryScanService.js +71 -0
  279. package/build/dist/Server/Services/NetworkDeviceDiscoveryScanService.js.map +1 -1
  280. package/build/dist/Server/Services/ProbeService.js +64 -0
  281. package/build/dist/Server/Services/ProbeService.js.map +1 -1
  282. package/build/dist/Server/Services/ProjectService.js +103 -7
  283. package/build/dist/Server/Services/ProjectService.js.map +1 -1
  284. package/build/dist/Server/Services/RecommendationDismissalService.js +50 -0
  285. package/build/dist/Server/Services/RecommendationDismissalService.js.map +1 -0
  286. package/build/dist/Server/Services/RunbookAgentJobService.js +63 -5
  287. package/build/dist/Server/Services/RunbookAgentJobService.js.map +1 -1
  288. package/build/dist/Server/Services/ScheduledMaintenanceService.js +83 -0
  289. package/build/dist/Server/Services/ScheduledMaintenanceService.js.map +1 -1
  290. package/build/dist/Server/Services/ScheduledMaintenanceTemplateService.js +65 -0
  291. package/build/dist/Server/Services/ScheduledMaintenanceTemplateService.js.map +1 -1
  292. package/build/dist/Server/Services/StatusPageGroupService.js +136 -0
  293. package/build/dist/Server/Services/StatusPageGroupService.js.map +1 -1
  294. package/build/dist/Server/Services/TraceDropFilterService.js +93 -0
  295. package/build/dist/Server/Services/TraceDropFilterService.js.map +1 -1
  296. package/build/dist/Server/Types/Workflow/Components/API/Delete.js +11 -0
  297. package/build/dist/Server/Types/Workflow/Components/API/Delete.js.map +1 -1
  298. package/build/dist/Server/Types/Workflow/Components/API/Get.js +11 -0
  299. package/build/dist/Server/Types/Workflow/Components/API/Get.js.map +1 -1
  300. package/build/dist/Server/Types/Workflow/Components/API/Patch.js +11 -0
  301. package/build/dist/Server/Types/Workflow/Components/API/Patch.js.map +1 -1
  302. package/build/dist/Server/Types/Workflow/Components/API/Post.js +11 -0
  303. package/build/dist/Server/Types/Workflow/Components/API/Post.js.map +1 -1
  304. package/build/dist/Server/Types/Workflow/Components/API/Put.js +12 -0
  305. package/build/dist/Server/Types/Workflow/Components/API/Put.js.map +1 -1
  306. package/build/dist/Server/Utils/Database/ProjectScopedReferenceValidator.js +132 -0
  307. package/build/dist/Server/Utils/Database/ProjectScopedReferenceValidator.js.map +1 -0
  308. package/build/dist/Server/Utils/DropFilterValidation.js +80 -0
  309. package/build/dist/Server/Utils/DropFilterValidation.js.map +1 -0
  310. package/build/dist/Server/Utils/Errors.js +3 -0
  311. package/build/dist/Server/Utils/Errors.js.map +1 -1
  312. package/build/dist/Server/Utils/Monitor/MonitorAlert.js +30 -12
  313. package/build/dist/Server/Utils/Monitor/MonitorAlert.js.map +1 -1
  314. package/build/dist/Server/Utils/Monitor/MonitorIncident.js +38 -18
  315. package/build/dist/Server/Utils/Monitor/MonitorIncident.js.map +1 -1
  316. package/build/dist/Server/Utils/Telemetry/AppMetrics.js +26 -0
  317. package/build/dist/Server/Utils/Telemetry/AppMetrics.js.map +1 -1
  318. package/build/dist/Types/Date.js +14 -4
  319. package/build/dist/Types/Date.js.map +1 -1
  320. package/build/dist/Types/JSONFunctions.js +8 -3
  321. package/build/dist/Types/JSONFunctions.js.map +1 -1
  322. package/build/dist/Types/Kubernetes/KubernetesCostIngest.js.map +1 -1
  323. package/build/dist/Types/Monitor/KubernetesAlertTemplates.js +192 -0
  324. package/build/dist/Types/Monitor/KubernetesAlertTemplates.js.map +1 -1
  325. package/build/dist/Types/Monitor/MonitorStep.js +6 -1
  326. package/build/dist/Types/Monitor/MonitorStep.js.map +1 -1
  327. package/build/dist/Types/Monitor/MonitorStepMetricMonitor.js +32 -1
  328. package/build/dist/Types/Monitor/MonitorStepMetricMonitor.js.map +1 -1
  329. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationCatalog.js +294 -0
  330. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationCatalog.js.map +1 -0
  331. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationSeverityMapper.js +110 -0
  332. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationSeverityMapper.js.map +1 -0
  333. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationTypes.js +58 -0
  334. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationTypes.js.map +1 -0
  335. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationUtil.js +451 -0
  336. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationUtil.js.map +1 -0
  337. package/build/dist/Types/Permission.js +40 -0
  338. package/build/dist/Types/Permission.js.map +1 -1
  339. package/build/dist/Types/Recommendation/RecommendationType.js +21 -0
  340. package/build/dist/Types/Recommendation/RecommendationType.js.map +1 -0
  341. package/build/dist/Types/Runbook/RunbookExecutionDeadline.js +93 -0
  342. package/build/dist/Types/Runbook/RunbookExecutionDeadline.js.map +1 -0
  343. package/build/dist/Types/Runbook/RunbookStepTimeout.js +142 -0
  344. package/build/dist/Types/Runbook/RunbookStepTimeout.js.map +1 -0
  345. package/build/dist/Types/Telemetry/DropFilterSampling.js +75 -0
  346. package/build/dist/Types/Telemetry/DropFilterSampling.js.map +1 -0
  347. package/build/dist/UI/Components/AI/GenerateFromAIModal.js +1 -2
  348. package/build/dist/UI/Components/AI/GenerateFromAIModal.js.map +1 -1
  349. package/build/dist/UI/Components/Accordion/Accordion.js +9 -2
  350. package/build/dist/UI/Components/Accordion/Accordion.js.map +1 -1
  351. package/build/dist/UI/Components/BulkUpdate/BulkLabelActions.js +1 -1
  352. package/build/dist/UI/Components/BulkUpdate/BulkLabelActions.js.map +1 -1
  353. package/build/dist/UI/Components/CodeEditor/CodeEditor.js +27 -14
  354. package/build/dist/UI/Components/CodeEditor/CodeEditor.js.map +1 -1
  355. package/build/dist/UI/Components/Date/RangeStartAndEndDateView.js +1 -1
  356. package/build/dist/UI/Components/Date/RangeStartAndEndDateView.js.map +1 -1
  357. package/build/dist/UI/Components/EditionLabel/EditionLabel.js +2 -20
  358. package/build/dist/UI/Components/EditionLabel/EditionLabel.js.map +1 -1
  359. package/build/dist/UI/Components/Error/PageError.js +1 -3
  360. package/build/dist/UI/Components/Error/PageError.js.map +1 -1
  361. package/build/dist/UI/Components/ErrorBoundary.js +145 -11
  362. package/build/dist/UI/Components/ErrorBoundary.js.map +1 -1
  363. package/build/dist/UI/Components/InfoCard/InfoCard.js +28 -1
  364. package/build/dist/UI/Components/InfoCard/InfoCard.js.map +1 -1
  365. package/build/dist/UI/Components/LogsViewer/components/LogTimeRangePicker.js +13 -2
  366. package/build/dist/UI/Components/LogsViewer/components/LogTimeRangePicker.js.map +1 -1
  367. package/build/dist/UI/Components/MasterPage/MasterPage.js +50 -1
  368. package/build/dist/UI/Components/MasterPage/MasterPage.js.map +1 -1
  369. package/build/dist/UI/Components/Modal/Modal.js +4 -32
  370. package/build/dist/UI/Components/Modal/Modal.js.map +1 -1
  371. package/build/dist/UI/Components/ModelFormModal/ModelFormModal.js.map +1 -1
  372. package/build/dist/UI/Components/ModelTable/BaseModelTable.js +308 -40
  373. package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
  374. package/build/dist/UI/Components/ModelTable/ColumnCustomizationModal.js +230 -0
  375. package/build/dist/UI/Components/ModelTable/ColumnCustomizationModal.js.map +1 -0
  376. package/build/dist/UI/Components/ModelTable/ColumnPreference.js +258 -0
  377. package/build/dist/UI/Components/ModelTable/ColumnPreference.js.map +1 -0
  378. package/build/dist/UI/Components/ModelTable/CustomFieldColumns.js +168 -0
  379. package/build/dist/UI/Components/ModelTable/CustomFieldColumns.js.map +1 -0
  380. package/build/dist/UI/Components/ModelTable/TableView.js +13 -2
  381. package/build/dist/UI/Components/ModelTable/TableView.js.map +1 -1
  382. package/build/dist/UI/Components/ModelTable/useCustomFieldColumns.js +84 -0
  383. package/build/dist/UI/Components/ModelTable/useCustomFieldColumns.js.map +1 -0
  384. package/build/dist/UI/Components/Runbook/StepTimeoutInput.js +101 -0
  385. package/build/dist/UI/Components/Runbook/StepTimeoutInput.js.map +1 -0
  386. package/build/dist/UI/Components/SideMenu/SideMenu.js +15 -5
  387. package/build/dist/UI/Components/SideMenu/SideMenu.js.map +1 -1
  388. package/build/dist/UI/Components/StatusPage/ResourceGroupSection.js +73 -0
  389. package/build/dist/UI/Components/StatusPage/ResourceGroupSection.js.map +1 -0
  390. package/build/dist/UI/Components/Table/Table.js +15 -2
  391. package/build/dist/UI/Components/Table/Table.js.map +1 -1
  392. package/build/dist/UI/Components/Table/TableRow.js +11 -6
  393. package/build/dist/UI/Components/Table/TableRow.js.map +1 -1
  394. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryTimeRangePicker.js +13 -2
  395. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryTimeRangePicker.js.map +1 -1
  396. package/build/dist/UI/Utils/DropdownAlignment.js +98 -0
  397. package/build/dist/UI/Utils/DropdownAlignment.js.map +1 -0
  398. package/build/dist/UI/Utils/TableFilterUrlState.js +48 -15
  399. package/build/dist/UI/Utils/TableFilterUrlState.js.map +1 -1
  400. package/build/dist/Utils/API.js +39 -2
  401. package/build/dist/Utils/API.js.map +1 -1
  402. package/build/dist/Utils/Monitor/MonitorSummaryProbeUtil.js +97 -0
  403. package/build/dist/Utils/Monitor/MonitorSummaryProbeUtil.js.map +1 -0
  404. package/build/dist/Utils/NetworkDiscovery/ScanTargetUtil.js +330 -0
  405. package/build/dist/Utils/NetworkDiscovery/ScanTargetUtil.js.map +1 -0
  406. package/build/dist/Utils/StatusPage/GroupNestingLayout.js +280 -0
  407. package/build/dist/Utils/StatusPage/GroupNestingLayout.js.map +1 -0
  408. package/build/dist/Utils/StatusPage/GroupTree.js +214 -0
  409. package/build/dist/Utils/StatusPage/GroupTree.js.map +1 -0
  410. package/build/dist/Utils/StatusPage/ResourceUptime.js +60 -16
  411. package/build/dist/Utils/StatusPage/ResourceUptime.js.map +1 -1
  412. package/build/dist/Utils/UserPreferences.js +33 -0
  413. package/build/dist/Utils/UserPreferences.js.map +1 -1
  414. package/package.json +1 -1
@@ -0,0 +1,404 @@
1
+ import TelemetryTimeRangePicker, {
2
+ TIME_RANGE_DROPDOWN_WIDTH_IN_PX,
3
+ } from "../../../UI/Components/TelemetryViewer/components/TelemetryTimeRangePicker";
4
+ import RangeStartAndEndDateTime from "../../../Types/Time/RangeStartAndEndDateTime";
5
+ import TimeRange from "../../../Types/Time/TimeRange";
6
+ import InBetween from "../../../Types/BaseDatabase/InBetween";
7
+ import "@testing-library/jest-dom/extend-expect";
8
+ import {
9
+ act,
10
+ cleanup,
11
+ fireEvent,
12
+ render,
13
+ screen,
14
+ } from "@testing-library/react";
15
+ import React from "react";
16
+ import {
17
+ afterEach,
18
+ beforeEach,
19
+ describe,
20
+ expect,
21
+ jest,
22
+ test,
23
+ } from "@jest/globals";
24
+
25
+ const BUTTON_TEST_ID: string = "telemetry-time-range-picker-button";
26
+ const DROPDOWN_TEST_ID: string = "telemetry-time-range-picker-dropdown";
27
+
28
+ const ORIGINAL_INNER_WIDTH: number = window.innerWidth;
29
+ const originalGetBoundingClientRect: () => DOMRect =
30
+ Element.prototype.getBoundingClientRect;
31
+
32
+ /*
33
+ * jsdom performs no layout, so every rect comes back zero-sized. These helpers
34
+ * let a test describe where the trigger button sits inside a viewport of a
35
+ * given width, which is the only input the alignment logic reads.
36
+ */
37
+ function setViewportWidth(width: number): void {
38
+ Object.defineProperty(window, "innerWidth", {
39
+ configurable: true,
40
+ writable: true,
41
+ value: width,
42
+ });
43
+ }
44
+
45
+ function stubTriggerRect(left: number, right: number): void {
46
+ Element.prototype.getBoundingClientRect = function (this: Element): DOMRect {
47
+ if (
48
+ this instanceof HTMLElement &&
49
+ this.getAttribute("data-testid") === BUTTON_TEST_ID
50
+ ) {
51
+ return {
52
+ left: left,
53
+ right: right,
54
+ top: 40,
55
+ bottom: 68,
56
+ width: right - left,
57
+ height: 28,
58
+ x: left,
59
+ y: 40,
60
+ toJSON: (): Record<string, unknown> => {
61
+ return {};
62
+ },
63
+ } as DOMRect;
64
+ }
65
+
66
+ return originalGetBoundingClientRect.call(this);
67
+ };
68
+ }
69
+
70
+ function renderPicker(
71
+ value: RangeStartAndEndDateTime,
72
+ onChange: (value: RangeStartAndEndDateTime) => void = jest.fn(),
73
+ ): void {
74
+ render(<TelemetryTimeRangePicker value={value} onChange={onChange} />);
75
+ }
76
+
77
+ function getTrigger(): HTMLElement {
78
+ return screen.getByTestId(BUTTON_TEST_ID);
79
+ }
80
+
81
+ function getDropdown(): HTMLElement {
82
+ return screen.getByTestId(DROPDOWN_TEST_ID);
83
+ }
84
+
85
+ function openDropdown(): HTMLElement {
86
+ fireEvent.click(getTrigger());
87
+ return getDropdown();
88
+ }
89
+
90
+ function hasClass(element: HTMLElement, className: string): boolean {
91
+ return element.classList.contains(className);
92
+ }
93
+
94
+ describe("TelemetryTimeRangePicker", () => {
95
+ beforeEach(() => {
96
+ setViewportWidth(1440);
97
+ stubTriggerRect(64, 180);
98
+ });
99
+
100
+ afterEach(() => {
101
+ cleanup();
102
+ Element.prototype.getBoundingClientRect = originalGetBoundingClientRect;
103
+ setViewportWidth(ORIGINAL_INNER_WIDTH);
104
+ });
105
+
106
+ describe("dropdown alignment", () => {
107
+ test("opens rightwards when the trigger sits at the left of the metrics toolbar", () => {
108
+ /*
109
+ * Regression: the panel was hard-coded to `right-0`, so on the metric
110
+ * explorer — where the time range button is the first control in the
111
+ * toolbar — the 288px panel rendered off the left edge of the content
112
+ * area and on top of the app sidebar.
113
+ */
114
+ renderPicker({ range: TimeRange.PAST_ONE_HOUR });
115
+
116
+ const dropdown: HTMLElement = openDropdown();
117
+
118
+ expect(hasClass(dropdown, "left-0")).toBe(true);
119
+ expect(hasClass(dropdown, "right-0")).toBe(false);
120
+ expect(dropdown.getAttribute("data-align")).toBe("left");
121
+ });
122
+
123
+ test("stays left aligned for a trigger flush against x=0", () => {
124
+ stubTriggerRect(0, 116);
125
+ renderPicker({ range: TimeRange.PAST_ONE_HOUR });
126
+
127
+ expect(hasClass(openDropdown(), "left-0")).toBe(true);
128
+ });
129
+
130
+ test("stays left aligned for a trigger in the middle of a wide toolbar", () => {
131
+ stubTriggerRect(500, 616);
132
+ renderPicker({ range: TimeRange.PAST_ONE_HOUR });
133
+
134
+ expect(hasClass(openDropdown(), "left-0")).toBe(true);
135
+ });
136
+
137
+ test("flips to right alignment when the trigger is near the right edge", () => {
138
+ stubTriggerRect(1310, 1430);
139
+ renderPicker({ range: TimeRange.PAST_ONE_HOUR });
140
+
141
+ const dropdown: HTMLElement = openDropdown();
142
+
143
+ expect(hasClass(dropdown, "right-0")).toBe(true);
144
+ expect(hasClass(dropdown, "left-0")).toBe(false);
145
+ expect(dropdown.getAttribute("data-align")).toBe("right");
146
+ });
147
+
148
+ test("never emits both positioning classes at once", () => {
149
+ stubTriggerRect(1310, 1430);
150
+ renderPicker({ range: TimeRange.PAST_ONE_HOUR });
151
+
152
+ const dropdown: HTMLElement = openDropdown();
153
+
154
+ expect(
155
+ hasClass(dropdown, "left-0") && hasClass(dropdown, "right-0"),
156
+ ).toBe(false);
157
+ });
158
+
159
+ test("stays left aligned on a narrow mobile viewport", () => {
160
+ setViewportWidth(375);
161
+ stubTriggerRect(12, 128);
162
+ renderPicker({ range: TimeRange.PAST_ONE_HOUR });
163
+
164
+ expect(hasClass(openDropdown(), "left-0")).toBe(true);
165
+ });
166
+
167
+ test("re-measures alignment when the window is resized", () => {
168
+ stubTriggerRect(1000, 1116);
169
+ renderPicker({ range: TimeRange.PAST_ONE_HOUR });
170
+
171
+ expect(hasClass(openDropdown(), "left-0")).toBe(true);
172
+
173
+ // Shrink the window so the same trigger no longer has room to its right.
174
+ act(() => {
175
+ setViewportWidth(1200);
176
+ window.dispatchEvent(new Event("resize"));
177
+ });
178
+
179
+ expect(hasClass(getDropdown(), "right-0")).toBe(true);
180
+ });
181
+
182
+ test("keeps the panel on screen when neither edge fits the viewport", () => {
183
+ /*
184
+ * Viewport narrower than the 288px panel: left alignment clips by 60px,
185
+ * right alignment would clip by 116px, so left is the lesser evil.
186
+ */
187
+ setViewportWidth(300);
188
+ stubTriggerRect(64, 180);
189
+ renderPicker({ range: TimeRange.PAST_ONE_HOUR });
190
+
191
+ const dropdown: HTMLElement = openDropdown();
192
+
193
+ expect(hasClass(dropdown, "left-0")).toBe(true);
194
+ expect(hasClass(dropdown, "max-w-[calc(100vw-1rem)]")).toBe(true);
195
+ });
196
+
197
+ test("re-measures alignment when scrolling moves the trigger sideways", () => {
198
+ renderPicker({ range: TimeRange.PAST_ONE_HOUR });
199
+
200
+ expect(hasClass(openDropdown(), "left-0")).toBe(true);
201
+
202
+ act(() => {
203
+ stubTriggerRect(1310, 1430);
204
+ window.dispatchEvent(new Event("scroll"));
205
+ });
206
+
207
+ expect(hasClass(getDropdown(), "right-0")).toBe(true);
208
+ });
209
+
210
+ test("re-evaluates alignment each time the dropdown is reopened", () => {
211
+ renderPicker({ range: TimeRange.PAST_ONE_HOUR });
212
+
213
+ expect(hasClass(openDropdown(), "left-0")).toBe(true);
214
+
215
+ fireEvent.click(getTrigger());
216
+ expect(screen.queryByTestId(DROPDOWN_TEST_ID)).toBeNull();
217
+
218
+ stubTriggerRect(1310, 1430);
219
+ expect(hasClass(openDropdown(), "right-0")).toBe(true);
220
+ });
221
+
222
+ test("stops listening for layout changes once closed", () => {
223
+ const removedEvents: Array<string> = [];
224
+ const originalRemoveEventListener: typeof window.removeEventListener =
225
+ window.removeEventListener.bind(window);
226
+
227
+ window.removeEventListener = ((
228
+ type: string,
229
+ listener: EventListenerOrEventListenerObject,
230
+ options?: boolean | EventListenerOptions,
231
+ ): void => {
232
+ removedEvents.push(type);
233
+ originalRemoveEventListener(type, listener, options);
234
+ }) as typeof window.removeEventListener;
235
+
236
+ try {
237
+ renderPicker({ range: TimeRange.PAST_ONE_HOUR });
238
+ openDropdown();
239
+ fireEvent.click(getTrigger());
240
+
241
+ expect(removedEvents).toContain("resize");
242
+ expect(removedEvents).toContain("scroll");
243
+ } finally {
244
+ window.removeEventListener = originalRemoveEventListener;
245
+ }
246
+ });
247
+
248
+ test("ignores layout events once the dropdown is closed", () => {
249
+ renderPicker({ range: TimeRange.PAST_ONE_HOUR });
250
+ openDropdown();
251
+ fireEvent.click(getTrigger());
252
+
253
+ /*
254
+ * Firing a resize after close must not resurrect the panel or throw
255
+ * because a listener outlived the element it measured.
256
+ */
257
+ act(() => {
258
+ setViewportWidth(300);
259
+ window.dispatchEvent(new Event("resize"));
260
+ });
261
+
262
+ expect(screen.queryByTestId(DROPDOWN_TEST_ID)).toBeNull();
263
+ });
264
+
265
+ test("keeps the panel anchored below the trigger inside a relative container", () => {
266
+ renderPicker({ range: TimeRange.PAST_ONE_HOUR });
267
+
268
+ const dropdown: HTMLElement = openDropdown();
269
+
270
+ expect(hasClass(dropdown, "absolute")).toBe(true);
271
+ expect(hasClass(dropdown, "top-full")).toBe(true);
272
+ expect(
273
+ dropdown.parentElement?.classList.contains("relative") ?? false,
274
+ ).toBe(true);
275
+ });
276
+
277
+ test("caps the panel width so it cannot exceed the viewport", () => {
278
+ renderPicker({ range: TimeRange.PAST_ONE_HOUR });
279
+
280
+ expect(hasClass(openDropdown(), "max-w-[calc(100vw-1rem)]")).toBe(true);
281
+ });
282
+
283
+ test("exports a width constant that matches the rendered w-72 panel", () => {
284
+ expect(TIME_RANGE_DROPDOWN_WIDTH_IN_PX).toBe(288);
285
+
286
+ renderPicker({ range: TimeRange.PAST_ONE_HOUR });
287
+ expect(hasClass(openDropdown(), "w-72")).toBe(true);
288
+ });
289
+ });
290
+
291
+ describe("open and close behaviour", () => {
292
+ test("is closed until the trigger is clicked", () => {
293
+ renderPicker({ range: TimeRange.PAST_ONE_HOUR });
294
+
295
+ expect(screen.queryByTestId(DROPDOWN_TEST_ID)).toBeNull();
296
+ expect(getTrigger().getAttribute("aria-expanded")).toBe("false");
297
+ });
298
+
299
+ test("marks the trigger as expanded while open", () => {
300
+ renderPicker({ range: TimeRange.PAST_ONE_HOUR });
301
+ openDropdown();
302
+
303
+ expect(getTrigger().getAttribute("aria-expanded")).toBe("true");
304
+ });
305
+
306
+ test("closes when clicking outside the picker", () => {
307
+ renderPicker({ range: TimeRange.PAST_ONE_HOUR });
308
+ openDropdown();
309
+
310
+ fireEvent.mouseDown(document.body);
311
+
312
+ expect(screen.queryByTestId(DROPDOWN_TEST_ID)).toBeNull();
313
+ });
314
+
315
+ test("stays open when clicking inside the dropdown", () => {
316
+ renderPicker({ range: TimeRange.PAST_ONE_HOUR });
317
+ const dropdown: HTMLElement = openDropdown();
318
+
319
+ fireEvent.mouseDown(dropdown);
320
+
321
+ expect(screen.queryByTestId(DROPDOWN_TEST_ID)).not.toBeNull();
322
+ });
323
+ });
324
+
325
+ describe("range selection", () => {
326
+ test("shows the label of the currently selected preset", () => {
327
+ renderPicker({ range: TimeRange.PAST_ONE_HOUR });
328
+
329
+ expect(getTrigger().textContent).toContain("Past 1 Hour");
330
+ });
331
+
332
+ test("emits the picked preset and closes the dropdown", () => {
333
+ const onChange: (value: RangeStartAndEndDateTime) => void = jest.fn();
334
+ renderPicker({ range: TimeRange.PAST_ONE_HOUR }, onChange);
335
+ openDropdown();
336
+
337
+ fireEvent.click(screen.getByText("Past 30 Minutes"));
338
+
339
+ expect(onChange).toHaveBeenCalledWith({
340
+ range: TimeRange.PAST_THIRTY_MINS,
341
+ });
342
+ expect(screen.queryByTestId(DROPDOWN_TEST_ID)).toBeNull();
343
+ });
344
+
345
+ test("lists every preset option plus the custom entry", () => {
346
+ renderPicker({ range: TimeRange.PAST_ONE_HOUR });
347
+ const dropdown: HTMLElement = openDropdown();
348
+
349
+ const optionLabels: Array<string> = Array.from(
350
+ dropdown.querySelectorAll("button"),
351
+ ).map((button: HTMLButtonElement): string => {
352
+ return (button.textContent || "").trim();
353
+ });
354
+
355
+ const expectedLabels: Array<string> = [
356
+ "Past 5 Minutes",
357
+ "Past 15 Minutes",
358
+ "Past 30 Minutes",
359
+ "Past 1 Hour",
360
+ "Past 2 Hours",
361
+ "Past 3 Hours",
362
+ "Past 1 Day",
363
+ "Past 2 Days",
364
+ "Past 1 Week",
365
+ "Past 2 Weeks",
366
+ "Past 1 Month",
367
+ "Past 3 Months",
368
+ "Custom Range...",
369
+ ];
370
+
371
+ expect(optionLabels).toEqual(expectedLabels);
372
+ });
373
+
374
+ test("renders the custom range editor when a custom range is active", () => {
375
+ renderPicker({
376
+ range: TimeRange.CUSTOM,
377
+ startAndEndDate: new InBetween<Date>(
378
+ new Date("2024-01-01T00:00:00.000Z"),
379
+ new Date("2024-01-02T00:00:00.000Z"),
380
+ ),
381
+ });
382
+
383
+ const dropdown: HTMLElement = openDropdown();
384
+
385
+ // The custom editor lives in the same panel, so alignment still applies.
386
+ expect(hasClass(dropdown, "left-0")).toBe(true);
387
+ expect(dropdown.querySelectorAll("input").length).toBeGreaterThan(0);
388
+ });
389
+
390
+ test("keeps the taller custom panel inside the viewport near the right edge", () => {
391
+ stubTriggerRect(1310, 1430);
392
+
393
+ renderPicker({
394
+ range: TimeRange.CUSTOM,
395
+ startAndEndDate: new InBetween<Date>(
396
+ new Date("2024-01-01T00:00:00.000Z"),
397
+ new Date("2024-01-02T00:00:00.000Z"),
398
+ ),
399
+ });
400
+
401
+ expect(hasClass(openDropdown(), "right-0")).toBe(true);
402
+ });
403
+ });
404
+ });
@@ -0,0 +1,306 @@
1
+ import {
2
+ DEFAULT_DROPDOWN_VIEWPORT_MARGIN_IN_PX,
3
+ DropdownHorizontalAlignment,
4
+ getDropdownAlignmentClassName,
5
+ getDropdownHorizontalAlignment,
6
+ } from "../../../UI/Utils/DropdownAlignment";
7
+ import { describe, expect, test } from "@jest/globals";
8
+
9
+ // Tailwind `w-72`, the width every telemetry time range dropdown is styled to.
10
+ const DROPDOWN_WIDTH: number = 288;
11
+
12
+ describe("getDropdownHorizontalAlignment", () => {
13
+ describe("triggers with room to the right", () => {
14
+ test("left aligns a trigger at the far left of a desktop viewport", () => {
15
+ /*
16
+ * This is the metric explorer toolbar case: the time range button is the
17
+ * first control in the toolbar, so the popup must open rightwards.
18
+ */
19
+ expect(
20
+ getDropdownHorizontalAlignment({
21
+ anchorLeft: 64,
22
+ anchorRight: 180,
23
+ dropdownWidth: DROPDOWN_WIDTH,
24
+ viewportWidth: 1440,
25
+ }),
26
+ ).toBe(DropdownHorizontalAlignment.Left);
27
+ });
28
+
29
+ test("left aligns when the trigger starts at x=0", () => {
30
+ expect(
31
+ getDropdownHorizontalAlignment({
32
+ anchorLeft: 0,
33
+ anchorRight: 116,
34
+ dropdownWidth: DROPDOWN_WIDTH,
35
+ viewportWidth: 1024,
36
+ }),
37
+ ).toBe(DropdownHorizontalAlignment.Left);
38
+ });
39
+
40
+ test("left aligns a mid-page trigger that still fits", () => {
41
+ expect(
42
+ getDropdownHorizontalAlignment({
43
+ anchorLeft: 500,
44
+ anchorRight: 616,
45
+ dropdownWidth: DROPDOWN_WIDTH,
46
+ viewportWidth: 1440,
47
+ }),
48
+ ).toBe(DropdownHorizontalAlignment.Left);
49
+ });
50
+
51
+ test("left aligns when the popup lands exactly on the right gutter", () => {
52
+ const viewportWidth: number = 1000;
53
+ const anchorLeft: number =
54
+ viewportWidth - DEFAULT_DROPDOWN_VIEWPORT_MARGIN_IN_PX - DROPDOWN_WIDTH;
55
+
56
+ expect(
57
+ getDropdownHorizontalAlignment({
58
+ anchorLeft: anchorLeft,
59
+ anchorRight: anchorLeft + 116,
60
+ dropdownWidth: DROPDOWN_WIDTH,
61
+ viewportWidth: viewportWidth,
62
+ }),
63
+ ).toBe(DropdownHorizontalAlignment.Left);
64
+ });
65
+ });
66
+
67
+ describe("triggers near the right edge", () => {
68
+ test("flips to right alignment one pixel past the right gutter", () => {
69
+ const viewportWidth: number = 1000;
70
+ const anchorLeft: number =
71
+ viewportWidth -
72
+ DEFAULT_DROPDOWN_VIEWPORT_MARGIN_IN_PX -
73
+ DROPDOWN_WIDTH +
74
+ 1;
75
+
76
+ expect(
77
+ getDropdownHorizontalAlignment({
78
+ anchorLeft: anchorLeft,
79
+ anchorRight: viewportWidth - 20,
80
+ dropdownWidth: DROPDOWN_WIDTH,
81
+ viewportWidth: viewportWidth,
82
+ }),
83
+ ).toBe(DropdownHorizontalAlignment.Right);
84
+ });
85
+
86
+ test("right aligns a trigger flush against the right edge", () => {
87
+ expect(
88
+ getDropdownHorizontalAlignment({
89
+ anchorLeft: 1300,
90
+ anchorRight: 1436,
91
+ dropdownWidth: DROPDOWN_WIDTH,
92
+ viewportWidth: 1440,
93
+ }),
94
+ ).toBe(DropdownHorizontalAlignment.Right);
95
+ });
96
+ });
97
+
98
+ describe("viewports smaller than the popup", () => {
99
+ test("prefers the edge that clips least when neither side fits", () => {
100
+ /*
101
+ * A 200px viewport cannot contain a 288px popup. Left alignment clips by
102
+ * 288 + 10 - 192 = 106; right alignment clips by 8 - (150 - 288) = 146.
103
+ * Left wins.
104
+ */
105
+ expect(
106
+ getDropdownHorizontalAlignment({
107
+ anchorLeft: 10,
108
+ anchorRight: 150,
109
+ dropdownWidth: DROPDOWN_WIDTH,
110
+ viewportWidth: 200,
111
+ }),
112
+ ).toBe(DropdownHorizontalAlignment.Left);
113
+ });
114
+
115
+ test("picks right alignment when that is the smaller overflow", () => {
116
+ /*
117
+ * Left alignment clips by 180 + 288 - 292 = 176; right alignment clips by
118
+ * 8 - (290 - 288) = 6. Right wins.
119
+ */
120
+ expect(
121
+ getDropdownHorizontalAlignment({
122
+ anchorLeft: 180,
123
+ anchorRight: 290,
124
+ dropdownWidth: DROPDOWN_WIDTH,
125
+ viewportWidth: 300,
126
+ }),
127
+ ).toBe(DropdownHorizontalAlignment.Right);
128
+ });
129
+
130
+ test("prefers left alignment when both edges clip by the same amount", () => {
131
+ /*
132
+ * Anchor built so both edges clip by exactly `overflow` pixels:
133
+ * left overflow = 14 + 288 - (300 - 8) = 10
134
+ * right overflow = 8 - (286 - 288) = 10
135
+ */
136
+ const viewportWidth: number = 300;
137
+ const margin: number = 8;
138
+ const width: number = 288;
139
+ const overflow: number = 10;
140
+ const anchorLeft: number = viewportWidth - margin - width + overflow;
141
+ const anchorRight: number = width + margin - overflow;
142
+
143
+ expect(
144
+ getDropdownHorizontalAlignment({
145
+ anchorLeft: anchorLeft,
146
+ anchorRight: anchorRight,
147
+ dropdownWidth: width,
148
+ viewportWidth: viewportWidth,
149
+ viewportMargin: margin,
150
+ }),
151
+ ).toBe(DropdownHorizontalAlignment.Left);
152
+ });
153
+ });
154
+
155
+ describe("viewport margin", () => {
156
+ test("honours a custom margin when deciding to flip", () => {
157
+ const viewportWidth: number = 1000;
158
+ const anchorLeft: number = 700;
159
+
160
+ // With no gutter the popup fits: 700 + 288 = 988 <= 1000.
161
+ expect(
162
+ getDropdownHorizontalAlignment({
163
+ anchorLeft: anchorLeft,
164
+ anchorRight: 820,
165
+ dropdownWidth: DROPDOWN_WIDTH,
166
+ viewportWidth: viewportWidth,
167
+ viewportMargin: 0,
168
+ }),
169
+ ).toBe(DropdownHorizontalAlignment.Left);
170
+
171
+ // With a 40px gutter it no longer fits: 988 > 960.
172
+ expect(
173
+ getDropdownHorizontalAlignment({
174
+ anchorLeft: anchorLeft,
175
+ anchorRight: 820,
176
+ dropdownWidth: DROPDOWN_WIDTH,
177
+ viewportWidth: viewportWidth,
178
+ viewportMargin: 40,
179
+ }),
180
+ ).toBe(DropdownHorizontalAlignment.Right);
181
+ });
182
+
183
+ test("treats an explicit zero margin as zero, not as the default", () => {
184
+ const viewportWidth: number = 1000;
185
+ const anchorLeft: number = viewportWidth - DROPDOWN_WIDTH;
186
+
187
+ expect(
188
+ getDropdownHorizontalAlignment({
189
+ anchorLeft: anchorLeft,
190
+ anchorRight: viewportWidth,
191
+ dropdownWidth: DROPDOWN_WIDTH,
192
+ viewportWidth: viewportWidth,
193
+ viewportMargin: 0,
194
+ }),
195
+ ).toBe(DropdownHorizontalAlignment.Left);
196
+
197
+ expect(
198
+ getDropdownHorizontalAlignment({
199
+ anchorLeft: anchorLeft,
200
+ anchorRight: viewportWidth,
201
+ dropdownWidth: DROPDOWN_WIDTH,
202
+ viewportWidth: viewportWidth,
203
+ }),
204
+ ).toBe(DropdownHorizontalAlignment.Right);
205
+ });
206
+ });
207
+
208
+ describe("unmeasurable layouts", () => {
209
+ test("defaults to left when the popup has no width yet", () => {
210
+ expect(
211
+ getDropdownHorizontalAlignment({
212
+ anchorLeft: 1400,
213
+ anchorRight: 1440,
214
+ dropdownWidth: 0,
215
+ viewportWidth: 1440,
216
+ }),
217
+ ).toBe(DropdownHorizontalAlignment.Left);
218
+ });
219
+
220
+ test("defaults to left when the viewport has no width", () => {
221
+ expect(
222
+ getDropdownHorizontalAlignment({
223
+ anchorLeft: 0,
224
+ anchorRight: 0,
225
+ dropdownWidth: DROPDOWN_WIDTH,
226
+ viewportWidth: 0,
227
+ }),
228
+ ).toBe(DropdownHorizontalAlignment.Left);
229
+ });
230
+
231
+ test("defaults to left for non-finite measurements", () => {
232
+ expect(
233
+ getDropdownHorizontalAlignment({
234
+ anchorLeft: 0,
235
+ anchorRight: 100,
236
+ dropdownWidth: Number.NaN,
237
+ viewportWidth: 1440,
238
+ }),
239
+ ).toBe(DropdownHorizontalAlignment.Left);
240
+
241
+ expect(
242
+ getDropdownHorizontalAlignment({
243
+ anchorLeft: 0,
244
+ anchorRight: 100,
245
+ dropdownWidth: DROPDOWN_WIDTH,
246
+ viewportWidth: Number.POSITIVE_INFINITY,
247
+ }),
248
+ ).toBe(DropdownHorizontalAlignment.Left);
249
+ });
250
+ });
251
+
252
+ describe("common breakpoints", () => {
253
+ /*
254
+ * The picker is the first control in the toolbar on every telemetry page,
255
+ * so it must stay left aligned no matter how narrow the screen gets.
256
+ */
257
+ const leftmostAnchorCases: Array<{ name: string; viewportWidth: number }> =
258
+ [
259
+ { name: "mobile", viewportWidth: 375 },
260
+ { name: "tablet", viewportWidth: 768 },
261
+ { name: "laptop", viewportWidth: 1280 },
262
+ { name: "desktop", viewportWidth: 1920 },
263
+ ];
264
+
265
+ test.each(leftmostAnchorCases)(
266
+ "left aligns a leftmost toolbar trigger on $name",
267
+ (testCase: { name: string; viewportWidth: number }) => {
268
+ expect(
269
+ getDropdownHorizontalAlignment({
270
+ anchorLeft: 16,
271
+ anchorRight: 132,
272
+ dropdownWidth: DROPDOWN_WIDTH,
273
+ viewportWidth: testCase.viewportWidth,
274
+ }),
275
+ ).toBe(DropdownHorizontalAlignment.Left);
276
+ },
277
+ );
278
+ });
279
+ });
280
+
281
+ describe("getDropdownAlignmentClassName", () => {
282
+ test("maps left alignment to the Tailwind left-0 class", () => {
283
+ expect(
284
+ getDropdownAlignmentClassName(DropdownHorizontalAlignment.Left),
285
+ ).toBe("left-0");
286
+ });
287
+
288
+ test("maps right alignment to the Tailwind right-0 class", () => {
289
+ expect(
290
+ getDropdownAlignmentClassName(DropdownHorizontalAlignment.Right),
291
+ ).toBe("right-0");
292
+ });
293
+
294
+ test("never returns both positioning classes at once", () => {
295
+ const classNames: Array<string> = [
296
+ getDropdownAlignmentClassName(DropdownHorizontalAlignment.Left),
297
+ getDropdownAlignmentClassName(DropdownHorizontalAlignment.Right),
298
+ ];
299
+
300
+ for (const className of classNames) {
301
+ expect(
302
+ className.includes("left-0") && className.includes("right-0"),
303
+ ).toBe(false);
304
+ }
305
+ });
306
+ });