diff --git a/packages/wouter/src/memory-location.js b/packages/wouter/src/memory-location.js index 8e212ef..d018ac4 100644 --- a/packages/wouter/src/memory-location.js +++ b/packages/wouter/src/memory-location.js @@ -7,10 +7,12 @@ import { useSyncExternalStore } from "./react-deps.js"; export const memoryLocation = ({ path = "/", + searchPath = "", static: staticLocation, record, } = {}) => { - let currentPath = path; + let currentPath = path + (searchPath && (path.split("?")[1] ? "&" : "?")) + searchPath; + let currentSearch = currentPath.split("?")[1] || ""; const history = [currentPath]; const emitter = mitt(); @@ -24,6 +26,7 @@ export const memoryLocation = ({ } currentPath = path; + currentSearch = path.split("?")[1] || ""; emitter.emit("navigate", path); }; @@ -39,15 +42,20 @@ export const memoryLocation = ({ navigate, ]; + const useMemoryQuery = () => [ + useSyncExternalStore(subscribe, () => currentSearch) + ]; + function reset() { // clean history array with mutation to preserve link history.splice(0, history.length); - navigateImplementation(path); + navigateImplementation(path + (searchPath && (path.split("?")[1] ? "&" : "?")) + searchPath); } return { hook: useMemoryLocation, + searchHook: useMemoryQuery, navigate, history: record ? history : undefined, reset: record ? reset : undefined, diff --git a/packages/wouter/test/memory-location.test.ts b/packages/wouter/test/memory-location.test.ts index 5b20a99..7ae3c00 100644 --- a/packages/wouter/test/memory-location.test.ts +++ b/packages/wouter/test/memory-location.test.ts @@ -23,6 +23,17 @@ it("should support initial path", () => { unmount(); }); +it("should support initial path with query", () => { + const { searchHook } = memoryLocation({ path: "/test-case?foo=bar" }); + // const { searchHook } = memoryLocation({ path: "/test-case", searchPath: "foo=bar" }); + + const { result, unmount } = renderHook(() => searchHook()); + const [value] = result.current; + + expect(value).toBe("foo=bar"); + unmount(); +}); + it('should return location hook that has initial path "/" by default', () => { const { hook } = memoryLocation(); @@ -33,6 +44,16 @@ it('should return location hook that has initial path "/" by default', () => { unmount(); }); +it('should return search hook that has initial query "" by default', () => { + const { searchHook } = memoryLocation(); + + const { result, unmount } = renderHook(() => searchHook()); + const [value] = result.current; + + expect(value).toBe(""); + unmount(); +}); + it("should return standalone `navigate` method", () => { const { hook, navigate } = memoryLocation(); diff --git a/packages/wouter/test/use-search.test.tsx b/packages/wouter/test/use-search.test.tsx index ad8fe4d..43e7823 100644 --- a/packages/wouter/test/use-search.test.tsx +++ b/packages/wouter/test/use-search.test.tsx @@ -1,6 +1,7 @@ import { renderHook, act } from "@testing-library/react"; import { useSearch, Router } from "wouter"; import { navigate } from "wouter/use-browser-location"; +import { memoryLocation } from "wouter/memory-location"; import { it, expect, beforeEach } from "vitest"; beforeEach(() => history.replaceState(null, "", "/")); @@ -24,6 +25,18 @@ it("can be customized in the Router", () => { expect(result.current).toEqual("none"); }); +it("can be customized with memoryLocation", () => { + const { searchHook } = memoryLocation({ path: "/foo?key=value" }); + + const { result } = renderHook(() => useSearch(), { + wrapper: (props) => { + return {props.children}; + }, + }); + + expect(result.current).toEqual("key=value"); +}); + it("unescapes search string", () => { const { result: searchResult } = renderHook(() => useSearch()); diff --git a/packages/wouter/types/memory-location.d.ts b/packages/wouter/types/memory-location.d.ts index 5563903..665e131 100644 --- a/packages/wouter/types/memory-location.d.ts +++ b/packages/wouter/types/memory-location.d.ts @@ -1,20 +1,22 @@ -import { BaseLocationHook, Path } from "./location-hook.js"; +import { BaseLocationHook, BaseSearchHook, Path, SearchString } from "./location-hook.js"; type Navigate = ( to: Path, options?: { replace?: boolean; state?: S } ) => void; -type HookReturnValue = { hook: BaseLocationHook; navigate: Navigate }; +type HookReturnValue = { hook: BaseLocationHook; searchHook: BaseSearchHook, navigate: Navigate }; type StubHistory = { history: Path[]; reset: () => void }; export function memoryLocation(options?: { path?: Path; + searchPath?: SearchString; static?: boolean; record?: false; }): HookReturnValue; export function memoryLocation(options?: { path?: Path; + searchPath?: SearchString; static?: boolean; record: true; }): HookReturnValue & StubHistory;