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

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/index.cjs.js CHANGED
@@ -22228,13 +22228,27 @@ function applyOrderBy(records, _order_by) {
22228
22228
  directions.push(direction);
22229
22229
  iteratees.push((record) => {
22230
22230
  const value = record[field];
22231
+ if (isNull(value) || value === void 0) {
22232
+ return direction === "asc" ? -Infinity : -Infinity;
22233
+ }
22231
22234
  if (isString$1(value) && /\d/.test(value)) {
22232
22235
  return value.replace(/(\d+)/g, (num) => num.padStart(10, "0"));
22233
22236
  }
22234
22237
  return value;
22235
22238
  });
22236
22239
  });
22237
- records = orderBy$1(records, iteratees, directions);
22240
+ let sortedRecords = orderBy$1(records, iteratees, directions);
22241
+ order_by.forEach((item) => {
22242
+ const field = Object.keys(item)[0];
22243
+ const direction = item[field];
22244
+ if (direction === "desc") {
22245
+ sortedRecords = [
22246
+ ...sortedRecords.filter((record) => !isNull(record[field]) && record[field] !== void 0),
22247
+ ...sortedRecords.filter((record) => isNull(record[field]) || record[field] === void 0)
22248
+ ];
22249
+ }
22250
+ });
22251
+ records = sortedRecords;
22238
22252
  }
22239
22253
  return records;
22240
22254
  }
package/index.es.js CHANGED
@@ -22210,13 +22210,27 @@ function applyOrderBy(records, _order_by) {
22210
22210
  directions.push(direction);
22211
22211
  iteratees.push((record) => {
22212
22212
  const value = record[field];
22213
+ if (isNull(value) || value === void 0) {
22214
+ return direction === "asc" ? -Infinity : -Infinity;
22215
+ }
22213
22216
  if (isString$1(value) && /\d/.test(value)) {
22214
22217
  return value.replace(/(\d+)/g, (num) => num.padStart(10, "0"));
22215
22218
  }
22216
22219
  return value;
22217
22220
  });
22218
22221
  });
22219
- records = orderBy$1(records, iteratees, directions);
22222
+ let sortedRecords = orderBy$1(records, iteratees, directions);
22223
+ order_by.forEach((item) => {
22224
+ const field = Object.keys(item)[0];
22225
+ const direction = item[field];
22226
+ if (direction === "desc") {
22227
+ sortedRecords = [
22228
+ ...sortedRecords.filter((record) => !isNull(record[field]) && record[field] !== void 0),
22229
+ ...sortedRecords.filter((record) => isNull(record[field]) || record[field] === void 0)
22230
+ ];
22231
+ }
22232
+ });
22233
+ records = sortedRecords;
22220
22234
  }
22221
22235
  return records;
22222
22236
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@teselagen/ui",
3
- "version": "0.8.6-beta.12",
3
+ "version": "0.8.6-beta.13",
4
4
  "main": "./src/index.js",
5
5
  "type": "module",
