From 76c9f388a8ba0c6c50e07451de2fe25cef3d5889 Mon Sep 17 00:00:00 2001 From: Lukas Date: Sun, 19 Apr 2026 19:36:44 +0200 Subject: [PATCH] Fix HistoryPage stale appliedQuery effect and add mount query regression test --- Docs/Tasks.md | 2 +- frontend/src/pages/HistoryPage.tsx | 15 ++++++++++----- .../src/pages/__tests__/HistoryPage.test.tsx | 16 +++++++++++++++- 3 files changed, 26 insertions(+), 7 deletions(-) diff --git a/Docs/Tasks.md b/Docs/Tasks.md index 2e62b69..76ef538 100644 --- a/Docs/Tasks.md +++ b/Docs/Tasks.md @@ -37,7 +37,7 @@ Issues are grouped by category and ordered roughly by severity. Each entry descr --- -### TASK-002 — `HistoryPage` filter effect has a stale `appliedQuery` dependency +### TASK-002 — `HistoryPage` filter effect has a stale `appliedQuery` dependency (done) **Where found:** `frontend/src/pages/HistoryPage.tsx` lines 214–230. The `useEffect` lists `appliedQuery` as a dependency, reads it inside the effect, and then calls `setAppliedQuery` — which triggers the same effect again immediately. diff --git a/frontend/src/pages/HistoryPage.tsx b/frontend/src/pages/HistoryPage.tsx index 01b56ae..e432a6b 100644 --- a/frontend/src/pages/HistoryPage.tsx +++ b/frontend/src/pages/HistoryPage.tsx @@ -6,7 +6,7 @@ * Rows with repeatedly-banned IPs are highlighted in amber. */ -import { useEffect, useState } from "react"; +import { useEffect, useRef, useState } from "react"; import { Badge, Button, @@ -199,10 +199,14 @@ export function HistoryPage(): React.JSX.Element { const [originFilter, setOriginFilter] = useState("all"); const [jailFilter, setJailFilter] = useState(""); const [ipFilter, setIpFilter] = useState(""); - const [appliedQuery, setAppliedQuery] = useState({ + const defaultQuery: HistoryQuery = { + range: "7d", source: "archive", page_size: PAGE_SIZE, - }); + page: 1, + }; + const [appliedQuery, setAppliedQuery] = useState(defaultQuery); + const appliedQueryRef = useRef(defaultQuery); // Per-IP detail navigation const [selectedIp, setSelectedIp] = useState(null); @@ -221,13 +225,14 @@ export function HistoryPage(): React.JSX.Element { page_size: PAGE_SIZE, }; - if (areHistoryQueriesEqual(nextQuery, appliedQuery)) { + if (areHistoryQueriesEqual(nextQuery, appliedQueryRef.current)) { return; } setPage(1); setAppliedQuery(nextQuery); - }, [range, originFilter, jailFilter, ipFilter, setPage, appliedQuery]); + appliedQueryRef.current = nextQuery; + }, [range, originFilter, jailFilter, ipFilter, setPage]); const totalPages = Math.max(1, Math.ceil(total / PAGE_SIZE)); diff --git a/frontend/src/pages/__tests__/HistoryPage.test.tsx b/frontend/src/pages/__tests__/HistoryPage.test.tsx index 87063a9..a32e302 100644 --- a/frontend/src/pages/__tests__/HistoryPage.test.tsx +++ b/frontend/src/pages/__tests__/HistoryPage.test.tsx @@ -28,7 +28,7 @@ vi.mock("../../components/WorldMap", () => ({ })); vi.mock("../../api/config", () => ({ - fetchMapColorThresholds: async () => ({ + fetchMapColorThresholds: () => ({ threshold_low: 10, threshold_medium: 50, threshold_high: 100, @@ -74,4 +74,18 @@ describe("HistoryPage", () => { expect(lastQuery).toMatchObject({ origin: "blocklist" }); }); }); + + it("never calls history without the default range on mount", async () => { + render( + + + , + ); + + await waitFor(() => { + expect(lastQuery?.range).toBe("7d"); + }); + + expect(mockUseHistory.mock.calls.every((call) => call[0].range === "7d")).toBe(true); + }); });