@oneuptime/common 14.0.8 → 14.0.9

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 (639) hide show
  1. package/Models/DatabaseModels/Index.ts +2 -0
  2. package/Models/DatabaseModels/KubernetesAiAgent.ts +364 -0
  3. package/Models/DatabaseModels/KubernetesCluster.ts +3 -3
  4. package/Models/DatabaseModels/Project.ts +55 -5
  5. package/Models/DatabaseModels/ProxmoxResource.ts +49 -1
  6. package/Models/DatabaseModels/RunnerJob.ts +75 -0
  7. package/Models/DatabaseModels/TeamComplianceSetting.ts +143 -2
  8. package/Models/DatabaseModels/WorkspaceProjectAuthToken.ts +17 -0
  9. package/Server/API/AIChatAPI.ts +6 -3
  10. package/Server/API/AIInvestigationAPI.ts +17 -10
  11. package/Server/API/AutoRemediationAPI.ts +207 -25
  12. package/Server/API/KubernetesClusterAiAccessAPI.ts +623 -21
  13. package/Server/API/MicrosoftTeamsAPI.ts +93 -14
  14. package/Server/API/ProxmoxResourceAPI.ts +244 -14
  15. package/Server/Infrastructure/GlobalCache.ts +32 -0
  16. package/Server/Infrastructure/Postgres/SchemaMigrations/1796000000000-AddTeamComplianceRuleScope.ts +99 -0
  17. package/Server/Infrastructure/Postgres/SchemaMigrations/1796100000000-AddKubernetesAiAgentAndAiDefaults.ts +137 -0
  18. package/Server/Infrastructure/Postgres/SchemaMigrations/1796200000000-AddProxmoxResourceNativePushColumns.ts +42 -0
  19. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +6 -0
  20. package/Server/Middleware/TelemetryIngest.ts +41 -18
  21. package/Server/Services/AIService.ts +70 -1
  22. package/Server/Services/AlertService.ts +352 -11
  23. package/Server/Services/AlertSeverityService.ts +65 -15
  24. package/Server/Services/AutoRemediationRuleService.ts +1 -1
  25. package/Server/Services/IncidentService.ts +280 -104
  26. package/Server/Services/IncidentSeverityService.ts +66 -15
  27. package/Server/Services/KubernetesAiAgentJobService.ts +185 -0
  28. package/Server/Services/KubernetesAiAgentService.ts +1634 -0
  29. package/Server/Services/KubernetesClusterAiAccessService.ts +1118 -451
  30. package/Server/Services/KubernetesClusterService.ts +129 -21
  31. package/Server/Services/ProjectService.ts +32 -0
  32. package/Server/Services/ProxmoxResourceService.ts +322 -5
  33. package/Server/Services/RunnerJobService.ts +241 -104
  34. package/Server/Services/RunnerService.ts +1 -1
  35. package/Server/Services/ScheduledMaintenanceService.ts +737 -1
  36. package/Server/Services/ScheduledMaintenanceStateTimelineService.ts +414 -56
  37. package/Server/Services/TeamComplianceSettingService.ts +1125 -9
  38. package/Server/Utils/AI/ClusterAccess/ClusterAccessContext.ts +67 -9
  39. package/Server/Utils/AI/ClusterAccess/KubectlInvestigationToolkit.ts +39 -10
  40. package/Server/Utils/AI/ClusterAccess/KubectlJobRunner.ts +14 -3
  41. package/Server/Utils/AI/Remediation/RemediationCommandTools.ts +37 -15
  42. package/Server/Utils/AI/Remediation/RemediationExecutionRunner.ts +67 -24
  43. package/Server/Utils/AI/SRE/AIInvestigationEngine.ts +48 -5
  44. package/Server/Utils/AI/SRE/IncidentPostmortemRunner.ts +40 -8
  45. package/Server/Utils/AI/SRE/InvestigationEligibility.ts +13 -2
  46. package/Server/Utils/AI/SRE/InvestigationGrader.ts +1 -1
  47. package/Server/Utils/AutoRemediation/CommandPlanExecutor.ts +6 -1
  48. package/Server/Utils/Telemetry/InventoryEntityRegistry.ts +133 -7
  49. package/Server/Utils/Telemetry/ProxmoxCephSnapshotScan.ts +58 -4
  50. package/Server/Utils/Telemetry/ProxmoxNativeNodeLiveness.ts +447 -0
  51. package/Server/Utils/Telemetry/ProxmoxNativePush.ts +903 -0
  52. package/Server/Utils/Telemetry/TelemetryEntity.ts +87 -27
  53. package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +684 -47
  54. package/Tests/App/AdminDashboard/RowActionPlacementOverrides.test.tsx +278 -0
  55. package/Tests/App/Dashboard/AIChatChartWidgetZoom.test.tsx +738 -0
  56. package/Tests/App/Dashboard/AIFeaturesSettingsPointers.test.tsx +147 -0
  57. package/Tests/App/Dashboard/AIInsightsKubernetesClusterPage.test.tsx +1523 -0
  58. package/Tests/App/Dashboard/AdminUserNotificationMethodsPage.test.tsx +463 -3
  59. package/Tests/App/Dashboard/AffectedResourcesPickerDropdownPlacement.test.tsx +958 -0
  60. package/Tests/App/Dashboard/AffectedResourcesPickerFocusLeave.test.tsx +595 -0
  61. package/Tests/App/Dashboard/AskAiAvailability.test.ts +20 -3
  62. package/Tests/App/Dashboard/AskAiDisabled.test.tsx +8 -3
  63. package/Tests/App/Dashboard/CephRateChartReload.test.tsx +1456 -0
  64. package/Tests/App/Dashboard/CephTimeRangeZoom.test.tsx +1229 -0
  65. package/Tests/App/Dashboard/ChartCardTimeRangeZoom.test.tsx +151 -0
  66. package/Tests/App/Dashboard/ChartCardZoomAffordances.test.tsx +257 -0
  67. package/Tests/App/Dashboard/ChartRefetchFrame.test.tsx +141 -0
  68. package/Tests/App/Dashboard/ChartZoomHarness.tsx +249 -0
  69. package/Tests/App/Dashboard/ChartZoomStandIn.tsx +131 -0
  70. package/Tests/App/Dashboard/CloudServerlessSlowLoads.test.tsx +628 -0
  71. package/Tests/App/Dashboard/CloudServerlessTimeRangeZoom.test.tsx +607 -0
  72. package/Tests/App/Dashboard/ClusterAccessNotice.test.tsx +91 -19
  73. package/Tests/App/Dashboard/CompanionMetricsTabZoom.test.tsx +632 -0
  74. package/Tests/App/Dashboard/ContainerChartZoomStandIn.tsx +195 -0
  75. package/Tests/App/Dashboard/ContainerHostOverviewSlowLoads.test.tsx +542 -0
  76. package/Tests/App/Dashboard/ContainerHostOverviewTimeRangeZoom.test.tsx +848 -0
  77. package/Tests/App/Dashboard/ContainerMetricsTimeRangeZoom.test.tsx +561 -0
  78. package/Tests/App/Dashboard/DashboardBoardWideZoomShell.test.tsx +1147 -0
  79. package/Tests/App/Dashboard/DashboardEditPermissions.test.tsx +3 -2
  80. package/Tests/App/Dashboard/DashboardHistogramWidgetDragCursor.test.tsx +399 -0
  81. package/Tests/App/Dashboard/DashboardHistogramWidgetZoom.test.tsx +985 -0
  82. package/Tests/App/Dashboard/DashboardHistogramWidgetZoomHint.test.tsx +803 -0
  83. package/Tests/App/Dashboard/DashboardLazyPages.test.tsx +1 -0
  84. package/Tests/App/Dashboard/DashboardSettingsPreviewZoom.test.tsx +679 -0
  85. package/Tests/App/Dashboard/DashboardSloAndDataSourceChartZoom.test.tsx +881 -0
  86. package/Tests/App/Dashboard/DashboardTraceStatusWidgets.test.tsx +878 -0
  87. package/Tests/App/Dashboard/DatabaseMetricChartModalTimeRangeZoom.test.tsx +442 -0
  88. package/Tests/App/Dashboard/DatabaseOverviewTimeRangeZoom.test.tsx +831 -0
  89. package/Tests/App/Dashboard/DatabaseServerComponents.test.tsx +43 -0
  90. package/Tests/App/Dashboard/DockerSwarmInsightsTimeRangeZoom.test.tsx +524 -0
  91. package/Tests/App/Dashboard/DockerSwarmRateChartRefresh.test.tsx +259 -0
  92. package/Tests/App/Dashboard/EdgeDetailPanelHistory.test.tsx +763 -0
  93. package/Tests/App/Dashboard/EdgeDetailPanelTimeRangeZoom.test.tsx +547 -0
  94. package/Tests/App/Dashboard/EdgeHistoryBucketsCases.tsx +565 -0
  95. package/Tests/App/Dashboard/EdgeHistoryBucketsKathmandu.test.tsx +73 -0
  96. package/Tests/App/Dashboard/EdgeHistoryBucketsKolkata.test.tsx +74 -0
  97. package/Tests/App/Dashboard/EdgeHistoryBucketsNewYork.test.tsx +72 -0
  98. package/Tests/App/Dashboard/EdgeHistoryBucketsUtc.test.tsx +72 -0
  99. package/Tests/App/Dashboard/EmbeddedMetricCardRefresh.test.tsx +310 -0
  100. package/Tests/App/Dashboard/EmbeddedMetricCardTimeRangeZoom.test.tsx +536 -0
  101. package/Tests/App/Dashboard/EscalationRuleReadiness.test.tsx +3 -1
  102. package/Tests/App/Dashboard/EscalationRuleRowActions.test.tsx +660 -0
  103. package/Tests/App/Dashboard/EventNotes.test.tsx +2 -2
  104. package/Tests/App/Dashboard/EventOverviewPages.test.tsx +587 -13
  105. package/Tests/App/Dashboard/EventOverviewSnapshotExplorerZoom.test.tsx +1139 -0
  106. package/Tests/App/Dashboard/EventOverviewSnapshotZoomAcrossTabs.test.tsx +1057 -0
  107. package/Tests/App/Dashboard/EventOverviewTelemetrySnapshotZoom.test.tsx +757 -0
  108. package/Tests/App/Dashboard/ExceptionOccurrenceTrendZoom.test.tsx +675 -0
  109. package/Tests/App/Dashboard/ExceptionOccurrencesZoomSurvivesViewSwitch.test.tsx +405 -0
  110. package/Tests/App/Dashboard/ExceptionSignalPages.test.tsx +67 -3
  111. package/Tests/App/Dashboard/ExceptionTablesSpanStatus.test.tsx +515 -0
  112. package/Tests/App/Dashboard/ExplorerOwnZoomRegression.test.tsx +739 -0
  113. package/Tests/App/Dashboard/FlowBandwidthAxisCases.tsx +461 -0
  114. package/Tests/App/Dashboard/FlowBandwidthAxisKathmandu.test.tsx +25 -0
  115. package/Tests/App/Dashboard/FlowBandwidthAxisKolkata.test.tsx +25 -0
  116. package/Tests/App/Dashboard/FlowBandwidthAxisNewYork.test.tsx +25 -0
  117. package/Tests/App/Dashboard/FlowBandwidthAxisUtc.test.tsx +24 -0
  118. package/Tests/App/Dashboard/FlowTopTalkersZoomRefetch.test.tsx +722 -0
  119. package/Tests/App/Dashboard/FlowViewportGuard.test.tsx +2526 -0
  120. package/Tests/App/Dashboard/HostPagesSlowLoads.test.tsx +872 -0
  121. package/Tests/App/Dashboard/HostPagesTimeRangeZoom.test.tsx +1208 -0
  122. package/Tests/App/Dashboard/IncidentRootCauseTimeRangeZoom.test.tsx +358 -0
  123. package/Tests/App/Dashboard/InfrastructureGraph.test.tsx +100 -9
  124. package/Tests/App/Dashboard/InfrastructureMetricsTabTimeRangeZoom.test.tsx +485 -0
  125. package/Tests/App/Dashboard/InvestigationDrawerCompanionZoom.test.tsx +655 -0
  126. package/Tests/App/Dashboard/InvestigationDrawerZoom.test.tsx +830 -0
  127. package/Tests/App/Dashboard/InvestigationNotStarted.test.tsx +338 -2
  128. package/Tests/App/Dashboard/InvoicesPayInvoice.test.tsx +454 -16
  129. package/Tests/App/Dashboard/KubernetesClusterAiAccessSettings.test.ts +799 -819
  130. package/Tests/App/Dashboard/KubernetesClusterAiAgentOverviewCard.test.tsx +367 -0
  131. package/Tests/App/Dashboard/KubernetesClusterAiAgentStatus.test.ts +1202 -0
  132. package/Tests/App/Dashboard/KubernetesClusterAiDocumentationMarkdown.test.ts +106 -0
  133. package/Tests/App/Dashboard/KubernetesClusterAiNavigation.test.tsx +354 -0
  134. package/Tests/App/Dashboard/KubernetesClusterAiPage.test.tsx +1884 -2054
  135. package/Tests/App/Dashboard/KubernetesClusterAiPageServerParity.test.ts +478 -179
  136. package/Tests/App/Dashboard/KubernetesClusterMetricTooltips.test.ts +50 -0
  137. package/Tests/App/Dashboard/KubernetesClusterOverviewTooltips.test.tsx +2 -1
  138. package/Tests/App/Dashboard/KubernetesClusterOverviewZoom.test.tsx +1217 -0
  139. package/Tests/App/Dashboard/KubernetesClusterPagesZoom.test.tsx +731 -0
  140. package/Tests/App/Dashboard/KubernetesCostsLoadRecovery.test.tsx +869 -0
  141. package/Tests/App/Dashboard/KubernetesCostsZoom.test.tsx +1214 -0
  142. package/Tests/App/Dashboard/KubernetesCostsZoomExactHours.test.tsx +692 -0
  143. package/Tests/App/Dashboard/KubernetesNetworkThroughputReload.test.tsx +902 -0
  144. package/Tests/App/Dashboard/KubernetesNetworkZoomBucketEdges.test.tsx +571 -0
  145. package/Tests/App/Dashboard/KubernetesOverviewZoomBucketEdges.test.tsx +697 -0
  146. package/Tests/App/Dashboard/KubernetesResourceMetricsTabZoom.test.tsx +633 -0
  147. package/Tests/App/Dashboard/LogsExplorerTimeRangeZoom.test.tsx +470 -0
  148. package/Tests/App/Dashboard/LogsInsightsTimeRangeZoom.test.tsx +948 -0
  149. package/Tests/App/Dashboard/LogsViewerPinnedWindowZoom.test.tsx +486 -0
  150. package/Tests/App/Dashboard/MetricChartsBucketInspectorDoubleClick.test.tsx +1001 -0
  151. package/Tests/App/Dashboard/MetricExplorerInvestigationAutoRefresh.test.tsx +514 -0
  152. package/Tests/App/Dashboard/MetricExplorerSavedViewZoomRoundTrip.test.tsx +581 -0
  153. package/Tests/App/Dashboard/MetricExplorerTimeRangeZoom.test.tsx +1162 -0
  154. package/Tests/App/Dashboard/MetricMonitorPreviewZoomLabel.test.ts +72 -0
  155. package/Tests/App/Dashboard/MetricRowSparklineTimeRangeZoom.test.tsx +204 -0
  156. package/Tests/App/Dashboard/MetricViewFirstResultLands.test.tsx +408 -0
  157. package/Tests/App/Dashboard/MetricViewOwnZoomResetPlacement.test.tsx +952 -0
  158. package/Tests/App/Dashboard/MetricViewTimeRange.test.ts +139 -0
  159. package/Tests/App/Dashboard/MetricViewTimeRangeZoom.test.tsx +556 -0
  160. package/Tests/App/Dashboard/MonitorMetricsChartWrappersZoom.test.tsx +552 -0
  161. package/Tests/App/Dashboard/MonitorMetricsTabsTimeRangeZoom.test.tsx +740 -0
  162. package/Tests/App/Dashboard/MonitorOverviewChartsTimeRangeZoom.test.tsx +735 -0
  163. package/Tests/App/Dashboard/MonitorStepPreviewsTimeRangeZoom.test.tsx +835 -0
  164. package/Tests/App/Dashboard/NetworkDeviceTimeRangeZoom.test.tsx +1115 -0
  165. package/Tests/App/Dashboard/OnCallPreventionGuards.test.tsx +186 -17
  166. package/Tests/App/Dashboard/OnCallRulesDeleteGuard.test.tsx +155 -46
  167. package/Tests/App/Dashboard/PasskeySettings.test.tsx +87 -2
  168. package/Tests/App/Dashboard/ProjectAISettingsPages.test.tsx +884 -0
  169. package/Tests/App/Dashboard/ProxmoxNodeRemove.test.tsx +621 -0
  170. package/Tests/App/Dashboard/ProxmoxOverviewSlowLoads.test.tsx +584 -0
  171. package/Tests/App/Dashboard/ProxmoxPagesTimeRangeZoom.test.tsx +823 -0
  172. package/Tests/App/Dashboard/ProxmoxRateChartReload.test.tsx +1253 -0
  173. package/Tests/App/Dashboard/PublicDashboardBoardWideZoom.test.tsx +605 -0
  174. package/Tests/App/Dashboard/RealRowsLineChart.tsx +250 -0
  175. package/Tests/App/Dashboard/RechartsHostsPlotCursor.test.tsx +513 -0
  176. package/Tests/App/Dashboard/RowActionPlacementOverrides.test.tsx +865 -0
  177. package/Tests/App/Dashboard/RumOverviewTimeRangeZoom.test.tsx +877 -0
  178. package/Tests/App/Dashboard/RunnerKubernetesAgentForm.test.tsx +20 -4
  179. package/Tests/App/Dashboard/RunnerSupersededByAiAgentNote.test.tsx +296 -0
  180. package/Tests/App/Dashboard/SecurityEventConnectionsTable.test.tsx +146 -36
  181. package/Tests/App/Dashboard/ServiceDatabaseSlowLoads.test.tsx +1166 -0
  182. package/Tests/App/Dashboard/ServiceMapGraph.test.tsx +1243 -104
  183. package/Tests/App/Dashboard/ServiceOverviewTimeRangeZoom.test.tsx +792 -0
  184. package/Tests/App/Dashboard/SloBudgetBurnDownZoom.test.tsx +771 -0
  185. package/Tests/App/Dashboard/SloBudgetBurnDownZoomAffordances.test.tsx +380 -0
  186. package/Tests/App/Dashboard/SloEventMetricsBarChartZoom.test.tsx +369 -0
  187. package/Tests/App/Dashboard/SloHistoryChartsTimeRangeZoom.test.tsx +694 -0
  188. package/Tests/App/Dashboard/SloHistoryChartsZoomAffordances.test.tsx +497 -0
  189. package/Tests/App/Dashboard/SloMetricsTimeRangeZoom.test.tsx +541 -0
  190. package/Tests/App/Dashboard/SlowLoadHarness.ts +123 -0
  191. package/Tests/App/Dashboard/SnapshotExplorerPrimaryZoom.test.tsx +1194 -0
  192. package/Tests/App/Dashboard/SnapshotViewerPinnedWindow.test.tsx +620 -0
  193. package/Tests/App/Dashboard/TelemetrySnapshotPanelTimeRangeZoom.test.tsx +765 -0
  194. package/Tests/App/Dashboard/TelemetrySnapshotZoom.test.tsx +516 -0
  195. package/Tests/App/Dashboard/TelemetrySnapshotZoomAcrossTabs.test.tsx +827 -0
  196. package/Tests/App/Dashboard/TimeRangeZoomPageHarness.tsx +463 -0
  197. package/Tests/App/Dashboard/TraceChartEditorStatusSplit.test.tsx +543 -0
  198. package/Tests/App/Dashboard/TraceStatusPresentation.test.tsx +967 -0
  199. package/Tests/App/Dashboard/TraceTimelineMinimapZoom.test.tsx +315 -0
  200. package/Tests/App/Dashboard/TracesAnalyticsStatusSplit.test.tsx +778 -0
  201. package/Tests/App/Dashboard/TracesAnalyticsViewZoom.test.tsx +349 -0
  202. package/Tests/App/Dashboard/TracesExplorerTimeRangeZoom.test.tsx +551 -0
  203. package/Tests/App/Dashboard/TracesStatusChartAndFacet.test.tsx +687 -0
  204. package/Tests/App/Dashboard/TracesViewerStaleResponses.test.tsx +458 -0
  205. package/Tests/App/Dashboard/UptimeStripsExcludedFromZoom.test.tsx +406 -0
  206. package/Tests/App/Dashboard/VMwareOverviewSlowLoads.test.tsx +535 -0
  207. package/Tests/App/Dashboard/VMwarePagesTimeRangeZoom.test.tsx +606 -0
  208. package/Tests/App/Dashboard/ValueWidgetSparklineStaleHover.test.tsx +1292 -0
  209. package/Tests/App/Dashboard/ValueWidgetSparklineZoom.test.tsx +765 -0
  210. package/Tests/App/StatusPage/StatusPageResourceExplorer.test.tsx +98 -0
  211. package/Tests/Models/DatabaseModels/KubernetesAiAgentModel.test.ts +295 -0
  212. package/Tests/Models/DatabaseModels/TeamComplianceSettingModel.test.ts +347 -0
  213. package/Tests/Server/API/AIChatRouteAuthorization.test.ts +58 -0
  214. package/Tests/Server/API/AIGenerationProjectAIToggle.test.ts +1 -1
  215. package/Tests/Server/API/AIInvestigationAPI.test.ts +123 -45
  216. package/Tests/Server/API/AutoRemediationApproveClusterRound.test.ts +1329 -0
  217. package/Tests/Server/API/AutoRemediationApproveKubectlRecheck.test.ts +25 -2
  218. package/Tests/Server/API/KubernetesClusterAiAccessAPI.test.ts +2 -0
  219. package/Tests/Server/API/KubernetesClusterAiAccessAPIAgent.test.ts +903 -0
  220. package/Tests/Server/API/MicrosoftTeamsChatsRefreshRoute.test.ts +667 -0
  221. package/Tests/Server/API/MicrosoftTeamsManifest.test.ts +294 -6
  222. package/Tests/Server/API/ProxmoxResourceAPIRemoveNode.test.ts +1932 -0
  223. package/Tests/Server/Infrastructure/GlobalCache.test.ts +195 -0
  224. package/Tests/Server/Infrastructure/Postgres/AddKubernetesAiAgentAndAiDefaultsMigration.test.ts +623 -0
  225. package/Tests/Server/Infrastructure/Postgres/AddKubernetesClusterAiAccessMigration.test.ts +87 -10
  226. package/Tests/Server/Infrastructure/Postgres/AddTeamComplianceRuleScopeMigration.test.ts +622 -0
  227. package/Tests/Server/Middleware/TelemetryIngestBrowserKey.test.ts +61 -4
  228. package/Tests/Server/Middleware/TelemetryIngestKubernetesAgentRunnerPinnedKey.test.ts +98 -2
  229. package/Tests/Server/Middleware/TelemetryIngestKubernetesAgentRunnerRateLimit.test.ts +105 -2
  230. package/Tests/Server/Services/AIServiceBalanceBlocker.test.ts +282 -0
  231. package/Tests/Server/Services/AlertMonitorClearWritesNull.test.ts +409 -0
  232. package/Tests/Server/Services/AlertMonitorEditFeed.test.ts +474 -0
  233. package/Tests/Server/Services/AlertMonitorEditGuard.test.ts +610 -0
  234. package/Tests/Server/Services/AutoRemediationClusterRuleEngine.test.ts +2 -0
  235. package/Tests/Server/Services/AutoRemediationRuleCommandRunners.test.ts +2 -1
  236. package/Tests/Server/Services/IncidentAffectedMonitorsEdit.test.ts +1360 -0
  237. package/Tests/Server/Services/IncidentAlertSloLinkSurvivesEdit.test.ts +3 -1
  238. package/Tests/Server/Services/IncidentSeverityDeletePausesComplianceRules.test.ts +488 -0
  239. package/Tests/Server/Services/KubernetesAiAgentJobService.test.ts +329 -0
  240. package/Tests/Server/Services/KubernetesAiAgentServiceHelpers.test.ts +618 -0
  241. package/Tests/Server/Services/KubernetesAiAgentServiceLifecycle.test.ts +1444 -0
  242. package/Tests/Server/Services/KubernetesAiAgentServiceRegister.test.ts +1666 -0
  243. package/Tests/Server/Services/KubernetesClusterAiAccessDeletedRunnerRemedy.test.ts +140 -111
  244. package/Tests/Server/Services/KubernetesClusterAiAccessGapNextSteps.test.ts +267 -105
  245. package/Tests/Server/Services/KubernetesClusterAiAccessLegacySuperseded.test.ts +440 -0
  246. package/Tests/Server/Services/KubernetesClusterAiAccessService.test.ts +567 -411
  247. package/Tests/Server/Services/KubernetesClusterAiAccessTarget.test.ts +1168 -0
  248. package/Tests/Server/Services/KubernetesClusterAiSettingsPermission.test.ts +410 -12
  249. package/Tests/Server/Services/KubernetesClusterServiceAiAccessFeed.test.ts +55 -0
  250. package/Tests/Server/Services/ProjectServiceNewProjectAiDefaults.test.ts +288 -0
  251. package/Tests/Server/Services/ProxmoxResourcePostgres.test.ts +5633 -0
  252. package/Tests/Server/Services/ProxmoxResourceService.test.ts +6458 -5
  253. package/Tests/Server/Services/RunnerJobEnqueueKubectl.test.ts +48 -15
  254. package/Tests/Server/Services/RunnerJobEnqueueKubectlAiAgent.test.ts +620 -0
  255. package/Tests/Server/Services/RunnerJobKubectlWriteScope.test.ts +170 -3
  256. package/Tests/Server/Services/RunnerJobTimeoutReason.test.ts +72 -0
  257. package/Tests/Server/Services/RunnerServiceAgentName.test.ts +6 -4
  258. package/Tests/Server/Services/ScheduledMaintenanceAttachmentEdit.test.ts +2113 -0
  259. package/Tests/Server/Services/ScheduledMaintenanceMonitorRelease.test.ts +972 -0
  260. package/Tests/Server/Services/TeamComplianceSettingPostgres.test.ts +2237 -0
  261. package/Tests/Server/Services/TeamComplianceSettingService.test.ts +3988 -138
  262. package/Tests/Server/Utils/AI/ClusterAccessContext.test.ts +107 -1
  263. package/Tests/Server/Utils/AI/IncidentPostmortemRunner.test.ts +351 -0
  264. package/Tests/Server/Utils/AI/InvestigationClusterAccessWiring.test.ts +2 -0
  265. package/Tests/Server/Utils/AI/InvestigationEligibility.test.ts +310 -0
  266. package/Tests/Server/Utils/AI/KubectlInvestigationToolkit.test.ts +70 -0
  267. package/Tests/Server/Utils/AI/KubectlJobRunnerFinishedJob.test.ts +58 -0
  268. package/Tests/Server/Utils/AI/RemediationCommandToolsKubectl.test.ts +2 -0
  269. package/Tests/Server/Utils/AI/RemediationCommandToolsKubectlOutcome.test.ts +2 -0
  270. package/Tests/Server/Utils/AI/RemediationCommandToolsLiveCluster.test.ts +2 -0
  271. package/Tests/Server/Utils/AI/RemediationCommandToolsRunnerScope.test.ts +125 -1
  272. package/Tests/Server/Utils/AI/RemediationExecutionRunner.test.ts +252 -3
  273. package/Tests/Server/Utils/AI/RemediationExecutionRunnerClusterMode.test.ts +2 -0
  274. package/Tests/Server/Utils/AI/RemediationExecutionRunnerProjectGates.test.ts +240 -0
  275. package/Tests/Server/Utils/AI/RemediationKubectlCopyParity.test.ts +2 -0
  276. package/Tests/Server/Utils/AutoRemediation/CommandPlanExecutor.test.ts +2 -0
  277. package/Tests/Server/Utils/AutoRemediation/CommandPlanExecutorUnknownRun.test.ts +2 -0
  278. package/Tests/Server/Utils/AutoRemediation/CommandPlanVerification.test.ts +2 -0
  279. package/Tests/Server/Utils/Telemetry/HostAssetAttributesFromOtlp.test.ts +68 -0
  280. package/Tests/Server/Utils/Telemetry/InventoryMirrorPostgres.test.ts +370 -0
  281. package/Tests/Server/Utils/Telemetry/NetworkDeviceInventoryAttributes.test.ts +522 -0
  282. package/Tests/Server/Utils/Telemetry/ProxmoxCephSnapshotScan.test.ts +155 -0
  283. package/Tests/Server/Utils/Telemetry/ProxmoxNativeNodeLiveness.test.ts +8631 -0
  284. package/Tests/Server/Utils/Telemetry/ProxmoxNativePush.test.ts +2852 -0
  285. package/Tests/Server/Utils/Telemetry/TelemetryEntity.test.ts +367 -1
  286. package/Tests/Server/Utils/Telemetry/TelemetryIngestionKeyGuard.test.ts +45 -3
  287. package/Tests/Server/Utils/Workspace/MicrosoftTeamsChats.test.ts +25 -0
  288. package/Tests/Server/Utils/Workspace/MicrosoftTeamsGroupChatNames.test.ts +3119 -0
  289. package/Tests/Server/Utils/Workspace/MicrosoftTeamsGroupChatNamesPostgres.test.ts +1721 -0
  290. package/Tests/Types/Kubernetes/KubernetesAgentChartAiAccess.test.ts +581 -113
  291. package/Tests/Types/Kubernetes/KubernetesAiAgentAccessContract.test.ts +510 -0
  292. package/Tests/Types/Team/ComplianceRule.test.ts +1029 -0
  293. package/Tests/Types/Team/TeamComplianceStatus.test.ts +176 -0
  294. package/Tests/UI/Components/ActionButton/RowActions.test.tsx +641 -0
  295. package/Tests/UI/Components/ActionButton/RowActionsAccessibility.test.tsx +414 -0
  296. package/Tests/UI/Components/ActionButton/SplitActionButtons.test.ts +565 -0
  297. package/Tests/UI/Components/Charts/BarChartDragToSelect.test.tsx +386 -0
  298. package/Tests/UI/Components/Charts/ChartDoubleClickResetWithoutDblclick.test.tsx +1025 -0
  299. package/Tests/UI/Components/Charts/ChartGroupPageZoomHint.test.tsx +196 -0
  300. package/Tests/UI/Components/Charts/ChartPlaceholdersTakeDoubleClick.test.tsx +1861 -0
  301. package/Tests/UI/Components/Charts/ChartPlotClicksWhileResetArmed.test.tsx +382 -0
  302. package/Tests/UI/Components/Charts/ChartRangeSelection.test.tsx +704 -0
  303. package/Tests/UI/Components/Charts/ChartRangeSelectionDoubleClickReset.test.tsx +1481 -0
  304. package/Tests/UI/Components/Charts/ChartRangeSelectionRealRecharts.test.tsx +918 -0
  305. package/Tests/UI/Components/Charts/ChartRowBucketStart.test.ts +350 -0
  306. package/Tests/UI/Components/Charts/DoubleClickReset.test.tsx +996 -0
  307. package/Tests/UI/Components/Charts/HistogramDoubleClickResetRealRecharts.test.tsx +965 -0
  308. package/Tests/UI/Components/Charts/HistogramDriftThenRefetch.test.tsx +277 -0
  309. package/Tests/UI/Components/Charts/HistogramRangeSelectionDataChange.test.tsx +1757 -0
  310. package/Tests/UI/Components/Charts/HistogramRangeSelectionRealRecharts.test.tsx +560 -0
  311. package/Tests/UI/Components/Charts/TimeRangeZoom/ChartPageTimeRangeZoom.test.tsx +513 -0
  312. package/Tests/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomContext.test.tsx +845 -0
  313. package/Tests/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomHint.test.tsx +119 -0
  314. package/Tests/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomHintTailwind.test.tsx +274 -0
  315. package/Tests/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomUtil.test.ts +433 -0
  316. package/Tests/UI/Components/Charts/TimeRangeZoom/UseTimeRangeZoom.test.tsx +863 -0
  317. package/Tests/UI/Components/Charts/TimeRangeZoom/ZoomDocsPromises.test.tsx +1147 -0
  318. package/Tests/UI/Components/DisabledButtonTooltip.test.tsx +14 -3
  319. package/Tests/UI/Components/EntityDropdownEscape.test.tsx +454 -0
  320. package/Tests/UI/Components/EntityDropdownFocusLeave.test.tsx +420 -0
  321. package/Tests/UI/Components/Forms/FormFieldDropdownDisabled.test.tsx +233 -0
  322. package/Tests/UI/Components/Input.test.tsx +80 -1
  323. package/Tests/UI/Components/LogsAnalyticsViewZoom.test.tsx +947 -0
  324. package/Tests/UI/Components/LogsHistogramDragTooltip.test.tsx +17 -5
  325. package/Tests/UI/Components/LogsViewerAnalyticsZoom.test.tsx +501 -0
  326. package/Tests/UI/Components/LogsViewerHistogramZoom.test.tsx +1 -1
  327. package/Tests/UI/Components/ModelTable/BaseModelTableBulkDelete.test.tsx +6 -4
  328. package/Tests/UI/Components/ModelTable/BaseModelTablePermissionGating.test.tsx +60 -8
  329. package/Tests/UI/Components/ModelTable/BaseModelTableRowActions.test.tsx +476 -0
  330. package/Tests/UI/Components/ModelTable/ModelTableWrapContent.test.tsx +7 -5
  331. package/Tests/UI/Components/MoreMenuPortal.test.tsx +623 -0
  332. package/Tests/UI/Components/StatusPage/ResourceGridActions.test.tsx +538 -0
  333. package/Tests/UI/Components/StatusPage/ResourceGridSelection.test.tsx +14 -4
  334. package/Tests/UI/Components/TableCellWrapping.test.tsx +3 -3
  335. package/Tests/UI/Components/TableRowActions.test.tsx +472 -0
  336. package/Tests/UI/Components/TelemetryHistogramDragTooltip.test.tsx +1 -1
  337. package/Tests/UI/Components/TelemetryViewer/TelemetryViewerHistogramClickZoom.test.tsx +1 -1
  338. package/Tests/UI/Components/TelemetryViewer/TelemetryViewerZoomScope.test.tsx +470 -0
  339. package/Tests/UI/Components/TelemetryViewer/UseViewerTimeRangeZoom.test.tsx +571 -0
  340. package/Tests/UI/Components/TelemetryViewer/ViewerDoubleClickResetUnderDataChange.test.tsx +928 -0
  341. package/Tests/UI/Components/TelemetryViewer/ViewerFollowsEnclosingZoom.test.tsx +1569 -0
  342. package/Tests/UI/Components/Toggle.test.tsx +66 -0
  343. package/Tests/UI/MicrosoftTeams/MicrosoftTeamsChatsCardRefreshNames.test.tsx +1023 -0
  344. package/Tests/UI/Rum/ChunkLoader.test.ts +201 -0
  345. package/Tests/UI/Rum/FidelityNotices.test.ts +54 -0
  346. package/Tests/UI/Rum/ReplayCorrelationPanel.test.tsx +72 -0
  347. package/Tests/UI/Rum/ReplayFrameCapture.test.ts +1009 -9
  348. package/Tests/UI/Rum/ReplayRecordedAssets.test.ts +1069 -0
  349. package/Tests/UI/Rum/ReplayStage.test.tsx +913 -2
  350. package/Tests/UI/Rum/SessionReplayTable.test.tsx +443 -8
  351. package/Tests/UI/Rum/SessionReplayUsersTable.test.tsx +439 -9
  352. package/Tests/UI/Rum/UserFlowTablesRowActions.test.tsx +501 -0
  353. package/Tests/Utils/AiRemediation/KubectlPolicyChartRbacParity.test.ts +47 -19
  354. package/Tests/Utils/AiRemediation/KubectlWriteScopeCallerParity.test.ts +2 -0
  355. package/Tests/Utils/AiRemediation/KubernetesAiAgentPolicyCopyParity.test.ts +396 -0
  356. package/Tests/Utils/Dashboard/DashboardTimeRangeZoom.test.ts +104 -1
  357. package/Types/AI/AIRunType.ts +9 -4
  358. package/Types/AI/InvestigationNotStartedReason.ts +6 -0
  359. package/Types/AutoRemediation/AiRemediationCommandPlan.ts +14 -16
  360. package/Types/AutoRemediation/AiRemediationCommandPolicyVerdict.ts +28 -0
  361. package/Types/Dashboard/DashboardComponents/DashboardTraceChartComponent.ts +4 -2
  362. package/Types/Kubernetes/KubernetesClusterAiAccess.ts +263 -32
  363. package/Types/Kubernetes/KubernetesClusterAiAccessPermissions.ts +20 -16
  364. package/Types/Kubernetes/KubernetesClusterAiInsights.ts +81 -0
  365. package/Types/Monitor/ProxmoxAlertTemplates.ts +4 -4
  366. package/Types/Team/ComplianceNotificationChannel.ts +22 -0
  367. package/Types/Team/ComplianceRule.ts +434 -0
  368. package/Types/Team/ComplianceRuleType.ts +21 -0
  369. package/Types/Team/TeamComplianceStatus.ts +81 -0
  370. package/Types/Telemetry/TelemetryIngestSurface.ts +28 -0
  371. package/UI/Components/ActionButton/ActionButtonSchema.ts +21 -0
  372. package/UI/Components/ActionButton/RowActions.tsx +238 -0
  373. package/UI/Components/ActionButton/SplitActionButtons.ts +173 -0
  374. package/UI/Components/Charts/Area/AreaChart.tsx +32 -2
  375. package/UI/Components/Charts/Bar/BarChart.tsx +40 -3
  376. package/UI/Components/Charts/ChartGroup/ChartGroup.tsx +37 -11
  377. package/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.tsx +80 -243
  378. package/UI/Components/Charts/ChartLibrary/BarChart/BarChart.tsx +122 -31
  379. package/UI/Components/Charts/ChartLibrary/LineChart/LineChart.tsx +95 -244
  380. package/UI/Components/Charts/ChartLibrary/Utils/DoubleClick.ts +214 -0
  381. package/UI/Components/Charts/ChartLibrary/Utils/UseChartBucketsChange.ts +32 -0
  382. package/UI/Components/Charts/ChartLibrary/Utils/UseChartRangeSelection.ts +599 -0
  383. package/UI/Components/Charts/Line/LineChart.tsx +32 -2
  384. package/UI/Components/Charts/TimeRangeZoom/ResetTimeRangeZoomButton.tsx +70 -0
  385. package/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomContext.tsx +226 -0
  386. package/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomHint.tsx +73 -0
  387. package/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomUtil.ts +163 -0
  388. package/UI/Components/Charts/TimeRangeZoom/UseTimeRangeZoom.ts +215 -0
  389. package/UI/Components/Charts/Utils/DataPoint.ts +20 -8
  390. package/UI/Components/Charts/Utils/XAxis.ts +75 -0
  391. package/UI/Components/Charts/Utils/useHistogramRangeSelection.ts +493 -59
  392. package/UI/Components/EntityDropdown/EntityDropdown.tsx +107 -11
  393. package/UI/Components/Forms/Fields/FormField.tsx +10 -0
  394. package/UI/Components/Input/Input.tsx +90 -57
  395. package/UI/Components/List/ListRow.tsx +8 -74
  396. package/UI/Components/LogsViewer/LogsViewer.tsx +40 -11
  397. package/UI/Components/LogsViewer/components/LogTimeRangePicker.tsx +22 -6
  398. package/UI/Components/LogsViewer/components/LogsAnalyticsView.tsx +209 -6
  399. package/UI/Components/LogsViewer/components/LogsHistogram.tsx +127 -84
  400. package/UI/Components/LogsViewer/components/LogsViewerToolbar.tsx +3 -0
  401. package/UI/Components/ModelTable/BaseModelTable.tsx +12 -1
  402. package/UI/Components/MoreMenu/MoreMenu.tsx +365 -30
  403. package/UI/Components/MoreMenu/MoreMenuItem.tsx +70 -17
  404. package/UI/Components/OrderedStatesList/Item.tsx +7 -72
  405. package/UI/Components/StatusPage/ResourceGrid.tsx +126 -77
  406. package/UI/Components/Table/TableBody.tsx +8 -2
  407. package/UI/Components/Table/TableRow.tsx +20 -140
  408. package/UI/Components/TelemetryViewer/TelemetryViewer.tsx +41 -10
  409. package/UI/Components/TelemetryViewer/components/TelemetryHistogram.tsx +47 -9
  410. package/UI/Components/TelemetryViewer/components/TelemetryTimeRangePicker.tsx +25 -6
  411. package/UI/Components/TelemetryViewer/types.ts +2 -0
  412. package/UI/Components/TelemetryViewer/useViewerTimeRangeZoom.ts +448 -0
  413. package/UI/Components/Toggle/Toggle.tsx +26 -0
  414. package/UI/Components/Tooltip/Tooltip.tsx +8 -1
  415. package/UI/Styles/Theme.css +14 -2
  416. package/Utils/AiRemediation/CommandPolicy.ts +1 -1
  417. package/Utils/AiRemediation/KubectlPolicy.ts +1 -1
  418. package/Utils/Dashboard/DashboardTimeRangeZoom.ts +46 -2
  419. package/build/dist/Models/DatabaseModels/Index.js +2 -0
  420. package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
  421. package/build/dist/Models/DatabaseModels/KubernetesAiAgent.js +385 -0
  422. package/build/dist/Models/DatabaseModels/KubernetesAiAgent.js.map +1 -0
  423. package/build/dist/Models/DatabaseModels/KubernetesCluster.js +3 -3
  424. package/build/dist/Models/DatabaseModels/KubernetesCluster.js.map +1 -1
  425. package/build/dist/Models/DatabaseModels/Project.js +56 -5
  426. package/build/dist/Models/DatabaseModels/Project.js.map +1 -1
  427. package/build/dist/Models/DatabaseModels/ProxmoxResource.js +51 -1
  428. package/build/dist/Models/DatabaseModels/ProxmoxResource.js.map +1 -1
  429. package/build/dist/Models/DatabaseModels/RunnerJob.js +74 -0
  430. package/build/dist/Models/DatabaseModels/RunnerJob.js.map +1 -1
  431. package/build/dist/Models/DatabaseModels/TeamComplianceSetting.js +134 -3
  432. package/build/dist/Models/DatabaseModels/TeamComplianceSetting.js.map +1 -1
  433. package/build/dist/Models/DatabaseModels/WorkspaceProjectAuthToken.js.map +1 -1
  434. package/build/dist/Server/API/AIChatAPI.js +6 -1
  435. package/build/dist/Server/API/AIChatAPI.js.map +1 -1
  436. package/build/dist/Server/API/AIInvestigationAPI.js +17 -10
  437. package/build/dist/Server/API/AIInvestigationAPI.js.map +1 -1
  438. package/build/dist/Server/API/AutoRemediationAPI.js +144 -24
  439. package/build/dist/Server/API/AutoRemediationAPI.js.map +1 -1
  440. package/build/dist/Server/API/KubernetesClusterAiAccessAPI.js +436 -22
  441. package/build/dist/Server/API/KubernetesClusterAiAccessAPI.js.map +1 -1
  442. package/build/dist/Server/API/MicrosoftTeamsAPI.js +69 -13
  443. package/build/dist/Server/API/MicrosoftTeamsAPI.js.map +1 -1
  444. package/build/dist/Server/API/ProxmoxResourceAPI.js +187 -14
  445. package/build/dist/Server/API/ProxmoxResourceAPI.js.map +1 -1
  446. package/build/dist/Server/Infrastructure/GlobalCache.js +27 -0
  447. package/build/dist/Server/Infrastructure/GlobalCache.js.map +1 -1
  448. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796000000000-AddTeamComplianceRuleScope.js +50 -0
  449. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796000000000-AddTeamComplianceRuleScope.js.map +1 -0
  450. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796100000000-AddKubernetesAiAgentAndAiDefaults.js +84 -0
  451. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796100000000-AddKubernetesAiAgentAndAiDefaults.js.map +1 -0
  452. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796200000000-AddProxmoxResourceNativePushColumns.js +31 -0
  453. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796200000000-AddProxmoxResourceNativePushColumns.js.map +1 -0
  454. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +6 -0
  455. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  456. package/build/dist/Server/Middleware/TelemetryIngest.js +34 -19
  457. package/build/dist/Server/Middleware/TelemetryIngest.js.map +1 -1
  458. package/build/dist/Server/Services/AIService.js +59 -1
  459. package/build/dist/Server/Services/AIService.js.map +1 -1
  460. package/build/dist/Server/Services/AlertService.js +248 -13
  461. package/build/dist/Server/Services/AlertService.js.map +1 -1
  462. package/build/dist/Server/Services/AlertSeverityService.js +51 -15
  463. package/build/dist/Server/Services/AlertSeverityService.js.map +1 -1
  464. package/build/dist/Server/Services/AutoRemediationRuleService.js +1 -1
  465. package/build/dist/Server/Services/AutoRemediationRuleService.js.map +1 -1
  466. package/build/dist/Server/Services/IncidentService.js +199 -72
  467. package/build/dist/Server/Services/IncidentService.js.map +1 -1
  468. package/build/dist/Server/Services/IncidentSeverityService.js +51 -15
  469. package/build/dist/Server/Services/IncidentSeverityService.js.map +1 -1
  470. package/build/dist/Server/Services/KubernetesAiAgentJobService.js +158 -0
  471. package/build/dist/Server/Services/KubernetesAiAgentJobService.js.map +1 -0
  472. package/build/dist/Server/Services/KubernetesAiAgentService.js +1145 -0
  473. package/build/dist/Server/Services/KubernetesAiAgentService.js.map +1 -0
  474. package/build/dist/Server/Services/KubernetesClusterAiAccessService.js +842 -371
  475. package/build/dist/Server/Services/KubernetesClusterAiAccessService.js.map +1 -1
  476. package/build/dist/Server/Services/KubernetesClusterService.js +86 -17
  477. package/build/dist/Server/Services/KubernetesClusterService.js.map +1 -1
  478. package/build/dist/Server/Services/ProjectService.js +22 -0
  479. package/build/dist/Server/Services/ProjectService.js.map +1 -1
  480. package/build/dist/Server/Services/ProxmoxResourceService.js +258 -3
  481. package/build/dist/Server/Services/ProxmoxResourceService.js.map +1 -1
  482. package/build/dist/Server/Services/RunnerJobService.js +178 -92
  483. package/build/dist/Server/Services/RunnerJobService.js.map +1 -1
  484. package/build/dist/Server/Services/RunnerService.js +1 -1
  485. package/build/dist/Server/Services/RunnerService.js.map +1 -1
  486. package/build/dist/Server/Services/ScheduledMaintenanceService.js +535 -3
  487. package/build/dist/Server/Services/ScheduledMaintenanceService.js.map +1 -1
  488. package/build/dist/Server/Services/ScheduledMaintenanceStateTimelineService.js +316 -46
  489. package/build/dist/Server/Services/ScheduledMaintenanceStateTimelineService.js.map +1 -1
  490. package/build/dist/Server/Services/TeamComplianceSettingService.js +833 -7
  491. package/build/dist/Server/Services/TeamComplianceSettingService.js.map +1 -1
  492. package/build/dist/Server/Utils/AI/ClusterAccess/ClusterAccessContext.js +52 -9
  493. package/build/dist/Server/Utils/AI/ClusterAccess/ClusterAccessContext.js.map +1 -1
  494. package/build/dist/Server/Utils/AI/ClusterAccess/KubectlInvestigationToolkit.js +13 -9
  495. package/build/dist/Server/Utils/AI/ClusterAccess/KubectlInvestigationToolkit.js.map +1 -1
  496. package/build/dist/Server/Utils/AI/ClusterAccess/KubectlJobRunner.js +7 -3
  497. package/build/dist/Server/Utils/AI/ClusterAccess/KubectlJobRunner.js.map +1 -1
  498. package/build/dist/Server/Utils/AI/Remediation/RemediationCommandTools.js +29 -15
  499. package/build/dist/Server/Utils/AI/Remediation/RemediationCommandTools.js.map +1 -1
  500. package/build/dist/Server/Utils/AI/Remediation/RemediationExecutionRunner.js +56 -20
  501. package/build/dist/Server/Utils/AI/Remediation/RemediationExecutionRunner.js.map +1 -1
  502. package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js +39 -5
  503. package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js.map +1 -1
  504. package/build/dist/Server/Utils/AI/SRE/IncidentPostmortemRunner.js +30 -3
  505. package/build/dist/Server/Utils/AI/SRE/IncidentPostmortemRunner.js.map +1 -1
  506. package/build/dist/Server/Utils/AI/SRE/InvestigationEligibility.js +13 -2
  507. package/build/dist/Server/Utils/AI/SRE/InvestigationEligibility.js.map +1 -1
  508. package/build/dist/Server/Utils/AI/SRE/InvestigationGrader.js +1 -1
  509. package/build/dist/Server/Utils/AutoRemediation/CommandPlanExecutor.js +4 -2
  510. package/build/dist/Server/Utils/AutoRemediation/CommandPlanExecutor.js.map +1 -1
  511. package/build/dist/Server/Utils/Telemetry/InventoryEntityRegistry.js +111 -6
  512. package/build/dist/Server/Utils/Telemetry/InventoryEntityRegistry.js.map +1 -1
  513. package/build/dist/Server/Utils/Telemetry/ProxmoxCephSnapshotScan.js +40 -4
  514. package/build/dist/Server/Utils/Telemetry/ProxmoxCephSnapshotScan.js.map +1 -1
  515. package/build/dist/Server/Utils/Telemetry/ProxmoxNativeNodeLiveness.js +296 -0
  516. package/build/dist/Server/Utils/Telemetry/ProxmoxNativeNodeLiveness.js.map +1 -0
  517. package/build/dist/Server/Utils/Telemetry/ProxmoxNativePush.js +699 -0
  518. package/build/dist/Server/Utils/Telemetry/ProxmoxNativePush.js.map +1 -0
  519. package/build/dist/Server/Utils/Telemetry/TelemetryEntity.js +77 -19
  520. package/build/dist/Server/Utils/Telemetry/TelemetryEntity.js.map +1 -1
  521. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +505 -42
  522. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
  523. package/build/dist/Types/AI/AIRunType.js +9 -4
  524. package/build/dist/Types/AI/AIRunType.js.map +1 -1
  525. package/build/dist/Types/AutoRemediation/AiRemediationCommandPlan.js +10 -16
  526. package/build/dist/Types/AutoRemediation/AiRemediationCommandPlan.js.map +1 -1
  527. package/build/dist/Types/AutoRemediation/AiRemediationCommandPolicyVerdict.js +28 -0
  528. package/build/dist/Types/AutoRemediation/AiRemediationCommandPolicyVerdict.js.map +1 -0
  529. package/build/dist/Types/Kubernetes/KubernetesClusterAiAccess.js +72 -16
  530. package/build/dist/Types/Kubernetes/KubernetesClusterAiAccess.js.map +1 -1
  531. package/build/dist/Types/Kubernetes/KubernetesClusterAiAccessPermissions.js +20 -16
  532. package/build/dist/Types/Kubernetes/KubernetesClusterAiAccessPermissions.js.map +1 -1
  533. package/build/dist/Types/Kubernetes/KubernetesClusterAiInsights.js +17 -0
  534. package/build/dist/Types/Kubernetes/KubernetesClusterAiInsights.js.map +1 -0
  535. package/build/dist/Types/Monitor/ProxmoxAlertTemplates.js +4 -4
  536. package/build/dist/Types/Monitor/ProxmoxAlertTemplates.js.map +1 -1
  537. package/build/dist/Types/Team/ComplianceNotificationChannel.js +23 -0
  538. package/build/dist/Types/Team/ComplianceNotificationChannel.js.map +1 -0
  539. package/build/dist/Types/Team/ComplianceRule.js +304 -0
  540. package/build/dist/Types/Team/ComplianceRule.js.map +1 -0
  541. package/build/dist/Types/Team/ComplianceRuleType.js +21 -0
  542. package/build/dist/Types/Team/ComplianceRuleType.js.map +1 -1
  543. package/build/dist/Types/Team/TeamComplianceStatus.js +2 -0
  544. package/build/dist/Types/Team/TeamComplianceStatus.js.map +1 -0
  545. package/build/dist/Types/Telemetry/TelemetryIngestSurface.js +25 -0
  546. package/build/dist/Types/Telemetry/TelemetryIngestSurface.js.map +1 -1
  547. package/build/dist/UI/Components/ActionButton/ActionButtonSchema.js +20 -1
  548. package/build/dist/UI/Components/ActionButton/ActionButtonSchema.js.map +1 -1
  549. package/build/dist/UI/Components/ActionButton/RowActions.js +113 -0
  550. package/build/dist/UI/Components/ActionButton/RowActions.js.map +1 -0
  551. package/build/dist/UI/Components/ActionButton/SplitActionButtons.js +71 -0
  552. package/build/dist/UI/Components/ActionButton/SplitActionButtons.js.map +1 -0
  553. package/build/dist/UI/Components/Charts/Area/AreaChart.js +16 -1
  554. package/build/dist/UI/Components/Charts/Area/AreaChart.js.map +1 -1
  555. package/build/dist/UI/Components/Charts/Bar/BarChart.js +16 -1
  556. package/build/dist/UI/Components/Charts/Bar/BarChart.js.map +1 -1
  557. package/build/dist/UI/Components/Charts/ChartGroup/ChartGroup.js +27 -5
  558. package/build/dist/UI/Components/Charts/ChartGroup/ChartGroup.js.map +1 -1
  559. package/build/dist/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.js +61 -194
  560. package/build/dist/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.js.map +1 -1
  561. package/build/dist/UI/Components/Charts/ChartLibrary/BarChart/BarChart.js +84 -26
  562. package/build/dist/UI/Components/Charts/ChartLibrary/BarChart/BarChart.js.map +1 -1
  563. package/build/dist/UI/Components/Charts/ChartLibrary/LineChart/LineChart.js +75 -196
  564. package/build/dist/UI/Components/Charts/ChartLibrary/LineChart/LineChart.js.map +1 -1
  565. package/build/dist/UI/Components/Charts/ChartLibrary/Utils/DoubleClick.js +133 -0
  566. package/build/dist/UI/Components/Charts/ChartLibrary/Utils/DoubleClick.js.map +1 -1
  567. package/build/dist/UI/Components/Charts/ChartLibrary/Utils/UseChartBucketsChange.js +26 -0
  568. package/build/dist/UI/Components/Charts/ChartLibrary/Utils/UseChartBucketsChange.js.map +1 -0
  569. package/build/dist/UI/Components/Charts/ChartLibrary/Utils/UseChartRangeSelection.js +371 -0
  570. package/build/dist/UI/Components/Charts/ChartLibrary/Utils/UseChartRangeSelection.js.map +1 -0
  571. package/build/dist/UI/Components/Charts/Line/LineChart.js +16 -1
  572. package/build/dist/UI/Components/Charts/Line/LineChart.js.map +1 -1
  573. package/build/dist/UI/Components/Charts/TimeRangeZoom/ResetTimeRangeZoomButton.js +35 -0
  574. package/build/dist/UI/Components/Charts/TimeRangeZoom/ResetTimeRangeZoomButton.js.map +1 -0
  575. package/build/dist/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomContext.js +107 -0
  576. package/build/dist/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomContext.js.map +1 -0
  577. package/build/dist/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomHint.js +41 -0
  578. package/build/dist/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomHint.js.map +1 -0
  579. package/build/dist/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomUtil.js +117 -0
  580. package/build/dist/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomUtil.js.map +1 -0
  581. package/build/dist/UI/Components/Charts/TimeRangeZoom/UseTimeRangeZoom.js +121 -0
  582. package/build/dist/UI/Components/Charts/TimeRangeZoom/UseTimeRangeZoom.js.map +1 -0
  583. package/build/dist/UI/Components/Charts/Utils/DataPoint.js +15 -7
  584. package/build/dist/UI/Components/Charts/Utils/DataPoint.js.map +1 -1
  585. package/build/dist/UI/Components/Charts/Utils/XAxis.js +70 -0
  586. package/build/dist/UI/Components/Charts/Utils/XAxis.js.map +1 -1
  587. package/build/dist/UI/Components/Charts/Utils/useHistogramRangeSelection.js +305 -38
  588. package/build/dist/UI/Components/Charts/Utils/useHistogramRangeSelection.js.map +1 -1
  589. package/build/dist/UI/Components/EntityDropdown/EntityDropdown.js +89 -14
  590. package/build/dist/UI/Components/EntityDropdown/EntityDropdown.js.map +1 -1
  591. package/build/dist/UI/Components/Forms/Fields/FormField.js +11 -2
  592. package/build/dist/UI/Components/Forms/Fields/FormField.js.map +1 -1
  593. package/build/dist/UI/Components/Input/Input.js +69 -54
  594. package/build/dist/UI/Components/Input/Input.js.map +1 -1
  595. package/build/dist/UI/Components/List/ListRow.js +4 -39
  596. package/build/dist/UI/Components/List/ListRow.js.map +1 -1
  597. package/build/dist/UI/Components/LogsViewer/LogsViewer.js +31 -8
  598. package/build/dist/UI/Components/LogsViewer/LogsViewer.js.map +1 -1
  599. package/build/dist/UI/Components/LogsViewer/components/LogTimeRangePicker.js +10 -1
  600. package/build/dist/UI/Components/LogsViewer/components/LogTimeRangePicker.js.map +1 -1
  601. package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js +139 -21
  602. package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js.map +1 -1
  603. package/build/dist/UI/Components/LogsViewer/components/LogsHistogram.js +30 -8
  604. package/build/dist/UI/Components/LogsViewer/components/LogsHistogram.js.map +1 -1
  605. package/build/dist/UI/Components/LogsViewer/components/LogsViewerToolbar.js +1 -1
  606. package/build/dist/UI/Components/LogsViewer/components/LogsViewerToolbar.js.map +1 -1
  607. package/build/dist/UI/Components/ModelTable/BaseModelTable.js +10 -0
  608. package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
  609. package/build/dist/UI/Components/MoreMenu/MoreMenu.js +253 -14
  610. package/build/dist/UI/Components/MoreMenu/MoreMenu.js.map +1 -1
  611. package/build/dist/UI/Components/MoreMenu/MoreMenuItem.js +43 -13
  612. package/build/dist/UI/Components/MoreMenu/MoreMenuItem.js.map +1 -1
  613. package/build/dist/UI/Components/OrderedStatesList/Item.js +2 -38
  614. package/build/dist/UI/Components/OrderedStatesList/Item.js.map +1 -1
  615. package/build/dist/UI/Components/StatusPage/ResourceGrid.js +53 -15
  616. package/build/dist/UI/Components/StatusPage/ResourceGrid.js.map +1 -1
  617. package/build/dist/UI/Components/Table/TableBody.js +6 -2
  618. package/build/dist/UI/Components/Table/TableBody.js.map +1 -1
  619. package/build/dist/UI/Components/Table/TableRow.js +12 -75
  620. package/build/dist/UI/Components/Table/TableRow.js.map +1 -1
  621. package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js +32 -7
  622. package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js.map +1 -1
  623. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogram.js +27 -9
  624. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogram.js.map +1 -1
  625. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryTimeRangePicker.js +11 -1
  626. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryTimeRangePicker.js.map +1 -1
  627. package/build/dist/UI/Components/TelemetryViewer/useViewerTimeRangeZoom.js +272 -0
  628. package/build/dist/UI/Components/TelemetryViewer/useViewerTimeRangeZoom.js.map +1 -0
  629. package/build/dist/UI/Components/Toggle/Toggle.js +14 -1
  630. package/build/dist/UI/Components/Toggle/Toggle.js.map +1 -1
  631. package/build/dist/UI/Components/Tooltip/Tooltip.js +1 -1
  632. package/build/dist/UI/Components/Tooltip/Tooltip.js.map +1 -1
  633. package/build/dist/Utils/AiRemediation/CommandPolicy.js +1 -1
  634. package/build/dist/Utils/AiRemediation/CommandPolicy.js.map +1 -1
  635. package/build/dist/Utils/AiRemediation/KubectlPolicy.js +1 -1
  636. package/build/dist/Utils/AiRemediation/KubectlPolicy.js.map +1 -1
  637. package/build/dist/Utils/Dashboard/DashboardTimeRangeZoom.js +27 -1
  638. package/build/dist/Utils/Dashboard/DashboardTimeRangeZoom.js.map +1 -1
  639. package/package.json +1 -1
