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 =>