Skip to content

Commit 8efa9b6

Browse files
committed
refactor(core): rename search params hook to useCollectionURLPersistence
- rename useSearchParamsPersistence to useCollectionURLPersistence - rename hook files to use-collection-url-persistence - update exports, tests, and nextjs example usage - keep API unreleased so no deprecation alias
1 parent ca2a603 commit 8efa9b6

4 files changed

Lines changed: 109 additions & 98 deletions

File tree

examples/nextjs-app/src/modules/pages/data-table-demo.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ import {
66
createColumnHelper,
77
Layout,
88
type RowAction,
9-
useSearchParamsPersistence,
9+
type TableFieldName,
10+
useCollectionURLPersistence,
1011
} from "@tailor-platform/app-shell";
1112
import { useState } from "react";
1213
import { type Product, useProductsQuery } from "./mock-data";
@@ -141,7 +142,9 @@ const productRowActions: RowAction<Product>[] = [
141142
// ---------------------------------------------------------------------------
142143

143144
const DataTableDemoPage = () => {
144-
const persistence = useSearchParamsPersistence({ prefix: "dt1" });
145+
const persistence = useCollectionURLPersistence<TableFieldName<typeof productMetadata>>({
146+
prefix: "dt1",
147+
});
145148
const { variables, control } = useCollectionVariables({
146149
params: { pageSize: 5 },
147150
tableMetadata: productMetadata,

packages/core/src/hooks/use-search-params-persistence.test.tsx renamed to packages/core/src/hooks/use-collection-url-persistence.test.tsx

Lines changed: 41 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,61 +1,72 @@
11
import { renderHook, act } from "@testing-library/react";
2+
import type { ReactNode } from "react";
23
import { describe, it, expect, vi, beforeEach } from "vitest";
3-
import { useSearchParamsPersistence } from "./use-search-params-persistence";
4+
import { MemoryRouter } from "react-router";
5+
import { useCollectionURLPersistence } from "./use-collection-url-persistence";
46

57
beforeEach(() => {
6-
// Reset URL to root before each test
7-
window.history.replaceState(null, "", "/");
8+
vi.useRealTimers();
89
});
910

11+
function createWrapper(initialEntry: string = "/") {
12+
return function Wrapper({ children }: { children: ReactNode }) {
13+
return <MemoryRouter initialEntries={[initialEntry]}>{children}</MemoryRouter>;
14+
};
15+
}
16+
17+
function renderPersistence(
18+
options?: Parameters<typeof useCollectionURLPersistence>[0],
19+
initialEntry: string = "/",
20+
) {
21+
return renderHook(() => useCollectionURLPersistence(options), {
22+
wrapper: createWrapper(initialEntry),
23+
});
24+
}
25+
1026
/** Helper: read current snapshot */
11-
function readSnapshot(result: { current: ReturnType<typeof useSearchParamsPersistence> }) {
27+
function readSnapshot(result: { current: ReturnType<typeof useCollectionURLPersistence> }) {
1228
return result.current.read();
1329
}
1430

15-
describe("useSearchParamsPersistence", () => {
31+
describe("useCollectionURLPersistence", () => {
1632
// ---------------------------------------------------------------------------
1733
// read (initial)
1834
// ---------------------------------------------------------------------------
1935
describe("read (initial)", () => {
2036
it("returns undefined when no search params exist", () => {
21-
const { result } = renderHook(() => useSearchParamsPersistence());
37+
const { result } = renderPersistence();
2238

2339
expect(readSnapshot(result)).toBeUndefined();
2440
});
2541

2642
it("returns pageSize from URL", () => {
27-
window.history.replaceState(null, "", "/?p=50");
28-
const { result } = renderHook(() => useSearchParamsPersistence());
43+
const { result } = renderPersistence(undefined, "/?p=50");
2944

3045
expect(readSnapshot(result)?.pageSize).toBe(50);
3146
});
3247

3348
it("returns sort from URL", () => {
34-
window.history.replaceState(null, "", "/?s=name:asc");
35-
const { result } = renderHook(() => useSearchParamsPersistence());
49+
const { result } = renderPersistence(undefined, "/?s=name:asc");
3650

3751
expect(readSnapshot(result)?.sort).toEqual([{ field: "name", direction: "Asc" }]);
3852
});
3953

4054
it("returns sort desc from URL", () => {
41-
window.history.replaceState(null, "", "/?s=createdAt:desc");
42-
const { result } = renderHook(() => useSearchParamsPersistence());
55+
const { result } = renderPersistence(undefined, "/?s=createdAt:desc");
4356

4457
expect(readSnapshot(result)?.sort).toEqual([{ field: "createdAt", direction: "Desc" }]);
4558
});
4659

4760
it("returns single-value filter from URL", () => {
48-
window.history.replaceState(null, "", "/?f.status:eq=ACTIVE");
49-
const { result } = renderHook(() => useSearchParamsPersistence());
61+
const { result } = renderPersistence(undefined, "/?f.status:eq=ACTIVE");
5062

5163
expect(readSnapshot(result)?.filters).toEqual([
5264
{ field: "status", operator: "eq", value: "ACTIVE" },
5365
]);
5466
});
5567

5668
it("returns multi-value filter from URL (repeated params)", () => {
57-
window.history.replaceState(null, "", "/?f.status:in=ACTIVE&f.status:in=PENDING");
58-
const { result } = renderHook(() => useSearchParamsPersistence());
69+
const { result } = renderPersistence(undefined, "/?f.status:in=ACTIVE&f.status:in=PENDING");
5970

6071
expect(readSnapshot(result)?.filters).toEqual([
6172
{ field: "status", operator: "in", value: ["ACTIVE", "PENDING"] },
@@ -64,17 +75,15 @@ describe("useSearchParamsPersistence", () => {
6475

6576
it("returns JSON object filter value (e.g. between)", () => {
6677
const url = `/?f.amount:between=${encodeURIComponent(JSON.stringify({ min: 10, max: 100 }))}`;
67-
window.history.replaceState(null, "", url);
68-
const { result } = renderHook(() => useSearchParamsPersistence());
78+
const { result } = renderPersistence(undefined, url);
6979

7080
expect(readSnapshot(result)?.filters).toEqual([
7181
{ field: "amount", operator: "between", value: { min: 10, max: 100 } },
7282
]);
7383
});
7484

7585
it("returns all state together", () => {
76-
window.history.replaceState(null, "", "/?p=25&s=name:desc&f.status:eq=ACTIVE");
77-
const { result } = renderHook(() => useSearchParamsPersistence());
86+
const { result } = renderPersistence(undefined, "/?p=25&s=name:desc&f.status:eq=ACTIVE");
7887

7988
expect(readSnapshot(result)).toEqual({
8089
pageSize: 25,
@@ -84,22 +93,19 @@ describe("useSearchParamsPersistence", () => {
8493
});
8594

8695
it("respects prefix option", () => {
87-
window.history.replaceState(null, "", "/?t1.p=30&p=10");
88-
const { result } = renderHook(() => useSearchParamsPersistence({ prefix: "t1" }));
96+
const { result } = renderPersistence({ prefix: "t1" }, "/?t1.p=30&p=10");
8997

9098
expect(readSnapshot(result)?.pageSize).toBe(30);
9199
});
92100

93101
it("ignores invalid pageSize values", () => {
94-
window.history.replaceState(null, "", "/?p=abc");
95-
const { result } = renderHook(() => useSearchParamsPersistence());
102+
const { result } = renderPersistence(undefined, "/?p=abc");
96103

97104
expect(readSnapshot(result)).toBeUndefined();
98105
});
99106

100107
it("ignores filter keys without operator", () => {
101-
window.history.replaceState(null, "", "/?f.status=ACTIVE");
102-
const { result } = renderHook(() => useSearchParamsPersistence());
108+
const { result } = renderPersistence(undefined, "/?f.status=ACTIVE");
103109

104110
expect(readSnapshot(result)).toBeUndefined();
105111
});
@@ -110,7 +116,7 @@ describe("useSearchParamsPersistence", () => {
110116
// ---------------------------------------------------------------------------
111117
describe("write", () => {
112118
it("writes pageSize to URL", () => {
113-
const { result } = renderHook(() => useSearchParamsPersistence());
119+
const { result } = renderPersistence();
114120

115121
act(() => {
116122
result.current.write({ filters: [], sort: [], pageSize: 50 });
@@ -120,7 +126,7 @@ describe("useSearchParamsPersistence", () => {
120126
});
121127

122128
it("writes sort to URL", () => {
123-
const { result } = renderHook(() => useSearchParamsPersistence());
129+
const { result } = renderPersistence();
124130

125131
act(() => {
126132
result.current.write({
@@ -134,7 +140,7 @@ describe("useSearchParamsPersistence", () => {
134140
});
135141

136142
it("writes single-value filter to URL", () => {
137-
const { result } = renderHook(() => useSearchParamsPersistence());
143+
const { result } = renderPersistence();
138144

139145
act(() => {
140146
result.current.write({
@@ -150,7 +156,7 @@ describe("useSearchParamsPersistence", () => {
150156
});
151157

152158
it("writes multi-value filter as repeated params", () => {
153-
const { result } = renderHook(() => useSearchParamsPersistence());
159+
const { result } = renderPersistence();
154160

155161
act(() => {
156162
result.current.write({
@@ -166,7 +172,7 @@ describe("useSearchParamsPersistence", () => {
166172
});
167173

168174
it("writes object filter value as JSON", () => {
169-
const { result } = renderHook(() => useSearchParamsPersistence());
175+
const { result } = renderPersistence();
170176

171177
act(() => {
172178
result.current.write({
@@ -188,8 +194,7 @@ describe("useSearchParamsPersistence", () => {
188194
});
189195

190196
it("clears old filters when writing new state", () => {
191-
window.history.replaceState(null, "", "/?f.status:eq=OLD");
192-
const { result } = renderHook(() => useSearchParamsPersistence());
197+
const { result } = renderPersistence(undefined, "/?f.status:eq=OLD");
193198

194199
act(() => {
195200
result.current.write({
@@ -205,7 +210,7 @@ describe("useSearchParamsPersistence", () => {
205210
});
206211

207212
it("uses prefix when writing", () => {
208-
const { result } = renderHook(() => useSearchParamsPersistence({ prefix: "t1" }));
213+
const { result } = renderPersistence({ prefix: "t1" });
209214

210215
act(() => {
211216
result.current.write({ filters: [], sort: [], pageSize: 40 });
@@ -222,7 +227,7 @@ describe("useSearchParamsPersistence", () => {
222227
it("debounces write when debounceMs is set", () => {
223228
vi.useFakeTimers();
224229

225-
const { result } = renderHook(() => useSearchParamsPersistence({ debounceMs: 100 }));
230+
const { result } = renderPersistence({ debounceMs: 100 });
226231

227232
act(() => {
228233
result.current.write({ filters: [], sort: [], pageSize: 50 });
@@ -244,7 +249,7 @@ describe("useSearchParamsPersistence", () => {
244249
it("only applies the last write within debounce window", () => {
245250
vi.useFakeTimers();
246251

247-
const { result } = renderHook(() => useSearchParamsPersistence({ debounceMs: 100 }));
252+
const { result } = renderPersistence({ debounceMs: 100 });
248253

249254
act(() => {
250255
result.current.write({ filters: [], sort: [], pageSize: 10 });

0 commit comments

Comments
 (0)