6
6
  "exports": {
@@ -230,6 +230,8 @@ function applyWhereClause(records, where) {
230
230
  // order_by looks like this: [{ some_field: "asc" }, { some_other_field: "desc" }] or {some_field: "asc"}
231
231
  // returns the records sorted by the order_by clause
232
232
  function applyOrderBy(records, _order_by) {
233
+ // console.log('records start:', records)
234
+ // console.log('_order_by:', _order_by)
233
235
  const order_by = isArray(_order_by)
234
236
  ? _order_by
235
237
  : isEmpty(_order_by)
@@ -251,6 +253,10 @@ function applyOrderBy(records, _order_by) {
251
253
  // Create a custom iteratee function for natural sorting
252
254
  iteratees.push(record => {
253
255
  const value = record[field];
256
+ // Handle null values based on sort direction
257
+ if (isNull(value) || value === undefined) {
258
+ return direction === 'asc' ? -Infinity : -Infinity;
259
+ }
254
260
  // Use natural sorting only for strings that contain numbers
255
261
  if (isString(value) && /\d/.test(value)) {
256
262
  // Return the value in a format that can be naturally sorted
@@ -260,9 +266,26 @@ function applyOrderBy(records, _order_by) {
260
266
  return value;
261
267
  });
262
268
  });
263
-
264
- // Use the custom iteratees for natural sorting
265
- records = orderBy(records, iteratees, directions);
269
+
270
+ // First sort normally
271
+ let sortedRecords = orderBy(records, iteratees, directions);
272
+
273
+ // Then ensure entries with a value for the field come before entries without a value
274
+ // for any field sorted in descending order
275
+ order_by.forEach((item) => {
276
+ const field = Object.keys(item)[0];
277
+ const direction = item[field];
278
+
279
+ if (direction === 'desc') {
280
+ // For descending sorts, we want entries with values to appear before entries without values
281
+ sortedRecords = [
282
+ ...sortedRecords.filter(record => !isNull(record[field]) && record[field] !== undefined),
283
+ ...sortedRecords.filter(record => isNull(record[field]) || record[field] === undefined)
284
+ ];
285
+ }
286
+ });
287
+
288
+ records = sortedRecords;
266
289
  }
267
290
  return records;
268
291
  }
@@ -379,42 +379,152 @@ describe("filterLocalEntitiesToHasura", () => {
379
379
  ]);
380
380
  expect(result.entityCount).toBe(3);
381
381
  });