@@ -0,0 +1,1481 @@
1
+ import { afterEach, beforeEach, describe, expect, test } from "@jest/globals";
2
+ import {
3
+ RenderHookResult,
4
+ act,
5
+ cleanup,
6
+ fireEvent,
7
+ renderHook,
8
+ } from "@testing-library/react";
9
+ import React from "react";
10
+ import getJestMockFunction, { MockFunction } from "../../../MockType";
11
+ import useChartRangeSelection, {
12
+ CHART_PRESS_STILLNESS_PX,
13
+ ChartRangeSelection,
14
+ ChartRangeSelectionRootProps,
15
+ RANGE_SELECTION_THROTTLED_EVENTS,
16
+ RangeSelectionChartState,
17
+ UseChartRangeSelectionOptions,
18
+ } from "../../../../UI/Components/Charts/ChartLibrary/Utils/UseChartRangeSelection";
19
+ import {
20
+ CHART_DATA_POINT_DATE_KEY,
21
+ CHART_DATA_POINT_X_AXIS_KEY,
22
+ } from "../../../../UI/Components/Charts/ChartLibrary/Types/ChartDataPoint";
23
+
24
+ /*
25
+ * Issue #4116: right after a drag zooms a page, the page refetches, and the
26
+ * reader double-clicks to undo the zoom at exactly that moment. When the new
27
+ * rows landed during the double-click's second press, recharts re-keyed the
28
+ * node under the press, and Chrome sent that press no click and no dblclick:
29
+ * the chart stayed zoomed, and the press's release - read against the new
30
+ * rows - could even zoom it further, to a window made of old and new
31
+ * buckets.
32
+ *
33
+ * useChartRangeSelection (the line, area and bar charts) now takes the
34
+ * chart's onTimeRangeReset and no longer depends on the dblclick arriving.
35
+ * The second press of a double-click (MouseEvent.detail 2, the browser's own
36
+ * click count) IS the reset: it starts no selection and paints nothing, and
37
+ * its release arms the reset for the next task. The browser's dblclick, when
38
+ * it does arrive, comes before that task, resets, and stands the fallback
39
+ * down - so a double-click resets exactly once either way.
40
+ *
41
+ * These drive the hook's chart-level handlers the way recharts 3 calls them,
42
+ * handler(state, event), with rows shaped like the real charts' (the x-axis
43
+ * label under the index key, the bucket start as epoch ms), and pin:
44
+ *
45
+ * - what chartEventProps hands the chart root with and without a select
46
+ * handler and a reset - including a chart that only resets (a vertical
47
+ * bar chart), which still has to see presses and releases;
48
+ * - that the reset press never selects, paints or renders, wherever the
49
+ * pointer goes before its release - on the chart or off it - and leaves
50
+ * nothing armed for a release it did not begin;
51
+ * - that a double-click resets exactly once whether or not its dblclick
52
+ * arrives, with the reset the chart handed in last;
53
+ * - that everything that is not a main-button second press - a drag, a
54
+ * click, a right-click, a triple-click's third press - behaves as it
55
+ * did before, but for one rule: a drag needs the POINTER to move, by
56
+ * more than CHART_PRESS_STILLNESS_PX. A press whose row changes under a
57
+ * still pointer - new rows landing mid-press, a hand's drift across a
58
+ * row's edge - is a plain click, not a two-bucket zoom. A caller that
59
+ * hands no mouse events still drags by the change of row alone;
60
+ * - that a release only the page hears is read from the native event the
61
+ * page-wide listener hands on: a press let go where it went down selects
62
+ * nothing, a drag let go far off still selects, and the count tells a
63
+ * later click's release (1), which ends a reset press whose own release
64
+ * was lost without resetting, from the reset press's own (2).
65
+ */
66
+
67
+ type Rows = Array<Record<string, unknown>>;
68
+ type SelectHandler = (startTime: Date, endTime: Date) => void;
69
+ type ResetHandler = () => void;
70
+ type Band = [string | null, string | null];
71
+
72
+ const MINUTE_MS: number = 60 * 1000;
73
+ const HALF_MINUTE_MS: number = 30 * 1000;
74
+ const FIRST_BUCKET_MS: number = Date.parse("2026-09-28T10:00:00.000Z");
75
+
76
+ const NO_BAND: Band = [null, null];
77
+
78
+ function buildRows(
79
+ firstBucketMs: number,
80
+ bucketMs: number,
81
+ count: number,
82
+ ): Rows {
83
+ return Array.from(
84
+ { length: count },
85
+ (_: unknown, rowIndex: number): Record<string, unknown> => {
86
+ const bucketStartMs: number = firstBucketMs + rowIndex * bucketMs;
87
+ return {
88
+ [CHART_DATA_POINT_X_AXIS_KEY]: new Date(bucketStartMs)
89
+ .toISOString()
90
+ .slice(11, 19),
91
+ [CHART_DATA_POINT_DATE_KEY]: bucketStartMs,
92
+ Requests: 100 + rowIndex,
93
+ };
94
+ },
95
+ );
96
+ }
97
+
98
+ // The rows on screen: ten one-minute buckets from 10:00.
99
+ const ROWS: Rows = buildRows(FIRST_BUCKET_MS, MINUTE_MS, 10);
100
+
101
+ /*
102
+ * What the refetch after a zoom brings in: the same ten minutes in twice as
103
+ * many buckets. A pointer that has not moved from 10:04 - the fifth old
104
+ * bucket - is over the ninth new one.
105
+ */
106
+ const REFETCHED_ROWS: Rows = buildRows(FIRST_BUCKET_MS, HALF_MINUTE_MS, 20);
107
+
108
+ // Where recharts has no bucket under the pointer: an axis, a margin.
109
+ const OFF_THE_ROWS: RangeSelectionChartState = {
110
+ activeTooltipIndex: null,
111
+ activeLabel: undefined,
112
+ };
113
+
114
+ // What recharts reports with the pointer over a row.
115
+ function at(rowIndex: number, rows: Rows = ROWS): RangeSelectionChartState {
116
+ const label: unknown = rows[rowIndex]?.[CHART_DATA_POINT_X_AXIS_KEY];
117
+ return {
118
+ // recharts 3 reports the tooltip index as a string.
119
+ activeTooltipIndex: String(rowIndex),
120
+ activeLabel: typeof label === "string" ? label : undefined,
121
+ };
122
+ }
123
+
124
+ interface ChartSetup {
125
+ onTimeRangeSelect?: SelectHandler | undefined;
126
+ onTimeRangeReset?: ResetHandler | undefined;
127
+ enabled?: boolean | undefined;
128
+ data?: Rows | undefined;
129
+ }
130
+
131
+ interface ChartHarness {
132
+ result: { current: ChartRangeSelection };
133
+ rerender: (setup: ChartSetup) => void;
134
+ unmount: () => void;
135
+ // How many times the chart has rendered so far.
136
+ renders: () => number;
137
+ }
138
+
139
+ function hookOptions(setup: ChartSetup): UseChartRangeSelectionOptions {
140
+ return {
141
+ data: setup.data ?? ROWS,
142
+ index: CHART_DATA_POINT_X_AXIS_KEY,
143
+ onTimeRangeSelect: setup.onTimeRangeSelect,
144
+ enabled: setup.enabled,
145
+ onTimeRangeReset: setup.onTimeRangeReset,
146
+ };
147
+ }
148
+
149
+ function renderChart(setup: ChartSetup): ChartHarness {
150
+ let renderCount: number = 0;
151
+ const hook: RenderHookResult<
152
+ ChartRangeSelection,
153
+ UseChartRangeSelectionOptions
154
+ > = renderHook(
155
+ (options: UseChartRangeSelectionOptions): ChartRangeSelection => {
156
+ renderCount += 1;
157
+ return useChartRangeSelection(options);
158
+ },
159
+ { initialProps: hookOptions(setup) },
160
+ );
161
+
162
+ return {
163
+ result: hook.result,
164
+ rerender: (next: ChartSetup): void => {
165
+ hook.rerender(hookOptions(next));
166
+ },
167
+ unmount: hook.unmount,
168
+ renders: (): number => {
169
+ return renderCount;
170
+ },
171
+ };
172
+ }
173
+
174
+ interface ClickInit {
175
+ // The browser's click count: 2 on a double-click's second press and release.
176
+ detail?: number;
177
+ // The button pressed or let go: 0 main, 1 middle, 2 secondary.
178
+ button?: number;
179
+ // Where the pointer is, if not where clientXOf puts the row reported.
180
+ clientX?: number;
181
+ }
182
+
183
+ // MouseEvent.buttons while each button is held.
184
+ const HELD_BUTTONS: Record<number, number> = { 0: 1, 1: 4, 2: 2 };
185
+
186
+ // How far apart the rows sit on screen, as a drawn chart would place them.
187
+ const ROW_WIDTH_PX: number = 20;
188
+
189
+ // Where the pointer is when recharts reports this row under it.
190
+ function clientXOf(state: RangeSelectionChartState): number {
191
+ const index: number = Number(state.activeTooltipIndex);
192
+ return Number.isFinite(index) ? index * ROW_WIDTH_PX : 0;
193
+ }
194
+
195
+ // The part of the React event recharts hands the handlers that they read.
196
+ function mouseEvent(
197
+ detail: number,
198
+ button: number,
199
+ buttons: number,
200
+ clientX: number,
201
+ ): React.MouseEvent<SVGGraphicsElement> {
202
+ return {
203
+ detail: detail,
204
+ button: button,
205
+ buttons: buttons,
206
+ clientX: clientX,
207
+ } as unknown as React.MouseEvent<SVGGraphicsElement>;
208
+ }
209
+
210
+ function press(
211
+ chart: ChartHarness,
212
+ state: RangeSelectionChartState,
213
+ init: ClickInit = {},
214
+ ): void {
215
+ const onMouseDown: ChartRangeSelectionRootProps["onMouseDown"] =
216
+ chart.result.current.chartEventProps.onMouseDown;
217
+ if (!onMouseDown) {
218
+ throw new Error("The chart root was handed no onMouseDown: no press.");
219
+ }
220
+ const button: number = init.button ?? 0;
221
+ act(() => {
222
+ onMouseDown(
223
+ state,
224
+ mouseEvent(
225
+ init.detail ?? 1,
226
+ button,
227
+ HELD_BUTTONS[button] ?? 1,
228
+ init.clientX ?? clientXOf(state),
229
+ ),
230
+ );
231
+ });
232
+ }
233
+
234
+ function move(
235
+ chart: ChartHarness,
236
+ state: RangeSelectionChartState,
237
+ buttons: number = 1,
238
+ clientX: number = clientXOf(state),
239
+ ): void {
240
+ const onMouseMove: ChartRangeSelectionRootProps["onMouseMove"] =
241
+ chart.result.current.chartEventProps.onMouseMove;
242
+ if (!onMouseMove) {
243
+ throw new Error("The chart root was handed no onMouseMove: no drag.");
244
+ }
245
+ act(() => {
246
+ onMouseMove(state, mouseEvent(0, 0, buttons, clientX));
247
+ });
248
+ }
249
+
250
+ function release(
251
+ chart: ChartHarness,
252
+ state: RangeSelectionChartState,
253
+ init: ClickInit = {},
254
+ ): void {
255
+ const onMouseUp: ChartRangeSelectionRootProps["onMouseUp"] =
256
+ chart.result.current.chartEventProps.onMouseUp;
257
+ if (!onMouseUp) {
258
+ throw new Error("The chart root was handed no onMouseUp: no release.");
259
+ }
260
+ act(() => {
261
+ onMouseUp(
262
+ state,
263
+ mouseEvent(
264
+ init.detail ?? 1,
265
+ init.button ?? 0,
266
+ 0,
267
+ init.clientX ?? clientXOf(state),
268
+ ),
269
+ );
270
+ });
271
+ }
272
+
273
+ // The browser's dblclick, when it reaches the chart root.
274
+ function doubleClick(chart: ChartHarness): void {
275
+ const onDoubleClick: ChartRangeSelectionRootProps["onDoubleClick"] =
276
+ chart.result.current.chartEventProps.onDoubleClick;
277
+ if (!onDoubleClick) {
278
+ throw new Error("The chart root was handed no onDoubleClick.");
279
+ }
280
+ act(() => {
281
+ onDoubleClick();
282
+ });
283
+ }
284
+
285
+ // A whole click: press and release on one row, with the browser's count.
286
+ function click(
287
+ chart: ChartHarness,
288
+ state: RangeSelectionChartState,
289
+ detail: number,
290
+ ): void {
291
+ press(chart, state, { detail: detail });
292
+ release(chart, state, { detail: detail });
293
+ }
294
+
295
+ /*
296
+ * The handlers as a caller that hands them recharts' state alone - no
297
+ * mouse event - calls them: the shape they had before they read the event.
298
+ */
299
+ function pressWithoutEvent(
300
+ chart: ChartHarness,
301
+ state: RangeSelectionChartState,
302
+ ): void {
303
+ const onMouseDown: ChartRangeSelectionRootProps["onMouseDown"] =
304
+ chart.result.current.chartEventProps.onMouseDown;
305
+ if (!onMouseDown) {
306
+ throw new Error("The chart root was handed no onMouseDown: no press.");
307
+ }
308
+ act(() => {
309
+ onMouseDown(state);
310
+ });
311
+ }
312
+
313
+ function moveWithoutEvent(
314
+ chart: ChartHarness,
315
+ state: RangeSelectionChartState,
316
+ ): void {
317
+ const onMouseMove:
318
+ | ((chartState: RangeSelectionChartState) => void)
319
+ | undefined = chart.result.current.chartEventProps.onMouseMove as
320
+ | ((chartState: RangeSelectionChartState) => void)
321
+ | undefined;
322
+ if (!onMouseMove) {
323
+ throw new Error("The chart root was handed no onMouseMove: no drag.");
324
+ }
325
+ act(() => {
326
+ onMouseMove(state);
327
+ });
328
+ }
329
+
330
+ function releaseWithoutEvent(
331
+ chart: ChartHarness,
332
+ state: RangeSelectionChartState,
333
+ ): void {
334
+ const onMouseUp: ChartRangeSelectionRootProps["onMouseUp"] =
335
+ chart.result.current.chartEventProps.onMouseUp;
336
+ if (!onMouseUp) {
337
+ throw new Error("The chart root was handed no onMouseUp: no release.");
338
+ }
339
+ act(() => {
340
+ onMouseUp(state);
341
+ });
342
+ }
343
+
344
+ /*
345
+ * What Chrome runs between a release and its click: the microtasks, where
346
+ * React commits. The dblclick comes after them, the next task after that.
347
+ */
348
+ async function flushMicrotasks(): Promise<void> {
349
+ await act(async (): Promise<void> => {
350
+ await Promise.resolve();
351
+ });
352
+ }
353
+
354
+ // The next task: what a setTimeout(0) armed during a release waits for.
355
+ function nextTask(): void {
356
+ act(() => {
357
+ jest.advanceTimersByTime(0);
358
+ });
359
+ }
360
+
361
+ function allTimers(): void {
362
+ act(() => {
363
+ jest.runAllTimers();
364
+ });
365
+ }
366
+
367
+ function band(chart: ChartHarness): Band {
368
+ return [
369
+ chart.result.current.selectionStartLabel,
370
+ chart.result.current.selectionEndLabel,
371
+ ];
372
+ }
373
+
374
+ function selections(select: MockFunction): Array<[string, string]> {
375
+ return select.mock.calls.map((call: Array<unknown>): [string, string] => {
376
+ return [(call[0] as Date).toISOString(), (call[1] as Date).toISOString()];
377
+ });
378
+ }
379
+
380
+ function keysOf(props: ChartRangeSelectionRootProps): Array<string> {
381
+ return Object.keys(props).sort();
382
+ }
383
+
384
+ // The page-wide mouseup listeners a press adds to hear a release off the chart.
385
+ interface PageListenerSpies {
386
+ add: jest.SpiedFunction<typeof window.addEventListener>;
387
+ remove: jest.SpiedFunction<typeof window.removeEventListener>;
388
+ }
389
+
390
+ function spyOnPageListeners(): PageListenerSpies {
391
+ return {
392
+ add: jest.spyOn(window, "addEventListener"),
393
+ remove: jest.spyOn(window, "removeEventListener"),
394
+ };
395
+ }
396
+
397
+ function mouseUpListeners(calls: Array<Array<unknown>>): Array<unknown> {
398
+ return calls
399
+ .filter((call: Array<unknown>): boolean => {
400
+ return call[0] === "mouseup";
401
+ })
402
+ .map((call: Array<unknown>): unknown => {
403
+ return call[1];
404
+ });
405
+ }
406
+
407
+ function expectNoMouseUpListenerLeft(spies: PageListenerSpies): void {
408
+ const removed: Array<unknown> = mouseUpListeners(spies.remove.mock.calls);
409
+ for (const added of mouseUpListeners(spies.add.mock.calls)) {
410
+ expect(removed).toContain(added);
411
+ }
412
+ }
413
+
414
+ // The page-wide mouseup listener added last: the press in progress's own.
415
+ function lastMouseUpListener(
416
+ spies: PageListenerSpies,
417
+ ): (event: MouseEvent) => void {
418
+ const listeners: Array<unknown> = mouseUpListeners(spies.add.mock.calls);
419
+ const listener: unknown = listeners[listeners.length - 1];
420
+ if (typeof listener !== "function") {
421
+ throw new Error("No press added a page-wide mouseup listener.");
422
+ }
423
+ return listener as (event: MouseEvent) => void;
424
+ }
425
+
426
+ interface ResetOnlyCase {
427
+ name: string;
428
+ setup: (select: MockFunction, reset: MockFunction) => ChartSetup;
429
+ }
430
+
431
+ /*
432
+ * The two ways a chart ends up offering a double-click but no drag: no
433
+ * select handler at all, or one it cannot use (a vertical bar chart, whose
434
+ * time runs down the y-axis).
435
+ */
436
+ const RESET_ONLY_CASES: Array<ResetOnlyCase> = [
437
+ {
438
+ name: "no select handler",
439
+ setup: (_select: MockFunction, reset: MockFunction): ChartSetup => {
440
+ return { onTimeRangeReset: reset };
441
+ },
442
+ },
443
+ {
444
+ name: "selection turned off, as on a vertical bar chart",
445
+ setup: (select: MockFunction, reset: MockFunction): ChartSetup => {
446
+ return {
447
+ onTimeRangeSelect: select,
448
+ enabled: false,
449
+ onTimeRangeReset: reset,
450
+ };
451
+ },
452
+ },
453
+ ];
454
+
455
+ beforeEach(() => {
456
+ jest.useFakeTimers();
457
+ });
458
+
459
+ afterEach(() => {
460
+ cleanup();
461
+ jest.restoreAllMocks();
462
+ jest.useRealTimers();
463
+ });
464
+
465
+ describe("what the chart root is handed", () => {
466
+ test("a chart that drags and resets takes presses, moves, releases and the dblclick, with the crosshair and unthrottled moves", () => {
467
+ const chart: ChartHarness = renderChart({
468
+ onTimeRangeSelect: getJestMockFunction(),
469
+ onTimeRangeReset: getJestMockFunction(),
470
+ });
471
+ const props: ChartRangeSelectionRootProps =
472
+ chart.result.current.chartEventProps;
473
+
474
+ expect(keysOf(props)).toEqual([
475
+ "onDoubleClick",
476
+ "onMouseDown",
477
+ "onMouseMove",
478
+ "onMouseUp",
479
+ "style",
480
+ "throttledEvents",
481
+ ]);
482
+ expect(typeof props.onMouseDown).toBe("function");
483
+ expect(typeof props.onMouseMove).toBe("function");
484
+ expect(typeof props.onMouseUp).toBe("function");
485
+ expect(typeof props.onDoubleClick).toBe("function");
486
+ expect(props.style).toEqual({ cursor: "crosshair" });
487
+ expect(props.throttledEvents).toEqual(RANGE_SELECTION_THROTTLED_EVENTS);
488
+ expect(props.throttledEvents).not.toContain("mousemove");
489
+ expect(chart.result.current.canSelect).toBe(true);
490
+ });
491
+
492
+ test("a chart that drags with no zoom to undo gets no dblclick handler", () => {
493
+ const chart: ChartHarness = renderChart({
494
+ onTimeRangeSelect: getJestMockFunction(),
495
+ });
496
+
497
+ expect(keysOf(chart.result.current.chartEventProps)).toEqual([
498
+ "onMouseDown",
499
+ "onMouseMove",
500
+ "onMouseUp",
501
+ "style",
502
+ "throttledEvents",
503
+ ]);
504
+ expect(chart.result.current.canSelect).toBe(true);
505
+ });
506
+
507
+ for (const resetOnly of RESET_ONLY_CASES) {
508
+ test(`a chart that only resets (${resetOnly.name}) takes presses, releases and the dblclick - no moves, no crosshair, recharts' own throttling`, () => {
509
+ const chart: ChartHarness = renderChart(
510
+ resetOnly.setup(getJestMockFunction(), getJestMockFunction()),
511
+ );
512
+ const props: ChartRangeSelectionRootProps =
513
+ chart.result.current.chartEventProps;
514
+
515
+ expect(keysOf(props)).toEqual([
516
+ "onDoubleClick",
517
+ "onMouseDown",
518
+ "onMouseUp",
519
+ ]);
520
+ expect(typeof props.onMouseDown).toBe("function");
521
+ expect(typeof props.onMouseUp).toBe("function");
522
+ expect(typeof props.onDoubleClick).toBe("function");
523
+ expect(chart.result.current.canSelect).toBe(false);
524
+ });
525
+ }
526
+
527
+ test("a chart that neither drags nor resets is handed nothing", () => {
528
+ const plain: ChartHarness = renderChart({});
529
+ const verticalBars: ChartHarness = renderChart({
530
+ onTimeRangeSelect: getJestMockFunction(),
531
+ enabled: false,
532
+ });
533
+
534
+ expect(keysOf(plain.result.current.chartEventProps)).toEqual([]);
535
+ expect(keysOf(verticalBars.result.current.chartEventProps)).toEqual([]);
536
+ expect(plain.result.current.canSelect).toBe(false);
537
+ expect(verticalBars.result.current.canSelect).toBe(false);
538
+ });
539
+ });
540
+
541
+ describe("the second press of a double-click is the reset", () => {
542
+ test("dragged across the rows, it selects nothing and paints no band", () => {
543
+ const select: MockFunction = getJestMockFunction();
544
+ const chart: ChartHarness = renderChart({
545
+ onTimeRangeSelect: select,
546
+ onTimeRangeReset: getJestMockFunction(),
547
+ });
548
+
549
+ press(chart, at(2), { detail: 2 });
550
+ for (const rowIndex of [3, 4, 5, 6]) {
551
+ move(chart, at(rowIndex));
552
+ expect(band(chart)).toEqual(NO_BAND);
553
+ }
554
+ release(chart, at(6), { detail: 2 });
555
+ allTimers();
556
+
557
+ expect(select).not.toHaveBeenCalled();
558
+ expect(band(chart)).toEqual(NO_BAND);
559
+ });
560
+
561
+ test("its release resets once, on the next task - not during the release", async () => {
562
+ const reset: MockFunction = getJestMockFunction();
563
+ const chart: ChartHarness = renderChart({
564
+ onTimeRangeSelect: getJestMockFunction(),
565
+ onTimeRangeReset: reset,
566
+ });
567
+
568
+ press(chart, at(2), { detail: 2 });
569
+ move(chart, at(4));
570
+ release(chart, at(4), { detail: 2 });
571
+ expect(reset).not.toHaveBeenCalled();
572
+
573
+ // The dblclick has its chance first: it comes before the next task.
574
+ await flushMicrotasks();
575
+ expect(reset).not.toHaveBeenCalled();
576
+
577
+ nextTask();
578
+ expect(reset).toHaveBeenCalledTimes(1);
579
+ expect(reset).toHaveBeenCalledWith();
580
+
581
+ allTimers();
582
+ expect(reset).toHaveBeenCalledTimes(1);
583
+ });
584
+
585
+ test("it renders nothing, wherever the pointer goes, so the node it landed on stays for its click and dblclick", () => {
586
+ const chart: ChartHarness = renderChart({
587
+ onTimeRangeSelect: getJestMockFunction(),
588
+ onTimeRangeReset: getJestMockFunction(),
589
+ });
590
+ const rendersBefore: number = chart.renders();
591
+
592
+ press(chart, at(3), { detail: 2 });
593
+ move(chart, at(4));
594
+ move(chart, at(7));
595
+ release(chart, at(7), { detail: 2 });
596
+
597
+ expect(chart.renders()).toBe(rendersBefore);
598
+ });
599
+
600
+ test("whatever it listens for on the page, it stops listening once released", () => {
601
+ const chart: ChartHarness = renderChart({
602
+ onTimeRangeSelect: getJestMockFunction(),
603
+ onTimeRangeReset: getJestMockFunction(),
604
+ });
605
+ const pageListeners: PageListenerSpies = spyOnPageListeners();
606
+
607
+ press(chart, at(3), { detail: 2 });
608
+ move(chart, at(6));
609
+ release(chart, at(6), { detail: 2 });
610
+
611
+ expectNoMouseUpListenerLeft(pageListeners);
612
+ });
613
+
614
+ /*
615
+ * The hook's own design: the reset press ends on its release wherever
616
+ * that is, heard by the window as a drag's is. (The browser would send
617
+ * the chart no dblclick there; the reset does not wait for one.)
618
+ */
619
+ test("let go off the chart, it still resets, once: only the window hears that release", () => {
620
+ const select: MockFunction = getJestMockFunction();
621
+ const reset: MockFunction = getJestMockFunction();
622
+ const chart: ChartHarness = renderChart({
623
+ onTimeRangeSelect: select,
624
+ onTimeRangeReset: reset,
625
+ });
626
+
627
+ click(chart, at(4), 1);
628
+ press(chart, at(4), { detail: 2 });
629
+ move(chart, at(9));
630
+ fireEvent.mouseUp(window);
631
+ expect(reset).not.toHaveBeenCalled();
632
+
633
+ nextTask();
634
+ expect(reset).toHaveBeenCalledTimes(1);
635
+ allTimers();
636
+ expect(reset).toHaveBeenCalledTimes(1);
637
+ expect(select).not.toHaveBeenCalled();
638
+ expect(band(chart)).toEqual(NO_BAND);
639
+ });
640
+
641
+ /*
642
+ * While the reset press listened for no release off the chart, one let go
643
+ * there stayed "the reset press", and the next mouseup the chart heard -
644
+ * from a press that began anywhere else on the page - reset the zoom.
645
+ */
646
+ test("let go off the chart, it leaves nothing armed for a later release over the chart that it did not begin", () => {
647
+ const select: MockFunction = getJestMockFunction();
648
+ const reset: MockFunction = getJestMockFunction();
649
+ const chart: ChartHarness = renderChart({
650
+ onTimeRangeSelect: select,
651
+ onTimeRangeReset: reset,
652
+ });
653
+
654
+ click(chart, at(4), 1);
655
+ press(chart, at(4), { detail: 2 });
656
+ fireEvent.mouseUp(window);
657
+ allTimers();
658
+ const resetsSoFar: number = reset.mock.calls.length;
659
+
660
+ /*
661
+ * Later, a press somewhere else on the page - a text selection in the
662
+ * table under the chart, say - is let go over the chart. No press on
663
+ * the chart began it, so it ends no double-click.
664
+ */
665
+ release(chart, at(6), { detail: 1 });
666
+ allTimers();
667
+
668
+ expect(reset).toHaveBeenCalledTimes(resetsSoFar);
669
+ expect(select).not.toHaveBeenCalled();
670
+ });
671
+
672
+ test("a chart unmounted during it stops listening, and resets nothing when the button comes up", () => {
673
+ const reset: MockFunction = getJestMockFunction();
674
+ const chart: ChartHarness = renderChart({
675
+ onTimeRangeSelect: getJestMockFunction(),
676
+ onTimeRangeReset: reset,
677
+ });
678
+ const pageListeners: PageListenerSpies = spyOnPageListeners();
679
+
680
+ click(chart, at(4), 1);
681
+ press(chart, at(4), { detail: 2 });
682
+ chart.unmount();
683
+ expectNoMouseUpListenerLeft(pageListeners);
684
+
685
+ fireEvent.mouseUp(window);
686
+ allTimers();
687
+ expect(reset).not.toHaveBeenCalled();
688
+ });
689
+
690
+ test("a whole double-click whose dblclick never arrives resets once, and selects nothing", async () => {
691
+ const select: MockFunction = getJestMockFunction();
692
+ const reset: MockFunction = getJestMockFunction();
693
+ const chart: ChartHarness = renderChart({
694
+ onTimeRangeSelect: select,
695
+ onTimeRangeReset: reset,
696
+ });
697
+
698
+ click(chart, at(4), 1);
699
+ click(chart, at(4), 2);
700
+ await flushMicrotasks();
701
+ // No click and no dblclick: the node under the press was re-keyed.
702
+ nextTask();
703
+
704
+ expect(reset).toHaveBeenCalledTimes(1);
705
+ allTimers();
706
+ expect(reset).toHaveBeenCalledTimes(1);
707
+ expect(select).not.toHaveBeenCalled();
708
+ });
709
+
710
+ test("a whole double-click whose dblclick arrives resets exactly once, from the dblclick", async () => {
711
+ const select: MockFunction = getJestMockFunction();
712
+ const reset: MockFunction = getJestMockFunction();
713
+ const chart: ChartHarness = renderChart({
714
+ onTimeRangeSelect: select,
715
+ onTimeRangeReset: reset,
716
+ });
717
+
718
+ click(chart, at(4), 1);
719
+ click(chart, at(4), 2);
720
+ // Chrome's order: mouseup, microtasks, click, dblclick, then the next task.
721
+ await flushMicrotasks();
722
+ expect(reset).not.toHaveBeenCalled();
723
+ doubleClick(chart);
724
+ expect(reset).toHaveBeenCalledTimes(1);
725
+
726
+ // The fallback the release armed has stood down.
727
+ allTimers();
728
+ expect(reset).toHaveBeenCalledTimes(1);
729
+ expect(select).not.toHaveBeenCalled();
730
+ });
731
+
732
+ test("a dblclick on its own resets at once, and only once", () => {
733
+ const select: MockFunction = getJestMockFunction();
734
+ const reset: MockFunction = getJestMockFunction();
735
+ const chart: ChartHarness = renderChart({
736
+ onTimeRangeSelect: select,
737
+ onTimeRangeReset: reset,
738
+ });
739
+
740
+ doubleClick(chart);
741
+ expect(reset).toHaveBeenCalledTimes(1);
742
+
743
+ allTimers();
744
+ expect(reset).toHaveBeenCalledTimes(1);
745
+ expect(select).not.toHaveBeenCalled();
746
+ });
747
+
748
+ test("new rows landing during the second press make no window of old and new buckets, and the double-click still resets once", () => {
749
+ const select: MockFunction = getJestMockFunction();
750
+ const reset: MockFunction = getJestMockFunction();
751
+ const chart: ChartHarness = renderChart({
752
+ onTimeRangeSelect: select,
753
+ onTimeRangeReset: reset,
754
+ });
755
+
756
+ click(chart, at(4), 1);
757
+ press(chart, at(4), { detail: 2 });
758
+ // The refetch lands under the press and the chart redraws its rows.
759
+ chart.rerender({
760
+ onTimeRangeSelect: select,
761
+ onTimeRangeReset: reset,
762
+ data: REFETCHED_ROWS,
763
+ });
764
+ /*
765
+ * The pointer has not moved, but recharts now reports a bucket of the
766
+ * new rows - and Chrome sends this press no click and no dblclick.
767
+ */
768
+ release(chart, at(8, REFETCHED_ROWS), { detail: 2 });
769
+
770
+ expect(select).not.toHaveBeenCalled();
771
+ expect(band(chart)).toEqual(NO_BAND);
772
+
773
+ nextTask();
774
+ expect(reset).toHaveBeenCalledTimes(1);
775
+ allTimers();
776
+ expect(reset).toHaveBeenCalledTimes(1);
777
+ expect(select).not.toHaveBeenCalled();
778
+ });
779
+
780
+ test("a double-click off the rows - on an axis or a margin - resets too", () => {
781
+ const select: MockFunction = getJestMockFunction();
782
+ const reset: MockFunction = getJestMockFunction();
783
+ const chart: ChartHarness = renderChart({
784
+ onTimeRangeSelect: select,
785
+ onTimeRangeReset: reset,
786
+ });
787
+
788
+ click(chart, OFF_THE_ROWS, 1);
789
+ click(chart, OFF_THE_ROWS, 2);
790
+ nextTask();
791
+
792
+ expect(reset).toHaveBeenCalledTimes(1);
793
+ expect(select).not.toHaveBeenCalled();
794
+ });
795
+
796
+ test("it drops a drag whose release was lost, band and page-wide listener included", () => {
797
+ const select: MockFunction = getJestMockFunction();
798
+ const reset: MockFunction = getJestMockFunction();
799
+ const chart: ChartHarness = renderChart({
800
+ onTimeRangeSelect: select,
801
+ onTimeRangeReset: reset,
802
+ });
803
+
804
+ press(chart, at(1));
805
+ move(chart, at(4));
806
+ expect(band(chart)).toEqual(["10:01:00", "10:04:00"]);
807
+
808
+ /*
809
+ * That drag's release never reached the page, and this press is the
810
+ * second of a double-click.
811
+ */
812
+ press(chart, at(4), { detail: 2 });
813
+ expect(band(chart)).toEqual(NO_BAND);
814
+ release(chart, at(4), { detail: 2 });
815
+ // The same release reaching the window: no drag is listening any more.
816
+ fireEvent.mouseUp(window);
817
+ nextTask();
818
+
819
+ expect(select).not.toHaveBeenCalled();
820
+ expect(reset).toHaveBeenCalledTimes(1);
821
+ });
822
+
823
+ test("it does not suppress the click that follows it, even after crossing rows", () => {
824
+ const chart: ChartHarness = renderChart({
825
+ onTimeRangeSelect: getJestMockFunction(),
826
+ onTimeRangeReset: getJestMockFunction(),
827
+ });
828
+
829
+ press(chart, at(2), { detail: 2 });
830
+ move(chart, at(3));
831
+ move(chart, at(6));
832
+ release(chart, at(6), { detail: 2 });
833
+ expect(chart.result.current.isClickSuppressed()).toBe(false);
834
+
835
+ nextTask();
836
+ expect(chart.result.current.isClickSuppressed()).toBe(false);
837
+ });
838
+ });
839
+
840
+ describe("presses that are not the reset", () => {
841
+ interface DragTrace {
842
+ bands: Array<Band>;
843
+ selected: Array<[string, string]>;
844
+ suppressedAfterRelease: boolean;
845
+ suppressedAfterNextTask: boolean;
846
+ renders: number;
847
+ }
848
+
849
+ function plainDrag(chart: ChartHarness, select: MockFunction): DragTrace {
850
+ const rendersBefore: number = chart.renders();
851
+ const bands: Array<Band> = [];
852
+ press(chart, at(2));
853
+ bands.push(band(chart));
854
+ move(chart, at(3));
855
+ bands.push(band(chart));
856
+ move(chart, at(5));
857
+ bands.push(band(chart));
858
+ release(chart, at(5));
859
+ bands.push(band(chart));
860
+ const suppressedAfterRelease: boolean =
861
+ chart.result.current.isClickSuppressed();
862
+ nextTask();
863
+
864
+ return {
865
+ bands: bands,
866
+ selected: selections(select),
867
+ suppressedAfterRelease: suppressedAfterRelease,
868
+ suppressedAfterNextTask: chart.result.current.isClickSuppressed(),
869
+ renders: chart.renders() - rendersBefore,
870
+ };
871
+ }
872
+
873
+ test("a plain drag selects exactly as it does on a chart with no reset", () => {
874
+ const selectWithoutReset: MockFunction = getJestMockFunction();
875
+ const selectWithReset: MockFunction = getJestMockFunction();
876
+ const reset: MockFunction = getJestMockFunction();
877
+
878
+ const withoutReset: DragTrace = plainDrag(
879
+ renderChart({ onTimeRangeSelect: selectWithoutReset }),
880
+ selectWithoutReset,
881
+ );
882
+ const withReset: DragTrace = plainDrag(
883
+ renderChart({
884
+ onTimeRangeSelect: selectWithReset,
885
+ onTimeRangeReset: reset,
886
+ }),
887
+ selectWithReset,
888
+ );
889
+
890
+ expect(withReset).toEqual(withoutReset);
891
+ expect(withReset.bands).toEqual([
892
+ NO_BAND,
893
+ ["10:02:00", "10:03:00"],
894
+ ["10:02:00", "10:05:00"],
895
+ NO_BAND,
896
+ ]);
897
+ // From the start of the first bucket to the END of the last.
898
+ expect(withReset.selected).toEqual([
899
+ ["2026-09-28T10:02:00.000Z", "2026-09-28T10:06:00.000Z"],
900
+ ]);
901
+ expect(withReset.suppressedAfterRelease).toBe(true);
902
+ expect(withReset.suppressedAfterNextTask).toBe(false);
903
+
904
+ allTimers();
905
+ expect(reset).not.toHaveBeenCalled();
906
+ });
907
+
908
+ test("a plain click selects nothing and resets nothing", () => {
909
+ const select: MockFunction = getJestMockFunction();
910
+ const reset: MockFunction = getJestMockFunction();
911
+ const chart: ChartHarness = renderChart({
912
+ onTimeRangeSelect: select,
913
+ onTimeRangeReset: reset,
914
+ });
915
+
916
+ click(chart, at(4), 1);
917
+ expect(band(chart)).toEqual(NO_BAND);
918
+ expect(chart.result.current.isClickSuppressed()).toBe(false);
919
+
920
+ allTimers();
921
+ expect(select).not.toHaveBeenCalled();
922
+ expect(reset).not.toHaveBeenCalled();
923
+ });
924
+
925
+ for (const otherButton of [
926
+ { name: "right", button: 2 },
927
+ { name: "middle", button: 1 },
928
+ ]) {
929
+ test(`a ${otherButton.name}-button second press is not the reset, and does not drag`, () => {
930
+ const select: MockFunction = getJestMockFunction();
931
+ const reset: MockFunction = getJestMockFunction();
932
+ const chart: ChartHarness = renderChart({
933
+ onTimeRangeSelect: select,
934
+ onTimeRangeReset: reset,
935
+ });
936
+
937
+ press(chart, at(4), { detail: 1, button: otherButton.button });
938
+ release(chart, at(4), { detail: 1, button: otherButton.button });
939
+ press(chart, at(4), { detail: 2, button: otherButton.button });
940
+ move(chart, at(7), HELD_BUTTONS[otherButton.button]);
941
+ release(chart, at(7), { detail: 2, button: otherButton.button });
942
+ allTimers();
943
+
944
+ expect(reset).not.toHaveBeenCalled();
945
+ expect(select).not.toHaveBeenCalled();
946
+ expect(band(chart)).toEqual(NO_BAND);
947
+ });
948
+ }
949
+
950
+ test("with no zoom to undo, a second press drags as it always did", () => {
951
+ const select: MockFunction = getJestMockFunction();
952
+ const chart: ChartHarness = renderChart({ onTimeRangeSelect: select });
953
+
954
+ click(chart, at(2), 1);
955
+ press(chart, at(2), { detail: 2 });
956
+ move(chart, at(4));
957
+ expect(band(chart)).toEqual(["10:02:00", "10:04:00"]);
958
+ release(chart, at(4), { detail: 2 });
959
+
960
+ expect(selections(select)).toEqual([
961
+ ["2026-09-28T10:02:00.000Z", "2026-09-28T10:05:00.000Z"],
962
+ ]);
963
+ });
964
+
965
+ test("a press handed no event is never the reset: it drags as it always did", () => {
966
+ const select: MockFunction = getJestMockFunction();
967
+ const reset: MockFunction = getJestMockFunction();
968
+ const chart: ChartHarness = renderChart({
969
+ onTimeRangeSelect: select,
970
+ onTimeRangeReset: reset,
971
+ });
972
+
973
+ act(() => {
974
+ chart.result.current.chartEventProps.onMouseDown?.(at(3));
975
+ });
976
+ move(chart, at(5));
977
+ act(() => {
978
+ chart.result.current.chartEventProps.onMouseUp?.(at(5));
979
+ });
980
+ allTimers();
981
+
982
+ expect(selections(select)).toEqual([
983
+ ["2026-09-28T10:03:00.000Z", "2026-09-28T10:06:00.000Z"],
984
+ ]);
985
+ expect(reset).not.toHaveBeenCalled();
986
+ });
987
+
988
+ /*
989
+ * The browser sends dblclick at a click count of 2 only, so the fallback
990
+ * stands in for it at 2 only: a triple-click's third press is a press.
991
+ */
992
+ test("a triple-click's third press, on a chart with a zoom to undo, is an ordinary press: it arms no reset of its own", () => {
993
+ const select: MockFunction = getJestMockFunction();
994
+ const reset: MockFunction = getJestMockFunction();
995
+ const chart: ChartHarness = renderChart({
996
+ onTimeRangeSelect: select,
997
+ onTimeRangeReset: reset,
998
+ });
999
+
1000
+ click(chart, at(4), 1);
1001
+ click(chart, at(4), 2);
1002
+ doubleClick(chart);
1003
+
1004
+ expect(reset).toHaveBeenCalledTimes(1);
1005
+ expect(jest.getTimerCount()).toBe(0);
1006
+
1007
+ // The third press and its release, which the browser counts 3.
1008
+ click(chart, at(4), 3);
1009
+
1010
+ expect(jest.getTimerCount()).toBe(0);
1011
+ expect(chart.result.current.isClickSuppressed()).toBe(false);
1012
+
1013
+ allTimers();
1014
+
1015
+ expect(reset).toHaveBeenCalledTimes(1);
1016
+ expect(select).not.toHaveBeenCalled();
1017
+ });
1018
+
1019
+ test("a drag from a press the browser counts 3 selects, as any drag does, and resets nothing", () => {
1020
+ const select: MockFunction = getJestMockFunction();
1021
+ const reset: MockFunction = getJestMockFunction();
1022
+ const chart: ChartHarness = renderChart({
1023
+ onTimeRangeSelect: select,
1024
+ onTimeRangeReset: reset,
1025
+ });
1026
+
1027
+ press(chart, at(2), { detail: 3 });
1028
+ move(chart, at(3));
1029
+ move(chart, at(5));
1030
+
1031
+ expect(band(chart)).toEqual(["10:02:00", "10:05:00"]);
1032
+
1033
+ release(chart, at(5), { detail: 3 });
1034
+ allTimers();
1035
+
1036
+ expect(selections(select)).toEqual([
1037
+ ["2026-09-28T10:02:00.000Z", "2026-09-28T10:06:00.000Z"],
1038
+ ]);
1039
+ expect(reset).not.toHaveBeenCalled();
1040
+ });
1041
+ });
1042
+
1043
+ /*
1044
+ * A drag needs the pointer itself to move, by more than
1045
+ * CHART_PRESS_STILLNESS_PX. The row recharts reports under the pointer can
1046
+ * change while the pointer stays put: new rows land mid-press (the refetch
1047
+ * after a zoom), or a hand's drift crosses a row's edge. Either way the
1048
+ * press is a plain click, which keeps its meaning (a bucket click, a legend
1049
+ * toggle) - never a zoom into two buckets the reader did not drag across.
1050
+ */
1051
+ describe("a press becomes a drag only when the pointer moves", () => {
1052
+ const CHART_KINDS: Array<{
1053
+ name: string;
1054
+ setup: (select: MockFunction, reset: MockFunction) => ChartSetup;
1055
+ }> = [
1056
+ {
1057
+ name: "a chart with a zoom to undo",
1058
+ setup: (select: MockFunction, reset: MockFunction): ChartSetup => {
1059
+ return { onTimeRangeSelect: select, onTimeRangeReset: reset };
1060
+ },
1061
+ },
1062
+ {
1063
+ name: "a chart with none",
1064
+ setup: (select: MockFunction): ChartSetup => {
1065
+ return { onTimeRangeSelect: select };
1066
+ },
1067
+ },
1068
+ ];
1069
+
1070
+ for (const kind of CHART_KINDS) {
1071
+ test(`on ${kind.name}, a press at a row's edge whose pointer drifts 2px onto the next row paints no band and renders nothing, and its release there is a plain click`, () => {
1072
+ const select: MockFunction = getJestMockFunction();
1073
+ const reset: MockFunction = getJestMockFunction();
1074
+ const chart: ChartHarness = renderChart(kind.setup(select, reset));
1075
+ const pressX: number = clientXOf(at(2));
1076
+ const rendersBefore: number = chart.renders();
1077
+
1078
+ press(chart, at(2), { clientX: pressX });
1079
+ // The hand is never quite still: recharts reports the next row now.
1080
+ move(chart, at(3), 1, pressX + 2);
1081
+
1082
+ expect(band(chart)).toEqual(NO_BAND);
1083
+
1084
+ release(chart, at(3), { clientX: pressX + 2 });
1085
+
1086
+ expect(select).not.toHaveBeenCalled();
1087
+ expect(band(chart)).toEqual(NO_BAND);
1088
+ expect(chart.result.current.isClickSuppressed()).toBe(false);
1089
+ expect(chart.renders()).toBe(rendersBefore);
1090
+
1091
+ allTimers();
1092
+
1093
+ expect(select).not.toHaveBeenCalled();
1094
+ expect(reset).not.toHaveBeenCalled();
1095
+ });
1096
+
1097
+ test(`on ${kind.name}, new rows landing under a still press put another row under it: no band, and its release selects nothing`, () => {
1098
+ const select: MockFunction = getJestMockFunction();
1099
+ const reset: MockFunction = getJestMockFunction();
1100
+ const chart: ChartHarness = renderChart(kind.setup(select, reset));
1101
+ const pressX: number = clientXOf(at(4));
1102
+
1103
+ press(chart, at(4), { clientX: pressX });
1104
+ // The refetch lands mid-press, and the chart redraws its rows.
1105
+ chart.rerender({ ...kind.setup(select, reset), data: REFETCHED_ROWS });
1106
+ /*
1107
+ * The pointer has not moved from 10:04, the fifth old row; recharts
1108
+ * reports the row now under it, the ninth new one.
1109
+ */
1110
+ move(chart, at(8, REFETCHED_ROWS), 1, pressX);
1111
+
1112
+ expect(band(chart)).toEqual(NO_BAND);
1113
+
1114
+ release(chart, at(8, REFETCHED_ROWS), { clientX: pressX });
1115
+
1116
+ expect(select).not.toHaveBeenCalled();
1117
+ expect(band(chart)).toEqual(NO_BAND);
1118
+ expect(chart.result.current.isClickSuppressed()).toBe(false);
1119
+
1120
+ allTimers();
1121
+
1122
+ expect(select).not.toHaveBeenCalled();
1123
+ expect(reset).not.toHaveBeenCalled();
1124
+ });
1125
+ }
1126
+
1127
+ // Pressed at the very edge of its row, so a few pixels reach the next one.
1128
+ const DIRECTIONS: Array<{
1129
+ name: string;
1130
+ pressedRow: number;
1131
+ otherRow: number;
1132
+ sign: number;
1133
+ }> = [
1134
+ { name: "rightwards", pressedRow: 2, otherRow: 3, sign: 1 },
1135
+ { name: "leftwards", pressedRow: 3, otherRow: 2, sign: -1 },
1136
+ ];
1137
+
1138
+ for (const direction of DIRECTIONS) {
1139
+ test(`a pointer moved ${direction.name} by CHART_PRESS_STILLNESS_PX onto the next row has not moved; one pixel further, it is a drag across both rows`, () => {
1140
+ const select: MockFunction = getJestMockFunction();
1141
+ const chart: ChartHarness = renderChart({
1142
+ onTimeRangeSelect: select,
1143
+ onTimeRangeReset: getJestMockFunction(),
1144
+ });
1145
+ const pressX: number = clientXOf(at(direction.pressedRow));
1146
+
1147
+ press(chart, at(direction.pressedRow), { clientX: pressX });
1148
+ move(
1149
+ chart,
1150
+ at(direction.otherRow),
1151
+ 1,
1152
+ pressX + direction.sign * CHART_PRESS_STILLNESS_PX,
1153
+ );
1154
+
1155
+ expect(band(chart)).toEqual(NO_BAND);
1156
+
1157
+ const draggedX: number =
1158
+ pressX + direction.sign * (CHART_PRESS_STILLNESS_PX + 1);
1159
+ move(chart, at(direction.otherRow), 1, draggedX);
1160
+
1161
+ expect(band(chart)).toEqual(["10:02:00", "10:03:00"]);
1162
+
1163
+ release(chart, at(direction.otherRow), { clientX: draggedX });
1164
+
1165
+ expect(selections(select)).toEqual([
1166
+ ["2026-09-28T10:02:00.000Z", "2026-09-28T10:04:00.000Z"],
1167
+ ]);
1168
+ });
1169
+ }
1170
+
1171
+ test("a real drag - the pointer crossing a row's width at a time - paints its band as it goes and selects exactly", () => {
1172
+ const select: MockFunction = getJestMockFunction();
1173
+ const chart: ChartHarness = renderChart({
1174
+ onTimeRangeSelect: select,
1175
+ onTimeRangeReset: getJestMockFunction(),
1176
+ });
1177
+
1178
+ // Rows sit ROW_WIDTH_PX (20px) apart: each move is a real one.
1179
+ press(chart, at(2));
1180
+ move(chart, at(3));
1181
+
1182
+ expect(band(chart)).toEqual(["10:02:00", "10:03:00"]);
1183
+
1184
+ move(chart, at(5));
1185
+
1186
+ expect(band(chart)).toEqual(["10:02:00", "10:05:00"]);
1187
+
1188
+ release(chart, at(5));
1189
+
1190
+ expect(selections(select)).toEqual([
1191
+ ["2026-09-28T10:02:00.000Z", "2026-09-28T10:06:00.000Z"],
1192
+ ]);
1193
+ expect(band(chart)).toEqual(NO_BAND);
1194
+ expect(chart.result.current.isClickSuppressed()).toBe(true);
1195
+
1196
+ nextTask();
1197
+
1198
+ expect(chart.result.current.isClickSuppressed()).toBe(false);
1199
+ });
1200
+
1201
+ test("a flick - let go well away, over another row, before any move came in - selects from the one to the other", () => {
1202
+ const select: MockFunction = getJestMockFunction();
1203
+ const chart: ChartHarness = renderChart({
1204
+ onTimeRangeSelect: select,
1205
+ onTimeRangeReset: getJestMockFunction(),
1206
+ });
1207
+
1208
+ press(chart, at(2));
1209
+ release(chart, at(6));
1210
+
1211
+ expect(selections(select)).toEqual([
1212
+ ["2026-09-28T10:02:00.000Z", "2026-09-28T10:07:00.000Z"],
1213
+ ]);
1214
+ });
1215
+
1216
+ test("a caller that hands the handlers no mouse events drags by the change of row alone, as it always did", () => {
1217
+ const select: MockFunction = getJestMockFunction();
1218
+ const chart: ChartHarness = renderChart({
1219
+ onTimeRangeSelect: select,
1220
+ onTimeRangeReset: getJestMockFunction(),
1221
+ });
1222
+
1223
+ pressWithoutEvent(chart, at(2));
1224
+ moveWithoutEvent(chart, at(4));
1225
+
1226
+ expect(band(chart)).toEqual(["10:02:00", "10:04:00"]);
1227
+
1228
+ releaseWithoutEvent(chart, at(4));
1229
+
1230
+ expect(selections(select)).toEqual([
1231
+ ["2026-09-28T10:02:00.000Z", "2026-09-28T10:05:00.000Z"],
1232
+ ]);
1233
+ expect(band(chart)).toEqual(NO_BAND);
1234
+ });
1235
+
1236
+ test("a caller that hands no mouse events, releasing over another row with no move between, selects from the one to the other", () => {
1237
+ const select: MockFunction = getJestMockFunction();
1238
+ const chart: ChartHarness = renderChart({
1239
+ onTimeRangeSelect: select,
1240
+ onTimeRangeReset: getJestMockFunction(),
1241
+ });
1242
+
1243
+ pressWithoutEvent(chart, at(2));
1244
+ releaseWithoutEvent(chart, at(5));
1245
+
1246
+ expect(selections(select)).toEqual([
1247
+ ["2026-09-28T10:02:00.000Z", "2026-09-28T10:06:00.000Z"],
1248
+ ]);
1249
+ });
1250
+ });
1251
+
1252
+ /*
1253
+ * A release off the chart never reaches the chart; the page-wide listener
1254
+ * the press added hears it, and hands the hook the native event: where
1255
+ * the pointer is, and the browser's click count.
1256
+ */
1257
+ describe("a release only the page hears", () => {
1258
+ test("a press let go where it went down - dragged straight off the chart, no move between - selects nothing, and the page stops listening", () => {
1259
+ const select: MockFunction = getJestMockFunction();
1260
+ const reset: MockFunction = getJestMockFunction();
1261
+ const chart: ChartHarness = renderChart({
1262
+ onTimeRangeSelect: select,
1263
+ onTimeRangeReset: reset,
1264
+ });
1265
+ const pageListeners: PageListenerSpies = spyOnPageListeners();
1266
+ const pressX: number = clientXOf(at(4));
1267
+
1268
+ press(chart, at(4), { clientX: pressX });
1269
+ fireEvent.mouseUp(window, { clientX: pressX, detail: 1, button: 0 });
1270
+ allTimers();
1271
+
1272
+ expect(select).not.toHaveBeenCalled();
1273
+ expect(reset).not.toHaveBeenCalled();
1274
+ expect(band(chart)).toEqual(NO_BAND);
1275
+ expect(chart.result.current.isClickSuppressed()).toBe(false);
1276
+ expectNoMouseUpListenerLeft(pageListeners);
1277
+ });
1278
+
1279
+ test("a real drag let go far off the chart still selects up to the last row it crossed", () => {
1280
+ const select: MockFunction = getJestMockFunction();
1281
+ const chart: ChartHarness = renderChart({
1282
+ onTimeRangeSelect: select,
1283
+ onTimeRangeReset: getJestMockFunction(),
1284
+ });
1285
+
1286
+ press(chart, at(2));
1287
+ move(chart, at(3));
1288
+ move(chart, at(5));
1289
+ fireEvent.mouseUp(window, {
1290
+ clientX: clientXOf(at(5)) + 400,
1291
+ detail: 1,
1292
+ button: 0,
1293
+ });
1294
+
1295
+ expect(selections(select)).toEqual([
1296
+ ["2026-09-28T10:02:00.000Z", "2026-09-28T10:06:00.000Z"],
1297
+ ]);
1298
+ expect(band(chart)).toEqual(NO_BAND);
1299
+ });
1300
+
1301
+ /*
1302
+ * The count is what tells a later click's release (1) - the reset
1303
+ * press's own was lost, the window having lost focus mid-press - from
1304
+ * the reset press's own (2). A listener that handed the hook no event
1305
+ * would reset for both.
1306
+ */
1307
+ const PAGE_RELEASES: Array<{
1308
+ name: string;
1309
+ detail: number;
1310
+ resets: number;
1311
+ }> = [
1312
+ {
1313
+ name: "a later click's release, counted 1, ends the reset press and resets nothing",
1314
+ detail: 1,
1315
+ resets: 0,
1316
+ },
1317
+ {
1318
+ name: "the reset press's own release, counted 2, resets once",
1319
+ detail: 2,
1320
+ resets: 1,
1321
+ },
1322
+ ];
1323
+
1324
+ for (const pageRelease of PAGE_RELEASES) {
1325
+ test(`the listener a reset press adds is handed the native MouseEvent: ${pageRelease.name}`, () => {
1326
+ const select: MockFunction = getJestMockFunction();
1327
+ const reset: MockFunction = getJestMockFunction();
1328
+ const chart: ChartHarness = renderChart({
1329
+ onTimeRangeSelect: select,
1330
+ onTimeRangeReset: reset,
1331
+ });
1332
+ const pageListeners: PageListenerSpies = spyOnPageListeners();
1333
+
1334
+ click(chart, at(4), 1);
1335
+ press(chart, at(4), { detail: 2 });
1336
+ const listener: (event: MouseEvent) => void =
1337
+ lastMouseUpListener(pageListeners);
1338
+
1339
+ act(() => {
1340
+ listener(
1341
+ new MouseEvent("mouseup", {
1342
+ detail: pageRelease.detail,
1343
+ button: 0,
1344
+ clientX: 300,
1345
+ }),
1346
+ );
1347
+ });
1348
+
1349
+ expectNoMouseUpListenerLeft(pageListeners);
1350
+
1351
+ nextTask();
1352
+
1353
+ expect(reset).toHaveBeenCalledTimes(pageRelease.resets);
1354
+
1355
+ allTimers();
1356
+
1357
+ expect(reset).toHaveBeenCalledTimes(pageRelease.resets);
1358
+ expect(select).not.toHaveBeenCalled();
1359
+ expect(band(chart)).toEqual(NO_BAND);
1360
+ });
1361
+ }
1362
+ });
1363
+
1364
+ describe("a chart that only resets", () => {
1365
+ for (const resetOnly of RESET_ONLY_CASES) {
1366
+ describe(resetOnly.name, () => {
1367
+ test("a single press starts nothing: no page-wide release listener, no selection", () => {
1368
+ const select: MockFunction = getJestMockFunction();
1369
+ const reset: MockFunction = getJestMockFunction();
1370
+ const chart: ChartHarness = renderChart(resetOnly.setup(select, reset));
1371
+ const pageListeners: PageListenerSpies = spyOnPageListeners();
1372
+ const rendersBefore: number = chart.renders();
1373
+
1374
+ press(chart, at(2), { detail: 1 });
1375
+ expect(mouseUpListeners(pageListeners.add.mock.calls)).toEqual([]);
1376
+
1377
+ release(chart, at(6), { detail: 1 });
1378
+ fireEvent.mouseUp(window);
1379
+ allTimers();
1380
+
1381
+ expect(select).not.toHaveBeenCalled();
1382
+ expect(reset).not.toHaveBeenCalled();
1383
+ expect(band(chart)).toEqual(NO_BAND);
1384
+ expect(chart.renders()).toBe(rendersBefore);
1385
+ });
1386
+
1387
+ test("a double-click whose dblclick never arrives resets once", () => {
1388
+ const select: MockFunction = getJestMockFunction();
1389
+ const reset: MockFunction = getJestMockFunction();
1390
+ const chart: ChartHarness = renderChart(resetOnly.setup(select, reset));
1391
+
1392
+ click(chart, at(5), 1);
1393
+ click(chart, at(5), 2);
1394
+ expect(reset).not.toHaveBeenCalled();
1395
+
1396
+ nextTask();
1397
+ expect(reset).toHaveBeenCalledTimes(1);
1398
+ allTimers();
1399
+ expect(reset).toHaveBeenCalledTimes(1);
1400
+ expect(select).not.toHaveBeenCalled();
1401
+ });
1402
+
1403
+ test("a double-click whose dblclick arrives resets exactly once", () => {
1404
+ const select: MockFunction = getJestMockFunction();
1405
+ const reset: MockFunction = getJestMockFunction();
1406
+ const chart: ChartHarness = renderChart(resetOnly.setup(select, reset));
1407
+
1408
+ click(chart, at(5), 1);
1409
+ click(chart, at(5), 2);
1410
+ doubleClick(chart);
1411
+ expect(reset).toHaveBeenCalledTimes(1);
1412
+
1413
+ allTimers();
1414
+ expect(reset).toHaveBeenCalledTimes(1);
1415
+ expect(select).not.toHaveBeenCalled();
1416
+ });
1417
+ });
1418
+ }
1419
+ });
1420
+
1421
+ describe("the reset is read when it happens", () => {
1422
+ test("the fallback runs the reset the chart handed in last, not the one it had at the press", () => {
1423
+ const select: MockFunction = getJestMockFunction();
1424
+ const resetAtThePress: MockFunction = getJestMockFunction();
1425
+ const resetNow: MockFunction = getJestMockFunction();
1426
+ const chart: ChartHarness = renderChart({
1427
+ onTimeRangeSelect: select,
1428
+ onTimeRangeReset: resetAtThePress,
1429
+ });
1430
+
1431
+ click(chart, at(4), 1);
1432
+ click(chart, at(4), 2);
1433
+ // The chart re-renders before the next task, with a new reset.
1434
+ chart.rerender({ onTimeRangeSelect: select, onTimeRangeReset: resetNow });
1435
+ nextTask();
1436
+
1437
+ expect(resetNow).toHaveBeenCalledTimes(1);
1438
+ expect(resetAtThePress).not.toHaveBeenCalled();
1439
+ });
1440
+
1441
+ test("a dblclick handler taken before a re-render runs the reset handed in last", () => {
1442
+ const select: MockFunction = getJestMockFunction();
1443
+ const resetBefore: MockFunction = getJestMockFunction();
1444
+ const resetNow: MockFunction = getJestMockFunction();
1445
+ const chart: ChartHarness = renderChart({
1446
+ onTimeRangeSelect: select,
1447
+ onTimeRangeReset: resetBefore,
1448
+ });
1449
+ const onDoubleClick: ChartRangeSelectionRootProps["onDoubleClick"] =
1450
+ chart.result.current.chartEventProps.onDoubleClick;
1451
+ expect(typeof onDoubleClick).toBe("function");
1452
+
1453
+ chart.rerender({ onTimeRangeSelect: select, onTimeRangeReset: resetNow });
1454
+ act(() => {
1455
+ onDoubleClick?.();
1456
+ });
1457
+
1458
+ expect(resetNow).toHaveBeenCalledTimes(1);
1459
+ expect(resetBefore).not.toHaveBeenCalled();
1460
+ });
1461
+
1462
+ test("a reset withdrawn before the next task - the zoom already undone - runs nothing", () => {
1463
+ const select: MockFunction = getJestMockFunction();
1464
+ const reset: MockFunction = getJestMockFunction();
1465
+ const chart: ChartHarness = renderChart({
1466
+ onTimeRangeSelect: select,
1467
+ onTimeRangeReset: reset,
1468
+ });
1469
+
1470
+ click(chart, at(4), 1);
1471
+ click(chart, at(4), 2);
1472
+ chart.rerender({ onTimeRangeSelect: select });
1473
+ allTimers();
1474
+
1475
+ expect(reset).not.toHaveBeenCalled();
1476
+ expect(select).not.toHaveBeenCalled();
1477
+ expect(keysOf(chart.result.current.chartEventProps)).not.toContain(
1478
+ "onDoubleClick",
1479
+ );
1480
+ });
1481
+ });