06a403144f20a2aa46ff4ee5f810f06e27869ef5
chmalee
  Tue Feb 10 16:05:26 2026 -0800
Fix visual bug in hubSpaceUI, sometimes when adding new rows to the DataTable, we get confused and show the directory row and table headers twice, refs #36830

diff --git src/hg/js/hgMyData.js src/hg/js/hgMyData.js
index 834372566c2..9dd31e0fa85 100644
--- src/hg/js/hgMyData.js
+++ src/hg/js/hgMyData.js
@@ -1089,30 +1089,47 @@
             }
         }
     }
 
     function deleteFileFromTable(pathList) {
         // req is an object with properties of an uploaded file, make a new row
         // for it in the filesTable
         let table = $("#filesTable").DataTable();
         let rows = table.rows((idx, data) => pathList.includes(data.fullPath));
         rows.remove().draw();
         let toKeep = (elem) => !pathList.includes(elem.fullPath);
         pathList.forEach((f) => {
             updateQuota(-uiState.filesHash[f].fileSize);
         });
         uiState.fileList = uiState.fileList.filter(toKeep);
+        // Rebuild filesHash from remaining fileList to remove stale entries
+        uiState.filesHash = {};
+        parseFileListIntoHash(uiState.fileList);
+        // If the currently viewed hub directory was deleted (its data is in oldRowData
+        // because dataTableCustomOrder moved it to the header), clean up that stale state
+        if (oldRowData && pathList.includes(oldRowData.fullPath)) {
+            let thead = document.querySelector(
+                ".dt-scroll-headInner > table:nth-child(1) > thead:nth-child(1)");
+            if (thead && thead.childNodes.length > 1) {
+                thead.removeChild(thead.lastChild);
+            }
+            oldRowData = null;
+            dataTableShowTopLevel(table);
+            dataTableEmptyBreadcrumb(table);
+            table.order([{name: "uploadTime", dir: "desc"}]);
+            table.draw();
+        }
         history.replaceState(uiState, "", document.location.href);
     }
 
     function addFileToHub(rowData) {
         // a file has been uploaded and a hub has been created, present a modal
         // to choose which hub to associate this track to
         // backend wise: move the file into the hub directory
         //               update the hubSpace row with the hub name
         // frontend wise: move the file row into a 'child' of the hub row
         console.log(`sending addToHub req for ${rowData.fileName} to `);
         cart.setCgiAndUrl(fileListEndpoint);
         cart.send({addToHub: {hubName: "", dataFile: ""}});
         cart.flush();
     }
 
@@ -1155,31 +1172,36 @@
                     if (rowData.fullPath === obj.fullPath) {
                         table.row(allRows[j]).invalidate();
                         break;
                     }
                 }
             }
         }
 
         // show all the new rows we just added, note the double draw, we need
         // to have the new rows rendered to do the order because the order
         // will copy the actual DOM node
         parseFileListIntoHash(uiState.fileList);
         dataTableShowDir(table, hubDirData.fileName, hubDirData.fullPath);
         table.draw();
         dataTableCustomOrder(table, hubDirData);
-        table.columns.adjust().draw();
+        // Flush dataTableCustomOrder's row.remove() so DataTables internal state is clean,
+        // then defer columns.adjust() to allow browser reflow after header DOM manipulation
+        table.draw();
+        setTimeout(function() {
+            table.columns.adjust();
+        }, 0);
     }
 
     function doRowSelect(evtype, table, indexes) {
         let selectedRow = table.row(indexes);
         let rowTr = selectedRow.node();
         if (rowTr) {
             handleCheckboxSelect(evtype, table, selectedRow);
         }
     }
 
     function indentActionButton(rowTr, rowData) {
         let numIndents = "0px"; //data.parentDir !== "" ? data.fullPath.split('/').length - 1: 0;
         if (rowData.fileType !== "dir") {
             numIndents = "10px";
         }