382
- it("should order by age ascending", () => {
383
- const result = filterLocalEntitiesToHasura(records, {
384
- order_by: { age: "asc" }
385
- });
382
+ it("should order by age ascending and put null vals last by default", () => {
383
+ const result = filterLocalEntitiesToHasura(
384
+ [{ id: 111, name: "Null Age", age: null, city: "Unknown" }, ...records],
385
+ {
386
+ order_by: { age: "asc" }
387
+ }
388
+ );
386
389
  expect(result.entities).toEqual([
387
390
  records[3],
388
391
  records[1],
389
392
  records[0],
390
- records[2]
393
+ records[2],
394
+ { id: 111, name: "Null Age", age: null, city: "Unknown" }
391
395
  ]);
392
396
  expect(result.entitiesAcrossPages).toEqual([
393
397
  records[3],
394
398
  records[1],
395
399
  records[0],
396
- records[2]
400
+ records[2],
401
+ { id: 111, name: "Null Age", age: null, city: "Unknown" }
397
402
  ]);
398
- expect(result.entityCount).toBe(4);
403
+ expect(result.entityCount).toBe(5);
399
404
  });
400
-
401
- it("should order by age descending", () => {
402
- const result = filterLocalEntitiesToHasura(records, {
403
- order_by: { age: "desc" }
404
- });
405
+ it("should order by age desc and put null/undefined vals last by default", () => {
406
+ const result = filterLocalEntitiesToHasura(
407
+ [
408
+ { id: 111, name: "Null Age", age: null, city: "Unknown" },
409
+ { id: 1121, name: "Undefined Age", age: undefined, city: "Unknown" },
410
+ ...records
411
+ ],
412
+ {
413
+ order_by: { age: "desc" }
414
+ }
415
+ );
405
416
  expect(result.entities).toEqual([
406
417
  records[2],
407
418
  records[0],
408
419
  records[1],
409
- records[3]
420
+ records[3],
421
+ { id: 111, name: "Null Age", age: null, city: "Unknown" },
422
+ { id: 1121, name: "Undefined Age", age: undefined, city: "Unknown" }
410
423
  ]);
411
- expect(result.entitiesAcrossPages).toEqual([
424
+ expect(result.entityCount).toBe(6);
425
+ });
426
+
427
+ it.only("should order by updatedAt descending, putting null/undefined vals last ", () => {
428
+ const result = filterLocalEntitiesToHasura(
429
+ [
430
+ {
431
+ name: "-10_signal",
432
+ color: "#4ECDC4",
433
+ isGenbankStandardType: true,
434
+ __typename: "featureTypeOverride"
435
+ },
436
+ {
437
+ name: "-35_signal",
438
+ color: "#F7FFF7",
439
+ isGenbankStandardType: true,
440
+ __typename: "featureTypeOverride"
441
+ },
442
+ {
443
+ name: "3'clip",
444
+ color: "#FF6B6B",
445
+ isGenbankStandardType: true,
446
+ __typename: "featureTypeOverride"
447
+ },
448
+ {
449
+ name: "3'UTR",
450
+ color: "#FFE66D",
451
+ isGenbankStandardType: true,
452
+ __typename: "featureTypeOverride"
453
+ },
454
+
455
+ {
456
+ __typename: "featureTypeOverride",
457
+ id: "33a90fcb-fc26-406f-a6d5-41ac4ba8ea64",
458
+ name: "lalala",
459
+ description: null,
460
+ color: "#81bb41",
461
+ genbankEquivalentType: null,
462
+ updatedAt: "2025-06-03T01:02:24.737499+00:00",
463
+ isHidden: false,
464
+ isCustomType: true
465
+ }
466
+ ],
467
+ {
468
+ order_by: [{ updatedAt: "desc" }]
469
+ }
470
+ );
471
+ expect(result.entities).toEqual([
472
+ {
473
+ __typename: "featureTypeOverride",
474
+ id: "33a90fcb-fc26-406f-a6d5-41ac4ba8ea64",
475
+ name: "lalala",
476
+ description: null,
477
+ color: "#81bb41",
478
+ genbankEquivalentType: null,
479
+ updatedAt: "2025-06-03T01:02:24.737499+00:00",
480
+ isHidden: false,
481
+ isCustomType: true
482
+ },
483
+ {
484
+ name: "-10_signal",
485
+ color: "#4ECDC4",
486
+ isGenbankStandardType: true,
487
+ __typename: "featureTypeOverride"
488
+ },
489
+ {
490
+ name: "-35_signal",
491
+ color: "#F7FFF7",
492
+ isGenbankStandardType: true,
493
+ __typename: "featureTypeOverride"
494
+ },
495
+ {
496
+ name: "3'clip",
497
+ color: "#FF6B6B",
498
+ isGenbankStandardType: true,
499
+ __typename: "featureTypeOverride"
500
+ },
501
+ {
502
+ name: "3'UTR",
503
+ color: "#FFE66D",
504
+ isGenbankStandardType: true,
505
+ __typename: "featureTypeOverride"
506
+ }
507
+ ]);
508
+ });
509
+ it("should order by age descending, putting null/undefined vals last ", () => {
510
+ const result = filterLocalEntitiesToHasura(
511
+ [
512
+ { id: 111, name: "Null Age", age: null, city: "Unknown" },
513
+ { id: 1121, name: "Undefined Age", age: undefined, city: "Unknown" },
514
+ ...records
515
+ ],
516
+ {
517
+ order_by: { age: "desc" }
518
+ }
519
+ );
520
+ expect(result.entities).toEqual([
412
521
  records[2],
413
522
  records[0],
414
523
  records[1],
415
- records[3]
524
+ records[3],
525
+ { id: 111, name: "Null Age", age: null, city: "Unknown" },
526
+ { id: 1121, name: "Undefined Age", age: undefined, city: "Unknown" }
416
527
  ]);
417
- expect(result.entityCount).toBe(4);
418
528
  });
419
529
 
420
530
  it("should order by name ascending", () => {
@@ -21,18 +21,18 @@ export function tableQueryParamsToHasuraClauses({
21
21
  const uniqueFieldsByPath = {};
22
22
 
23
23
  // Split the search term by comma to support multi-term searching
24
- const searchTerms = searchTerm.split(',');
24
+ const searchTerms = searchTerm.split(",");
25
25
 
26
26
  schema.fields.forEach(field => {
27
27
  const { type, path, searchDisabled } = field;
28
28
  if (uniqueFieldsByPath[path]) return; // Skip if already added
29
29
  uniqueFieldsByPath[path] = true;
30
30
  if (searchDisabled || field.filterDisabled || type === "color") return;
31
-
31
+
32
32
  // Process each search term
33
33
  searchTerms.forEach(term => {
34
34
  const filterValue = term.trim(); // Trim the term to handle spaces after commas
35
-
35
+
36
36
  if (type === "string" || type === "lookup") {
37
37
  const o = set({}, path, { _ilike: `%${filterValue}%` });
38
38
  searchTermFilters.push(o);