@teselagen/ui 0.8.8 → 0.9.1

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.
Files changed (43) hide show
  1. package/DataTable/utils/filterLocalEntitiesToHasura.d.ts +5 -0
  2. package/DataTable/utils/getAllRows.d.ts +1 -1
  3. package/DataTable/utils/getRowCopyText.d.ts +1 -3
  4. package/DataTable/utils/handleCopyColumn.d.ts +1 -1
  5. package/DataTable/utils/handleCopyRows.d.ts +1 -5
  6. package/DataTable/utils/handleCopyTable.d.ts +1 -1
  7. package/DataTable/utils/index.d.ts +0 -1
  8. package/DataTable/utils/initializeHasuraWhereAndFilter.d.ts +1 -0
  9. package/DataTable/utils/queryParams.d.ts +16 -12
  10. package/DataTable/utils/rowClick.d.ts +1 -1
  11. package/DataTable/utils/tableQueryParamsToHasuraClauses.d.ts +26 -0
  12. package/FormComponents/Uploader.d.ts +1 -3
  13. package/FormComponents/tryToMatchSchemas.d.ts +1 -1
  14. package/MenuBar/index.d.ts +1 -3
  15. package/README.md +1 -1
  16. package/ResizableDraggableDialog/index.d.ts +1 -3
  17. package/TagSelect/index.d.ts +1 -1
  18. package/index.cjs.js +39715 -36506
  19. package/index.d.ts +2 -0
  20. package/index.es.js +38714 -35505
  21. package/package.json +2 -2
  22. package/src/DataTable/Columns.js +2 -2
  23. package/src/DataTable/DisplayOptions.js +1 -1
  24. package/src/DataTable/FilterAndSortMenu.js +27 -30
  25. package/src/DataTable/index.js +101 -84
  26. package/src/DataTable/style.css +1 -1
  27. package/src/DataTable/utils/filterLocalEntitiesToHasura.js +356 -0
  28. package/src/DataTable/utils/filterLocalEntitiesToHasura.test.js +1285 -0
  29. package/src/DataTable/utils/getAllRows.js +2 -6
  30. package/src/DataTable/utils/handleCopyColumn.js +2 -2
  31. package/src/DataTable/utils/handleCopyTable.js +2 -2
  32. package/src/DataTable/utils/initializeHasuraWhereAndFilter.js +15 -0
  33. package/src/DataTable/utils/queryParams.js +153 -770
  34. package/src/DataTable/utils/tableQueryParamsToHasuraClauses.js +277 -0
  35. package/src/DataTable/utils/tableQueryParamsToHasuraClauses.test.js +245 -0
  36. package/src/DataTable/utils/withTableParams.js +3 -16
  37. package/src/FormComponents/index.js +2 -2
  38. package/src/TgSelect/index.js +15 -0
  39. package/src/index.js +2 -0
  40. package/src/utils/determineBlackOrWhiteTextColor.js +8 -1
  41. package/ui.css +10537 -0
  42. package/utils/determineBlackOrWhiteTextColor.d.ts +1 -2
  43. package/utils/hotkeyUtils.d.ts +1 -3
