b8b56383b460baeded0eb044c5c50998564f4b9d jcasper Sun Jan 25 22:35:48 2026 -0800 Initial pass at making data types optional for the new faceted composite UI. refs #36320 diff --git src/hg/js/facetedComposite.js src/hg/js/facetedComposite.js index e14690b3867..2a2f31e613b 100644 --- src/hg/js/facetedComposite.js +++ src/hg/js/facetedComposite.js @@ -71,30 +71,35 @@ method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded" }, body: `hgsid=${hgsid}&db=${db}&${uriForUpdate}`, }).then(() => { // 'disable' any CSS named elements here to them keep out of cart const dtLength = submitBtnEvent. target.form.querySelector("select[name$='_length']"); if (dtLength) { dtLength.disabled = true; } submitBtnEvent.target.form.submit(); // release submit event }); } 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: "<b>Data type</b>", })); Object.keys(embeddedData.dataTypes).forEach(name => { const label = document.createElement("label"); label.innerHTML = ` <input type="checkbox" class="cbgroup" value="${name}">${name}`; selector.appendChild(label); }); const selectedDataTypes = new Set( // get dataTypes selected initially Object.entries(embeddedData.dataTypes).filter(([_, val]) => val === 1) .map(([key]) => key) ); // initialize data type checkboxes (using class instead of 'name') @@ -224,31 +229,32 @@ const filtersDiv = document.getElementById("filters"); colNames.forEach((key) => { // skip attributes if they should be excluded from checkbox sets if (excludeCheckboxes.includes(key)) { return; } const sortedPossibleVals = Array.from(possibleValues[key].entries()); sortedPossibleVals.sort((a, b) => // neg: less-than a[1] !== b[1] ? b[1] - a[1] : a[0].localeCompare(b[0])); // Use 'maxCheckboxes' most frequent items (if they appear > 1 time) let topToShow = sortedPossibleVals .filter(([val, count]) => - val.trim().toUpperCase() !== "NA" && count > 1) + val.trim().toUpperCase() !== "NA") // why only > 1? + //val.trim().toUpperCase() !== "NA" && count > 1) .slice(0, maxCheckboxes); // Any "other/Other/OTHER" entry will be put at the end let otherKey = null, otherValue = null; topToShow = topToShow.filter(([val, value]) => { if (val.toLowerCase() === "other") { otherKey = val; otherValue = value; return false; } return true; }); if (otherValue !== null) { topToShow.push([otherKey, otherValue]); } @@ -305,45 +311,60 @@ clearBtn.textContent = "Clear"; clearBtn.type = "button"; // prevent form submit if inside a form clearBtn.addEventListener("click", () => { cboxes.forEach(cb => cb.checked = false); // Uncheck all // Recalculate the (now cleared) search term and update table table.column(colIdx + 1).search("", true, false).draw(); }); // Prepend the "clear" button attrDiv.insertBefore(clearBtn, attrDiv.children[1] || null); }); return table; // to chain calls } // end initFilters function initSubmit(table) { // logic for the submit event const { mdid, primaryKey } = embeddedData; // mdid: metadata identifier + const hasDataTypes = embeddedData.dataTypes && + Object.keys(embeddedData.dataTypes).length > 0; + document.getElementById("Submit").addEventListener("click", (submitBtnEvent) => { submitBtnEvent.preventDefault(); // hold the submit button event const selectedRows = table.rows({selected: true}).data().toArray(); + const uriForUpdate = new URLSearchParams({ "cartDump.metaDataId": mdid, "noDisplay": 1 }); + selectedRows.forEach(obj => // 'de' for data element + uriForUpdate.append(`${mdid}.de`, obj[primaryKey])); + + if (hasDataTypes) { + // Collect checked data types const selectedDataTypes = []; document.querySelectorAll("input.cbgroup").forEach(cb => { if (cb.checked) { selectedDataTypes.push(cb.value); } }); - const uriForUpdate = new URLSearchParams({ "cartDump.metaDataId": mdid, "noDisplay": 1 }); - selectedRows.forEach(obj => // 'de' for data element - uriForUpdate.append(`${mdid}.de`, obj[primaryKey])); + // Require at least one data type when the selector exists + if (selectedDataTypes.length === 0) { + alert("Please select at least one data type."); + return; // abort submission + } selectedDataTypes.forEach(dat => // 'dt' for data type uriForUpdate.append(`${mdid}.dt`, dat)); + } else { + // No data types configured for this track: send empty-string sentinel + uriForUpdate.append(`${mdid}.dt`, ""); + } updateVisibilities(uriForUpdate, submitBtnEvent); }); } // end initSubmit function initAll(dataForTable) { initDataTypeSelector(); const table = initTable(dataForTable); initFilters(table, dataForTable); initSubmit(table); } function loadDataAndInit() { // load data and call init functions const { mdid, primaryKey, metadataUrl, colorSettingsUrl } = embeddedData; const CACHE_KEY = mdid;