@teselagen/ui 0.8.6-beta.13 → 0.8.6-beta.15

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.
@@ -383,7 +383,7 @@ describe("filterLocalEntitiesToHasura", () => {
383
383
  const result = filterLocalEntitiesToHasura(
384
384
  [{ id: 111, name: "Null Age", age: null, city: "Unknown" }, ...records],
385
385
  {
386
- order_by: { age: "asc" }
386
+ order_by: { path: "age", direction: "asc" }
387
387
  }
388
388
  );
389
389
  expect(result.entities).toEqual([
@@ -402,6 +402,414 @@ describe("filterLocalEntitiesToHasura", () => {
402
402
  ]);
403
403
  expect(result.entityCount).toBe(5);
404
404
  });
405
+ it("should not reorder ents if ordering on something that doesn't exist for any of them ", () => {
406
+ const ents = [
407
+ {
408
+ name: "Tom88",
409
+ id: "3JKXdPA4KiEqSqMswYXBE",
410
+ type: "fail",
411
+ howMany: "fail",
412
+ isProtein: true,
413
+ weather: "WAY TOO HOT"
414
+ },
415
+ {
416
+ name: "Tom89 a",
417
+ id: "18zbauHCMxFlkbGfGmRBn",
418
+ type: "too old",
419
+ howMany: "15",
420
+ isProtein: true,
421
+ weather: "cloudy"
422
+ },
423
+ {
424
+ name: "Tom90 a",
425
+ id: "Nj1AFBQ-GWZ7TEvhUOsFj",
426
+ type: "new",
427
+ howMany: "3",
428
+ isProtein: true,
429
+ weather: "HOT"
430
+ },
431
+ {
432
+ name: "Tom91 a",
433
+ id: "X0a4uOF8vwofrciUfQ8Zn",
434
+ type: "old",
435
+ howMany: 2,
436
+ isProtein: true,
437
+ weather: "cloudy"
438
+ },
439
+ {
440
+ name: "Tom92 a",
441
+ id: "rbeGUYOa6u8tsQgK2MQKv",
442
+ type: "new",
443
+ howMany: 5,
444
+ isProtein: true,
445
+ weather: "HOT"
446
+ },
447
+ {
448
+ name: "Tom93",
449
+ id: "Hq_SOIylSuFnQDyXZtojT",
450
+ type: "old",
451
+ howMany: 5,
452
+ isProtein: true,
453
+ weather: "rainy"
454
+ },
455
+ {
456
+ name: "Tom94 a",
457
+ id: "XOd1GtMqr1y-dzKknfxFc",
458
+ type: "old",
459
+ howMany: "3",
460
+ isProtein: true,
461
+ weather: "HOT"
462
+ },
463
+ {
464
+ name: "Tom95 a",
465
+ id: "Bw1rASgu9XLFC7DzTFw9K",
466
+ type: "old",
467
+ howMany: 5,
468
+ isProtein: true,
469
+ weather: "rainy"
470
+ },
471
+ {
472
+ name: "Tom96 a",
473
+ id: "j8pHKMzXi1n9Ce7roXP5f",
474
+ type: "new",
475
+ howMany: 40,
476
+ isProtein: true,
477
+ weather: "cloudy"
478
+ },
479
+ {
480
+ name: "Tom97 a",
481
+ id: "pLhv2XoDSy7tmmJI61f4-",
482
+ type: "new",
483
+ howMany: 40,
484
+ isProtein: true,
485
+ weather: "HOT"
486
+ },
487
+ {
488
+ name: "Tom98",
489
+ id: "I0WqsADFT9QxZVzEDrki0",
490
+ type: "old",
491
+ howMany: "3",
492
+ isProtein: true,
493
+ weather: "cloudy"
494
+ },
495
+ {
496
+ name: "Tom99 a",
497
+ id: "oTLzlqWtuYbcicyUn3LFl",
498
+ type: "new",
499
+ howMany: 2,
500
+ isProtein: true,
501
+ weather: "rainy"
502
+ },
503
+ {
504
+ name: "Tom100 a",
505
+ id: "M4piV2ZfizeWmf6ZKNr_4",
506
+ type: "new",
507
+ howMany: 40,
508
+ isProtein: true,
509
+ weather: "cloudy"
510
+ },
511
+ {
512
+ name: "Tom101 a",
513
+ id: "E1Yr5zaY41PrmAs2uC-be",
514
+ type: "old",
515
+ howMany: 40,
516
+ isProtein: true,
517
+ weather: "cloudy"
518
+ },
519
+ {
520
+ name: "Tom102 a",
521
+ id: "usnFso9ObH8RZpDmOyOv4",
522
+ type: "new",
523
+ howMany: 40,
524
+ isProtein: true,
525
+ weather: "cloudy"
526
+ },
527
+ {
528
+ name: "Tom103",
529
+ id: "_BRQ-t0CDnlSYU3ZJ73Ca",
530
+ type: "old",
531
+ howMany: 2,
532
+ isProtein: true,
533
+ weather: "rainy"
534
+ },
535
+ {
536
+ name: "Tom104 a",
537
+ id: "-wnRraVlIkY4RoaV5foeA",
538
+ type: "old",
539
+ howMany: "3",
540
+ isProtein: true,
541
+ weather: "rainy"
542
+ },
543
+ {
544
+ name: "Tom105 a",
545
+ id: "iY3jceZSdV6OrMytaKWS2",
546
+ type: "new",
547
+ howMany: 40,
548
+ isProtein: true,
549
+ weather: "HOT"
550
+ },
551
+ {
552
+ name: "Tom106 a",
553
+ id: "ZrZ0Vo9qm5cu9Zf6sHZsd",
554
+ type: "new",
555
+ howMany: "3",
556
+ isProtein: true,
557
+ weather: "HOT"
558
+ },
559
+ {
560
+ name: "Tom107 a",
561
+ id: "wvmW-7Wy_WtCAAOK6mnwr",
562
+ type: "new",
563
+ howMany: 5,
564
+ isProtein: true,
565
+ weather: "rainy"
566
+ },
567
+ {
568
+ name: "Nancy108",
569
+ id: "c-ht1uCS44JFSHekDvbRj",
570
+ type: "new",
571
+ howMany: 2,
572
+ isProtein: true,
573
+ weather: "cloudy"
574
+ },
575
+ {
576
+ name: "Nancy109",
577
+ id: "Oi3cFzKPebr7ZzYJu68w3",
578
+ type: "new",
579
+ howMany: 40,
580
+ isProtein: true,
581
+ weather: "HOT"
582
+ },
583
+ {
584
+ name: "Nancy110",
585
+ id: "0z2Z29YBISe41V7DL1JYJ",
586
+ type: "too old",
587
+ howMany: 2,
588
+ isProtein: true,
589
+ weather: "HOT"
590
+ },
591
+ {
592
+ name: "Nancy111",
593
+ id: "kaHP6YFgq4rNVsRpjkH5D",
594
+ type: "new",
595
+ howMany: 2,
596
+ isProtein: true,
597
+ weather: "HOT"
598
+ },
599
+ {
600
+ name: "Nancy112",
601
+ id: "C-XhKDnDXmdbJ-JXnj-yn",
602
+ type: "old",
603
+ howMany: 5,
604
+ isProtein: true,
605
+ weather: "HOT"
606
+ },
607
+ {
608
+ name: "Agnes Byrd",
609
+ id: "21Jd9-PxHBoHIXJ1j-Ei0",
610
+ type: "new",
611
+ howMany: 2,
612
+ isProtein: true,
613
+ weather: "HOT"
614
+ },
615
+ {
616
+ name: "Christopher Parks",
617
+ id: "jzP3oWirGfd4_c5KH2VmJ",
618
+ type: "old",
619
+ howMany: 2,
620
+ isProtein: true,
621
+ weather: "cloudy"
622
+ },
623
+ {
624
+ name: "Emily Hicks",
625
+ id: "rV4rRcgFKOn3AhIvNgs8R",
626
+ type: "new",
627
+ howMany: "3",
628
+ isProtein: true,
629
+ weather: "cloudy"
630
+ },
631
+ {
632
+ name: "Amanda Quinn",
633
+ id: "m29qs7y5yrlF2t_byAjRn",
634
+ type: "old",
635
+ howMany: 5,
636
+ isProtein: true,
637
+ weather: "HOT"
638
+ },
639
+ {
640
+ name: "Ola Roberts",
641
+ id: "DqjIk7lzU6_8cw3EAkQMN",
642
+ type: "old",
643
+ howMany: 40,
644
+ isProtein: true,
645
+ weather: "rainy"
646
+ },
647
+ {
648
+ name: "Theresa Rogers",
649
+ id: "XsUemLHKc5RpMmYiV3UeT",
650
+ type: "new",
651
+ howMany: 2,
652
+ isProtein: true,
653
+ weather: "cloudy"
654
+ },
655
+ {
656
+ name: "Sylvia Jackson",
657
+ id: "t7RhaB3SRKqkKSwAkMjwo",
658
+ type: "old",
659
+ howMany: 2,
660
+ isProtein: true,
661
+ weather: "cloudy"
662
+ },
663
+ {
664
+ name: "Eugene Crawford",
665
+ id: "gogZgycHEYGE8pQzFfKpn",
666
+ type: "old",
667
+ howMany: 40,
668
+ isProtein: true,
669
+ weather: "rainy"
670
+ },
671
+ {
672
+ name: "Josie Rodgers",
673
+ id: "P4gDKi3su3kBT2X8Nsoj5",
674
+ type: "old",
675
+ howMany: 2,
676
+ isProtein: true,
677
+ weather: "cloudy"
678
+ },
679
+ {
680
+ name: "Dale Nelson",
681
+ id: "cxNmge4Mr3emklYh-mYKO",
682
+ type: "old",
683
+ howMany: 2,
684
+ isProtein: true,
685
+ weather: "rainy"
686
+ },
687
+ {
688
+ name: "Vera Tran",
689
+ id: "BsNyrA-qstXd66wS-Z0hC",
690
+ type: "new",
691
+ howMany: 5,
692
+ isProtein: true,
693
+ weather: "rainy"
694
+ },
695
+ {
696
+ name: "Blake Tate",
697
+ id: "pAgunZo2FNcc_zHGxCs-1",
698
+ type: "old",
699
+ howMany: 2,
700
+ isProtein: true,
701
+ weather: "rainy"
702
+ },
703
+ {
704
+ name: "Amy Harrison",
705
+ id: "xn21FFjVyMD00qUMmHQ-k",
706
+ type: "new",
707
+ howMany: 40,
708
+ isProtein: true,
709
+ weather: "rainy"
710
+ },
711
+ {
712
+ name: "Raymond Swanson",
713
+ id: "lhBo3hBJYQKnhs7BIHaW0",
714
+ type: "old",
715
+ howMany: "3",
716
+ isProtein: true,
717
+ weather: "HOT"
718
+ },
719
+ {
720
+ name: "Kevin Tate",
721
+ id: "kgCPxens2N-AqSE41_CdM",
722
+ type: "old",
723
+ howMany: 40,
724
+ isProtein: true,
725
+ weather: "cloudy"
726
+ },
727
+ {
728
+ name: "Lenora Garrett",
729
+ id: "QsaiEKpRNErq_ZjFkedc2",
730
+ type: "old",
731
+ howMany: 5,
732
+ isProtein: true,
733
+ weather: "cloudy"
734
+ },
735
+ {
736
+ name: "Erik Spencer",
737
+ id: "fDzD7BVxS0W1F2e4AUmGm",
738
+ type: "old",
739
+ howMany: "3",
740
+ isProtein: true,
741
+ weather: "rainy"
742
+ },
743
+ {
744
+ name: "Mildred Mann",
745
+ id: "n3Vis7omDyOrAVeHToRt1",
746
+ type: "old",
747
+ howMany: 40,
748
+ isProtein: true,
749
+ weather: "HOT"
750
+ },
751
+ {
752
+ name: "Glen Wilkerson",
753
+ id: "cPK9vv-JfHBmNUwn8wUZI",
754
+ type: "old",
755
+ howMany: 40,
756
+ isProtein: true,
757
+ weather: "cloudy"
758
+ },
759
+ {
760
+ name: "Ida Alvarado",
761
+ id: "EkqcC3Lxn5O4EnebszsOu",
762
+ type: "new",
763
+ howMany: "3",
764
+ isProtein: true,
765
+ weather: "rainy"
766
+ },
767
+ {
768
+ name: "Hilda Miller",
769
+ id: "PVbWALmDv9bqr6kmsMP6g",
770
+ type: "new",
771
+ howMany: 5,
772
+ isProtein: true,
773
+ weather: "cloudy"
774
+ },
775
+ {
776
+ name: "Ella Douglas",
777
+ id: "HjMy4vzzloDP9zl2EtjYv",
778
+ type: "old",
779
+ howMany: "3",
780
+ isProtein: true,
781
+ weather: "cloudy"
782
+ },
783
+ {
784
+ name: "Leonard McKenzie",
785
+ id: "ZX8GMPeZMlN6_dfANi-NN",
786
+ type: "new",
787
+ howMany: "3",
788
+ isProtein: true,
789
+ weather: "rainy"
790
+ },
791
+ {
792
+ name: "Lora Love",
793
+ id: "EydnrigpkrEERB5cygoWx",
794
+ type: "new",
795
+ howMany: "3",
796
+ isProtein: true,
797
+ weather: "cloudy"
798
+ },
799
+ {
800
+ name: "Charlotte Ford",
801
+ id: "1lM9fZSEWpFl6PiNf4XkJ",
802
+ type: "old",
803
+ howMany: 40,
804
+ isProtein: true,
805
+ weather: "cloudy"
806
+ }
807
+ ];
808
+ const result = filterLocalEntitiesToHasura([...ents], {
809
+ order_by: { path: "updatedAt", direction: "desc" }
810
+ });
811
+ expect(result.entities).toEqual(ents);
812
+ });
405
813
  it("should order by age desc and put null/undefined vals last by default", () => {
406
814
  const result = filterLocalEntitiesToHasura(
407
815
  [
@@ -410,7 +818,7 @@ describe("filterLocalEntitiesToHasura", () => {
410
818
  ...records
411
819
  ],
412
820
  {
413
- order_by: { age: "desc" }
821
+ order_by: { path: "age", direction: "desc" }
414
822
  }
415
823
  );
416
824
  expect(result.entities).toEqual([
@@ -424,7 +832,7 @@ describe("filterLocalEntitiesToHasura", () => {
424
832
  expect(result.entityCount).toBe(6);
425
833
  });
426
834
 
427
- it.only("should order by updatedAt descending, putting null/undefined vals last ", () => {
835
+ it("should order by updatedAt descending, putting null/undefined vals last ", () => {
428
836
  const result = filterLocalEntitiesToHasura(
429
837
  [
430
838
  {
@@ -465,7 +873,7 @@ describe("filterLocalEntitiesToHasura", () => {
465
873
  }
466
874
  ],
467
875
  {
468
- order_by: [{ updatedAt: "desc" }]
876
+ order_by: [{ path: "updatedAt", direction: "desc", type: "timestamp" }]
469
877
  }
470
878
  );
471
879
  expect(result.entities).toEqual([
@@ -514,7 +922,7 @@ describe("filterLocalEntitiesToHasura", () => {
514
922
  ...records
515
923
  ],
516
924
  {
517
- order_by: { age: "desc" }
925
+ order_by: { path: "age", direction: "desc" }
518
926
  }
519
927
  );
520
928
  expect(result.entities).toEqual([
@@ -529,7 +937,7 @@ describe("filterLocalEntitiesToHasura", () => {
529
937
 
530
938
  it("should order by name ascending", () => {
531
939
  const result = filterLocalEntitiesToHasura(records, {
532
- order_by: { name: "asc" }
940
+ order_by: { path: "name", direction: "asc" }
533
941
  });
534
942
  expect(result.entities).toEqual([
535
943
  records[2],
@@ -548,7 +956,7 @@ describe("filterLocalEntitiesToHasura", () => {
548
956
 
549
957
  it("should order by name descending", () => {
550
958
  const result = filterLocalEntitiesToHasura(records, {
551
- order_by: { name: "desc" }
959
+ order_by: { path: "name", direction: "desc" }
552
960
  });
553
961
  expect(result.entities).toEqual([
554
962
  records[0],
@@ -568,7 +976,7 @@ describe("filterLocalEntitiesToHasura", () => {
568
976
  it("should filter and order", () => {
569
977
  const result = filterLocalEntitiesToHasura(records, {
570
978
  where: { city: { _eq: "London" } },
571
- order_by: { age: "desc" }
979
+ order_by: { path: "age", direction: "desc" }
572
980
  });
573
981
  expect(result.entities).toEqual([records[2], records[0]]);
574
982
  expect(result.entitiesAcrossPages).toEqual([records[2], records[0]]);
@@ -586,7 +994,7 @@ describe("filterLocalEntitiesToHasura", () => {
586
994
 
587
995
  it("should handle order_by with empty where", () => {
588
996
  const result = filterLocalEntitiesToHasura(records, {
589
- order_by: { age: "asc" }
997
+ order_by: { path: "age", direction: "asc" }
590
998
  });
591
999
  expect(result.entities).toEqual([
592
1000
  records[3],
@@ -654,7 +1062,7 @@ describe("filterLocalEntitiesToHasura", () => {
654
1062
  { id: 4, name: "a3" }
655
1063
  ];
656
1064
  const result = filterLocalEntitiesToHasura(recordsWithNames, {
657
- order_by: { name: "asc" }
1065
+ order_by: { path: "name", direction: "asc" }
658
1066
  });
659
1067
  expect(result.entities).toEqual([
660
1068
  recordsWithNames[0], // a1
@@ -673,7 +1081,10 @@ describe("filterLocalEntitiesToHasura", () => {
673
1081
  records[3] // Bob Williams, age 20
674
1082
  ];
675
1083
  const result = filterLocalEntitiesToHasura(modifiedRecords, {
676
- order_by: [{ age: "asc" }, { name: "desc" }]
1084
+ order_by: [
1085
+ { path: "age", direction: "asc" },
1086
+ { path: "name", direction: "desc" }
1087
+ ]
677
1088
  });
678
1089
  expect(result.entities).toEqual([
679
1090
  modifiedRecords[3], // age 20, name "Bob Williams"
@@ -686,7 +1097,7 @@ describe("filterLocalEntitiesToHasura", () => {
686
1097
  it("should apply limit and offset to filtered and ordered results", () => {
687
1098
  const result = filterLocalEntitiesToHasura(records, {
688
1099
  where: { city: { _eq: "London" } },
689
- order_by: { age: "desc" },
1100
+ order_by: { path: "age", direction: "desc" },
690
1101
  limit: 1,
691
1102
  offset: 1
692
1103
  });
@@ -730,4 +1141,206 @@ describe("filterLocalEntitiesToHasura", () => {
730
1141
  expect(result.entitiesAcrossPages).toEqual([records[0], records[2]]);
731
1142
  expect(result.entityCount).toBe(2);
732
1143
  });
1144
+ it("should order using custom sortFn", () => {
1145
+ // Create a custom sort function that sorts by the length of the name
1146
+ const sortByNameLength = record => record.name.length;
1147
+
1148
+ const result = filterLocalEntitiesToHasura(records, {
1149
+ order_by: { sortFn: sortByNameLength, direction: "asc" }
1150
+ });
1151
+
1152
+ // Expected order: "Bob Williams" (12), "John Doe" (8), "Jane Smith" (10), "Alice Johnson" (13)
1153
+ expect(result.entities).toEqual([
1154
+ records[0], // John Doe (8 chars)
1155
+ records[1], // Jane Smith (10 chars)
1156
+ records[3], // Bob Williams (12 chars)
1157
+ records[2] // Alice Johnson (13 chars)
1158
+ ]);
1159
+ expect(result.entityCount).toBe(4);
1160
+ });
1161
+
1162
+ it("should order using multiple custom sortFn functions", () => {
1163
+ // Sort first by whether the user is active, then by name length
1164
+ const sortByActive = record => (record.is_active ? 1 : 0);
1165
+ const sortByNameLength = record => record.name.length;
1166
+
1167
+ const result = filterLocalEntitiesToHasura(records, {
1168
+ order_by: { sortFn: [sortByActive, sortByNameLength], direction: "desc" }
1169
+ });
1170
+
1171
+ // First active users (John, Alice) sorted by name length DESC
1172
+ // Then inactive users (Jane, Bob) sorted by name length DESC
1173
+ expect(result.entities).toEqual([
1174
+ records[2], // Alice Johnson (active, 13 chars)
1175
+ records[0], // John Doe (active, 8 chars)
1176
+ records[3], // Bob Williams (inactive, 12 chars)
1177
+ records[1] // Jane Smith (inactive, 10 chars)
1178
+ ]);
1179
+ expect(result.entityCount).toBe(4);
1180
+ });
1181
+
1182
+ it("should handle custom sortFn with null values", () => {
1183
+ // Create test records with some null values
1184
+ const recordsWithNulls = [
1185
+ { id: 1, value: 10, name: "Item 1" },
1186
+ { id: 2, value: null, name: "Item 2" },
1187
+ { id: 3, value: 30, name: "Item 3" },
1188
+ { id: 4, value: 20, name: "Item 4" },
1189
+ { id: 5, value: null, name: "Item 5" }
1190
+ ];
1191
+
1192
+ // Sort function that handles null values
1193
+ const sortByValue = record => {
1194
+ return record.value === null ? -Infinity : record.value;
1195
+ };
1196
+
1197
+ const result = filterLocalEntitiesToHasura(recordsWithNulls, {
1198
+ order_by: { sortFn: sortByValue, direction: "desc" }
1199
+ });
1200
+
1201
+ // Nulls should appear at the end when sorting in descending order
1202
+ expect(result.entities).toEqual([
1203
+ recordsWithNulls[2], // value: 30
1204
+ recordsWithNulls[3], // value: 20
1205
+ recordsWithNulls[0], // value: 10
1206
+ recordsWithNulls[1], // value: null
1207
+ recordsWithNulls[4] // value: null
1208
+ ]);
1209
+ expect(result.entityCount).toBe(5);
1210
+ });
1211
+ it("should order using getValueToFilterOn", () => {
1212
+ // Create a function that returns a value to sort by
1213
+ const getCustomSortValue = record => {
1214
+ // Sort by the first letter of the name
1215
+ return record.name.charAt(0);
1216
+ };
1217
+
1218
+ const result = filterLocalEntitiesToHasura(records, {
1219
+ order_by: { getValueToFilterOn: getCustomSortValue, direction: "asc" }
1220
+ });
1221
+
1222
+ // Expected order based on first letter: A, B, J, J
1223
+ // Since J appears twice, their original order should be preserved
1224
+ expect(result.entities).toEqual([
1225
+ records[2], // Alice
1226
+ records[3], // Bob
1227
+ records[0], // John
1228
+ records[1] // Jane
1229
+ ]);
1230
+ expect(result.entityCount).toBe(4);
1231
+ });
1232
+
1233
+ it("should order using getValueToFilterOn with ownProps", () => {
1234
+ // Create a function that uses ownProps for customized sorting
1235
+ const getWeightedValue = (record, ownProps) => {
1236
+ // Create a weighted value based on age and a multiplier from ownProps
1237
+ return record.age * (ownProps.multiplier || 1);
1238
+ };
1239
+
1240
+ const result = filterLocalEntitiesToHasura(records, {
1241
+ order_by: {
1242
+ getValueToFilterOn: getWeightedValue,
1243
+ direction: "desc",
1244
+ ownProps: { multiplier: 2 }
1245
+ }
1246
+ });
1247
+
1248
+ // Expected order based on age * 2: 35*2, 30*2, 25*2, 20*2
1249
+ expect(result.entities).toEqual([
1250
+ records[2], // Alice Johnson (age: 35 * 2 = 70)
1251
+ records[0], // John Doe (age: 30 * 2 = 60)
1252
+ records[1], // Jane Smith (age: 25 * 2 = 50)
1253
+ records[3] // Bob Williams (age: 20 * 2 = 40)
1254
+ ]);
1255
+ expect(result.entityCount).toBe(4);
1256
+ });
1257
+
1258
+ it("should handle null/undefined values in getValueToFilterOn", () => {
1259
+ const recordsWithMissingProps = [
1260
+ { id: 1, name: "Alice", customProp: 100 },
1261
+ { id: 2, name: "Bob", customProp: null },
1262
+ { id: 3, name: "Charlie" }, // customProp is undefined
1263
+ { id: 4, name: "Dave", customProp: 200 }
1264
+ ];
1265
+
1266
+ // Function that uses a property that might be missing
1267
+ const getCustomPropValue = record => {
1268
+ return record.customProp;
1269
+ };
1270
+
1271
+ const result = filterLocalEntitiesToHasura(recordsWithMissingProps, {
1272
+ order_by: { getValueToFilterOn: getCustomPropValue, direction: "desc" }
1273
+ });
1274
+
1275
+ // Should order with valid values first, then nulls/undefined at the end
1276
+ expect(result.entities).toEqual([
1277
+ recordsWithMissingProps[3], // customProp: 200
1278
+ recordsWithMissingProps[0], // customProp: 100
1279
+ recordsWithMissingProps[2], // customProp: undefined
1280
+ recordsWithMissingProps[1] // customProp: null
1281
+ ]);
1282
+ expect(result.entityCount).toBe(4);
1283
+ });
1284
+
1285
+ it("should combine getValueToFilterOn with multiple sort criteria", () => {
1286
+ // Additional records with same category but different priority
1287
+ const extendedRecords = [
1288
+ ...records,
1289
+ {
1290
+ id: 111,
1291
+ name: "Tom Jones",
1292
+ age: 40,
1293
+ is_active: true,
1294
+ city: "London",
1295
+ tags: ["sql", "management"],
1296
+ email: "tom@example.com",
1297
+ data: { category: "A", type: "X", priority: 2 },
1298
+ username: "tom111"
1299
+ },
1300
+ {
1301
+ id: 222,
1302
+ name: "Emma Brown",
1303
+ age: 28,
1304
+ is_active: true,
1305
+ city: "London",
1306
+ tags: ["design", "javascript"],
1307
+ email: "emma@example.com",
1308
+ data: { category: "A", type: "X", priority: 1 },
1309
+ username: "emma222"
1310
+ }
1311
+ ];
1312
+
1313
+ // First sort by category
1314
+ const getCategoryValue = record => {
1315
+ return record.data.category;
1316
+ };
1317
+
1318
+ // Then sort by priority if available, putting records without priority at the end
1319
+ const getPriorityValue = record => {
1320
+ return record.data.priority !== undefined
1321
+ ? record.data.priority
1322
+ : Number.MAX_SAFE_INTEGER;
1323
+ };
1324
+
1325
+ const result = filterLocalEntitiesToHasura(extendedRecords, {
1326
+ order_by: [
1327
+ { getValueToFilterOn: getCategoryValue, direction: "asc" },
1328
+ { getValueToFilterOn: getPriorityValue, direction: "asc" }
1329
+ ]
1330
+ });
1331
+
1332
+ // All category A records, ordered by priority if available
1333
+ const categoryARecords = [
1334
+ extendedRecords[5], // Emma (category A, priority 1)
1335
+ extendedRecords[4], // Tom (category A, priority 2)
1336
+ extendedRecords[0], // John (category A, no priority)
1337
+ extendedRecords[2] // Alice (category A, no priority)
1338
+ ];
1339
+
1340
+ // Then category B, then category C
1341
+ expect(result.entities.slice(0, 4)).toEqual(categoryARecords);
1342
+ expect(result.entities[4]).toBe(extendedRecords[1]); // Jane (category B)
1343
+ expect(result.entities[5]).toBe(extendedRecords[3]); // Bob (category C)
1344
+ expect(result.entityCount).toBe(6);
1345
+ });
733
1346
  });