diff --git a/ui/src/context/FileViewerContext.test.ts b/ui/src/context/FileViewerContext.test.ts index 5388497bcc5..6a6ff198674 100644 --- a/ui/src/context/FileViewerContext.test.ts +++ b/ui/src/context/FileViewerContext.test.ts @@ -1,10 +1,11 @@ // @vitest-environment node -import { describe, expect, it } from "vitest"; +import { afterEach, describe, expect, it } from "vitest"; import { FILE_VIEWER_NAVIGATE_OPTIONS, readBrowseStateFromSearch, readFileViewerStateFromSearch, + shouldNavigateFileViewerSearch, writeBrowseStateToSearch, writeFolderViewerStateToSearch, writeFileViewerStateToSearch, @@ -17,6 +18,35 @@ describe("FILE_VIEWER_NAVIGATE_OPTIONS", () => { }); }); +describe("shouldNavigateFileViewerSearch", () => { + const originalWindow = globalThis.window; + + afterEach(() => { + Object.defineProperty(globalThis, "window", { + configurable: true, + value: originalWindow, + }); + }); + + it("uses the browser URL search when router state is stale", () => { + Object.defineProperty(globalThis, "window", { + configurable: true, + value: { location: { search: "?browse=1" } }, + }); + + expect(shouldNavigateFileViewerSearch("", "")).toBe(true); + }); + + it("keeps no-op navigation suppressed when the browser URL already matches", () => { + Object.defineProperty(globalThis, "window", { + configurable: true, + value: { location: { search: "" } }, + }); + + expect(shouldNavigateFileViewerSearch("", "?browse=1")).toBe(false); + }); +}); + describe("readFileViewerStateFromSearch", () => { it("returns null when no file param is present", () => { expect(readFileViewerStateFromSearch("")).toBeNull(); diff --git a/ui/src/context/FileViewerContext.tsx b/ui/src/context/FileViewerContext.tsx index 77ce2dac343..062b9959b71 100644 --- a/ui/src/context/FileViewerContext.tsx +++ b/ui/src/context/FileViewerContext.tsx @@ -53,6 +53,15 @@ export const FILE_VIEWER_NAVIGATE_OPTIONS = { preventScrollReset: true, } satisfies NavigateOptions; +export function getCurrentFileViewerSearch(fallbackSearch: string): string { + if (typeof window === "undefined") return fallbackSearch; + return window.location.search; +} + +export function shouldNavigateFileViewerSearch(nextSearch: string, fallbackSearch: string): boolean { + return nextSearch !== getCurrentFileViewerSearch(fallbackSearch); +} + export function readFileViewerStateFromSearch(search: string): FileViewerUrlState | null { const params = new URLSearchParams(search); const path = params.get("file"); @@ -198,7 +207,7 @@ function EnabledFileViewerProvider({ issueId, children }: Omit) => { - if (nextSearch === location.search) return; + if (!shouldNavigateFileViewerSearch(nextSearch, location.search)) return; navigate( { pathname: location.pathname, hash: location.hash, search: nextSearch }, { ...FILE_VIEWER_NAVIGATE_OPTIONS, ...opts, state: location.state }, @@ -285,9 +294,7 @@ function EnabledFileViewerProvider({ issueId, children }: Omit { - const currentSearch = typeof window === "undefined" - ? location.search - : (window.location.search || location.search); + const currentSearch = getCurrentFileViewerSearch(location.search); const params = new URLSearchParams(writeFileViewerStateToSearch(currentSearch, null).replace(/^\?/, "")); params.delete("browse"); params.delete("q");