@@ -0,0 +1,1285 @@
1
+ import { filterLocalEntitiesToHasura } from "./filterLocalEntitiesToHasura";
2
+
3
+ describe("filterLocalEntitiesToHasura", () => {
4
+ const records = [
5
+ {
6
+ id: 123,
7
+ name: "John Doe",
8
+ age: 30,
9
+ is_active: true,
10
+ city: "London",
11
+
12
+ tags: ["programming", "javascript"],
13
+ email: "john@example.com",
14
+ data: { category: "A", type: "X" },
15
+ username: "john123"
16
+ },
17
+ {
18
+ id: 456,
19
+ name: "Jane Smith",
20
+ age: 25,
21
+ is_active: false,
22
+ city: "Paris",
23
+ tags: ["javascript", "python"],
24
+ email: null,
25
+ data: { category: "B", type: "Y" },
26
+ username: "jane456"
27
+ },
28
+ {
29
+ id: 789,
30
+ name: "Alice Johnson",
31
+ age: 35,
32
+ is_active: true,
33
+ city: "London",
34
+ tags: ["programming", "python", "java"],
35
+ email: "alice@example.com",
36
+ data: { category: "A", type: "Z" },
37
+ username: "alice789"
38
+ },
39
+ {
40
+ id: 101,
41
+ name: "Bob Williams",
42
+ age: 20,
43
+ is_active: false,
44
+ city: "New York",
45
+ tags: ["java"],
46
+ email: "bob@example.com",
47
+ data: { category: "C", type: "X" },
48
+ username: "bob101"
49
+ }
50
+ ];
51
+
52
+ it("should filter by _eq", () => {
53
+ const result = filterLocalEntitiesToHasura(records, {
54
+ where: { id: { _eq: 123 } }
55
+ });
56
+ expect(result.entities).toEqual([records[0]]);
57
+ expect(result.entitiesAcrossPages).toEqual([records[0]]);
58
+ expect(result.entityCount).toBe(1);
59
+ });
60
+ it("should filter num fields by _eq", () => {
61
+ const result = filterLocalEntitiesToHasura(records, {
62
+ where: { age: { _eq: 30 } }
63
+ });
64
+ expect(result.entities).toEqual([records[0]]);
65
+ expect(result.entitiesAcrossPages).toEqual([records[0]]);
66
+ expect(result.entityCount).toBe(1);
67
+ });
68
+
69
+ it("should filter by _neq", () => {
70
+ const result = filterLocalEntitiesToHasura(records, {
71
+ where: { id: { _neq: 123 } }
72
+ });
73
+ expect(result.entities).toEqual(records.slice(1));
74
+ expect(result.entitiesAcrossPages).toEqual(records.slice(1));
75
+ expect(result.entityCount).toBe(3);
76
+ });
77
+
78
+ it("should filter by _gt", () => {
79
+ const result = filterLocalEntitiesToHasura(records, {
80
+ where: { age: { _gt: 30 } }
81
+ });
82
+ expect(result.entities).toEqual([records[2]]);
83
+ expect(result.entitiesAcrossPages).toEqual([records[2]]);
84
+ expect(result.entityCount).toBe(1);
85
+ });
86
+
87
+ it("should filter by _gte", () => {
88
+ const result = filterLocalEntitiesToHasura(records, {
89
+ where: { age: { _gte: 30 } }
90
+ });
91
+ expect(result.entities).toEqual([records[0], records[2]]);
92
+ expect(result.entitiesAcrossPages).toEqual([records[0], records[2]]);
93
+ expect(result.entityCount).toBe(2);
94
+ });
95
+
96
+ it("should filter by _lt", () => {
97
+ const result = filterLocalEntitiesToHasura(records, {
98
+ where: { age: { _lt: 25 } }
99
+ });
100
+ expect(result.entities).toEqual([records[3]]);
101
+ expect(result.entitiesAcrossPages).toEqual([records[3]]);
102
+ expect(result.entityCount).toBe(1);
103
+ });
104
+
105
+ it("should filter by _lte", () => {
106
+ const result = filterLocalEntitiesToHasura(records, {
107
+ where: { age: { _lte: 25 } }
108
+ });
109
+ expect(result.entities).toEqual([records[1], records[3]]);
110
+ expect(result.entitiesAcrossPages).toEqual([records[1], records[3]]);
111
+ expect(result.entityCount).toBe(2);
112
+ });
113
+
114
+ it("should filter by _like", () => {
115
+ const result = filterLocalEntitiesToHasura(records, {
116
+ where: { name: { _like: "%John%" } }
117
+ });
118
+ expect(result.entities).toEqual([records[0], records[2]]);
119
+ expect(result.entitiesAcrossPages).toEqual([records[0], records[2]]);
120
+ expect(result.entityCount).toBe(2);
121
+ });
122
+
123
+ it("should filter by _ilike", () => {
124
+ const result = filterLocalEntitiesToHasura(records, {
125
+ where: { name: { _ilike: "%john%" } }
126
+ });
127
+ expect(result.entities).toEqual([records[0], records[2]]);
128
+ expect(result.entitiesAcrossPages).toEqual([records[0], records[2]]);
129
+ expect(result.entityCount).toBe(2);
130
+ });
131
+
132
+ it("should filter by _nlike", () => {
133
+ const result = filterLocalEntitiesToHasura(records, {
134
+ where: { name: { _nlike: "%John%" } }
135
+ });
136
+ expect(result.entities).toEqual([records[1], records[3]]);
137
+ expect(result.entitiesAcrossPages).toEqual([records[1], records[3]]);
138
+ expect(result.entityCount).toBe(2);
139
+ });
140
+
141
+ it("should filter by _nilike", () => {
142
+ const result = filterLocalEntitiesToHasura(records, {
143
+ where: { name: { _nilike: "%john%" } }
144
+ });
145
+ expect(result.entities).toEqual([records[1], records[3]]);
146
+ expect(result.entitiesAcrossPages).toEqual([records[1], records[3]]);
147
+ expect(result.entityCount).toBe(2);
148
+ });
149
+
150
+ it("should filter by _starts_with", () => {
151
+ const result = filterLocalEntitiesToHasura(records, {
152
+ where: { name: { _starts_with: "John" } }
153
+ });
154
+ expect(result.entities).toEqual([records[0]]);
155
+ expect(result.entitiesAcrossPages).toEqual([records[0]]);
156
+ expect(result.entityCount).toBe(1);
157
+ });
158
+
159
+ it("should filter by _ends_with", () => {
160
+ const result = filterLocalEntitiesToHasura(records, {
161
+ where: { name: { _ends_with: "Doe" } }
162
+ });
163
+ expect(result.entities).toEqual([records[0]]);
164
+ expect(result.entitiesAcrossPages).toEqual([records[0]]);
165
+ expect(result.entityCount).toBe(1);
166
+ });
167
+
168
+ it("should filter by _is_null", () => {
169
+ const result = filterLocalEntitiesToHasura(records, {
170
+ where: { email: { _is_null: true } }
171
+ });
172
+ expect(result.entities).toEqual([records[1]]);
173
+ expect(result.entitiesAcrossPages).toEqual([records[1]]);
174
+ expect(result.entityCount).toBe(1);
175
+ });
176
+
177
+ it("should filter by _is_null false", () => {
178
+ const result = filterLocalEntitiesToHasura(records, {
179
+ where: { email: { _is_null: false } }
180
+ });
181
+ expect(result.entities).toEqual([records[0], records[2], records[3]]);
182
+ expect(result.entitiesAcrossPages).toEqual([
183
+ records[0],
184
+ records[2],
185
+ records[3]
186
+ ]);
187
+ expect(result.entityCount).toBe(3);
188
+ });
189
+
190
+ it("should filter by _and", () => {
191
+ const result = filterLocalEntitiesToHasura(records, {
192
+ where: {
193
+ _and: [{ age: { _gt: 25 } }, { city: { _eq: "London" } }]
194
+ }
195
+ });
196
+ expect(result.entities).toEqual([records[0], records[2]]);
197
+ expect(result.entitiesAcrossPages).toEqual([records[0], records[2]]);
198
+ expect(result.entityCount).toBe(2);
199
+ });
200
+
201
+ it("should filter by _or", () => {
202
+ const result = filterLocalEntitiesToHasura(records, {
203
+ where: {
204
+ _or: [{ city: { _eq: "London" } }, { city: { _eq: "Paris" } }]
205
+ }
206
+ });
207
+ expect(result.entities).toEqual([records[0], records[1], records[2]]);
208
+ expect(result.entitiesAcrossPages).toEqual([
209
+ records[0],
210
+ records[1],
211
+ records[2]
212
+ ]);
213
+ expect(result.entityCount).toBe(3);
214
+ });
215
+
216
+ it("should filter by _not", () => {
217
+ const result = filterLocalEntitiesToHasura(records, {
218
+ where: {
219
+ _not: { is_active: { _eq: true } }
220
+ }
221
+ });
222
+ expect(result.entities).toEqual([records[1], records[3]]);
223
+ expect(result.entitiesAcrossPages).toEqual([records[1], records[3]]);
224
+ expect(result.entityCount).toBe(2);
225
+ });
226
+
227
+ it("should filter by _contains", () => {
228
+ const result = filterLocalEntitiesToHasura(records, {
229
+ where: {
230
+ tags: { _contains: ["programming", "javascript"] }
231
+ }
232
+ });
233
+ expect(result.entities).toEqual([records[0]]);
234
+ expect(result.entitiesAcrossPages).toEqual([records[0]]);
235
+ expect(result.entityCount).toBe(1);
236
+ });
237
+
238
+ it("should filter by _contained_in", () => {
239
+ const result = filterLocalEntitiesToHasura(records, {
240
+ where: {
241
+ tags: { _contained_in: ["programming", "javascript", "python", "java"] }
242
+ }
243
+ });
244
+ expect(result.entities).toEqual(records);
245
+ expect(result.entitiesAcrossPages).toEqual(records);
246
+ expect(result.entityCount).toBe(4);
247
+ });
248
+
249
+ it("should filter by _has_key", () => {
250
+ const result = filterLocalEntitiesToHasura(records, {
251
+ where: { data: { _has_key: "category" } }
252
+ });
253
+ expect(result.entities).toEqual(records);
254
+ expect(result.entitiesAcrossPages).toEqual(records);
255
+ expect(result.entityCount).toBe(4);
256
+ });
257
+
258
+ it("should filter by _has_keys_any", () => {
259
+ const result = filterLocalEntitiesToHasura(records, {
260
+ where: {
261
+ data: { _has_keys_any: ["category", "missingKey"] }
262
+ }
263
+ });
264
+ expect(result.entities).toEqual(records);
265
+ expect(result.entitiesAcrossPages).toEqual(records);
266
+ expect(result.entityCount).toBe(4);
267
+ });
268
+
269
+ it("should filter by _has_keys_all", () => {
270
+ const result = filterLocalEntitiesToHasura(records, {
271
+ where: {
272
+ data: { _has_keys_all: ["category", "type"] }
273
+ }
274
+ });
275
+ expect(result.entities).toEqual(records);
276
+ expect(result.entitiesAcrossPages).toEqual(records);
277
+ expect(result.entityCount).toBe(4);
278
+ });
279
+
280
+ it("should filter by _similar", () => {
281
+ const result = filterLocalEntitiesToHasura(records, {
282
+ where: {
283
+ username: { _similar: "(john|alice)%" }
284
+ }
285
+ });
286
+ expect(result.entities).toEqual([records[0], records[2]]);
287
+ expect(result.entitiesAcrossPages).toEqual([records[0], records[2]]);
288
+ expect(result.entityCount).toBe(2);
289
+ });
290
+
291
+ it("should filter by range _gte and _lte", () => {
292
+ const result = filterLocalEntitiesToHasura(records, {
293
+ where: { age: { _gte: 25, _lte: 30 } }
294
+ });
295
+ expect(result.entities).toEqual([records[0], records[1]]);
296
+ expect(result.entitiesAcrossPages).toEqual([records[0], records[1]]);
297
+ expect(result.entityCount).toBe(2);
298
+ });
299
+
300
+ it("should filter by range _gt and _lt", () => {
301
+ const result = filterLocalEntitiesToHasura(records, {
302
+ where: { age: { _gt: 25, _lt: 35 } }
303
+ });
304
+ expect(result.entities).toEqual([records[0]]);
305
+ expect(result.entitiesAcrossPages).toEqual([records[0]]);
306
+ expect(result.entityCount).toBe(1);
307
+ });
308
+ it("should filter with nested filters in _and and _or properly ", () => {
309
+ const result = filterLocalEntitiesToHasura(
310
+ [
311
+ {
312
+ id: 1,
313
+ type: {
314
+ special: "01"
315
+ }
316
+ },
317
+ {
318
+ id: 2,
319
+ type: {
320
+ special: "02"
321
+ }
322
+ }
323
+ ],
324
+ {
325
+ where: {
326
+ _and: [
327
+ {
328
+ type: {
329
+ special: {
330
+ _ilike: "%01%"
331
+ }
332
+ }
333
+ }
334
+ ],
335
+ _or: []
336
+ }
337
+ }
338
+ );
339
+ expect(result.entities).toEqual([
340
+ {
341
+ id: 1,
342
+ type: {
343
+ special: "01"
344
+ }
345
+ }
346
+ ]);
347
+ expect(result.entitiesAcrossPages).toEqual([
348
+ {
349
+ id: 1,
350
+ type: {
351
+ special: "01"
352
+ }
353
+ }
354
+ ]);
355
+ expect(result.entityCount).toBe(1);
356
+ });
357
+
358
+ it("should handle empty where clause", () => {
359
+ const result = filterLocalEntitiesToHasura(records, {});
360
+ expect(result.entities).toEqual(records);
361
+ expect(result.entitiesAcrossPages).toEqual(records);
362
+ expect(result.entityCount).toBe(4);
363
+ });
364
+ it("should handle empty _and and _or clauses", () => {
365
+ const result = filterLocalEntitiesToHasura(records, {
366
+ where: { _and: [], _or: [] }
367
+ });
368
+ expect(result.entities).toEqual(records);
369
+ expect(result.entitiesAcrossPages).toEqual(records);
370
+ expect(result.entityCount).toBe(4);
371
+ });
372
+
373
+ it("should handle nested _and and _or", () => {
374
+ const result = filterLocalEntitiesToHasura(records, {
375
+ where: {
376
+ _and: [
377
+ { _or: [{ city: { _eq: "London" } }, { city: { _eq: "Paris" } }] },
378
+ { age: { _gt: 20 } }
379
+ ]
380
+ }
381
+ });
382
+ expect(result.entities).toEqual([records[0], records[1], records[2]]);
383
+ expect(result.entitiesAcrossPages).toEqual([
384
+ records[0],
385
+ records[1],
386
+ records[2]
387
+ ]);
388
+ expect(result.entityCount).toBe(3);
389
+ });
390
+ it("should order by age ascending and put null vals last by default", () => {
391
+ const result = filterLocalEntitiesToHasura(
392
+ [{ id: 111, name: "Null Age", age: null, city: "Unknown" }, ...records],
393
+ {
394
+ order_by: { path: "age", direction: "asc" }
395
+ }
396
+ );
397
+ expect(result.entities).toEqual([
398
+ records[3],
399
+ records[1],
400
+ records[0],
401
+ records[2],
402
+ { id: 111, name: "Null Age", age: null, city: "Unknown" }
403
+ ]);
404
+ expect(result.entitiesAcrossPages).toEqual([
405
+ records[3],
406
+ records[1],
407
+ records[0],
408
+ records[2],
409
+ { id: 111, name: "Null Age", age: null, city: "Unknown" }
410
+ ]);
411
+ expect(result.entityCount).toBe(5);
412
+ });
413
+ it("should not reorder ents if ordering on something that doesn't exist for any of them ", () => {
414
+ const ents = [
415
+ {
416
+ name: "Tom88",
417
+ id: "3JKXdPA4KiEqSqMswYXBE",
418
+ type: "fail",
419
+ howMany: "fail",
420
+ isProtein: true,
421
+ weather: "WAY TOO HOT"
422
+ },
423
+ {
424
+ name: "Tom89 a",
425
+ id: "18zbauHCMxFlkbGfGmRBn",
426
+ type: "too old",
427
+ howMany: "15",
428
+ isProtein: true,
429
+ weather: "cloudy"
430
+ },
431
+ {
432
+ name: "Tom90 a",
433
+ id: "Nj1AFBQ-GWZ7TEvhUOsFj",
434
+ type: "new",
435
+ howMany: "3",
436
+ isProtein: true,
437
+ weather: "HOT"
438
+ },
439
+ {
440
+ name: "Tom91 a",
441
+ id: "X0a4uOF8vwofrciUfQ8Zn",
442
+ type: "old",
443
+ howMany: 2,
444
+ isProtein: true,
445
+ weather: "cloudy"
446
+ },
447
+ {
448
+ name: "Tom92 a",
449
+ id: "rbeGUYOa6u8tsQgK2MQKv",
450
+ type: "new",
451
+ howMany: 5,
452
+ isProtein: true,
453
+ weather: "HOT"
454
+ },
455
+ {
456
+ name: "Tom93",
457
+ id: "Hq_SOIylSuFnQDyXZtojT",
458
+ type: "old",
459
+ howMany: 5,
460
+ isProtein: true,
461
+ weather: "rainy"
462
+ },
463
+ {
464
+ name: "Tom94 a",
465
+ id: "XOd1GtMqr1y-dzKknfxFc",
466
+ type: "old",
467
+ howMany: "3",
468
+ isProtein: true,
469
+ weather: "HOT"
470
+ },
471
+ {
472
+ name: "Tom95 a",
473
+ id: "Bw1rASgu9XLFC7DzTFw9K",
474
+ type: "old",
475
+ howMany: 5,
476
+ isProtein: true,
477
+ weather: "rainy"
478
+ },
479
+ {
480
+ name: "Tom96 a",
481
+ id: "j8pHKMzXi1n9Ce7roXP5f",
482
+ type: "new",
483
+ howMany: 40,
484
+ isProtein: true,
485
+ weather: "cloudy"
486
+ },
487
+ {
488
+ name: "Tom97 a",
489
+ id: "pLhv2XoDSy7tmmJI61f4-",
490
+ type: "new",
491
+ howMany: 40,
492
+ isProtein: true,
493
+ weather: "HOT"
494
+ },
495
+ {
496
+ name: "Tom98",
497
+ id: "I0WqsADFT9QxZVzEDrki0",
498
+ type: "old",
499
+ howMany: "3",
500
+ isProtein: true,
501
+ weather: "cloudy"
502
+ },
503
+ {
504
+ name: "Tom99 a",
505
+ id: "oTLzlqWtuYbcicyUn3LFl",
506
+ type: "new",
507
+ howMany: 2,
508
+ isProtein: true,
509
+ weather: "rainy"
510
+ },
511
+ {
512
+ name: "Tom100 a",
513
+ id: "M4piV2ZfizeWmf6ZKNr_4",
514
+ type: "new",
515
+ howMany: 40,
516
+ isProtein: true,
517
+ weather: "cloudy"
518
+ },
519
+ {
520
+ name: "Tom101 a",
521
+ id: "E1Yr5zaY41PrmAs2uC-be",
522
+ type: "old",
523
+ howMany: 40,
524
+ isProtein: true,
525
+ weather: "cloudy"
526
+ },
527
+ {
528
+ name: "Tom102 a",
529
+ id: "usnFso9ObH8RZpDmOyOv4",
530
+ type: "new",
531
+ howMany: 40,
532
+ isProtein: true,
533
+ weather: "cloudy"
534
+ },
535
+ {
536
+ name: "Tom103",
537
+ id: "_BRQ-t0CDnlSYU3ZJ73Ca",
538
+ type: "old",
539
+ howMany: 2,
540
+ isProtein: true,
541
+ weather: "rainy"
542
+ },
543
+ {
544
+ name: "Tom104 a",
545
+ id: "-wnRraVlIkY4RoaV5foeA",
546
+ type: "old",
547
+ howMany: "3",
548
+ isProtein: true,
549
+ weather: "rainy"
550
+ },
551
+ {
552
+ name: "Tom105 a",
553
+ id: "iY3jceZSdV6OrMytaKWS2",
554
+ type: "new",
555
+ howMany: 40,
556
+ isProtein: true,
557
+ weather: "HOT"
558
+ },
559
+ {
560
+ name: "Tom106 a",
561
+ id: "ZrZ0Vo9qm5cu9Zf6sHZsd",
562
+ type: "new",
563
+ howMany: "3",
564
+ isProtein: true,
565
+ weather: "HOT"
566
+ },
567
+ {
568
+ name: "Tom107 a",
569
+ id: "wvmW-7Wy_WtCAAOK6mnwr",
570
+ type: "new",
571
+ howMany: 5,
572
+ isProtein: true,
573
+ weather: "rainy"
574
+ },
575
+ {
576
+ name: "Nancy108",
577
+ id: "c-ht1uCS44JFSHekDvbRj",
578
+ type: "new",
579
+ howMany: 2,
580
+ isProtein: true,
581
+ weather: "cloudy"
582
+ },
583
+ {
584
+ name: "Nancy109",
585
+ id: "Oi3cFzKPebr7ZzYJu68w3",
586
+ type: "new",
587
+ howMany: 40,
588
+ isProtein: true,
589
+ weather: "HOT"
590
+ },
591
+ {
592
+ name: "Nancy110",
593
+ id: "0z2Z29YBISe41V7DL1JYJ",
594
+ type: "too old",
595
+ howMany: 2,
596
+ isProtein: true,
597
+ weather: "HOT"
598
+ },
599
+ {
600
+ name: "Nancy111",
601
+ id: "kaHP6YFgq4rNVsRpjkH5D",
602
+ type: "new",
603
+ howMany: 2,
604
+ isProtein: true,
605
+ weather: "HOT"
606
+ },
607
+ {
608
+ name: "Nancy112",
609
+ id: "C-XhKDnDXmdbJ-JXnj-yn",
610
+ type: "old",
611
+ howMany: 5,
612
+ isProtein: true,
613
+ weather: "HOT"
614
+ },
615
+ {
616
+ name: "Agnes Byrd",
617
+ id: "21Jd9-PxHBoHIXJ1j-Ei0",
618
+ type: "new",
619
+ howMany: 2,
620
+ isProtein: true,
621
+ weather: "HOT"
622
+ },
623
+ {
624
+ name: "Christopher Parks",
625
+ id: "jzP3oWirGfd4_c5KH2VmJ",
626
+ type: "old",
627
+ howMany: 2,
628
+ isProtein: true,
629
+ weather: "cloudy"
630
+ },
631
+ {
632
+ name: "Emily Hicks",
633
+ id: "rV4rRcgFKOn3AhIvNgs8R",
634
+ type: "new",
635
+ howMany: "3",
636
+ isProtein: true,
637
+ weather: "cloudy"
638
+ },
639
+ {
640
+ name: "Amanda Quinn",
641
+ id: "m29qs7y5yrlF2t_byAjRn",
642
+ type: "old",
643
+ howMany: 5,
644
+ isProtein: true,
645
+ weather: "HOT"
646
+ },
647
+ {
648
+ name: "Ola Roberts",
649
+ id: "DqjIk7lzU6_8cw3EAkQMN",
650
+ type: "old",
651
+ howMany: 40,
652
+ isProtein: true,
653
+ weather: "rainy"
654
+ },
655
+ {
656
+ name: "Theresa Rogers",
657
+ id: "XsUemLHKc5RpMmYiV3UeT",
658
+ type: "new",
659
+ howMany: 2,
660
+ isProtein: true,
661
+ weather: "cloudy"
662
+ },
663
+ {
664
+ name: "Sylvia Jackson",
665
+ id: "t7RhaB3SRKqkKSwAkMjwo",
666
+ type: "old",
667
+ howMany: 2,
668
+ isProtein: true,
669
+ weather: "cloudy"
670
+ },
671
+ {
672
+ name: "Eugene Crawford",
673
+ id: "gogZgycHEYGE8pQzFfKpn",
674
+ type: "old",
675
+ howMany: 40,
676
+ isProtein: true,
677
+ weather: "rainy"
678
+ },
679
+ {
680
+ name: "Josie Rodgers",
681
+ id: "P4gDKi3su3kBT2X8Nsoj5",
682
+ type: "old",
683
+ howMany: 2,
684
+ isProtein: true,
685
+ weather: "cloudy"
686
+ },
687
+ {
688
+ name: "Dale Nelson",
689
+ id: "cxNmge4Mr3emklYh-mYKO",
690
+ type: "old",
691
+ howMany: 2,
692
+ isProtein: true,
693
+ weather: "rainy"
694
+ },
695
+ {
696
+ name: "Vera Tran",
697
+ id: "BsNyrA-qstXd66wS-Z0hC",
698
+ type: "new",
699
+ howMany: 5,
700
+ isProtein: true,
701
+ weather: "rainy"
702
+ },
703
+ {
704
+ name: "Blake Tate",
705
+ id: "pAgunZo2FNcc_zHGxCs-1",
706
+ type: "old",
707
+ howMany: 2,
708
+ isProtein: true,
709
+ weather: "rainy"
710
+ },
711
+ {
712
+ name: "Amy Harrison",
713
+ id: "xn21FFjVyMD00qUMmHQ-k",
714
+ type: "new",
715
+ howMany: 40,
716
+ isProtein: true,
717
+ weather: "rainy"
718
+ },
719
+ {
720
+ name: "Raymond Swanson",
721
+ id: "lhBo3hBJYQKnhs7BIHaW0",
722
+ type: "old",
723
+ howMany: "3",
724
+ isProtein: true,
725
+ weather: "HOT"
726
+ },
727
+ {
728
+ name: "Kevin Tate",
729
+ id: "kgCPxens2N-AqSE41_CdM",
730
+ type: "old",
731
+ howMany: 40,
732
+ isProtein: true,
733
+ weather: "cloudy"
734
+ },
735
+ {
736
+ name: "Lenora Garrett",
737
+ id: "QsaiEKpRNErq_ZjFkedc2",
738
+ type: "old",
739
+ howMany: 5,
740
+ isProtein: true,
741
+ weather: "cloudy"
742
+ },
743
+ {
744
+ name: "Erik Spencer",
745
+ id: "fDzD7BVxS0W1F2e4AUmGm",
746
+ type: "old",
747
+ howMany: "3",
748
+ isProtein: true,
749
+ weather: "rainy"
750
+ },
751
+ {
752
+ name: "Mildred Mann",
753
+ id: "n3Vis7omDyOrAVeHToRt1",
754
+ type: "old",
755
+ howMany: 40,
756
+ isProtein: true,
757
+ weather: "HOT"
758
+ },
759
+ {
760
+ name: "Glen Wilkerson",
761
+ id: "cPK9vv-JfHBmNUwn8wUZI",
762
+ type: "old",
763
+ howMany: 40,
764
+ isProtein: true,
765
+ weather: "cloudy"
766
+ },
767
+ {
768
+ name: "Ida Alvarado",
769
+ id: "EkqcC3Lxn5O4EnebszsOu",
770
+ type: "new",
771
+ howMany: "3",
772
+ isProtein: true,
773
+ weather: "rainy"
774
+ },
775
+ {
776
+ name: "Hilda Miller",
777
+ id: "PVbWALmDv9bqr6kmsMP6g",
778
+ type: "new",
779
+ howMany: 5,
780
+ isProtein: true,
781
+ weather: "cloudy"
782
+ },
783
+ {
784
+ name: "Ella Douglas",
785
+ id: "HjMy4vzzloDP9zl2EtjYv",
786
+ type: "old",
787
+ howMany: "3",
788
+ isProtein: true,
789
+ weather: "cloudy"
790
+ },
791
+ {
792
+ name: "Leonard McKenzie",
793
+ id: "ZX8GMPeZMlN6_dfANi-NN",
794
+ type: "new",
795
+ howMany: "3",
796
+ isProtein: true,
797
+ weather: "rainy"
798
+ },
799
+ {
800
+ name: "Lora Love",
801
+ id: "EydnrigpkrEERB5cygoWx",
802
+ type: "new",
803
+ howMany: "3",
804
+ isProtein: true,
805
+ weather: "cloudy"
806
+ },
807
+ {
808
+ name: "Charlotte Ford",
809
+ id: "1lM9fZSEWpFl6PiNf4XkJ",
810
+ type: "old",
811
+ howMany: 40,
812
+ isProtein: true,
813
+ weather: "cloudy"
814
+ }
815
+ ];
816
+ const result = filterLocalEntitiesToHasura([...ents], {
817
+ order_by: { path: "updatedAt", direction: "desc" }
818
+ });
819
+ expect(result.entities).toEqual(ents);
820
+ });
821
+ it("should order by age desc and put null/undefined vals last by default", () => {
822
+ const result = filterLocalEntitiesToHasura(
823
+ [
824
+ { id: 111, name: "Null Age", age: null, city: "Unknown" },
825
+ { id: 1121, name: "Undefined Age", age: undefined, city: "Unknown" },
826
+ ...records
827
+ ],
828
+ {
829
+ order_by: { path: "age", direction: "desc" }
830
+ }
831
+ );
832
+ expect(result.entities).toEqual([
833
+ records[2],
834
+ records[0],
835
+ records[1],
836
+ records[3],
837
+ { id: 111, name: "Null Age", age: null, city: "Unknown" },
838
+ { id: 1121, name: "Undefined Age", age: undefined, city: "Unknown" }
839
+ ]);
840
+ expect(result.entityCount).toBe(6);
841
+ });
842
+
843
+ it("should order by updatedAt descending, putting null/undefined vals last ", () => {
844
+ const result = filterLocalEntitiesToHasura(
845
+ [
846
+ {
847
+ name: "-10_signal",
848
+ color: "#4ECDC4",
849
+ isGenbankStandardType: true,
850
+ __typename: "featureTypeOverride"
851
+ },
852
+ {
853
+ name: "-35_signal",
854
+ color: "#F7FFF7",
855
+ isGenbankStandardType: true,
856
+ __typename: "featureTypeOverride"
857
+ },
858
+ {
859
+ name: "3'clip",
860
+ color: "#FF6B6B",
861
+ isGenbankStandardType: true,
862
+ __typename: "featureTypeOverride"
863
+ },
864
+ {
865
+ name: "3'UTR",
866
+ color: "#FFE66D",
867
+ isGenbankStandardType: true,
868
+ __typename: "featureTypeOverride"
869
+ },
870
+
871
+ {
872
+ __typename: "featureTypeOverride",
873
+ id: "33a90fcb-fc26-406f-a6d5-41ac4ba8ea64",
874
+ name: "lalala",
875
+ description: null,
876
+ color: "#81bb41",
877
+ genbankEquivalentType: null,
878
+ updatedAt: "2025-06-03T01:02:24.737499+00:00",
879
+ isHidden: false,
880
+ isCustomType: true
881
+ }
882
+ ],
883
+ {
884
+ order_by: [{ path: "updatedAt", direction: "desc", type: "timestamp" }]
885
+ }
886
+ );
887
+ expect(result.entities).toEqual([
888
+ {
889
+ __typename: "featureTypeOverride",
890
+ id: "33a90fcb-fc26-406f-a6d5-41ac4ba8ea64",
891
+ name: "lalala",
892
+ description: null,
893
+ color: "#81bb41",
894
+ genbankEquivalentType: null,
895
+ updatedAt: "2025-06-03T01:02:24.737499+00:00",
896
+ isHidden: false,
897
+ isCustomType: true
898
+ },
899
+ {
900
+ name: "-10_signal",
901
+ color: "#4ECDC4",
902
+ isGenbankStandardType: true,
903
+ __typename: "featureTypeOverride"
904
+ },
905
+ {
906
+ name: "-35_signal",
907
+ color: "#F7FFF7",
908
+ isGenbankStandardType: true,
909
+ __typename: "featureTypeOverride"
910
+ },
911
+ {
912
+ name: "3'clip",
913
+ color: "#FF6B6B",
914
+ isGenbankStandardType: true,
915
+ __typename: "featureTypeOverride"
916
+ },
917
+ {
918
+ name: "3'UTR",
919
+ color: "#FFE66D",
920
+ isGenbankStandardType: true,
921
+ __typename: "featureTypeOverride"
922
+ }
923
+ ]);
924
+ });
925
+ it("should order by age descending, putting null/undefined vals last ", () => {
926
+ const result = filterLocalEntitiesToHasura(
927
+ [
928
+ { id: 111, name: "Null Age", age: null, city: "Unknown" },
929
+ { id: 1121, name: "Undefined Age", age: undefined, city: "Unknown" },
930
+ ...records
931
+ ],
932
+ {
933
+ order_by: { path: "age", direction: "desc" }
934
+ }
935
+ );
936
+ expect(result.entities).toEqual([
937
+ records[2],
938
+ records[0],
939
+ records[1],
940
+ records[3],
941
+ { id: 111, name: "Null Age", age: null, city: "Unknown" },
942
+ { id: 1121, name: "Undefined Age", age: undefined, city: "Unknown" }
943
+ ]);
944
+ });
945
+
946
+ it("should order by name ascending", () => {
947
+ const result = filterLocalEntitiesToHasura(records, {
948
+ order_by: { path: "name", direction: "asc" }
949
+ });
950
+ expect(result.entities).toEqual([
951
+ records[2],
952
+ records[3],
953
+ records[1],
954
+ records[0]
955
+ ]);
956
+ expect(result.entitiesAcrossPages).toEqual([
957
+ records[2],
958
+ records[3],
959
+ records[1],
960
+ records[0]
961
+ ]);
962
+ expect(result.entityCount).toBe(4);
963
+ });
964
+
965
+ it("should order by name descending", () => {
966
+ const result = filterLocalEntitiesToHasura(records, {
967
+ order_by: { path: "name", direction: "desc" }
968
+ });
969
+ expect(result.entities).toEqual([
970
+ records[0],
971
+ records[1],
972
+ records[3],
973
+ records[2]
974
+ ]);
975
+ expect(result.entitiesAcrossPages).toEqual([
976
+ records[0],
977
+ records[1],
978
+ records[3],
979
+ records[2]
980
+ ]);
981
+ expect(result.entityCount).toBe(4);
982
+ });
983
+
984
+ it("should filter and order", () => {
985
+ const result = filterLocalEntitiesToHasura(records, {
986
+ where: { city: { _eq: "London" } },
987
+ order_by: { path: "age", direction: "desc" }
988
+ });
989
+ expect(result.entities).toEqual([records[2], records[0]]);
990
+ expect(result.entitiesAcrossPages).toEqual([records[2], records[0]]);
991
+ expect(result.entityCount).toBe(2);
992
+ });
993
+
994
+ it("should handle empty order_by", () => {
995
+ const result = filterLocalEntitiesToHasura(records, {
996
+ where: { city: { _eq: "London" } }
997
+ });
998
+ expect(result.entities).toEqual([records[0], records[2]]);
999
+ expect(result.entitiesAcrossPages).toEqual([records[0], records[2]]);
1000
+ expect(result.entityCount).toBe(2);
1001
+ });
1002
+
1003
+ it("should handle order_by with empty where", () => {
1004
+ const result = filterLocalEntitiesToHasura(records, {
1005
+ order_by: { path: "age", direction: "asc" }
1006
+ });
1007
+ expect(result.entities).toEqual([
1008
+ records[3],
1009
+ records[1],
1010
+ records[0],
1011
+ records[2]
1012
+ ]);
1013
+ expect(result.entitiesAcrossPages).toEqual([
1014
+ records[3],
1015
+ records[1],
1016
+ records[0],
1017
+ records[2]
1018
+ ]);
1019
+ expect(result.entityCount).toBe(4);
1020
+ });
1021
+
1022
+ it("should apply limit", () => {
1023
+ const result = filterLocalEntitiesToHasura(records, { limit: 2 });
1024
+ expect(result.entities).toEqual([records[0], records[1]]);
1025
+ expect(result.entitiesAcrossPages).toEqual(records);
1026
+ expect(result.entityCount).toBe(4);
1027
+ });
1028
+
1029
+ it("should apply offset", () => {
1030
+ const result = filterLocalEntitiesToHasura(records, { offset: 2 });
1031
+ expect(result.entities).toEqual([records[2], records[3]]);
1032
+ expect(result.entitiesAcrossPages).toEqual(records);
1033
+ expect(result.entityCount).toBe(4);
1034
+ });
1035
+
1036
+ it("should apply limit and offset", () => {
1037
+ const result = filterLocalEntitiesToHasura(records, {
1038
+ limit: 1,
1039
+ offset: 2
1040
+ });
1041
+ expect(result.entities).toEqual([records[2]]);
1042
+ expect(result.entitiesAcrossPages).toEqual(records);
1043
+ expect(result.entityCount).toBe(4);
1044
+ });
1045
+
1046
+ it("should apply limit to filtered results", () => {
1047
+ const result = filterLocalEntitiesToHasura(records, {
1048
+ where: { city: { _eq: "London" } },
1049
+ limit: 1
1050
+ });
1051
+ expect(result.entities).toEqual([records[0]]);
1052
+ expect(result.entitiesAcrossPages).toEqual([records[0], records[2]]);
1053
+ expect(result.entityCount).toBe(2);
1054
+ });
1055
+
1056
+ it("should apply offset to filtered results", () => {
1057
+ const result = filterLocalEntitiesToHasura(records, {
1058
+ where: { city: { _eq: "London" } },
1059
+ offset: 1
1060
+ });
1061
+ expect(result.entities).toEqual([records[2]]);
1062
+ expect(result.entitiesAcrossPages).toEqual([records[0], records[2]]);
1063
+ expect(result.entityCount).toBe(2);
1064
+ });
1065
+ it("should order order well names properly, aka names a1, a11, a2, a3 should be sorted a1, a2, a3, a11", () => {
1066
+ const recordsWithNames = [
1067
+ { id: 1, name: "a1" },
1068
+ { id: 2, name: "a11" },
1069
+ { id: 3, name: "a2" },
1070
+ { id: 4, name: "a3" }
1071
+ ];
1072
+ const result = filterLocalEntitiesToHasura(recordsWithNames, {
1073
+ order_by: { path: "name", direction: "asc" }
1074
+ });
1075
+ expect(result.entities).toEqual([
1076
+ recordsWithNames[0], // a1
1077
+ recordsWithNames[2], // a2
1078
+ recordsWithNames[3], // a3
1079
+ recordsWithNames[1] // a11
1080
+ ]);
1081
+ expect(result.entityCount).toBe(4);
1082
+ });
1083
+ it("should order by multiple fields (age asc, name desc)", () => {
1084
+ // Make two of the ages the same for testing secondary sort
1085
+ const modifiedRecords = [
1086
+ records[1], // Jane Smith, age 25
1087
+ { ...records[0], age: 25 }, // John Doe, age 25
1088
+ records[2], // Alice Johnson, age 35
1089
+ records[3] // Bob Williams, age 20
1090
+ ];
1091
+ const result = filterLocalEntitiesToHasura(modifiedRecords, {
1092
+ order_by: [
1093
+ { path: "age", direction: "asc" },
1094
+ { path: "name", direction: "desc" }
1095
+ ]
1096
+ });
1097
+ expect(result.entities).toEqual([
1098
+ modifiedRecords[3], // age 20, name "Bob Williams"
1099
+ modifiedRecords[1], // age 25, name "John Doe" (name desc)
1100
+ modifiedRecords[0], // age 25, name "Jane Smith"
1101
+ modifiedRecords[2] // age 35, name "Alice Johnson"
1102
+ ]);
1103
+ expect(result.entityCount).toBe(4);
1104
+ });
1105
+ it("should apply limit and offset to filtered and ordered results", () => {
1106
+ const result = filterLocalEntitiesToHasura(records, {
1107
+ where: { city: { _eq: "London" } },
1108
+ order_by: { path: "age", direction: "desc" },
1109
+ limit: 1,
1110
+ offset: 1
1111
+ });
1112
+ expect(result.entities).toEqual([records[0]]);
1113
+ expect(result.entitiesAcrossPages).toEqual([records[2], records[0]]);
1114
+ expect(result.entityCount).toBe(2);
1115
+ });
1116
+
1117
+ it("should handle offset greater than array length", () => {
1118
+ const result = filterLocalEntitiesToHasura(records, { offset: 10 });
1119
+ expect(result.entities).toEqual([]);
1120
+ expect(result.entitiesAcrossPages).toEqual(records);
1121
+ expect(result.entityCount).toBe(4);
1122
+ });
1123
+
1124
+ it("should handle limit greater than array length", () => {
1125
+ const result = filterLocalEntitiesToHasura(records, { limit: 10 });
1126
+ expect(result.entities).toEqual(records);
1127
+ expect(result.entitiesAcrossPages).toEqual(records);
1128
+ expect(result.entityCount).toBe(4);
1129
+ });
1130
+
1131
+ it("should handle isInfinite option with filtering", () => {
1132
+ const result = filterLocalEntitiesToHasura(records, {
1133
+ where: { city: { _eq: "London" } },
1134
+ isInfinite: true
1135
+ });
1136
+ expect(result.entities).toEqual([records[0], records[2]]);
1137
+ expect(result.entitiesAcrossPages).toEqual([records[0], records[2]]);
1138
+ expect(result.entityCount).toBe(2);
1139
+ });
1140
+
1141
+ it("should handle isInfinite option with filtering, limit, and offset", () => {
1142
+ const result = filterLocalEntitiesToHasura(records, {
1143
+ where: { city: { _eq: "London" } },
1144
+ limit: 1,
1145
+ offset: 1,
1146
+ isInfinite: true
1147
+ });
1148
+ expect(result.entities).toEqual([records[0], records[2]]);
1149
+ expect(result.entitiesAcrossPages).toEqual([records[0], records[2]]);
1150
+ expect(result.entityCount).toBe(2);
1151
+ });
1152
+ it("should order using custom sortFn", () => {
1153
+ // Create a custom sort function that sorts by the length of the name
1154
+ const sortByNameLength = record => record.name.length;
1155
+
1156
+ const result = filterLocalEntitiesToHasura(records, {
1157
+ order_by: { sortFn: sortByNameLength, direction: "asc" }
1158
+ });
1159
+
1160
+ // Expected order: "Bob Williams" (12), "John Doe" (8), "Jane Smith" (10), "Alice Johnson" (13)
1161
+ expect(result.entities).toEqual([
1162
+ records[0], // John Doe (8 chars)
1163
+ records[1], // Jane Smith (10 chars)
1164
+ records[3], // Bob Williams (12 chars)
1165
+ records[2] // Alice Johnson (13 chars)
1166
+ ]);
1167
+ expect(result.entityCount).toBe(4);
1168
+ });
1169
+
1170
+ it("should order using multiple custom sortFn functions", () => {
1171
+ // Sort first by whether the user is active, then by name length
1172
+ const sortByActive = record => (record.is_active ? 1 : 0);
1173
+ const sortByNameLength = record => record.name.length;
1174
+
1175
+ const result = filterLocalEntitiesToHasura(records, {
1176
+ order_by: { sortFn: [sortByActive, sortByNameLength], direction: "desc" }
1177
+ });
1178
+
1179
+ // First active users (John, Alice) sorted by name length DESC
1180
+ // Then inactive users (Jane, Bob) sorted by name length DESC
1181
+ expect(result.entities).toEqual([
1182
+ records[2], // Alice Johnson (active, 13 chars)
1183
+ records[0], // John Doe (active, 8 chars)
1184
+ records[3], // Bob Williams (inactive, 12 chars)
1185
+ records[1] // Jane Smith (inactive, 10 chars)
1186
+ ]);
1187
+ expect(result.entityCount).toBe(4);
1188
+ });
1189
+
1190
+ it("should handle custom sortFn with null values", () => {
1191
+ // Create test records with some null values
1192
+ const recordsWithNulls = [
1193
+ { id: 1, value: 10, name: "Item 1" },
1194
+ { id: 2, value: null, name: "Item 2" },
1195
+ { id: 3, value: 30, name: "Item 3" },
1196
+ { id: 4, value: 20, name: "Item 4" },
1197
+ { id: 5, value: null, name: "Item 5" }
1198
+ ];
1199
+
1200
+ // Sort function that handles null values
1201
+ const sortByValue = record => {
1202
+ return record.value === null ? -Infinity : record.value;
1203
+ };
1204
+
1205
+ const result = filterLocalEntitiesToHasura(recordsWithNulls, {
1206
+ order_by: { sortFn: sortByValue, direction: "desc" }
1207
+ });
1208
+
1209
+ // Nulls should appear at the end when sorting in descending order
1210
+ expect(result.entities).toEqual([
1211
+ recordsWithNulls[2], // value: 30
1212
+ recordsWithNulls[3], // value: 20
1213
+ recordsWithNulls[0], // value: 10
1214
+ recordsWithNulls[1], // value: null
1215
+ recordsWithNulls[4] // value: null
1216
+ ]);
1217
+ expect(result.entityCount).toBe(5);
1218
+ });
1219
+ it("should order using array of string paths in sortFn", () => {
1220
+ // Records with rowPosition and columnPosition properties
1221
+ const gridRecords = [
1222
+ { id: 1, name: "Item A", rowPosition: 2, columnPosition: 3 },
1223
+ { id: 2, name: "Item B", rowPosition: 1, columnPosition: 2 },
1224
+ { id: 3, name: "Item C", rowPosition: 1, columnPosition: 1 },
1225
+ { id: 4, name: "Item D", rowPosition: 2, columnPosition: 1 },
1226
+ { id: 5, name: "Item E", rowPosition: 0, columnPosition: 0 }
1227
+ ];
1228
+
1229
+ const result = filterLocalEntitiesToHasura(gridRecords, {
1230
+ order_by: [
1231
+ { path: "rowPosition", direction: "asc" },
1232
+ { path: "columnPosition", direction: "asc" }
1233
+ ]
1234
+ });
1235
+
1236
+ // Should sort first by rowPosition ascending, then by columnPosition ascending
1237
+ expect(result.entities).toEqual([
1238
+ gridRecords[4], // rowPos: 0, colPos: 0
1239
+ gridRecords[2], // rowPos: 1, colPos: 1
1240
+ gridRecords[1], // rowPos: 1, colPos: 2
1241
+ gridRecords[3], // rowPos: 2, colPos: 1
1242
+ gridRecords[0] // rowPos: 2, colPos: 3
1243
+ ]);
1244
+ expect(result.entitiesAcrossPages).toEqual([
1245
+ gridRecords[4],
1246
+ gridRecords[2],
1247
+ gridRecords[1],
1248
+ gridRecords[3],
1249
+ gridRecords[0]
1250
+ ]);
1251
+ expect(result.entityCount).toBe(5);
1252
+ });
1253
+ it("should handle nulls and missing properties with array of string paths in sortFn", () => {
1254
+ // Records with some missing or null rowPosition and columnPosition properties
1255
+ const gridRecords = [
1256
+ { id: 1, name: "Item A", rowPosition: 2, columnPosition: 3 },
1257
+ { id: 2, name: "Item B", rowPosition: null, columnPosition: 2 },
1258
+ { id: 3, name: "Item C", rowPosition: 1, columnPosition: null },
1259
+ { id: 4, name: "Item D" }, // Missing both properties
1260
+ { id: 5, name: "Item E", rowPosition: 0, columnPosition: 0 }
1261
+ ];
1262
+
1263
+ const result = filterLocalEntitiesToHasura(gridRecords, {
1264
+ order_by: { sortFn: ["rowPosition", "columnPosition"], direction: "asc" }
1265
+ });
1266
+
1267
+ // Should sort first by rowPosition ascending, then by columnPosition ascending
1268
+ // Null or undefined values should be placed at the end
1269
+ expect(result.entities).toEqual([
1270
+ gridRecords[4], // rowPos: 0, colPos: 0
1271
+ gridRecords[2], // rowPos: 1, colPos: null
1272
+ gridRecords[0], // rowPos: 2, colPos: 3
1273
+ gridRecords[1], // rowPos: null, colPos: 2
1274
+ gridRecords[3] // rowPos: undefined, colPos: undefined
1275
+ ]);
1276
+ expect(result.entitiesAcrossPages).toEqual([
1277
+ gridRecords[4],
1278
+ gridRecords[2],
1279
+ gridRecords[0],
1280
+ gridRecords[1],
1281
+ gridRecords[3]
1282
+ ]);
1283
+ expect(result.entityCount).toBe(5);
1284
+ });
1285
+ });