From 4dc3bbff99936da202665f6c6ff4ffdc0db65223 Mon Sep 17 00:00:00 2001 From: 5unnyWind Date: Tue, 10 Jun 2025 16:39:04 +0800 Subject: [PATCH] feat: navigate support data: protocol --- packages/wouter/src/use-hash-location.js | 23 +++++++++++----- .../wouter/test/use-hash-location.test.tsx | 27 +++++++++++++++++++ 2 files changed, 43 insertions(+), 7 deletions(-) diff --git a/packages/wouter/src/use-hash-location.js b/packages/wouter/src/use-hash-location.js index 65fe6c1..a91dcdc 100644 --- a/packages/wouter/src/use-hash-location.js +++ b/packages/wouter/src/use-hash-location.js @@ -23,17 +23,26 @@ const subscribeToHashUpdates = (callback) => { const currentHashLocation = () => "/" + location.hash.replace(/^#?\/?/, ""); export const navigate = (to, { state = null, replace = false } = {}) => { - const [hash, search] = to.replace(/^#?\/?/, "").split("?"); - - const newRelativePath = - location.pathname + (search ? `?${search}` : location.search) + `#/${hash}`; const oldURL = location.href; - const newURL = new URL(newRelativePath, location.origin).href; + let newURL, newHistoryStatePath; + + if (location.protocol === "data:") { + const newHash = "#/" + to.replace(/^#?\/?/, ""); + newHistoryStatePath = newHash; + newURL = oldURL.split("#")[0] + newHash; + } else { + const [hash, search] = to.replace(/^#?\/?/, "").split("?"); + newHistoryStatePath = + location.pathname + + (search ? `?${search}` : location.search) + + `#/${hash}`; + newURL = new URL(newHistoryStatePath, location.origin).href; + } if (replace) { - history.replaceState(state, "", newRelativePath); + history.replaceState(state, "", newHistoryStatePath); } else { - history.pushState(state, "", newRelativePath); + history.pushState(state, "", newHistoryStatePath); } const event = diff --git a/packages/wouter/test/use-hash-location.test.tsx b/packages/wouter/test/use-hash-location.test.tsx index c5b1bf4..cbc780a 100644 --- a/packages/wouter/test/use-hash-location.test.tsx +++ b/packages/wouter/test/use-hash-location.test.tsx @@ -209,6 +209,33 @@ it("defines a custom way of rendering link hrefs", () => { expect(getByTestId("link")).toHaveAttribute("href", "#/app"); }); +it("handles navigation with data: protocol", async () => { + const originalHref = location.href; + location.href = "data:text/html,content"; + + expect(location.protocol).toBe("data:"); + + const { result } = renderHook(() => useHashLocation()); + const [, navigate] = result.current; + const initialHistoryLength = history.length; + + await waitForHashChangeEvent(() => { + navigate("/new-path"); + }); + + expect(location.hash).toBe("#/new-path"); + expect(history.length).toBe(initialHistoryLength + 1); + + await waitForHashChangeEvent(() => { + navigate("/another-path", { replace: true }); + }); + + expect(location.hash).toBe("#/another-path"); + expect(history.length).toBe(initialHistoryLength + 1); + + location.href = originalHref; +}); + it("interacts properly with the history stack", () => { const { result } = renderHook(() => useHashLocation()); const [, navigate] = result.current;