@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 +31 -18
- package/package.json +1 -1
- package/src/component/collection/collection-provider.tsx +16 -30
- package/src/component/collection/use-collection.test.ts +65 -76
- package/src/component/collection/use-collection.ts +67 -89
- package/src/component/collection/use-collection.typetest.ts +157 -293
- package/src/component/data-table/data-table.tsx +8 -6
- package/src/component/data-table/i18n.ts +12 -6
- package/src/component/data-table/pagination.tsx +3 -3
- package/src/component/data-table/search-filter-form.tsx +3 -3
- package/src/component/data-table/use-data-table.ts +6 -2
- package/src/component/index.ts +6 -9
- package/src/component/types.ts +145 -276
- package/src/tests/helpers.tsx +9 -5
|
@@ -1,304 +1,168 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Compile-time type tests for `
|
|
2
|
+
* Compile-time type tests for `BuildQueryVariables`.
|
|
3
3
|
*
|
|
4
|
-
* These tests verify that `
|
|
5
|
-
*
|
|
6
|
-
* metadata fields.
|
|
4
|
+
* These tests verify that `BuildQueryVariables` produces the correct
|
|
5
|
+
* filter input types from table metadata.
|
|
7
6
|
*
|
|
8
7
|
* To run: `npx tsc --noEmit` — any assertion that evaluates to `never`
|
|
9
8
|
* will cause a compile error, ensuring the test cases stay enforced.
|
|
10
9
|
*/
|
|
11
10
|
|
|
12
11
|
import type {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
ExtractOrderField,
|
|
12
|
+
BuildQueryVariables,
|
|
13
|
+
TableFieldName,
|
|
14
|
+
TableOrderableFieldName,
|
|
17
15
|
} from "../types";
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
//
|
|
21
|
-
//
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
type
|
|
25
|
-
|
|
26
|
-
|
|
16
|
+
import type { TableMetadata } from "../../generator/metadata-generator";
|
|
17
|
+
|
|
18
|
+
// =============================================================================
|
|
19
|
+
// 1. BuildQueryVariables produces correct filter types per field type
|
|
20
|
+
// =============================================================================
|
|
21
|
+
|
|
22
|
+
type TestTable = {
|
|
23
|
+
readonly name: "test";
|
|
24
|
+
readonly pluralForm: "tests";
|
|
25
|
+
readonly readAllowedRoles: readonly [];
|
|
26
|
+
readonly fields: readonly [
|
|
27
|
+
{ readonly name: "id"; readonly type: "uuid"; readonly required: true },
|
|
28
|
+
{
|
|
29
|
+
readonly name: "title";
|
|
30
|
+
readonly type: "string";
|
|
31
|
+
readonly required: true;
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
readonly name: "amount";
|
|
35
|
+
readonly type: "number";
|
|
36
|
+
readonly required: false;
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
readonly name: "status";
|
|
40
|
+
readonly type: "enum";
|
|
41
|
+
readonly required: true;
|
|
42
|
+
readonly enumValues: readonly ["active", "inactive"];
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
readonly name: "isActive";
|
|
46
|
+
readonly type: "boolean";
|
|
47
|
+
readonly required: false;
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
readonly name: "createdAt";
|
|
51
|
+
readonly type: "datetime";
|
|
52
|
+
readonly required: false;
|
|
53
|
+
},
|
|
54
|
+
{ readonly name: "tags"; readonly type: "array"; readonly required: false },
|
|
55
|
+
{
|
|
56
|
+
readonly name: "config";
|
|
57
|
+
readonly type: "nested";
|
|
58
|
+
readonly required: false;
|
|
59
|
+
},
|
|
60
|
+
];
|
|
27
61
|
};
|
|
28
62
|
|
|
29
|
-
//
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
type
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
type
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
}>;
|
|
136
|
-
type Fail2 = ValidateCollectionQuery<DocNarrowOrder, "name" | "email">;
|
|
137
|
-
type AssertFail2 = HasCollectionQueryError<Fail2> extends true ? true : never;
|
|
138
|
-
export const assertFail2: AssertFail2 = true;
|
|
139
|
-
|
|
140
|
-
// ✅ OrderInput allows all metadata fields
|
|
141
|
-
type DocWideOrder = FakeDoc<{
|
|
142
|
-
first?: number | null;
|
|
143
|
-
after?: string | null;
|
|
144
|
-
last?: number | null;
|
|
145
|
-
before?: string | null;
|
|
146
|
-
query?: { name?: unknown; email?: unknown } | null;
|
|
147
|
-
order?: readonly { field?: "name" | "email" | "phone" | null }[] | null;
|
|
148
|
-
}>;
|
|
149
|
-
type Pass2 = ValidateCollectionQuery<DocWideOrder, "name" | "email">;
|
|
150
|
-
type AssertPass2 = HasCollectionQueryError<Pass2> extends true ? never : true;
|
|
151
|
-
export const assertPass2: AssertPass2 = true;
|
|
152
|
-
|
|
153
|
-
// =============================================================================
|
|
154
|
-
// 3. QueryInput key compatibility
|
|
155
|
-
// =============================================================================
|
|
156
|
-
|
|
157
|
-
// ❌ Metadata has "name" | "phone" but QueryInput only has "name"
|
|
158
|
-
type DocNarrowQuery = FakeDoc<{
|
|
159
|
-
first?: number | null;
|
|
160
|
-
after?: string | null;
|
|
161
|
-
last?: number | null;
|
|
162
|
-
before?: string | null;
|
|
163
|
-
query?: { name?: unknown } | null;
|
|
164
|
-
order?: readonly { field?: "name" | "phone" | null }[] | null;
|
|
165
|
-
}>;
|
|
166
|
-
type Fail3 = ValidateCollectionQuery<DocNarrowQuery, "name" | "phone">;
|
|
167
|
-
type AssertFail3 = HasCollectionQueryError<Fail3> extends true ? true : never;
|
|
168
|
-
export const assertFail3: AssertFail3 = true;
|
|
169
|
-
|
|
170
|
-
// ✅ QueryInput has all metadata fields
|
|
171
|
-
type DocWideQuery = FakeDoc<{
|
|
172
|
-
first?: number | null;
|
|
173
|
-
after?: string | null;
|
|
174
|
-
last?: number | null;
|
|
175
|
-
before?: string | null;
|
|
176
|
-
query?: { name?: unknown; phone?: unknown; email?: unknown } | null;
|
|
177
|
-
order?: readonly { field?: "name" | "phone" | "email" | null }[] | null;
|
|
178
|
-
}>;
|
|
179
|
-
type Pass3 = ValidateCollectionQuery<DocWideQuery, "name" | "phone">;
|
|
180
|
-
type AssertPass3 = HasCollectionQueryError<Pass3> extends true ? never : true;
|
|
181
|
-
export const assertPass3: AssertPass3 = true;
|
|
182
|
-
|
|
183
|
-
// =============================================================================
|
|
184
|
-
// 4. Variable name typo detection
|
|
185
|
-
// =============================================================================
|
|
186
|
-
|
|
187
|
-
// ❌ Query uses $filter instead of $query (typo) — now caught because $query
|
|
188
|
-
// is required.
|
|
189
|
-
type DocFilterTypo = FakeDoc<{
|
|
190
|
-
first?: number | null;
|
|
191
|
-
after?: string | null;
|
|
192
|
-
last?: number | null;
|
|
193
|
-
before?: string | null;
|
|
194
|
-
filter?: { name?: unknown } | null;
|
|
195
|
-
order?: readonly { field?: "name" | null }[] | null;
|
|
196
|
-
}>;
|
|
197
|
-
type Typo1 = ValidateCollectionQuery<DocFilterTypo, "name">;
|
|
198
|
-
type AssertTypo1 = HasCollectionQueryError<Typo1> extends true ? true : never;
|
|
199
|
-
export const assertTypo1: AssertTypo1 = true;
|
|
200
|
-
|
|
201
|
-
// ❌ Query uses $queryy instead of $query (typo) — now caught
|
|
202
|
-
type DocQueryTypo = FakeDoc<{
|
|
203
|
-
first?: number | null;
|
|
204
|
-
after?: string | null;
|
|
205
|
-
last?: number | null;
|
|
206
|
-
before?: string | null;
|
|
207
|
-
queryy?: { name?: unknown } | null;
|
|
208
|
-
order?: readonly { field?: "name" | null }[] | null;
|
|
209
|
-
}>;
|
|
210
|
-
type Typo2 = ValidateCollectionQuery<DocQueryTypo, "name">;
|
|
211
|
-
type AssertTypo2 = HasCollectionQueryError<Typo2> extends true ? true : never;
|
|
212
|
-
export const assertTypo2: AssertTypo2 = true;
|
|
213
|
-
|
|
214
|
-
// =============================================================================
|
|
215
|
-
// 5. No gql-tada (plain DocumentNode) — all checks skipped
|
|
216
|
-
// =============================================================================
|
|
217
|
-
|
|
218
|
-
type PlainDoc = { kind: "Document" };
|
|
219
|
-
type Pass5 = ValidateCollectionQuery<PlainDoc, "name">;
|
|
220
|
-
type AssertPass5 = HasCollectionQueryError<Pass5> extends true ? never : true;
|
|
221
|
-
export const assertPass5: AssertPass5 = true;
|
|
222
|
-
|
|
223
|
-
// =============================================================================
|
|
224
|
-
// 6. No metadata (string field names) — field-level checks skipped but
|
|
225
|
-
// variable existence is still required
|
|
226
|
-
// =============================================================================
|
|
227
|
-
|
|
228
|
-
type Pass6 = ValidateCollectionQuery<DocOk>;
|
|
229
|
-
type AssertPass6 = HasCollectionQueryError<Pass6> extends true ? never : true;
|
|
230
|
-
export const assertPass6: AssertPass6 = true;
|
|
231
|
-
|
|
232
|
-
// ❌ No metadata but missing $query — still fails
|
|
233
|
-
type DocNoMetaNoQuery = FakeDoc<{
|
|
234
|
-
first?: number | null;
|
|
235
|
-
after?: string | null;
|
|
236
|
-
last?: number | null;
|
|
237
|
-
before?: string | null;
|
|
238
|
-
order?: readonly { field?: "name" | null }[] | null;
|
|
239
|
-
}>;
|
|
240
|
-
type Fail6 = ValidateCollectionQuery<DocNoMetaNoQuery>;
|
|
241
|
-
type AssertFail6 = HasCollectionQueryError<Fail6> extends true ? true : never;
|
|
242
|
-
export const assertFail6: AssertFail6 = true;
|
|
243
|
-
|
|
244
|
-
// =============================================================================
|
|
245
|
-
// 7. Helper type tests
|
|
246
|
-
// =============================================================================
|
|
247
|
-
|
|
248
|
-
// ExtractOrderField
|
|
249
|
-
type OrderField1 = ExtractOrderField<{
|
|
250
|
-
order?: readonly { field?: "a" | "b" | null }[] | null;
|
|
251
|
-
}>;
|
|
252
|
-
export const of1: OrderField1 = "a";
|
|
253
|
-
export const of2: OrderField1 = "b";
|
|
254
|
-
|
|
255
|
-
// ExtractQueryInputKeys
|
|
256
|
-
type QIKeys1 = ExtractQueryInputKeys<
|
|
257
|
-
FakeDoc<{ query?: { name?: unknown; email?: unknown } | null }>
|
|
258
|
-
>;
|
|
259
|
-
export const qk1: QIKeys1 = "name";
|
|
260
|
-
export const qk2: QIKeys1 = "email";
|
|
261
|
-
|
|
262
|
-
// =============================================================================
|
|
263
|
-
// 8. Missing $query — now fails (previously was "Mixed: $order only")
|
|
264
|
-
// =============================================================================
|
|
265
|
-
|
|
266
|
-
// ❌ Missing $query variable
|
|
267
|
-
type DocOrderOnly = FakeDoc<{
|
|
268
|
-
first?: number | null;
|
|
269
|
-
after?: string | null;
|
|
270
|
-
last?: number | null;
|
|
271
|
-
before?: string | null;
|
|
272
|
-
order?: readonly { field?: "name" | "email" | null }[] | null;
|
|
273
|
-
}>;
|
|
274
|
-
type Fail8 = ValidateCollectionQuery<DocOrderOnly, "name" | "email">;
|
|
275
|
-
type AssertFail8 = HasCollectionQueryError<Fail8> extends true ? true : never;
|
|
276
|
-
export const assertFail8: AssertFail8 = true;
|
|
277
|
-
|
|
278
|
-
// =============================================================================
|
|
279
|
-
// 9. Missing $order — now fails (previously was "Mixed: $query only")
|
|
280
|
-
// =============================================================================
|
|
281
|
-
|
|
282
|
-
// ❌ Missing $order variable
|
|
283
|
-
type DocQueryOnly = FakeDoc<{
|
|
284
|
-
first?: number | null;
|
|
285
|
-
after?: string | null;
|
|
286
|
-
last?: number | null;
|
|
287
|
-
before?: string | null;
|
|
288
|
-
query?: { name?: unknown; email?: unknown } | null;
|
|
289
|
-
}>;
|
|
290
|
-
type Fail9 = ValidateCollectionQuery<DocQueryOnly, "name" | "email">;
|
|
291
|
-
type AssertFail9 = HasCollectionQueryError<Fail9> extends true ? true : never;
|
|
292
|
-
export const assertFail9: AssertFail9 = true;
|
|
293
|
-
|
|
294
|
-
// ❌ Missing $order, and query keys don't cover metadata fields
|
|
295
|
-
type DocQueryOnlyMissing = FakeDoc<{
|
|
296
|
-
first?: number | null;
|
|
297
|
-
after?: string | null;
|
|
298
|
-
last?: number | null;
|
|
299
|
-
before?: string | null;
|
|
300
|
-
query?: { name?: unknown } | null;
|
|
301
|
-
}>;
|
|
302
|
-
type Fail9b = ValidateCollectionQuery<DocQueryOnlyMissing, "name" | "email">;
|
|
303
|
-
type AssertFail9b = HasCollectionQueryError<Fail9b> extends true ? true : never;
|
|
304
|
-
export const assertFail9b: AssertFail9b = true;
|
|
63
|
+
// Verify TestTable is assignable to TableMetadata
|
|
64
|
+
type AssertValidTable = TestTable extends TableMetadata ? true : never;
|
|
65
|
+
export const assertValidTable: AssertValidTable = true;
|
|
66
|
+
|
|
67
|
+
type TestQuery = BuildQueryVariables<TestTable>;
|
|
68
|
+
|
|
69
|
+
// ✅ String fields produce string operators
|
|
70
|
+
type AssertStringField = TestQuery extends {
|
|
71
|
+
title?: { eq?: string; contains?: string };
|
|
72
|
+
}
|
|
73
|
+
? true
|
|
74
|
+
: never;
|
|
75
|
+
export const assertStringField: AssertStringField = true;
|
|
76
|
+
|
|
77
|
+
// ✅ Number fields produce number operators with between
|
|
78
|
+
type AssertNumberField = TestQuery extends {
|
|
79
|
+
amount?: { eq?: number; gt?: number; between?: { min: number; max: number } };
|
|
80
|
+
}
|
|
81
|
+
? true
|
|
82
|
+
: never;
|
|
83
|
+
export const assertNumberField: AssertNumberField = true;
|
|
84
|
+
|
|
85
|
+
// ✅ Enum fields produce in/nin with string arrays
|
|
86
|
+
type AssertEnumField = TestQuery extends {
|
|
87
|
+
status?: { eq?: string; in?: string[]; nin?: string[] };
|
|
88
|
+
}
|
|
89
|
+
? true
|
|
90
|
+
: never;
|
|
91
|
+
export const assertEnumField: AssertEnumField = true;
|
|
92
|
+
|
|
93
|
+
// ✅ Boolean fields produce eq/ne operators
|
|
94
|
+
type AssertBooleanField = TestQuery extends {
|
|
95
|
+
isActive?: { eq?: boolean; ne?: boolean };
|
|
96
|
+
}
|
|
97
|
+
? true
|
|
98
|
+
: never;
|
|
99
|
+
export const assertBooleanField: AssertBooleanField = true;
|
|
100
|
+
|
|
101
|
+
// ✅ UUID fields produce eq/ne/in/nin operators
|
|
102
|
+
type AssertUuidField = TestQuery extends {
|
|
103
|
+
id?: { eq?: string; ne?: string; in?: string[]; nin?: string[] };
|
|
104
|
+
}
|
|
105
|
+
? true
|
|
106
|
+
: never;
|
|
107
|
+
export const assertUuidField: AssertUuidField = true;
|
|
108
|
+
|
|
109
|
+
// ✅ Datetime fields produce date operators
|
|
110
|
+
type AssertDatetimeField = TestQuery extends {
|
|
111
|
+
createdAt?: {
|
|
112
|
+
eq?: string;
|
|
113
|
+
gt?: string;
|
|
114
|
+
between?: { min: string; max: string };
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
? true
|
|
118
|
+
: never;
|
|
119
|
+
export const assertDatetimeField: AssertDatetimeField = true;
|
|
120
|
+
|
|
121
|
+
// ✅ Array and nested fields are excluded (not filterable)
|
|
122
|
+
type AssertNoArrayField = "tags" extends keyof TestQuery ? never : true;
|
|
123
|
+
export const assertNoArrayField: AssertNoArrayField = true;
|
|
124
|
+
|
|
125
|
+
type AssertNoNestedField = "config" extends keyof TestQuery ? never : true;
|
|
126
|
+
export const assertNoNestedField: AssertNoNestedField = true;
|
|
127
|
+
|
|
128
|
+
// =============================================================================
|
|
129
|
+
// 2. TableFieldName extracts all field names
|
|
130
|
+
// =============================================================================
|
|
131
|
+
|
|
132
|
+
type TestFieldNames = TableFieldName<TestTable>;
|
|
133
|
+
type AssertFieldNames =
|
|
134
|
+
| "id"
|
|
135
|
+
| "title"
|
|
136
|
+
| "amount"
|
|
137
|
+
| "status"
|
|
138
|
+
| "isActive"
|
|
139
|
+
| "createdAt"
|
|
140
|
+
| "tags"
|
|
141
|
+
| "config" extends TestFieldNames
|
|
142
|
+
? true
|
|
143
|
+
: never;
|
|
144
|
+
export const assertFieldNames: AssertFieldNames = true;
|
|
145
|
+
|
|
146
|
+
// =============================================================================
|
|
147
|
+
// 3. TableOrderableFieldName excludes non-orderable types
|
|
148
|
+
// =============================================================================
|
|
149
|
+
|
|
150
|
+
type TestOrderableNames = TableOrderableFieldName<TestTable>;
|
|
151
|
+
|
|
152
|
+
// uuid is NOT orderable
|
|
153
|
+
type AssertNoUuidOrder = "id" extends TestOrderableNames ? never : true;
|
|
154
|
+
export const assertNoUuidOrder: AssertNoUuidOrder = true;
|
|
155
|
+
|
|
156
|
+
// array and nested are NOT orderable
|
|
157
|
+
type AssertNoArrayOrder = "tags" extends TestOrderableNames ? never : true;
|
|
158
|
+
export const assertNoArrayOrder: AssertNoArrayOrder = true;
|
|
159
|
+
|
|
160
|
+
// string, number, enum, boolean, datetime ARE orderable
|
|
161
|
+
type AssertStringOrderable = "title" extends TestOrderableNames ? true : never;
|
|
162
|
+
export const assertStringOrderable: AssertStringOrderable = true;
|
|
163
|
+
|
|
164
|
+
type AssertNumberOrderable = "amount" extends TestOrderableNames ? true : never;
|
|
165
|
+
export const assertNumberOrderable: AssertNumberOrderable = true;
|
|
166
|
+
|
|
167
|
+
type AssertEnumOrderable = "status" extends TestOrderableNames ? true : never;
|
|
168
|
+
export const assertEnumOrderable: AssertEnumOrderable = true;
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
type ReactNode,
|
|
9
9
|
} from "react";
|
|
10
10
|
import { cn } from "../lib/utils";
|
|
11
|
-
import {
|
|
11
|
+
import { CollectionVariablesProvider } from "../collection/collection-provider";
|
|
12
12
|
import { Table } from "../table";
|
|
13
13
|
import type { RowAction, SortConfig, UseDataTableReturn } from "../types";
|
|
14
14
|
import {
|
|
@@ -53,14 +53,14 @@ function DataTableRoot({
|
|
|
53
53
|
/**
|
|
54
54
|
* Provider that shares `useDataTable()` state via React Context.
|
|
55
55
|
*
|
|
56
|
-
* Internally provides both `DataTableContext` and `
|
|
56
|
+
* Internally provides both `DataTableContext` and `CollectionVariablesContext`
|
|
57
57
|
* so that utility components (`Pagination`, `ColumnSelector`,
|
|
58
58
|
* `SearchFilterForm`, `CsvButton`) can consume data without explicit props.
|
|
59
59
|
*
|
|
60
60
|
* @example
|
|
61
61
|
* ```tsx
|
|
62
|
-
* const collection =
|
|
63
|
-
* const [result] = useQuery({ ...
|
|
62
|
+
* const { variables, ...collection } = useCollectionVariables({ params: { pageSize: 20 } });
|
|
63
|
+
* const [result] = useQuery({ query: GET_ORDERS, variables: { ...variables.pagination, query: variables.query, order: variables.order } });
|
|
64
64
|
* const table = useDataTable({ columns, data: result.data?.orders, collection });
|
|
65
65
|
*
|
|
66
66
|
* <DataTable.Provider value={table}>
|
|
@@ -110,10 +110,12 @@ function DataTableProviderComponent<TRow extends Record<string, unknown>>({
|
|
|
110
110
|
</DataTableContext.Provider>
|
|
111
111
|
);
|
|
112
112
|
|
|
113
|
-
// Wrap with
|
|
113
|
+
// Wrap with CollectionVariablesContext when collection is available
|
|
114
114
|
if (collectionValue) {
|
|
115
115
|
return (
|
|
116
|
-
<
|
|
116
|
+
<CollectionVariablesProvider value={collectionValue}>
|
|
117
|
+
{inner}
|
|
118
|
+
</CollectionVariablesProvider>
|
|
117
119
|
);
|
|
118
120
|
}
|
|
119
121
|
|
|
@@ -85,15 +85,18 @@ const en: DataTableLabels = {
|
|
|
85
85
|
eq: "=",
|
|
86
86
|
ne: "≠",
|
|
87
87
|
contains: "contains",
|
|
88
|
-
|
|
89
|
-
|
|
88
|
+
notContains: "not contains",
|
|
89
|
+
hasPrefix: "starts with",
|
|
90
|
+
hasSuffix: "ends with",
|
|
91
|
+
notHasPrefix: "not starts with",
|
|
92
|
+
notHasSuffix: "not ends with",
|
|
90
93
|
gt: ">",
|
|
91
94
|
gte: "≥",
|
|
92
95
|
lt: "<",
|
|
93
96
|
lte: "≤",
|
|
94
97
|
between: "between",
|
|
95
98
|
in: "in",
|
|
96
|
-
|
|
99
|
+
nin: "not in",
|
|
97
100
|
},
|
|
98
101
|
},
|
|
99
102
|
};
|
|
@@ -133,15 +136,18 @@ const ja: DataTableLabels = {
|
|
|
133
136
|
eq: "等しい",
|
|
134
137
|
ne: "等しくない",
|
|
135
138
|
contains: "含む",
|
|
136
|
-
|
|
137
|
-
|
|
139
|
+
notContains: "含まない",
|
|
140
|
+
hasPrefix: "で始まる",
|
|
141
|
+
hasSuffix: "で終わる",
|
|
142
|
+
notHasPrefix: "で始まらない",
|
|
143
|
+
notHasSuffix: "で終わらない",
|
|
138
144
|
gt: "より大きい",
|
|
139
145
|
gte: "以上",
|
|
140
146
|
lt: "より小さい",
|
|
141
147
|
lte: "以下",
|
|
142
148
|
between: "範囲内",
|
|
143
149
|
in: "含まれる",
|
|
144
|
-
|
|
150
|
+
nin: "含まれない",
|
|
145
151
|
},
|
|
146
152
|
},
|
|
147
153
|
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { useDataTableContext } from "./data-table-context";
|
|
2
|
-
import {
|
|
2
|
+
import { useCollectionVariablesContext } from "../collection/collection-provider";
|
|
3
3
|
import { getLabels } from "./i18n";
|
|
4
4
|
|
|
5
5
|
// =============================================================================
|
|
@@ -112,7 +112,7 @@ const btnClass =
|
|
|
112
112
|
/**
|
|
113
113
|
* Pagination controls with first/prev/next/last navigation and page indicator.
|
|
114
114
|
*
|
|
115
|
-
* Reads pagination state from `DataTableContext` and `
|
|
115
|
+
* Reads pagination state from `DataTableContext` and `CollectionVariablesContext`.
|
|
116
116
|
* Must be rendered inside `DataTable.Provider` but **outside** `DataTable.Root`.
|
|
117
117
|
*
|
|
118
118
|
* @example
|
|
@@ -139,7 +139,7 @@ export function Pagination({ labels, pageSizeOptions }: PaginationProps = {}) {
|
|
|
139
139
|
goToLastPage,
|
|
140
140
|
pageSize,
|
|
141
141
|
setPageSize,
|
|
142
|
-
} =
|
|
142
|
+
} = useCollectionVariablesContext();
|
|
143
143
|
|
|
144
144
|
const pl = getLabels(locale).pagination;
|
|
145
145
|
const firstLabel = labels?.first ?? pl.first;
|
|
@@ -14,7 +14,7 @@ import type {
|
|
|
14
14
|
} from "../types";
|
|
15
15
|
import { OPERATORS_BY_FILTER_TYPE } from "../types";
|
|
16
16
|
import { useDataTableContext } from "./data-table-context";
|
|
17
|
-
import {
|
|
17
|
+
import { useCollectionVariablesContext } from "../collection/collection-provider";
|
|
18
18
|
import { getLabels } from "./i18n";
|
|
19
19
|
|
|
20
20
|
/**
|
|
@@ -22,7 +22,7 @@ import { getLabels } from "./i18n";
|
|
|
22
22
|
*
|
|
23
23
|
* Renders a dropdown panel with type-specific filter inputs, operator
|
|
24
24
|
* selectors, and active filter badges. Reads `columns` from
|
|
25
|
-
* `DataTableContext` and filter state from `
|
|
25
|
+
* `DataTableContext` and filter state from `CollectionVariablesContext`.
|
|
26
26
|
* Must be rendered inside `DataTable.Provider`.
|
|
27
27
|
*
|
|
28
28
|
* All text is customisable through the optional `labels` prop (defaults
|
|
@@ -46,7 +46,7 @@ export function SearchFilterForm({
|
|
|
46
46
|
} = {}) {
|
|
47
47
|
const { columns, locale } = useDataTableContext();
|
|
48
48
|
const { filters, addFilter, removeFilter, clearFilters } =
|
|
49
|
-
|
|
49
|
+
useCollectionVariablesContext();
|
|
50
50
|
const sf = getLabels(locale).searchFilter;
|
|
51
51
|
const filterableColumns = columns.filter((col) => !!col.filter);
|
|
52
52
|
|
|
@@ -13,8 +13,12 @@ import type {
|
|
|
13
13
|
*
|
|
14
14
|
* @example
|
|
15
15
|
* ```tsx
|
|
16
|
-
* const collection =
|
|
17
|
-
* const
|
|
16
|
+
* const { variables, ...collection } = useCollectionVariables({ params: { pageSize: 20 } });
|
|
17
|
+
* const { query, order, pagination } = variables;
|
|
18
|
+
* const [result] = useQuery({
|
|
19
|
+
* query: GET_ORDERS,
|
|
20
|
+
* variables: { ...pagination, query, order },
|
|
21
|
+
* });
|
|
18
22
|
*
|
|
19
23
|
* const table = useDataTable<Order>({
|
|
20
24
|
* columns,
|
package/src/component/index.ts
CHANGED
|
@@ -8,6 +8,9 @@ export type {
|
|
|
8
8
|
SortState,
|
|
9
9
|
PageInfo,
|
|
10
10
|
QueryVariables,
|
|
11
|
+
PaginationVariables,
|
|
12
|
+
CollectionVariables,
|
|
13
|
+
BuildQueryVariables,
|
|
11
14
|
CollectionResult,
|
|
12
15
|
NodeType,
|
|
13
16
|
ColumnOptions,
|
|
@@ -23,11 +26,6 @@ export type {
|
|
|
23
26
|
TableFieldName,
|
|
24
27
|
OrderableFieldName,
|
|
25
28
|
TableOrderableFieldName,
|
|
26
|
-
ExtractOrderField,
|
|
27
|
-
ExtractQueryVariables,
|
|
28
|
-
ExtractQueryInputKeys,
|
|
29
|
-
ValidateCollectionQuery,
|
|
30
|
-
HasCollectionQueryError,
|
|
31
29
|
MatchingTableName,
|
|
32
30
|
MetadataFieldOptions,
|
|
33
31
|
MetadataFieldsOptions,
|
|
@@ -44,11 +42,10 @@ export {
|
|
|
44
42
|
} from "./types";
|
|
45
43
|
|
|
46
44
|
// Collection
|
|
47
|
-
export {
|
|
45
|
+
export { useCollectionVariables } from "./collection/use-collection";
|
|
48
46
|
export {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
useCollectionContext,
|
|
47
|
+
CollectionVariablesProvider,
|
|
48
|
+
useCollectionVariablesContext,
|
|
52
49
|
} from "./collection/collection-provider";
|
|
53
50
|
|
|
54
51
|
// Table (static)
|