From a904e4dfb8e55b7bcaf1e6bd106f11dfde490185 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20Qu=C3=A8ze?= Date: Thu, 7 Aug 2025 23:44:46 +0200 Subject: [PATCH] Show the vertical ruler in the timeline when hovering the network chart When hovering over the network chart, display a vertical ruler in the timeline to match the behavior of marker chart and stack chart. This makes it easier to correlate slow network request handling with high CPU use for other reasons in the timeline. --- src/components/network-chart/index.js | 40 ++++++++++++++++++++++++ src/test/components/NetworkChart.test.js | 34 ++++++++++++++++++++ 2 files changed, 74 insertions(+) diff --git a/src/components/network-chart/index.js b/src/components/network-chart/index.js index 132bfd065e..2d0b476cdc 100644 --- a/src/components/network-chart/index.js +++ b/src/components/network-chart/index.js @@ -14,6 +14,10 @@ import { withSize } from '../shared/WithSize'; import { NetworkChartEmptyReasons } from './NetworkChartEmptyReasons'; import { NetworkChartRow } from './NetworkChartRow'; import { ContextMenuTrigger } from '../shared/ContextMenuTrigger'; +import { + TIMELINE_MARGIN_LEFT, + TIMELINE_MARGIN_RIGHT, +} from '../../app-logic/constants'; import { getScrollToSelectionGeneration, @@ -26,6 +30,7 @@ import { changeSelectedNetworkMarker, changeRightClickedMarker, changeHoveredMarker, + changeMouseTimePosition, } from '../../actions/profile-view'; import type { SizeProps } from '../shared/WithSize'; import type { @@ -49,6 +54,7 @@ type DispatchProps = {| +changeSelectedNetworkMarker: typeof changeSelectedNetworkMarker, +changeRightClickedMarker: typeof changeRightClickedMarker, +changeHoveredMarker: typeof changeHoveredMarker, + +changeMouseTimePosition: typeof changeMouseTimePosition, |}; type StateProps = {| @@ -259,6 +265,37 @@ class NetworkChartImpl extends React.PureComponent { changeHoveredMarker(threadsKey, null); }; + _onMouseMove = (event: SyntheticMouseEvent) => { + const { timeRange, width, changeMouseTimePosition } = this.props; + + // Calculate the mouse position relative to the chart area + if (!event.currentTarget) { + return; + } + const rect = event.currentTarget.getBoundingClientRect(); + const mouseX = event.clientX - rect.left; + + // Account for timeline margins (similar to marker chart logic) + const chartWidth = width - TIMELINE_MARGIN_LEFT - TIMELINE_MARGIN_RIGHT; + const adjustedMouseX = mouseX - TIMELINE_MARGIN_LEFT; + + // Calculate the time position + const { start: rangeStart, end: rangeEnd } = timeRange; + const rangeLength = rangeEnd - rangeStart; + const xInUnitInterval = adjustedMouseX / chartWidth; + + if (xInUnitInterval < 0 || xInUnitInterval > 1) { + changeMouseTimePosition(null); + } else { + const xInTime = rangeStart + xInUnitInterval * rangeLength; + changeMouseTimePosition(xInTime); + } + }; + + _onMouseLeave = () => { + this.props.changeMouseTimePosition(null); + }; + _shouldDisplayTooltips = () => this.props.rightClickedMarkerIndex === null; _renderRow = (markerIndex: MarkerIndex, index: number): React.Node => { @@ -327,6 +364,8 @@ class NetworkChartImpl extends React.PureComponent { id="network-chart-tab" role="tabpanel" aria-labelledby="network-chart-tab-button" + onMouseMove={this._onMouseMove} + onMouseLeave={this._onMouseLeave} > {markerIndexes.length === 0 ? ( @@ -394,6 +433,7 @@ export const NetworkChart = explicitConnect< changeSelectedNetworkMarker, changeRightClickedMarker, changeHoveredMarker, + changeMouseTimePosition, }, component: withSize(NetworkChartImpl), }); diff --git a/src/test/components/NetworkChart.test.js b/src/test/components/NetworkChart.test.js index 533ebba7bb..cc048eb813 100644 --- a/src/test/components/NetworkChart.test.js +++ b/src/test/components/NetworkChart.test.js @@ -777,4 +777,38 @@ describe('calltree/ProfileCallTreeView navigation keys', () => { initialScrollGeneration ); }); + + it('changes the mouse time position when the mouse moves', function () { + const { getState, container } = setupWithPayload(getNetworkMarkers()); + + // Expect the mouseTimePosition to not be set at the beginning of the test. + expect(getState().profileView.viewOptions.mouseTimePosition).toBeNull(); + + const networkChart = ensureExists( + container.querySelector('.networkChart'), + 'Could not find the network chart element' + ); + + // Move the mouse over the network chart, ensure mouseTimePosition is set. + fireEvent.mouseMove(networkChart, { + clientX: TIMELINE_MARGIN_LEFT + 100, // Position within the chart area + clientY: 100, + }); + const mouseTimePosition = + getState().profileView.viewOptions.mouseTimePosition; + expect(typeof mouseTimePosition).toEqual('number'); + + // Move the mouse to a different position, ensure mouseTimePosition changed. + fireEvent.mouseMove(networkChart, { + clientX: TIMELINE_MARGIN_LEFT + 150, // Different position within chart area + clientY: 100, + }); + expect(getState().profileView.viewOptions.mouseTimePosition).not.toEqual( + mouseTimePosition + ); + + // Move the mouse out of the network chart, ensure mouseTimePosition is no longer set. + fireEvent.mouseLeave(networkChart); + expect(getState().profileView.viewOptions.mouseTimePosition).toBeNull(); + }); });