7d149b1935f8a1e5f11f31f3cb8da99d31b3a8e6
jcasper
  Fri Apr 10 16:34:48 2026 -0700
Replaced handrolled JSON structure in faceted composite output from hgTrackUi with
jsonWrite.h library-generated version, and added a defaultSortField trackDb setting to faceted
composites to improve the subtrack presentation order, refs #36320

diff --git src/hg/js/facetedComposite.js src/hg/js/facetedComposite.js
index b0818970768..052e352c952 100644
--- src/hg/js/facetedComposite.js
+++ src/hg/js/facetedComposite.js
@@ -137,43 +137,56 @@
             data: null,
             orderable: false,
             defaultContent: "",
             title: `
             <label title="Select all visible rows">
             <input type="checkbox" id="select-all"/></label>`,
             // no render function needed
         };
 
         const hasDataTypes = embeddedData.dataTypes &&
                              Object.keys(embeddedData.dataTypes).length > 0;
         const itemLabel = hasDataTypes ? "samples" : "tracks";
         const singularLabel = itemLabel.slice(0, -1);
 
         const columns = [checkboxColumn, ...ordinaryColumns];
+
+        // Determine which column to sort by: use defaultSortField if it matches
+        // a metadata column (case-insensitive, ignoring leading underscores),
+        // otherwise fall back to the first data column.
+        let defaultSortCol = 1;  // column 0 is checkboxes, 1 is first data col
+        if (embeddedData.defaultSortField) {
+            const target = embeddedData.defaultSortField.replace(/^_+/, "").toLowerCase();
+            const idx = colNames.findIndex(
+                c => c.replace(/^_+/, "").toLowerCase() === target);
+            if (idx >= 0)
+                defaultSortCol = idx + 1;  // +1 for the checkbox column
+        }
+
         const table = $("#theMetaDataTable").DataTable({
             data: metadata,
             deferRender: true,    // seems faster
             columns: columns,
             columnDefs: [ { targets:0, render: DataTable.render.select() } ],
             responsive: true,
             layout: {
                 topStart: 'pageLength',
                 topEnd: null,        // omit global search
                 bottomStart: 'info',
                 bottomEnd: 'paging'
             },
-            order: [[1, "asc"]],  // sort by the first data column, not checkbox
+            order: [[defaultSortCol, "asc"]],
             pageLength: 25,       // show 25 rows per page by default
             lengthMenu: [[10, 25, 50, 100, -1], [10, 25, 50, 100, "All"]],
             language: {
                 lengthMenu: `Show _MENU_ ${itemLabel}`,
                 select: {
                     rows: {
                         0: "",
                         1: `1 ${singularLabel} selected`,
                         _: `%d ${itemLabel} selected`
                     }
                 },
                 info: `Showing _START_ to _END_ of _TOTAL_ ${itemLabel}`,
                 infoFiltered: `(filtered from _MAX_ total ${itemLabel})`,
             },
             select: { style: "multi", selector: "td:not(:has(a))" },