mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-10-02 20:29:43 +00:00
Preserve History pointer inspection during matching live refreshes
Reset pointer state only when the selection changes, not on supplied sample updates. Add a mounted pointer-refresh regression and recheck delayed-target browser states. Change-source: pulse-maintainer
This commit is contained in:
parent
93531c9cd3
commit
1177e4d143
4 changed files with 47 additions and 6 deletions
|
|
@ -7675,7 +7675,8 @@ metric, range, sampling cap, supplied-data mode or access state changes. Late
|
|||
successes and failures cannot replace the current selection's samples, loading
|
||||
or error state, even if cancellation is ignored. Selection changes clear old
|
||||
readings and hover state while loading; current initial failures remain visible.
|
||||
Matching background refresh failures retain already loaded samples. Polls never
|
||||
Matching supplied-sample refreshes retain pointer inspection; changing the selection
|
||||
clears it. Matching background refresh failures retain already loaded samples. Polls never
|
||||
overlap and stop for supplied data (including empty arrays), unavailable targets,
|
||||
locked ranges and unmount.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
{
|
||||
"version": 1,
|
||||
"base_sha": "9dc6bd0ffe4de520bf9afc43ad10fb169026325d",
|
||||
"verified_at": "2026-10-02T14:15:24.462877Z",
|
||||
"base_sha": "93531c9cd3bfc9278633476ba26a1c77e66bc802",
|
||||
"verified_at": "2026-10-02T14:23:47.145834Z",
|
||||
"result": "passed",
|
||||
"changed_paths": [
|
||||
"frontend-modern/src/components/shared/useHistoryChartState.ts"
|
||||
],
|
||||
"content_sha256": {
|
||||
"frontend-modern/src/components/shared/useHistoryChartState.ts": "ff9dff0db01ae3141b3a130477cb8128ea4da8ea4e93f23c360278f6a47b152a"
|
||||
"frontend-modern/src/components/shared/useHistoryChartState.ts": "f10b5cfc7cb296c60ad7e87bf7c3001c4308d29af702b8c7a7e77e7c4c4c3fb6"
|
||||
},
|
||||
"routes": [
|
||||
"/browser-tests/history-selection.html"
|
||||
|
|
|
|||
|
|
@ -233,6 +233,32 @@ describe('HistoryChart', () => {
|
|||
rectSpy.mockRestore();
|
||||
});
|
||||
|
||||
it('keeps pointer inspection when matching supplied samples refresh', () => {
|
||||
const rectSpy = vi.spyOn(HTMLCanvasElement.prototype, 'getBoundingClientRect').mockReturnValue({
|
||||
x: 0,
|
||||
y: 0,
|
||||
left: 0,
|
||||
top: 0,
|
||||
right: 400,
|
||||
bottom: 120,
|
||||
width: 400,
|
||||
height: 120,
|
||||
toJSON: () => ({}),
|
||||
});
|
||||
const samples = (value: number) =>
|
||||
[1000, 2000, 3000].map((timestamp) => ({ timestamp, value, min: value, max: value }));
|
||||
const [data, setData] = createSignal(samples(10));
|
||||
const { container } = render(() => (
|
||||
<HistoryChart resourceType="agent" resourceId="a" metric="cpu" data={data()} />
|
||||
));
|
||||
fireEvent.mouseMove(container.querySelector('canvas')!, { clientX: 220 });
|
||||
expect(container.querySelector('[data-history-chart-tooltip="true"]')).not.toBeNull();
|
||||
setData(samples(20));
|
||||
expect(container.querySelector('[data-history-chart-tooltip="true"]')).not.toBeNull();
|
||||
expect(container.querySelector('[data-history-chart-tooltip="true"]')).toHaveTextContent('20');
|
||||
rectSpy.mockRestore();
|
||||
});
|
||||
|
||||
it('exposes the sub-day and Relay history ranges as first-class chart options', () => {
|
||||
expect(HISTORY_CHART_RANGES).toEqual(['1h', '6h', '12h', '24h', '7d', '14d', '30d', '90d']);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -90,6 +90,8 @@ export function useHistoryChartState(
|
|||
const dataMin = createMemo(() => getHistoryChartDataMin(data()));
|
||||
const dataMax = createMemo(() => getHistoryChartDataMax(data()));
|
||||
|
||||
let previousSelection: string | undefined;
|
||||
|
||||
// One effect owns a selection, its request and its polling timer. Cleanup
|
||||
// invalidates completions even when a transport ignores cancellation.
|
||||
createEffect(() => {
|
||||
|
|
@ -117,8 +119,20 @@ export function useHistoryChartState(
|
|||
setSource(suppliedData !== undefined ? 'live' : null);
|
||||
setError(null);
|
||||
setLoading(false);
|
||||
setHoveredPoint(null);
|
||||
setHoveredTimestamp(null);
|
||||
const selection = JSON.stringify([
|
||||
resourceType,
|
||||
resourceId,
|
||||
metric,
|
||||
chartRange,
|
||||
pointsCap,
|
||||
locked,
|
||||
suppliedData !== undefined,
|
||||
]);
|
||||
if (selection !== previousSelection) {
|
||||
setHoveredPoint(null);
|
||||
setHoveredTimestamp(null);
|
||||
}
|
||||
previousSelection = selection;
|
||||
if (suppliedData !== undefined || locked || !resourceId || !resourceType) return;
|
||||
|
||||
const loadData = async () => {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue