Skip to content

Commit 1df1c20

Browse files
committed
fix jobs state filter active matching
The jobs state sidebar could show `Running` as active even when another state was selected. This happened because the jobs route strips the default `state=running` from search params, so the running link was matching too broadly. The filter links now require exact + search-aware active matching and preserve non-state search params while setting `state`. This makes the active indicator follow the selected job state correctly. A regression test uses the same jobs search middleware behavior and asserts that only the selected state is active while `Running` is active for the default `/jobs` state. Fixes #526
1 parent d8bf160 commit 1df1c20

3 files changed

Lines changed: 73 additions & 1 deletion

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1313
- Workflow detail: improve default workflow diagram framing for legibility while still allowing manual zoom-out to view the full graph. [PR #524](https://github.com/riverqueue/riverui/pull/524).
1414
- Workflow detail: truncate long workflow names in the header to prevent overflow and add a copy button for the full name. [PR #524](https://github.com/riverqueue/riverui/pull/524).
1515
- JSON viewer: sort keys alphabetically in rendered and copied output for object payloads. [PR #525](https://github.com/riverqueue/riverui/pull/525).
16+
- Job state sidebar: only highlight `Running` when the selected jobs state is actually running, even with retained search filters in the URL. [Fixes #526](https://github.com/riverqueue/riverui/issues/526). [PR #XXX](https://github.com/riverqueue/riverui/pull/XXX).
1617

1718
## [v0.15.0] - 2026-02-26
1819

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import { JobState } from "@services/types";
2+
import {
3+
createMemoryHistory,
4+
createRootRoute,
5+
createRoute,
6+
createRouter,
7+
Outlet,
8+
RouterProvider,
9+
stripSearchParams,
10+
} from "@tanstack/react-router";
11+
import { render, screen } from "@testing-library/react";
12+
import { describe, expect, test } from "vitest";
13+
14+
import { defaultValues, jobSearchSchema } from "../routes/jobs/index.schema";
15+
import { JobStateFilters } from "./JobStateFilters";
16+
17+
const rootRoute = createRootRoute({
18+
component: () => <Outlet />,
19+
});
20+
21+
const jobsRoute = createRoute({
22+
component: () => <JobStateFilters />,
23+
getParentRoute: () => rootRoute,
24+
path: "/jobs",
25+
search: {
26+
middlewares: [stripSearchParams(defaultValues)],
27+
},
28+
validateSearch: jobSearchSchema,
29+
});
30+
31+
const routeTree = rootRoute.addChildren([jobsRoute]);
32+
33+
const renderWithLocation = async (location: string) => {
34+
const history = createMemoryHistory({
35+
initialEntries: [location],
36+
});
37+
38+
const router = createRouter({
39+
history,
40+
routeTree,
41+
});
42+
43+
await router.load();
44+
45+
return render(<RouterProvider router={router} />);
46+
};
47+
48+
describe("JobStateFilters", () => {
49+
test("only the selected state link is active", async () => {
50+
await renderWithLocation(`/jobs?state=${JobState.Discarded}`);
51+
52+
const discardedLink = await screen.findByRole("link", {
53+
name: "Discarded",
54+
});
55+
const runningLink = screen.getByRole("link", { name: "Running" });
56+
57+
expect(discardedLink).toHaveAttribute("data-status", "active");
58+
expect(runningLink).not.toHaveAttribute("data-status", "active");
59+
});
60+
61+
test("running is active when no state is explicitly selected", async () => {
62+
await renderWithLocation("/jobs");
63+
64+
const runningLink = await screen.findByRole("link", { name: "Running" });
65+
expect(runningLink).toHaveAttribute("data-status", "active");
66+
});
67+
});

src/components/JobStateFilters.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,10 @@ export const JobStateFilters: (
3434
return (
3535
<li key={item.name}>
3636
<Link
37+
activeOptions={{
38+
exact: true,
39+
includeSearch: true,
40+
}}
3741
activeProps={{
3842
className:
3943
"bg-gray-50 dark:bg-gray-800 text-indigo-600 dark:text-slate-100",
@@ -44,7 +48,7 @@ export const JobStateFilters: (
4448
"text-gray-700 dark:text-slate-400 hover:text-indigo-600 dark:hover:text-white hover:bg-gray-50 dark:hover:bg-gray-800",
4549
}}
4650
params={{}}
47-
search={{ state: item.state }}
51+
search={(old) => ({ ...old, state: item.state })}
4852
to="/jobs"
4953
>
5054
{item.name}

0 commit comments

Comments
 (0)