From d10e7a2055cd5241d1d4a26dbba479a9e0a576f4 Mon Sep 17 00:00:00 2001 From: Blake Gentry Date: Wed, 4 Mar 2026 08:20:25 -0600 Subject: [PATCH] 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 --- CHANGELOG.md | 1 + src/components/JobStateFilters.test.tsx | 67 +++++++++++++++++++++++++ src/components/JobStateFilters.tsx | 4 ++ 3 files changed, 72 insertions(+) create mode 100644 src/components/JobStateFilters.test.tsx diff --git a/CHANGELOG.md b/CHANGELOG.md index d323e14f..c7b19587 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - 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). - 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). - JSON viewer: sort keys alphabetically in rendered and copied output for object payloads. [PR #525](https://github.com/riverqueue/riverui/pull/525). +- 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 #527](https://github.com/riverqueue/riverui/pull/527). ## [v0.15.0] - 2026-02-26 diff --git a/src/components/JobStateFilters.test.tsx b/src/components/JobStateFilters.test.tsx new file mode 100644 index 00000000..fb76ba35 --- /dev/null +++ b/src/components/JobStateFilters.test.tsx @@ -0,0 +1,67 @@ +import { JobState } from "@services/types"; +import { + createMemoryHistory, + createRootRoute, + createRoute, + createRouter, + Outlet, + RouterProvider, + stripSearchParams, +} from "@tanstack/react-router"; +import { render, screen } from "@testing-library/react"; +import { describe, expect, test } from "vitest"; + +import { defaultValues, jobSearchSchema } from "../routes/jobs/index.schema"; +import { JobStateFilters } from "./JobStateFilters"; + +const rootRoute = createRootRoute({ + component: () => , +}); + +const jobsRoute = createRoute({ + component: () => , + getParentRoute: () => rootRoute, + path: "/jobs", + search: { + middlewares: [stripSearchParams(defaultValues)], + }, + validateSearch: jobSearchSchema, +}); + +const routeTree = rootRoute.addChildren([jobsRoute]); + +const renderWithLocation = async (location: string) => { + const history = createMemoryHistory({ + initialEntries: [location], + }); + + const router = createRouter({ + history, + routeTree, + }); + + await router.load(); + + return render(); +}; + +describe("JobStateFilters", () => { + test("only the selected state link is active", async () => { + await renderWithLocation(`/jobs?state=${JobState.Discarded}`); + + const discardedLink = await screen.findByRole("link", { + name: "Discarded", + }); + const runningLink = screen.getByRole("link", { name: "Running" }); + + expect(discardedLink).toHaveAttribute("data-status", "active"); + expect(runningLink).not.toHaveAttribute("data-status", "active"); + }); + + test("running is active when no state is explicitly selected", async () => { + await renderWithLocation("/jobs"); + + const runningLink = await screen.findByRole("link", { name: "Running" }); + expect(runningLink).toHaveAttribute("data-status", "active"); + }); +}); diff --git a/src/components/JobStateFilters.tsx b/src/components/JobStateFilters.tsx index d47be6c6..757119ed 100644 --- a/src/components/JobStateFilters.tsx +++ b/src/components/JobStateFilters.tsx @@ -34,6 +34,10 @@ export const JobStateFilters: ( return (