@izumisy-tailor/tailor-data-viewer 0.2.33 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -5,8 +5,8 @@ A low-level React component library for building data table interfaces with Tail
5
5
  ## Features
6
6
 
7
7
  - **Separation of Concerns**: Data fetching, query parameter management, and UI are fully decoupled
8
- - **`useCollection` Hook**: Manages filter, sort, and pagination state; outputs Tailor Platform-compatible GraphQL variables
9
- - **`Collection.Provider`**: Shares query parameters via React Context across sibling components
8
+ - **`useCollectionVariables` Hook**: Manages filter, sort, and pagination state; outputs Tailor Platform-compatible GraphQL variables
9
+ - **`CollectionVariablesProvider`**: Shares query parameters via React Context across sibling components
10
10
  - **`Table.*` Compound Components**: Static, unstyled table primitives (`<table>`, `<thead>`, `<tbody>`, `<tr>`, `<th>`, `<td>`)
11
11
  - **`DataTable.*` Compound Components**: Data-bound table with sort indicators, cell renderers, and `useDataTable` integration
12
12
  - **`useDataTable` Hook**: Integrates data, column visibility, row operations (optimistic updates), and props generators
@@ -15,7 +15,7 @@ A low-level React component library for building data table interfaces with Tail
15
15
  - **Utility Components**: `ColumnSelector`, `CsvButton`, `SearchFilterForm`, `Pagination` — all props-based, spreadable from hooks
16
16
  - **Multi-sort Support**: Multiple simultaneous sort fields
17
17
  - **Optimistic Updates**: `updateRow`, `deleteRow`, `insertRow` with rollback
18
- - **Presentation Agnostic**: Same `useCollection` can drive tables, kanbans, calendars, etc.
18
+ - **Presentation Agnostic**: Same `useCollectionVariables` can drive tables, kanbans, calendars, etc.
19
19
 
20
20
  ## Installation
21
21
 
@@ -47,7 +47,7 @@ npm install react react-dom
47
47
 
48
48
  ```tsx
49
49
  import {
50
- useCollection,
50
+ useCollectionVariables,
51
51
  useDataTable,
52
52
  DataTable,
53
53
  Pagination,
@@ -95,8 +95,15 @@ const columns = [
95
95
 
96
96
  // 2. Build a page
97
97
  function OrdersPage() {
98
- const collection = useCollection({ query: GET_ORDERS, params: { pageSize: 20 } });
99
- const [result] = useQuery({ ...collection.toQueryArgs() });
98
+ const { variables } = useCollectionVariables({ params: { pageSize: 20 } });
99
+ const [result] = useQuery({
100
+ query: GET_ORDERS,
101
+ variables: {
102
+ ...variables.pagination,
103
+ query: variables.query,
104
+ order: variables.order
105
+ }
106
+ });
100
107
 
101
108
  const table = useDataTable<Order>({
102
109
  columns,
@@ -119,21 +126,27 @@ function OrdersPage() {
119
126
 
120
127
  ## API Overview
121
128
 
122
- ### `useCollection(options)`
129
+ ### `useCollectionVariables(options)`
123
130
 
124
- Manages filter, sort, and pagination state. Returns `toQueryArgs()` which produces Tailor Platform-compatible arguments for `useQuery()`.
131
+ Manages filter, sort, and pagination state. Returns `variables` containing `query`, `order`, and `pagination` sub-properties in Tailor Platform-compatible format.
125
132
 
126
133
  ```tsx
