diff --git a/CHANGELOG.md b/CHANGELOG.md index d323e14..c7b1958 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 0000000..fb76ba3 --- /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 d47be6c..757119e 100644 --- a/src/components/JobStateFilters.tsx +++ b/src/components/JobStateFilters.tsx @@ -34,6 +34,10 @@ export const JobStateFilters: ( return (