bd91a935679eda68e6bf59ee40e480f3cbd0f6e3 chmalee Fri Jun 5 12:48:09 2026 -0700 Fix hubSpace genome selection committing the wrong genome to hub.txt, refs #37713 Co-Authored-By: Claude Opus 4.8 (1M context) diff --git src/hg/js/hgMyData.js src/hg/js/hgMyData.js index ab94bbd7b77..2c2d84bd049 100644 --- src/hg/js/hgMyData.js +++ src/hg/js/hgMyData.js @@ -225,47 +225,51 @@ let ret = h('div', { class: "uppy-Dashboard-FileCard-label", style: "display: inline-block; width: 78%" }, // first child of div "Select from popular assemblies:", // second div child h('select', { id: `${file.meta.name}DbSelect`, style: "margin-left: 5px", onChange: e => { let val = e.target.value; let label = e.target.selectedOptions[0].label; let hub = hubCreate.assemblyHubByGenome(val); let newParentDir = hub ? hub.fileName : hubCreate.uiState.hubNameDefault; - // Uppy's file card keeps controlled-input - // state per field and commits it to meta on - // Upload, overwriting setFileMeta. Route - // genome through the field-level onChange and - // force the Hub Name input value to match. + // we call onChange here, which will do an onChange with a potentially + // stale metadata if the user has also edited parentDir. later we will + // fix that up and use the genome name as the recommended parentDir + // or a pre-existing hub if one exists onChange(val); - uppy.setFileMeta(file.id, { - genomeLabel: label, - hubType: hub ? "assemblyHub" : "trackHub", - parentDir: newParentDir, - }); + file.meta.genome = val; + file.meta.genomeLabel = label; + file.meta.hubType = hub ? "assemblyHub" : "trackHub"; + file.meta.parentDir = newParentDir; + // Sync the Hub Name field in a later tick. In this + // tick its onChange would spread the same stale + // state as the genome onChange above and revert + // genome; deferring lets genome flush first. + setTimeout(function() { let pd = document.getElementById("uppy-Dashboard-FileCard-input-parentDir"); if (pd) { pd.value = newParentDir; pd.dispatchEvent(new Event("input", {bubbles: true})); pd.dispatchEvent(new Event("change", {bubbles: true})); } + }, 0); } }, hubCreate.makeGenomeSelectOptions(file.meta.genome, file.meta.genomeLabel).map( (genomeObj) => { return h('option', { value: genomeObj.value, label: genomeObj.label, selected: file.meta.genome !== null ? genomeObj.value === file.meta.genome : genomeObj.value === hubCreate.defaultDb() }); }) ), h('p', { class: "uppy-Dashboard-FileCard-label", style: "display: block; width: 78%", }, "or search for your genome:"), // third div child @@ -1371,58 +1375,56 @@ function makeGenomeSelectOptions(value, label) { // Returns an array of options for genomes, if value and label exist, add that // as an additional option let ret = []; let cartChoice = {}; cartChoice.id = cartDb; cartChoice.label = cartDb; cartChoice.value = cartDb.split(" ").slice(-1)[0]; if (cartChoice.value.startsWith("hub_")) { cartChoice.label = cartDb.split(" ").slice(0,-1).join(" "); // take off the actual db value } cartChoice.selected = value && label ? false: true; defaultGenomeChoices[cartChoice.label] = cartChoice; - // next time around our value/label pair will be a default. this time around we - // want it selected because it was explicitly asked for, but it may not be next time + // Add an explicitly chosen genome (e.g. from the search box) before + // building the list so it is selectable on this render, not the next. + // Skip assembly-hub genomes, which the loop below adds with a suffix. + if (value && label && !(label in defaultGenomeChoices) && + !genomeIsAssemblyHub(value)) { + defaultGenomeChoices[label] = {value: value, label: label}; + } ret = Object.values(defaultGenomeChoices); // Include the user's uploaded assembly hubs as options. One entry per // assembly hub (dedupe by genome name), taken from the dir row in // filesHash. This lets users picking a dropdown genome target a hub // they already created. let seenAsmHub = {}; for (let fullPath in uiState.filesHash) { let fd = uiState.filesHash[fullPath]; if (fd.fileType === "dir" && fd.hubType === "assemblyHub" && fd.genome && !seenAsmHub[fd.genome]) { seenAsmHub[fd.genome] = true; ret.push({ value: fd.genome, label: `${fd.genome} (your assembly hub)`, }); } } - // Cache the value/label pair so it's a default next time - but skip - // assembly-hub genomes, those are added by the loop above with the - // "(your assembly hub)" suffix and would otherwise show up twice. - if (value && label && !(label in defaultGenomeChoices) && - !genomeIsAssemblyHub(value)) { - defaultGenomeChoices[label] = {value: value, label: label, selected: true}; - } return ret; } function makeTypeSelectOptions() { let ret = []; let autoChoice = {}; autoChoice.label = "Auto-detect from extension"; autoChoice.value = "Auto-detect from extension"; autoChoice.selected = true; ret.push(autoChoice); let choices = ["bigBed", "bam", "vcf", "vcf (bgzip or gzip compressed)", "bigWig", "hic", "cram", "bigBarChart", "bigGenePred", "bigMaf", "bigInteract", "bigPsl", "bigChain"]; choices.forEach( (e) => { let choice = {}; choice.id = e; choice.label = e;