127
- const collection = useCollection({
128
- query: GET_ORDERS,
134
+ const { variables, ...collection } = useCollectionVariables({
129
135
  params: {
130
136
  pageSize: 20,
131
137
  initialSort: [{ field: "createdAt", direction: "Desc" }],
132
138
  },
133
139
  });
134
140
 
135
- // Spread toQueryArgs() into useQuery
136
- const [result] = useQuery({ ...collection.toQueryArgs() });
141
+ // Use variables in your query
142
+ const [result] = useQuery({
143
+ query: GET_ORDERS,
144
+ variables: {
145
+ ...variables.pagination,
146
+ query: variables.query,
147
+ order: variables.order,
148
+ },
149
+ });
137
150
 
138
151
  // Filter operations
139
152
  collection.addFilter("status", "eq", "ACTIVE");
@@ -157,15 +170,15 @@ collection.hasPrevPage; // boolean
157
170
  collection.hasNextPage; // boolean
158
171
  ```
159
172
 
160
- ### `DataTable.Provider` / `useDataTableContext()` / `useCollectionContext()`
173
+ ### `DataTable.Provider` / `useDataTableContext()` / `useCollectionVariablesContext()`
161
174
 
162
175
  `DataTable.Provider` wraps the table UI and provides both data table and collection context. All utility components (`Pagination`, `ColumnSelector`, `CsvButton`, `SearchFilterForm`) read from this context — no prop spreading needed.
163
176
 
164
- When `collection` is passed to `useDataTable`, `DataTable.Provider` automatically wraps a `Collection.Provider` so child components can use `useCollectionContext()`.
177
+ When `collection` is passed to `useDataTable`, `DataTable.Provider` automatically wraps a `CollectionVariablesProvider` so child components can use `useCollectionVariablesContext()`.
165
178
 
166
179
  ```tsx
167
180
  <DataTable.Provider value={table}>
168
- <StatusFilter /> {/* useCollectionContext() inside */}
181
+ <StatusFilter /> {/* useCollectionVariablesContext() inside */}
169
182
  <DataTable.Root>
170
183
  <DataTable.Headers />
171
184
  <DataTable.Body />
@@ -174,13 +187,13 @@ When `collection` is passed to `useDataTable`, `DataTable.Provider` automaticall
174
187
  </DataTable.Provider>
175
188
  ```
176
189
 
177
- For cases where you need `Collection.Provider` without `DataTable.Provider` (e.g., non-table UIs), you can use it standalone:
190
+ For cases where you need `CollectionVariablesProvider` without `DataTable.Provider` (e.g., non-table UIs), you can use it standalone:
178
191
 
179
192
  ```tsx
180
- <Collection.Provider value={collection}>
193
+ <CollectionVariablesProvider value={collection}>
181
194
  <StatusFilter />
182
195
  <CustomKanbanBoard />
183
- </Collection.Provider>
196
+ </CollectionVariablesProvider>
184
197
  ```
185
198
 
186
199
  ### Column Definition Helper
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@izumisy-tailor/tailor-data-viewer",
3
3
  "private": false,
4
- "version": "0.2.33",
4
+ "version": "0.3.0",
5
5
  "type": "module",
6
6
  "description": "Flexible data viewer component for Tailor Platform",
7
7
  "files": [
@@ -1,7 +1,7 @@
1
1
  import { createContext, useContext, type ReactNode } from "react";
2
2
  import type { UseCollectionReturn } from "../types";
3
3
 
4
- const CollectionContext = createContext<UseCollectionReturn<
4
+ const CollectionVariablesContext = createContext<UseCollectionReturn<
5
5
  string,
6
6
  unknown
7
7
  > | null>(null);
@@ -11,16 +11,16 @@ const CollectionContext = createContext<UseCollectionReturn<
11
11
  *
12
12
  * @example
13
13
  * ```tsx
14
- * const collection = useCollection({ params: { pageSize: 20 } });
14
+ * const { variables, ...collection } = useCollectionVariables({ params: { pageSize: 20 } });
15
15
  *
16
- * <CollectionProvider value={collection}>
16
+ * <CollectionVariablesProvider value={collection}>
17
17
  * <FilterPanel />
18
18
  * <DataTable.Root>...</DataTable.Root>
19
- * <Pagination {...table} />
20
- * </CollectionProvider>
19
+ * <Pagination />
20
+ * </CollectionVariablesProvider>
21
21
  * ```
22
22
  */
23
- export function CollectionProvider({
23
+ export function CollectionVariablesProvider({
24
24
  value,
25
25
  children,
26
26
  }: {
@@ -28,56 +28,42 @@ export function CollectionProvider({
28
28
  children: ReactNode;
29
29
  }) {
30
30
  return (
31
- <CollectionContext.Provider value={value}>
31
+ <CollectionVariablesContext.Provider value={value}>
32
32
  {children}
33
- </CollectionContext.Provider>
33
+ </CollectionVariablesContext.Provider>
34
34
  );
35
35
  }
36
36
 
37
37
  /**
38
- * Hook to access collection state from the nearest `Collection.Provider`.
38
+ * Hook to access collection state from the nearest `CollectionVariablesProvider`.
39
39
  *
40
- * Returns the same interface as `useCollection()`. Pass a `TFieldName`
40
+ * Returns the same interface as `useCollectionVariables()`. Pass a `TFieldName`
41
41
  * type parameter to narrow method arguments like `addFilter` / `setSort`.
42
42
  *
43
43
  * @typeParam TFieldName - Union of allowed field name strings (default: `string`).
44
44
  *
45
- * @throws Error if used outside of `Collection.Provider`.
45
+ * @throws Error if used outside of `CollectionVariablesProvider`.
46
46
  *
47
47
  * @example
48
48
  * ```tsx
49
49
  * function StatusFilter() {
50
- * const { filters, addFilter, removeFilter } = useCollectionContext();
50
+ * const { filters, addFilter, removeFilter } = useCollectionVariablesContext();
51
51
  * // ...
52
52
  * }
53
53
  *
54
54
  * // With typed field names:
55
55
  * type TaskField = FieldName<typeof tableMetadata, "task">;
56
- * const { addFilter } = useCollectionContext<TaskField>();
56
+ * const { addFilter } = useCollectionVariablesContext<TaskField>();
57
57
  * ```
58
58
  */
59
- export function useCollectionContext<
59
+ export function useCollectionVariablesContext<
60
60
  TFieldName extends string = string,
61
61
  >(): UseCollectionReturn<TFieldName> {
62
- const ctx = useContext(CollectionContext);
62
+ const ctx = useContext(CollectionVariablesContext);
63
63
  if (!ctx) {
64
64
  throw new Error(
65
- "useCollectionContext must be used within <Collection.Provider>",
65
+ "useCollectionVariablesContext must be used within <CollectionVariablesProvider>",
66
66
  );
67
67
  }
68
68
  return ctx as UseCollectionReturn<TFieldName>;
69
69
  }
70
-
71
- /**
72
- * `Collection` namespace object providing the Provider component.
73
- *
74
- * @example
75
- * ```tsx
76
- * <Collection.Provider value={collection}>
77
- * ...
78
- * </Collection.Provider>
79
- * ```
80
- */
81
- export const Collection = {
82
- Provider: CollectionProvider,
83
- } as const;
@@ -1,18 +1,18 @@
1
1
  import { renderHook, act } from "@testing-library/react";
2
2
  import { describe, it, expect } from "vitest";
3
3
  import type { TableMetadataMap } from "../../generator/metadata-generator";
4
- import { useCollection } from "./use-collection";
4
+ import { useCollectionVariables } from "./use-collection";
5
5
 
6
- const FAKE_QUERY = { kind: "Document" } as const;
7
-
8
- describe("useCollection", () => {
6
+ describe("useCollectionVariables", () => {
9
7
  // ---------------------------------------------------------------------------
10
8
  // Initial state
11
9
  // ---------------------------------------------------------------------------
12
10
  describe("initial state", () => {
13
11
  it("returns default variables with pageSize 20", () => {
14
- const { result } = renderHook(() => useCollection({ query: FAKE_QUERY }));
15
- expect(result.current.toQueryArgs().variables).toEqual({ first: 20 });
12
+ const { result } = renderHook(() => useCollectionVariables({}));
13
+ expect(result.current.variables.pagination).toEqual({ first: 20 });
14
+ expect(result.current.variables.query).toBeUndefined();
15
+ expect(result.current.variables.order).toBeUndefined();
16
16
  expect(result.current.filters).toEqual([]);
17
17
  expect(result.current.sortStates).toEqual([]);
18
18
  expect(result.current.cursor).toBeNull();
@@ -21,15 +21,14 @@ describe("useCollection", () => {
21
21
 
22
22
  it("uses custom pageSize", () => {
23
23
  const { result } = renderHook(() =>
24
- useCollection({ query: FAKE_QUERY, params: { pageSize: 50 } }),
24
+ useCollectionVariables({ params: { pageSize: 50 } }),
25
25
  );
26
- expect(result.current.toQueryArgs().variables.first).toBe(50);
26
+ expect(result.current.variables.pagination.first).toBe(50);
27
27
  });
28
28
 
29
29
  it("applies initial sort", () => {
30
30
  const { result } = renderHook(() =>
31
- useCollection({
32
- query: FAKE_QUERY,
31
+ useCollectionVariables({
33
32
  params: {
34
33
  initialSort: [{ field: "createdAt", direction: "Desc" }],
35
34
  },
@@ -38,15 +37,14 @@ describe("useCollection", () => {
38
37
  expect(result.current.sortStates).toEqual([
39
38
  { field: "createdAt", direction: "Desc" },
40
39
  ]);
41
- expect(result.current.toQueryArgs().variables.order).toEqual([
40
+ expect(result.current.variables.order).toEqual([
42
41
  { field: "createdAt", direction: "Desc" },
43
42
  ]);
44
43
  });
45
44
 
46
45
  it("applies initial filters", () => {
47
46
  const { result } = renderHook(() =>
48
- useCollection({
49
- query: FAKE_QUERY,
47
+ useCollectionVariables({
50
48
  params: {
51
49
  initialFilters: [
52
50
  {
@@ -59,7 +57,7 @@ describe("useCollection", () => {
59
57
  }),
60
58
  );
61
59
  expect(result.current.filters).toHaveLength(1);
62
- expect(result.current.toQueryArgs().variables.query).toEqual({
60
+ expect(result.current.variables.query).toEqual({
63
61
  status: { eq: "ACTIVE" },
64
62
  });
65
63
  });
@@ -70,7 +68,7 @@ describe("useCollection", () => {
70
68
  // ---------------------------------------------------------------------------
71
69
  describe("filter operations", () => {
72
70
  it("adds a filter", () => {
73
- const { result } = renderHook(() => useCollection({ query: FAKE_QUERY }));
71
+ const { result } = renderHook(() => useCollectionVariables({}));
74
72
 
75
73
  act(() => {
76
74
  result.current.addFilter("status", "eq", "ACTIVE");
@@ -82,13 +80,13 @@ describe("useCollection", () => {
82
80
  operator: "eq",
83
81
  value: "ACTIVE",
84
82
  });
85
- expect(result.current.toQueryArgs().variables.query).toEqual({
83
+ expect(result.current.variables.query).toEqual({
86
84
  status: { eq: "ACTIVE" },
87
85
  });
88
86
  });
89
87
 
90
88
  it("replaces filter for same field", () => {
91
- const { result } = renderHook(() => useCollection({ query: FAKE_QUERY }));
89
+ const { result } = renderHook(() => useCollectionVariables({}));
92
90
 
93
91
  act(() => {
94
92
  result.current.addFilter("status", "eq", "ACTIVE");
@@ -102,7 +100,7 @@ describe("useCollection", () => {
102
100
  });
103
101
 
104
102
  it("sets filters in bulk", () => {
105
- const { result } = renderHook(() => useCollection({ query: FAKE_QUERY }));
103
+ const { result } = renderHook(() => useCollectionVariables({}));
106
104
 
107
105
  act(() => {
108
106
  result.current.setFilters([
@@ -120,14 +118,14 @@ describe("useCollection", () => {
120
118
  });
121
119
 
122
120
  expect(result.current.filters).toHaveLength(2);
123
- expect(result.current.toQueryArgs().variables.query).toEqual({
121
+ expect(result.current.variables.query).toEqual({
124
122
  status: { eq: "ACTIVE" },
125
123
  amount: { gte: 1000 },
126
124
  });
127
125
  });
128
126
 
129
127
  it("removes a filter", () => {
130
- const { result } = renderHook(() => useCollection({ query: FAKE_QUERY }));
128
+ const { result } = renderHook(() => useCollectionVariables({}));
131
129
 
132
130
  act(() => {
133
131
  result.current.addFilter("status", "eq", "ACTIVE");
@@ -142,7 +140,7 @@ describe("useCollection", () => {
142
140
  });
143
141
 
144
142
  it("clears all filters", () => {
145
- const { result } = renderHook(() => useCollection({ query: FAKE_QUERY }));
143
+ const { result } = renderHook(() => useCollectionVariables({}));
146
144
 
147
145
  act(() => {
148
146
  result.current.addFilter("status", "eq", "ACTIVE");
@@ -153,11 +151,11 @@ describe("useCollection", () => {
153
151
  });
154
152
 
155
153
  expect(result.current.filters).toHaveLength(0);
156
- expect(result.current.toQueryArgs().variables.query).toBeUndefined();
154
+ expect(result.current.variables.query).toBeUndefined();
157
155
  });
158
156
 
159
157
  it("resets pagination when filters change", () => {
160
- const { result } = renderHook(() => useCollection({ query: FAKE_QUERY }));
158
+ const { result } = renderHook(() => useCollectionVariables({}));
161
159
 
162
160
  // Navigate to next page
163
161
  act(() => {
@@ -179,7 +177,7 @@ describe("useCollection", () => {
179
177
  // ---------------------------------------------------------------------------
180
178
  describe("sort operations", () => {
181
179
  it("sets sort", () => {
182
- const { result } = renderHook(() => useCollection({ query: FAKE_QUERY }));
180
+ const { result } = renderHook(() => useCollectionVariables({}));
183
181
 
184
182
  act(() => {
185
183
  result.current.setSort("createdAt", "Desc");
@@ -188,13 +186,13 @@ describe("useCollection", () => {
188
186
  expect(result.current.sortStates).toEqual([
189
187
  { field: "createdAt", direction: "Desc" },
190
188
  ]);
191
- expect(result.current.toQueryArgs().variables.order).toEqual([
189
+ expect(result.current.variables.order).toEqual([
192
190
  { field: "createdAt", direction: "Desc" },
193
191
  ]);
194
192
  });
195
193
 
196
194
  it("appends sort for different fields", () => {
197
- const { result } = renderHook(() => useCollection({ query: FAKE_QUERY }));
195
+ const { result } = renderHook(() => useCollectionVariables({}));
198
196
 
199
197
  act(() => {
200
198
  result.current.setSort("createdAt", "Desc");
@@ -210,7 +208,7 @@ describe("useCollection", () => {
210
208
  });
211
209
 
212
210
  it("replaces direction for existing field", () => {
213
- const { result } = renderHook(() => useCollection({ query: FAKE_QUERY }));
211
+ const { result } = renderHook(() => useCollectionVariables({}));
214
212
 
215
213
  act(() => {
216
214
  result.current.setSort("createdAt", "Desc");
@@ -229,7 +227,7 @@ describe("useCollection", () => {
229
227
  });
230
228
 
231
229
  it("removes sort when direction is undefined", () => {
232
- const { result } = renderHook(() => useCollection({ query: FAKE_QUERY }));
230
+ const { result } = renderHook(() => useCollectionVariables({}));
233
231
 
234
232
  act(() => {
235
233
  result.current.setSort("createdAt", "Desc");
@@ -247,7 +245,7 @@ describe("useCollection", () => {
247
245
  });
248
246
 
249
247
  it("clears sort", () => {
250
- const { result } = renderHook(() => useCollection({ query: FAKE_QUERY }));
248
+ const { result } = renderHook(() => useCollectionVariables({}));
251
249
 
252
250
  act(() => {
253
251
  result.current.setSort("createdAt", "Desc");
@@ -257,7 +255,7 @@ describe("useCollection", () => {
257
255
  });
258
256
 
259
257
  expect(result.current.sortStates).toEqual([]);
260
- expect(result.current.toQueryArgs().variables.order).toBeUndefined();
258
+ expect(result.current.variables.order).toBeUndefined();
261
259
  });
262
260
  });
263
261
 
@@ -266,7 +264,7 @@ describe("useCollection", () => {
266
264
  // ---------------------------------------------------------------------------
267
265
  describe("pagination operations", () => {
268
266
  it("navigates to next page (forward)", () => {
269
- const { result } = renderHook(() => useCollection({ query: FAKE_QUERY }));
267
+ const { result } = renderHook(() => useCollectionVariables({}));
270
268
 
271
269
  act(() => {
272
270
  result.current.nextPage("cursor1");
@@ -274,14 +272,14 @@ describe("useCollection", () => {
274
272
 
275
273
  expect(result.current.cursor).toBe("cursor1");
276
274
  expect(result.current.paginationDirection).toBe("forward");
277
- expect(result.current.toQueryArgs().variables.after).toBe("cursor1");
278
- expect(result.current.toQueryArgs().variables.first).toBe(20);
279
- expect(result.current.toQueryArgs().variables.last).toBeUndefined();
280
- expect(result.current.toQueryArgs().variables.before).toBeUndefined();
275
+ expect(result.current.variables.pagination.after).toBe("cursor1");
276
+ expect(result.current.variables.pagination.first).toBe(20);
277
+ expect(result.current.variables.pagination.last).toBeUndefined();
278
+ expect(result.current.variables.pagination.before).toBeUndefined();
281
279
  });
282
280
 
283
281
  it("navigates to previous page (backward)", () => {
284
- const { result } = renderHook(() => useCollection({ query: FAKE_QUERY }));
282
+ const { result } = renderHook(() => useCollectionVariables({}));
285
283
 
286
284
  act(() => {
287
285
  result.current.prevPage("cursor1");
@@ -289,14 +287,14 @@ describe("useCollection", () => {
289
287
 
290
288
  expect(result.current.cursor).toBe("cursor1");
291
289
  expect(result.current.paginationDirection).toBe("backward");
292
- expect(result.current.toQueryArgs().variables.before).toBe("cursor1");
293
- expect(result.current.toQueryArgs().variables.last).toBe(20);
294
- expect(result.current.toQueryArgs().variables.first).toBeUndefined();
295
- expect(result.current.toQueryArgs().variables.after).toBeUndefined();
290
+ expect(result.current.variables.pagination.before).toBe("cursor1");
291
+ expect(result.current.variables.pagination.last).toBe(20);
292
+ expect(result.current.variables.pagination.first).toBeUndefined();
293
+ expect(result.current.variables.pagination.after).toBeUndefined();
296
294
  });
297
295
 
298
296
  it("switches direction on nextPage after prevPage", () => {
299
- const { result } = renderHook(() => useCollection({ query: FAKE_QUERY }));
297
+ const { result } = renderHook(() => useCollectionVariables({}));
300
298
 
301
299
  act(() => {
302
300
  result.current.prevPage("cursorB");
@@ -307,12 +305,12 @@ describe("useCollection", () => {
307
305
  result.current.nextPage("cursorA");
308
306
  });
309
307
  expect(result.current.paginationDirection).toBe("forward");
310
- expect(result.current.toQueryArgs().variables.after).toBe("cursorA");
311
- expect(result.current.toQueryArgs().variables.first).toBe(20);
308
+ expect(result.current.variables.pagination.after).toBe("cursorA");
309
+ expect(result.current.variables.pagination.first).toBe(20);
312
310
  });
313
311
 
314
312
  it("resets page to forward direction", () => {
315
- const { result } = renderHook(() => useCollection({ query: FAKE_QUERY }));
313
+ const { result } = renderHook(() => useCollectionVariables({}));
316
314
 
317
315
  act(() => {
318
316
  result.current.prevPage("cursor1");
@@ -326,7 +324,7 @@ describe("useCollection", () => {
326
324
  });
327
325
 
328
326
  it("tracks hasPrevPage from currentPage and hasNextPage from setPageInfo", () => {
329
- const { result } = renderHook(() => useCollection({ query: FAKE_QUERY }));
327
+ const { result } = renderHook(() => useCollectionVariables({}));
330
328
 
331
329
  // Initially on page 1: no prev, no next
332
330
  expect(result.current.hasPrevPage).toBe(false);
@@ -354,13 +352,12 @@ describe("useCollection", () => {
354
352
  });
355
353
 
356
354
  // ---------------------------------------------------------------------------
357
- // toQueryArgs
355
+ // variables
358
356
  // ---------------------------------------------------------------------------
359
- describe("toQueryArgs", () => {
357
+ describe("variables", () => {
360
358
  it("generates complete variables with filters, sort, and cursor", () => {
361
359
  const { result } = renderHook(() =>
362
- useCollection({
363
- query: FAKE_QUERY,
360
+ useCollectionVariables({
364
361
  params: {
365
362
  pageSize: 10,
366
363
  initialFilters: [
@@ -379,39 +376,33 @@ describe("useCollection", () => {
379
376
  result.current.nextPage("abc123");
380
377
  });
381
378
 
382
- expect(result.current.toQueryArgs()).toEqual({
383
- query: FAKE_QUERY,
384
- variables: {
379
+ expect(result.current.variables).toEqual({
380
+ query: { status: { eq: "ACTIVE" } },
381
+ order: [{ field: "createdAt", direction: "Desc" }],
382
+ pagination: {
385
383
  first: 10,
386
- query: { status: { eq: "ACTIVE" } },
387
- order: [{ field: "createdAt", direction: "Desc" }],
388
384
  after: "abc123",
389
385
  },
390
386
  });
391
387
  });
392
388
 
393
- it("omits undefined fields from variables", () => {
394
- const { result } = renderHook(() => useCollection({ query: FAKE_QUERY }));
395
- const { variables } = result.current.toQueryArgs();
396
- expect(variables).toEqual({ first: 20 });
397
- expect("query" in variables).toBe(false);
398
- expect("order" in variables).toBe(false);
399
- expect("after" in variables).toBe(false);
400
- expect("last" in variables).toBe(false);
401
- expect("before" in variables).toBe(false);
389
+ it("omits undefined fields from pagination", () => {
390
+ const { result } = renderHook(() => useCollectionVariables({}));
391
+ const { pagination } = result.current.variables;
392
+ expect(pagination).toEqual({ first: 20 });
393
+ expect("after" in pagination).toBe(false);
394
+ expect("last" in pagination).toBe(false);
395
+ expect("before" in pagination).toBe(false);
402
396
  });
403
397
 
404
- it("includes query in toQueryArgs result", () => {
405
- const fakeQuery = { kind: "Document" } as const;
398
+ it("returns undefined for query and order when empty", () => {
406
399
  const { result } = renderHook(() =>
407
- useCollection({ query: fakeQuery, params: { pageSize: 10 } }),
400
+ useCollectionVariables({ params: { pageSize: 10 } }),
408
401
  );
409
402
 
410
- const args = result.current.toQueryArgs();
411
- expect(args).toEqual({
412
- query: fakeQuery,
413
- variables: { first: 10 },
414
- });
403
+ expect(result.current.variables.query).toBeUndefined();
404
+ expect(result.current.variables.order).toBeUndefined();
405
+ expect(result.current.variables.pagination).toEqual({ first: 10 });
415
406
  });
416
407
  });
417
408
 
@@ -441,20 +432,18 @@ describe("useCollection", () => {
441
432
 
442
433
  it("works with tableMetadata", () => {
443
434
  const { result } = renderHook(() =>
444
- useCollection({
435
+ useCollectionVariables({
445
436
  tableMetadata: testMetadata.task,
446
- query: FAKE_QUERY,
447
437
  params: { pageSize: 10 },
448
438
  }),
449
439
  );
450
- expect(result.current.toQueryArgs().variables).toEqual({ first: 10 });
440
+ expect(result.current.variables.pagination).toEqual({ first: 10 });
451
441
  });
452
442
 
453
443
  it("applies typed initialSort", () => {
454
444
  const { result } = renderHook(() =>
455
- useCollection({
445
+ useCollectionVariables({
456
446
  tableMetadata: testMetadata.task,
457
- query: FAKE_QUERY,
458
447
  params: {
459
448
  initialSort: [{ field: "dueDate", direction: "Desc" }],
460
449
  },