07c318cffa6769b6a4b650a88a7887ae01de608d jcasper Wed Feb 4 20:07:42 2026 -0800 Impose an overall limit on active track count for featured composites, and allow alternate labels for the data type selectors, refs #36320 diff --git src/hg/js/facetedComposite.js src/hg/js/facetedComposite.js index e9da72619a2..859669f32fb 100644 --- src/hg/js/facetedComposite.js +++ src/hg/js/facetedComposite.js @@ -88,36 +88,37 @@ }); } function initDataTypeSelector() { // Skip if no dataTypes defined or empty object if (!embeddedData.dataTypes || Object.keys(embeddedData.dataTypes).length === 0) { return; } const selector = document.getElementById("dataTypeSelector"); selector.appendChild(Object.assign(document.createElement("label"), { innerHTML: "Data type", })); Object.keys(embeddedData.dataTypes).forEach(name => { const label = document.createElement("label"); + const dataType = embeddedData.dataTypes[name]; label.innerHTML = ` - ${name}`; + ${dataType.title}`; selector.appendChild(label); }); const selectedDataTypes = new Set( // get dataTypes selected initially - Object.entries(embeddedData.dataTypes).filter(([_, val]) => val === 1) + Object.entries(embeddedData.dataTypes).filter(([_, val]) => val.active === 1) .map(([key]) => key) ); // initialize data type checkboxes (using class instead of 'name') document.querySelectorAll("input.cbgroup") .forEach(cb => { cb.checked = selectedDataTypes.has(cb.value); }); // Capture initial data type state initialState.dataTypes = new Set(selectedDataTypes); } function initTable(allData) { const { metadata, rowToIdx, colNames } = allData; const ordinaryColumns = colNames.map(key => ({ // all but checkboxes data: key, @@ -346,30 +347,46 @@ if (cb.checked) { currentDataTypes.push(cb.value); } }); // Require at least one data type when the selector exists if (currentDataTypes.length === 0) { alert("Please select at least one data type."); return; // abort submission } } // Get current data element selections const currentDataElements = table.rows({selected: true}).data().toArray() .map(obj => obj[primaryKey]); + // Enforce an upper bound on the number of tracks on at the same time. + // This is imperfect when data types are present - some combinations might + // have been manually hidden by the user. But it should be a good ballpark. + const trackLimit = 1000; + if (hasDataTypes) { + if (currentDataTypes.length * currentDataElements.length > trackLimit) { + alert("You have turned on too many subtracks (over 1000) - please uncheck some."); + return; // abort submission + } + } else { + if (currentDataElements.length > trackLimit) { + alert("You have turned on too many subtracks (over 1000) - please uncheck some."); + return; // abort submission + } + } + // Build the parameters for the cart update const uriForUpdate = new URLSearchParams({ "cartDump.metaDataId": mdid, "noDisplay": 1 }); // Data elements: was and now if (initialState.dataElements.size > 0) { initialState.dataElements.forEach(de => uriForUpdate.append(`${mdid}.de_was`, de)); } else { uriForUpdate.append(`${mdid}.de_was`, ""); } if (currentDataElements.length > 0) { currentDataElements.forEach(de =>