From 22b49540a9366742c9664768146a57b99baabd79 Mon Sep 17 00:00:00 2001 From: wuall826 <121817690+wuall826@users.noreply.github.com> Date: Wed, 16 Aug 2023 16:00:07 +0000 Subject: [PATCH 1/8] [GLT-3764] Reduce run clutter metrics - newest --- ts/case-report.ts | 38 ++-- ts/component/table-builder.ts | 2 +- ts/data/case.ts | 1 - ts/data/sample.ts | 316 +++++++++++++++++++++++++++------- 4 files changed, 281 insertions(+), 76 deletions(-) diff --git a/ts/case-report.ts b/ts/case-report.ts index 2c868110..9bb40f98 100644 --- a/ts/case-report.ts +++ b/ts/case-report.ts @@ -24,7 +24,7 @@ import { Sample, subcategoryApplies as sampleSubcategoryApplies, } from "./data/sample"; -import { addTextDiv, makeNameDiv, makeTextDiv } from "./util/html-utils"; +import { addTextDiv, makeNameDiv } from "./util/html-utils"; import { getMetricRequirementText } from "./util/metrics"; import { get } from "./util/requests"; import { siteConfig } from "./util/site-config"; @@ -202,7 +202,15 @@ const sampleGateMetricsDefinition: TableDefinition = { headingClass: "print-width-20", child: true, addChildContents(object, parent, fragment) { - addMetricValueContents(parent.sample, [object], fragment, false); + const shouldCollapse = false; + addMetricValueContents( + parent.sample, + [object], + fragment, + false, + false, + true + ); }, getCellHighlight(reportSample, metric) { if (metric == null) { @@ -413,8 +421,7 @@ function displayQcSignOff(fragment: DocumentFragment, qcable: Qcable) { qcable.qcPassed, qcable.qcReason, qcable.qcUser, - qcable.qcDate, - qcable.qcNote + qcable.qcDate ); } @@ -433,8 +440,7 @@ function displaySignOff( qcPassed?: boolean, qcReason?: string, qcUser?: string, - qcDate?: string, - qcNote?: string + qcDate?: string ) { if (qcDate) { if (!qcUser) { @@ -443,11 +449,6 @@ function displaySignOff( addBoldText(fragment, qcReason || (qcPassed ? "Passed" : "Failed")); addTextDiv(qcUser, fragment); addTextDiv(qcDate, fragment); - if (qcNote) { - const noteDiv = makeTextDiv("Note: " + qcNote); - noteDiv.classList.add("mt-1em"); - fragment.appendChild(noteDiv); - } } else { addPendingText(fragment); } @@ -486,8 +487,12 @@ async function loadCase(caseId: string) { const receipts = getReportSamples(data, data.receipts, "RECEIPT"); new TableBuilder(sampleGateMetricsDefinition, "receiptTableContainer").build( - receipts + receipts.map((reportSample) => ({ + ...reportSample, + shouldCollapse: true, + })) ); + const extractions = getReportSamples( data, data.tests.flatMap((test) => test.extractions), @@ -520,10 +525,17 @@ async function loadCase(caseId: string) { data.tests.flatMap((test) => test.fullDepthSequencings), "FULL_DEPTH_SEQUENCING" ); + new TableBuilder( sampleGateMetricsDefinition, "fullDepthSequencingTableContainer" - ).build(fullDepths); + ).build( + fullDepths.map((reportSample) => ({ + ...reportSample, + shouldCollapse: false, + })) + ); + const informatics = getReportInformatics(data); new TableBuilder( requisitionGateMetricsDefinition, diff --git a/ts/component/table-builder.ts b/ts/component/table-builder.ts index 24776c17..e8871dc1 100644 --- a/ts/component/table-builder.ts +++ b/ts/component/table-builder.ts @@ -880,4 +880,4 @@ function getElement(element?: Type) { export const legendAction: StaticAction = { title: "Legend", handler: toggleLegend, -}; +}; \ No newline at end of file diff --git a/ts/data/case.ts b/ts/data/case.ts index 99c8740a..a121ed20 100644 --- a/ts/data/case.ts +++ b/ts/data/case.ts @@ -38,7 +38,6 @@ export interface Donor { export interface Qcable { qcPassed?: boolean; qcReason?: string; - qcNote?: string; qcUser?: string; qcDate?: string; dataReviewPassed?: boolean; diff --git a/ts/data/sample.ts b/ts/data/sample.ts index f46a5a83..7d491012 100644 --- a/ts/data/sample.ts +++ b/ts/data/sample.ts @@ -574,7 +574,9 @@ export function addMetricValueContents( sample: Sample, metrics: Metric[], fragment: DocumentFragment, - addTooltip: boolean + addTooltip: boolean, + shouldCollapse: boolean = true, + forceVisible: boolean = false ) { const metricNames = metrics .map((metric) => metric.name) @@ -586,16 +588,38 @@ export function addMetricValueContents( // handle metrics that have multiple values switch (metricName) { case METRIC_LABEL_Q30: - addQ30Contents(sample, metrics, fragment, addTooltip); + addQ30Contents( + sample, + metrics, + fragment, + addTooltip, + shouldCollapse, + forceVisible + ); return; case METRIC_LABEL_CLUSTERS_PF_1: case METRIC_LABEL_CLUSTERS_PF_2: - addClustersPfContents(sample, metrics, fragment, addTooltip); + addClustersPfContents( + sample, + metrics, + fragment, + addTooltip, + shouldCollapse, + forceVisible + ); return; case METRIC_LABEL_PHIX: - addPhixContents(sample, metrics, fragment, addTooltip); + addPhixContents( + sample, + metrics, + fragment, + addTooltip, + shouldCollapse, + forceVisible + ); return; } + if (metrics.every((metric) => metric.thresholdType === "BOOLEAN")) { // pass/fail based on QC status if (sample.qcPassed) { @@ -646,11 +670,43 @@ export function addMetricValueContents( } } +function createCollapseButton( + contentWrapper: HTMLElement, + toggleText: string, + shouldCollapse: boolean +): HTMLButtonElement { + const toggleButton = document.createElement("button"); + toggleButton.classList.add("fa", "fa-caret-down", "text-sm", "icon-button"); + toggleButton.style.color = ""; + toggleButton.textContent = toggleText; + toggleButton.classList.toggle("fa-caret-down", shouldCollapse); + + if (shouldCollapse) { + toggleButton.addEventListener("click", () => { + const isExpanded = contentWrapper.classList.toggle("hidden"); + toggleButton.classList.toggle("fa-caret-down", isExpanded); + toggleButton.classList.toggle("fa-caret-up", !isExpanded); + }); + + toggleButton.addEventListener("mouseover", () => { + toggleButton.style.color = "rgb(91, 158, 90)"; + }); + + toggleButton.addEventListener("mouseout", () => { + toggleButton.style.color = ""; + }); + } + + return toggleButton; +} + function addQ30Contents( sample: Sample, metrics: Metric[], fragment: DocumentFragment, - addTooltip: boolean + addTooltip: boolean, + shouldCollapse: boolean = true, + forceVisible: boolean = false ) { // run-level value is checked, but run and lane-level are both displayed if (!sample.run || !sample.run.percentOverQ30) { @@ -661,33 +717,85 @@ function addQ30Contents( } return; } + + // Create a wrapper for the contents + const contentWrapper = document.createElement("div"); fragment.appendChild( makeMetricDisplay(sample.run.percentOverQ30, metrics, addTooltip) ); - sample.run.lanes.forEach((lane) => { - if (!lane.percentOverQ30Read1) { - return; - } - let text = sample.run?.lanes.length === 1 ? "" : `L${lane.laneNumber} `; - text += `R1: ${lane.percentOverQ30Read1}`; - if (lane.percentOverQ30Read2) { - text += ";"; - } - if (lane.percentOverQ30Read2) { - text += ` R2: ${lane.percentOverQ30Read2}`; - } - const div = document.createElement("div"); - div.classList.add("whitespace-nowrap", "print-hanging"); - div.appendChild(document.createTextNode(text)); - fragment.appendChild(div); - }); + + if (forceVisible) { + // Skip collapsing and directly show content + sample.run.lanes.forEach((lane) => { + if (!lane.percentOverQ30Read1) { + return; + } + let text = sample.run?.lanes.length === 1 ? "" : `L${lane.laneNumber} `; + text += `R1: ${lane.percentOverQ30Read1}`; + if (lane.percentOverQ30Read2) { + text += `; R2: ${lane.percentOverQ30Read2}`; + } + const div = document.createElement("div"); + div.classList.add("whitespace-nowrap", "print-hanging"); + div.appendChild(document.createTextNode(text)); + contentWrapper.appendChild(div); + }); + + fragment.appendChild(contentWrapper); + } else if (shouldCollapse) { + // Button to toggle the content + const toggleButton = createCollapseButton( + contentWrapper, + "", + shouldCollapse + ); + + fragment.appendChild(toggleButton); + + // Div to hold the collapsible content (only if shouldCollapse is true) + contentWrapper.classList.add("hidden"); + sample.run.lanes.forEach((lane) => { + if (!lane.percentOverQ30Read1) { + return; + } + let text = sample.run?.lanes.length === 1 ? "" : `L${lane.laneNumber} `; + text += `R1: ${lane.percentOverQ30Read1}`; + if (lane.percentOverQ30Read2) { + text += `; R2: ${lane.percentOverQ30Read2}`; + } + const div = document.createElement("div"); + div.classList.add("whitespace-nowrap", "print-hanging"); + div.appendChild(document.createTextNode(text)); + contentWrapper.appendChild(div); + }); + + fragment.appendChild(contentWrapper); + } else { + // Content will not be collapsed, but still added to fragment + sample.run.lanes.forEach((lane) => { + if (!lane.percentOverQ30Read1) { + return; + } + let text = sample.run?.lanes.length === 1 ? "" : `L${lane.laneNumber} `; + text += `R1: ${lane.percentOverQ30Read1}`; + if (lane.percentOverQ30Read2) { + text += `; R2: ${lane.percentOverQ30Read2}`; + } + const div = document.createElement("div"); + div.classList.add("whitespace-nowrap", "print-hanging"); + div.appendChild(document.createTextNode(text)); + fragment.appendChild(div); + }); + } } function addClustersPfContents( sample: Sample, metrics: Metric[], fragment: DocumentFragment, - addTooltip: boolean + addTooltip: boolean, + shouldCollapse: boolean = true, + forceVisible: boolean = false ) { // For joined flowcells, run-level is checked // For non-joined, each lane is checked @@ -700,17 +808,20 @@ function addClustersPfContents( } return; } + const separatedMetrics = separateRunVsLaneMetrics(metrics, sample.run); const perRunMetrics = separatedMetrics[0]; const perLaneMetrics = separatedMetrics[1]; const tooltip = Tooltip.getInstance(); const runDiv = document.createElement("div"); const divisorUnit = getDivisorUnit(metrics); + runDiv.innerText = formatMetricValue( sample.run.clustersPf, metrics, divisorUnit ); + if (addTooltip && perRunMetrics.length) { // whether originally or not, these metrics are per run const addContents = (fragment: DocumentFragment) => { @@ -723,27 +834,59 @@ function addClustersPfContents( fragment.appendChild(runDiv); if (sample.run.lanes.length > 1) { + const contentWrapper = document.createElement("div"); const addContents = (fragment: DocumentFragment) => { // these metrics are per lane perLaneMetrics.forEach((metric) => addMetricRequirementText(metric, fragment) ); }; - sample.run.lanes.forEach((lane) => { - if (lane.clustersPf) { - const laneDiv = document.createElement("div"); - laneDiv.classList.add("whitespace-nowrap", "print-hanging"); - laneDiv.innerText = `L${lane.laneNumber}: ${formatMetricValue( - lane.clustersPf, - metrics, - divisorUnit - )}`; - if (addTooltip && perLaneMetrics.length) { - tooltip.addTarget(laneDiv, addContents); + + if (shouldCollapse) { + const toggleButton = createCollapseButton( + contentWrapper, + "", + shouldCollapse + ); + + fragment.appendChild(toggleButton); + + contentWrapper.classList.add("hidden"); + sample.run.lanes.forEach((lane) => { + if (lane.clustersPf) { + const laneDiv = document.createElement("div"); + laneDiv.classList.add("whitespace-nowrap", "print-hanging"); + laneDiv.innerText = `L${lane.laneNumber}: ${formatMetricValue( + lane.clustersPf, + metrics, + divisorUnit + )}`; + if (addTooltip && perLaneMetrics.length) { + tooltip.addTarget(laneDiv, addContents); + } + contentWrapper.appendChild(laneDiv); } - fragment.appendChild(laneDiv); - } - }); + }); + + fragment.appendChild(contentWrapper); + } else { + // Content will not be collapsed, but still added to fragment + sample.run.lanes.forEach((lane) => { + if (lane.clustersPf) { + const laneDiv = document.createElement("div"); + laneDiv.classList.add("whitespace-nowrap", "print-hanging"); + laneDiv.innerText = `L${lane.laneNumber}: ${formatMetricValue( + lane.clustersPf, + metrics, + divisorUnit + )}`; + if (addTooltip && perLaneMetrics.length) { + tooltip.addTarget(laneDiv, addContents); + } + fragment.appendChild(laneDiv); + } + }); + } } } @@ -836,7 +979,9 @@ function addPhixContents( sample: Sample, metrics: Metric[], fragment: DocumentFragment, - addTooltip: boolean + addTooltip: boolean, + shouldCollapse: boolean = true, + forceVisible: boolean = false ) { // There is no run-level metric, so we check each read of each lane if ( @@ -853,36 +998,85 @@ function addPhixContents( return; } + const contentWrapper = document.createElement("div"); + + const minPhixValue = Math.min( + ...sample.run.lanes.map((lane) => lane.percentPfixRead1) + ); + const minPhixDiv = document.createElement("div"); + minPhixDiv.classList.add("whitespace-nowrap", "print-hanging"); + minPhixDiv.innerText = `${minPhixValue.toFixed(2)}+/R`; + fragment.appendChild(minPhixDiv); + const tooltip = Tooltip.getInstance(); const addContents = (fragment: DocumentFragment) => { metrics.forEach((metric) => addMetricRequirementText(metric, fragment)); }; - const multipleLanes = sample.run.lanes.length > 1; - sample.run.lanes.forEach((lane) => { - const laneDiv = document.createElement("div"); - laneDiv.classList.add("whitespace-nowrap", "print-hanging"); - let text = multipleLanes ? `L${lane.laneNumber}` : ""; - if (nullOrUndefined(lane.percentPfixRead1)) { - const span = document.createElement("span"); - span.innerText = text + ": "; - laneDiv.appendChild(span); - laneDiv.appendChild(makeNotFoundIcon()); - } else { - if (multipleLanes) { - text += " "; - } - text += `R1: ${lane.percentPfixRead1}`; - if (!nullOrUndefined(lane.percentPfixRead2)) { - text += `; R2: ${lane.percentPfixRead2}`; + if (shouldCollapse) { + const toggleButton = createCollapseButton( + contentWrapper, + "", + shouldCollapse + ); + + fragment.appendChild(toggleButton); + + contentWrapper.classList.add("hidden"); + const multipleLanes = sample.run.lanes.length > 1; + sample.run.lanes.forEach((lane) => { + const laneDiv = document.createElement("div"); + laneDiv.classList.add("whitespace-nowrap", "print-hanging"); + let text = multipleLanes ? `L${lane.laneNumber}` : ""; + if (nullOrUndefined(lane.percentPfixRead1)) { + const span = document.createElement("span"); + span.innerText = text + ": "; + laneDiv.appendChild(span); + laneDiv.appendChild(makeNotFoundIcon()); + } else { + if (multipleLanes) { + text += " "; + } + text += `R1: ${lane.percentPfixRead1}`; + if (!nullOrUndefined(lane.percentPfixRead2)) { + text += `; R2: ${lane.percentPfixRead2}`; + } + laneDiv.innerText = text; + if (addTooltip) { + tooltip.addTarget(laneDiv, addContents); + } } - laneDiv.innerText = text; - if (addTooltip) { - tooltip.addTarget(laneDiv, addContents); + contentWrapper.appendChild(laneDiv); + }); + + fragment.appendChild(contentWrapper); + } else { + const multipleLanes = sample.run.lanes.length > 1; + sample.run.lanes.forEach((lane) => { + const laneDiv = document.createElement("div"); + laneDiv.classList.add("whitespace-nowrap", "print-hanging"); + let text = multipleLanes ? `L${lane.laneNumber}` : ""; + if (nullOrUndefined(lane.percentPfixRead1)) { + const span = document.createElement("span"); + span.innerText = text + ": "; + laneDiv.appendChild(span); + laneDiv.appendChild(makeNotFoundIcon()); + } else { + if (multipleLanes) { + text += " "; + } + text += `R1: ${lane.percentPfixRead1}`; + if (!nullOrUndefined(lane.percentPfixRead2)) { + text += `; R2: ${lane.percentPfixRead2}`; + } + laneDiv.innerText = text; + if (addTooltip) { + tooltip.addTarget(laneDiv, addContents); + } } - } - fragment.appendChild(laneDiv); - }); + fragment.appendChild(laneDiv); + }); + } } function getPhixHighlight( From b75afb54222dd3e1b7d487d24b95ff1373b809c5 Mon Sep 17 00:00:00 2001 From: wuall826 <121817690+wuall826@users.noreply.github.com> Date: Wed, 16 Aug 2023 12:09:21 -0400 Subject: [PATCH 2/8] Add files via upload --- changes/add_GLT-3764.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 changes/add_GLT-3764.md diff --git a/changes/add_GLT-3764.md b/changes/add_GLT-3764.md new file mode 100644 index 00000000..d38f5875 --- /dev/null +++ b/changes/add_GLT-3764.md @@ -0,0 +1 @@ +Collapse lane-level values to reduce run metric visual clutter - revisions \ No newline at end of file From 22d2ce6eb6c6d90b9141e6f3fe4d5e50bb968e7f Mon Sep 17 00:00:00 2001 From: wuall826 <121817690+wuall826@users.noreply.github.com> Date: Tue, 29 Aug 2023 16:17:36 +0000 Subject: [PATCH 3/8] implemented comments --- changes/add_GLT-3764.md | 2 +- ts/case-report.ts | 38 +++-- ts/data/case.ts | 1 + ts/data/sample.ts | 308 +++++++++++++--------------------------- 4 files changed, 117 insertions(+), 232 deletions(-) diff --git a/changes/add_GLT-3764.md b/changes/add_GLT-3764.md index d38f5875..4442596c 100644 --- a/changes/add_GLT-3764.md +++ b/changes/add_GLT-3764.md @@ -1 +1 @@ -Collapse lane-level values to reduce run metric visual clutter - revisions \ No newline at end of file +Collapse lane-level values to reduce run metric visual clutter \ No newline at end of file diff --git a/ts/case-report.ts b/ts/case-report.ts index 9bb40f98..747d68e5 100644 --- a/ts/case-report.ts +++ b/ts/case-report.ts @@ -4,6 +4,7 @@ import { TableBuilder, TableDefinition } from "./component/table-builder"; import { Metric, MetricCategory, MetricSubcategory } from "./data/assay"; import { Case, Qcable } from "./data/case"; import { qcStatuses } from "./data/qc-status"; +import { makeTextDiv } from "./util/html-utils"; import { getMetricValue, getRequisitionMetricCellHighlight, @@ -202,15 +203,7 @@ const sampleGateMetricsDefinition: TableDefinition = { headingClass: "print-width-20", child: true, addChildContents(object, parent, fragment) { - const shouldCollapse = false; - addMetricValueContents( - parent.sample, - [object], - fragment, - false, - false, - true - ); + addMetricValueContents(parent.sample, [object], fragment, false, !true); }, getCellHighlight(reportSample, metric) { if (metric == null) { @@ -421,7 +414,8 @@ function displayQcSignOff(fragment: DocumentFragment, qcable: Qcable) { qcable.qcPassed, qcable.qcReason, qcable.qcUser, - qcable.qcDate + qcable.qcDate, + qcable.qcNote ); } @@ -440,7 +434,8 @@ function displaySignOff( qcPassed?: boolean, qcReason?: string, qcUser?: string, - qcDate?: string + qcDate?: string, + qcNote?: string ) { if (qcDate) { if (!qcUser) { @@ -449,6 +444,11 @@ function displaySignOff( addBoldText(fragment, qcReason || (qcPassed ? "Passed" : "Failed")); addTextDiv(qcUser, fragment); addTextDiv(qcDate, fragment); + if (qcNote) { + const noteDiv = makeTextDiv("Note: " + qcNote); + noteDiv.classList.add("mt-1em"); + fragment.appendChild(noteDiv); + } } else { addPendingText(fragment); } @@ -487,10 +487,7 @@ async function loadCase(caseId: string) { const receipts = getReportSamples(data, data.receipts, "RECEIPT"); new TableBuilder(sampleGateMetricsDefinition, "receiptTableContainer").build( - receipts.map((reportSample) => ({ - ...reportSample, - shouldCollapse: true, - })) + receipts ); const extractions = getReportSamples( @@ -502,6 +499,7 @@ async function loadCase(caseId: string) { sampleGateMetricsDefinition, "extractionTableContainer" ).build(extractions); + const libraryPreps = getReportSamples( data, data.tests.flatMap((test) => test.libraryPreparations), @@ -511,6 +509,7 @@ async function loadCase(caseId: string) { sampleGateMetricsDefinition, "libraryPreparationTableContainer" ).build(libraryPreps); + const libraryQualifications = getReportSamples( data, data.tests.flatMap((test) => test.libraryQualifications), @@ -520,21 +519,16 @@ async function loadCase(caseId: string) { sampleGateMetricsDefinition, "libraryQualificationTableContainer" ).build(libraryQualifications); + const fullDepths = getReportSamples( data, data.tests.flatMap((test) => test.fullDepthSequencings), "FULL_DEPTH_SEQUENCING" ); - new TableBuilder( sampleGateMetricsDefinition, "fullDepthSequencingTableContainer" - ).build( - fullDepths.map((reportSample) => ({ - ...reportSample, - shouldCollapse: false, - })) - ); + ).build(fullDepths); const informatics = getReportInformatics(data); new TableBuilder( diff --git a/ts/data/case.ts b/ts/data/case.ts index a121ed20..36d3afb8 100644 --- a/ts/data/case.ts +++ b/ts/data/case.ts @@ -40,6 +40,7 @@ export interface Qcable { qcReason?: string; qcUser?: string; qcDate?: string; + qcNote?: string; dataReviewPassed?: boolean; dataReviewUser?: string; dataReviewDate?: string; diff --git a/ts/data/sample.ts b/ts/data/sample.ts index 7d491012..7d8a3d6c 100644 --- a/ts/data/sample.ts +++ b/ts/data/sample.ts @@ -14,7 +14,7 @@ import { import { Tooltip } from "../component/tooltip"; import { urls } from "../util/urls"; import { Metric, MetricCategory, MetricSubcategory } from "./assay"; -import { Donor, Qcable, Run } from "./case"; +import { Donor, Lane, Qcable, Run } from "./case"; import { QcStatus, qcStatuses } from "./qc-status"; import { anyFail, @@ -575,8 +575,7 @@ export function addMetricValueContents( metrics: Metric[], fragment: DocumentFragment, addTooltip: boolean, - shouldCollapse: boolean = true, - forceVisible: boolean = false + shouldCollapse: boolean = true ) { const metricNames = metrics .map((metric) => metric.name) @@ -588,14 +587,7 @@ export function addMetricValueContents( // handle metrics that have multiple values switch (metricName) { case METRIC_LABEL_Q30: - addQ30Contents( - sample, - metrics, - fragment, - addTooltip, - shouldCollapse, - forceVisible - ); + addQ30Contents(sample, metrics, fragment, addTooltip, shouldCollapse); return; case METRIC_LABEL_CLUSTERS_PF_1: case METRIC_LABEL_CLUSTERS_PF_2: @@ -604,19 +596,11 @@ export function addMetricValueContents( metrics, fragment, addTooltip, - shouldCollapse, - forceVisible + shouldCollapse ); return; case METRIC_LABEL_PHIX: - addPhixContents( - sample, - metrics, - fragment, - addTooltip, - shouldCollapse, - forceVisible - ); + addPhixContents(sample, metrics, fragment, shouldCollapse); return; } @@ -677,9 +661,8 @@ function createCollapseButton( ): HTMLButtonElement { const toggleButton = document.createElement("button"); toggleButton.classList.add("fa", "fa-caret-down", "text-sm", "icon-button"); - toggleButton.style.color = ""; + toggleButton.textContent = toggleText; - toggleButton.classList.toggle("fa-caret-down", shouldCollapse); if (shouldCollapse) { toggleButton.addEventListener("click", () => { @@ -689,24 +672,39 @@ function createCollapseButton( }); toggleButton.addEventListener("mouseover", () => { - toggleButton.style.color = "rgb(91, 158, 90)"; + toggleButton.classList.add("text-green-200"); }); toggleButton.addEventListener("mouseout", () => { - toggleButton.style.color = ""; + toggleButton.classList.remove("text-green-200"); }); } return toggleButton; } +function handleCollapse( + contentWrapper: HTMLElement, + metricWrapper: HTMLElement, + fragment: DocumentFragment, + shouldCollapse: boolean +) { + if (shouldCollapse) { + const toggleButton = createCollapseButton(contentWrapper, "", true); + metricWrapper.appendChild(toggleButton); + contentWrapper.classList.add("hidden"); + } + + fragment.appendChild(metricWrapper); + fragment.appendChild(contentWrapper); +} + function addQ30Contents( sample: Sample, metrics: Metric[], fragment: DocumentFragment, addTooltip: boolean, - shouldCollapse: boolean = true, - forceVisible: boolean = false + shouldCollapse: boolean = true ) { // run-level value is checked, but run and lane-level are both displayed if (!sample.run || !sample.run.percentOverQ30) { @@ -718,75 +716,27 @@ function addQ30Contents( return; } - // Create a wrapper for the contents - const contentWrapper = document.createElement("div"); - fragment.appendChild( + // Create a wrapper for the metric and the button + const metricWrapper = document.createElement("div"); + metricWrapper.style.display = "flex"; + metricWrapper.appendChild( makeMetricDisplay(sample.run.percentOverQ30, metrics, addTooltip) ); - if (forceVisible) { - // Skip collapsing and directly show content - sample.run.lanes.forEach((lane) => { - if (!lane.percentOverQ30Read1) { - return; - } - let text = sample.run?.lanes.length === 1 ? "" : `L${lane.laneNumber} `; - text += `R1: ${lane.percentOverQ30Read1}`; - if (lane.percentOverQ30Read2) { - text += `; R2: ${lane.percentOverQ30Read2}`; - } - const div = document.createElement("div"); - div.classList.add("whitespace-nowrap", "print-hanging"); - div.appendChild(document.createTextNode(text)); - contentWrapper.appendChild(div); - }); - - fragment.appendChild(contentWrapper); - } else if (shouldCollapse) { - // Button to toggle the content - const toggleButton = createCollapseButton( - contentWrapper, - "", - shouldCollapse - ); - - fragment.appendChild(toggleButton); + const contentWrapper = document.createElement("div"); - // Div to hold the collapsible content (only if shouldCollapse is true) - contentWrapper.classList.add("hidden"); - sample.run.lanes.forEach((lane) => { - if (!lane.percentOverQ30Read1) { - return; - } - let text = sample.run?.lanes.length === 1 ? "" : `L${lane.laneNumber} `; - text += `R1: ${lane.percentOverQ30Read1}`; - if (lane.percentOverQ30Read2) { - text += `; R2: ${lane.percentOverQ30Read2}`; - } - const div = document.createElement("div"); - div.classList.add("whitespace-nowrap", "print-hanging"); - div.appendChild(document.createTextNode(text)); - contentWrapper.appendChild(div); - }); + sample.run!.lanes.forEach((lane) => { + if (!lane.percentOverQ30Read1) return; + let text = sample.run?.lanes.length === 1 ? "" : `L${lane.laneNumber} `; + text += `R1: ${lane.percentOverQ30Read1}`; + if (lane.percentOverQ30Read2) text += `; R2: ${lane.percentOverQ30Read2}`; + const div = document.createElement("div"); + div.classList.add("whitespace-nowrap", "print-hanging"); + div.appendChild(document.createTextNode(text)); + contentWrapper.appendChild(div); + }); - fragment.appendChild(contentWrapper); - } else { - // Content will not be collapsed, but still added to fragment - sample.run.lanes.forEach((lane) => { - if (!lane.percentOverQ30Read1) { - return; - } - let text = sample.run?.lanes.length === 1 ? "" : `L${lane.laneNumber} `; - text += `R1: ${lane.percentOverQ30Read1}`; - if (lane.percentOverQ30Read2) { - text += `; R2: ${lane.percentOverQ30Read2}`; - } - const div = document.createElement("div"); - div.classList.add("whitespace-nowrap", "print-hanging"); - div.appendChild(document.createTextNode(text)); - fragment.appendChild(div); - }); - } + handleCollapse(contentWrapper, metricWrapper, fragment, shouldCollapse); } function addClustersPfContents( @@ -794,12 +744,8 @@ function addClustersPfContents( metrics: Metric[], fragment: DocumentFragment, addTooltip: boolean, - shouldCollapse: boolean = true, - forceVisible: boolean = false + shouldCollapse: boolean = true ) { - // For joined flowcells, run-level is checked - // For non-joined, each lane is checked - // Metric is sometimes specified "/lane", sometimes per run if (!sample.run || !sample.run.clustersPf) { if (sample.run && !sample.run.completionDate) { fragment.appendChild(makeSequencingIcon()); @@ -823,7 +769,6 @@ function addClustersPfContents( ); if (addTooltip && perRunMetrics.length) { - // whether originally or not, these metrics are per run const addContents = (fragment: DocumentFragment) => { perRunMetrics.forEach((metric) => addMetricRequirementText(metric, fragment) @@ -831,28 +776,24 @@ function addClustersPfContents( }; tooltip.addTarget(runDiv, addContents); } - fragment.appendChild(runDiv); - if (sample.run.lanes.length > 1) { - const contentWrapper = document.createElement("div"); - const addContents = (fragment: DocumentFragment) => { - // these metrics are per lane - perLaneMetrics.forEach((metric) => - addMetricRequirementText(metric, fragment) - ); - }; + // Create a wrapper for the metric and the button + const metricWrapper = document.createElement("div"); + metricWrapper.style.display = "flex"; + metricWrapper.style.alignItems = "center"; - if (shouldCollapse) { - const toggleButton = createCollapseButton( - contentWrapper, - "", - shouldCollapse - ); + metricWrapper.appendChild(runDiv); - fragment.appendChild(toggleButton); + if (sample.run.lanes.length > 1) { + const processLaneContents = () => { + const contentWrapper = document.createElement("div"); + const addContents = (fragment: DocumentFragment) => { + perLaneMetrics.forEach((metric) => + addMetricRequirementText(metric, fragment) + ); + }; - contentWrapper.classList.add("hidden"); - sample.run.lanes.forEach((lane) => { + sample.run!.lanes.forEach((lane) => { if (lane.clustersPf) { const laneDiv = document.createElement("div"); laneDiv.classList.add("whitespace-nowrap", "print-hanging"); @@ -861,32 +802,17 @@ function addClustersPfContents( metrics, divisorUnit )}`; - if (addTooltip && perLaneMetrics.length) { + if (perLaneMetrics.length) { tooltip.addTarget(laneDiv, addContents); } contentWrapper.appendChild(laneDiv); } }); - fragment.appendChild(contentWrapper); - } else { - // Content will not be collapsed, but still added to fragment - sample.run.lanes.forEach((lane) => { - if (lane.clustersPf) { - const laneDiv = document.createElement("div"); - laneDiv.classList.add("whitespace-nowrap", "print-hanging"); - laneDiv.innerText = `L${lane.laneNumber}: ${formatMetricValue( - lane.clustersPf, - metrics, - divisorUnit - )}`; - if (addTooltip && perLaneMetrics.length) { - tooltip.addTarget(laneDiv, addContents); - } - fragment.appendChild(laneDiv); - } - }); - } + handleCollapse(contentWrapper, metricWrapper, fragment, shouldCollapse); + }; + + processLaneContents(); } } @@ -979,11 +905,8 @@ function addPhixContents( sample: Sample, metrics: Metric[], fragment: DocumentFragment, - addTooltip: boolean, - shouldCollapse: boolean = true, - forceVisible: boolean = false + shouldCollapse: boolean = true ) { - // There is no run-level metric, so we check each read of each lane if ( !sample.run || !sample.run.lanes || @@ -998,85 +921,52 @@ function addPhixContents( return; } - const contentWrapper = document.createElement("div"); + const processLane = (lane: Lane, multipleLanes: boolean) => { + const laneDiv = document.createElement("div"); + laneDiv.classList.add("whitespace-nowrap", "print-hanging"); + let text = multipleLanes ? `L${lane.laneNumber}` : ""; + if (nullOrUndefined(lane.percentPfixRead1)) { + const span = document.createElement("span"); + span.innerText = text + ": "; + laneDiv.appendChild(span); + laneDiv.appendChild(makeNotFoundIcon()); + } else { + if (multipleLanes) { + text += " "; + } + text += `R1: ${lane.percentPfixRead1}`; + if (!nullOrUndefined(lane.percentPfixRead2)) { + text += `; R2: ${lane.percentPfixRead2}`; + } + laneDiv.innerText = text; + const tooltip = Tooltip.getInstance(); + const addContents = (fragment: DocumentFragment) => { + metrics.forEach((metric) => addMetricRequirementText(metric, fragment)); + }; + tooltip.addTarget(laneDiv, addContents); + } + return laneDiv; + }; const minPhixValue = Math.min( ...sample.run.lanes.map((lane) => lane.percentPfixRead1) ); + const metricWrapper = document.createElement("div"); + metricWrapper.style.display = "flex"; // Set metricWrapper to be a flex container + metricWrapper.style.alignItems = "center"; // Vertically center contents + const minPhixDiv = document.createElement("div"); minPhixDiv.classList.add("whitespace-nowrap", "print-hanging"); minPhixDiv.innerText = `${minPhixValue.toFixed(2)}+/R`; - fragment.appendChild(minPhixDiv); + metricWrapper.appendChild(minPhixDiv); - const tooltip = Tooltip.getInstance(); - const addContents = (fragment: DocumentFragment) => { - metrics.forEach((metric) => addMetricRequirementText(metric, fragment)); - }; - - if (shouldCollapse) { - const toggleButton = createCollapseButton( - contentWrapper, - "", - shouldCollapse - ); - - fragment.appendChild(toggleButton); - - contentWrapper.classList.add("hidden"); - const multipleLanes = sample.run.lanes.length > 1; - sample.run.lanes.forEach((lane) => { - const laneDiv = document.createElement("div"); - laneDiv.classList.add("whitespace-nowrap", "print-hanging"); - let text = multipleLanes ? `L${lane.laneNumber}` : ""; - if (nullOrUndefined(lane.percentPfixRead1)) { - const span = document.createElement("span"); - span.innerText = text + ": "; - laneDiv.appendChild(span); - laneDiv.appendChild(makeNotFoundIcon()); - } else { - if (multipleLanes) { - text += " "; - } - text += `R1: ${lane.percentPfixRead1}`; - if (!nullOrUndefined(lane.percentPfixRead2)) { - text += `; R2: ${lane.percentPfixRead2}`; - } - laneDiv.innerText = text; - if (addTooltip) { - tooltip.addTarget(laneDiv, addContents); - } - } - contentWrapper.appendChild(laneDiv); - }); + const contentWrapper = document.createElement("div"); + const multipleLanes = sample.run.lanes.length > 1; + sample.run.lanes.forEach((lane) => { + contentWrapper.appendChild(processLane(lane, multipleLanes)); + }); - fragment.appendChild(contentWrapper); - } else { - const multipleLanes = sample.run.lanes.length > 1; - sample.run.lanes.forEach((lane) => { - const laneDiv = document.createElement("div"); - laneDiv.classList.add("whitespace-nowrap", "print-hanging"); - let text = multipleLanes ? `L${lane.laneNumber}` : ""; - if (nullOrUndefined(lane.percentPfixRead1)) { - const span = document.createElement("span"); - span.innerText = text + ": "; - laneDiv.appendChild(span); - laneDiv.appendChild(makeNotFoundIcon()); - } else { - if (multipleLanes) { - text += " "; - } - text += `R1: ${lane.percentPfixRead1}`; - if (!nullOrUndefined(lane.percentPfixRead2)) { - text += `; R2: ${lane.percentPfixRead2}`; - } - laneDiv.innerText = text; - if (addTooltip) { - tooltip.addTarget(laneDiv, addContents); - } - } - fragment.appendChild(laneDiv); - }); - } + handleCollapse(contentWrapper, metricWrapper, fragment, shouldCollapse); } function getPhixHighlight( From c1afbc4607b6587d1dd386c16d16f85889422edf Mon Sep 17 00:00:00 2001 From: wuall826 <121817690+wuall826@users.noreply.github.com> Date: Fri, 1 Sep 2023 22:04:22 +0000 Subject: [PATCH 4/8] more comments implemented More comments implemented --- ts/case-report.ts | 2 +- ts/data/sample.ts | 90 ++++++++++++++++++++++------------------------- 2 files changed, 43 insertions(+), 49 deletions(-) diff --git a/ts/case-report.ts b/ts/case-report.ts index 747d68e5..abe56ec2 100644 --- a/ts/case-report.ts +++ b/ts/case-report.ts @@ -203,7 +203,7 @@ const sampleGateMetricsDefinition: TableDefinition = { headingClass: "print-width-20", child: true, addChildContents(object, parent, fragment) { - addMetricValueContents(parent.sample, [object], fragment, false, !true); + addMetricValueContents(parent.sample, [object], fragment, false, false); }, getCellHighlight(reportSample, metric) { if (metric == null) { diff --git a/ts/data/sample.ts b/ts/data/sample.ts index 7d8a3d6c..1bbed895 100644 --- a/ts/data/sample.ts +++ b/ts/data/sample.ts @@ -600,7 +600,7 @@ export function addMetricValueContents( ); return; case METRIC_LABEL_PHIX: - addPhixContents(sample, metrics, fragment, shouldCollapse); + addPhixContents(sample, metrics, fragment, addTooltip, shouldCollapse); return; } @@ -654,43 +654,40 @@ export function addMetricValueContents( } } -function createCollapseButton( - contentWrapper: HTMLElement, - toggleText: string, - shouldCollapse: boolean -): HTMLButtonElement { +function createCollapseButton(contentWrapper: HTMLElement): HTMLButtonElement { const toggleButton = document.createElement("button"); toggleButton.classList.add("fa", "fa-caret-down", "text-sm", "icon-button"); - toggleButton.textContent = toggleText; - - if (shouldCollapse) { - toggleButton.addEventListener("click", () => { - const isExpanded = contentWrapper.classList.toggle("hidden"); - toggleButton.classList.toggle("fa-caret-down", isExpanded); - toggleButton.classList.toggle("fa-caret-up", !isExpanded); - }); + toggleButton.addEventListener("click", () => { + const isExpanded = contentWrapper.classList.toggle("hidden"); + toggleButton.classList.toggle("fa-caret-down", isExpanded); + toggleButton.classList.toggle("fa-caret-up", !isExpanded); + }); - toggleButton.addEventListener("mouseover", () => { - toggleButton.classList.add("text-green-200"); - }); + toggleButton.addEventListener("mouseover", () => { + toggleButton.classList.add("text-green-200"); + }); - toggleButton.addEventListener("mouseout", () => { - toggleButton.classList.remove("text-green-200"); - }); - } + toggleButton.addEventListener("mouseout", () => { + toggleButton.classList.remove("text-green-200"); + }); return toggleButton; } function handleCollapse( + metricDisplay: HTMLElement, contentWrapper: HTMLElement, - metricWrapper: HTMLElement, fragment: DocumentFragment, shouldCollapse: boolean ) { + const metricWrapper = document.createElement("div"); + metricWrapper.className = "flex space-x-1"; + + metricWrapper.appendChild(metricDisplay); + if (shouldCollapse) { - const toggleButton = createCollapseButton(contentWrapper, "", true); + const toggleButton = createCollapseButton(contentWrapper); metricWrapper.appendChild(toggleButton); contentWrapper.classList.add("hidden"); } @@ -716,15 +713,13 @@ function addQ30Contents( return; } - // Create a wrapper for the metric and the button - const metricWrapper = document.createElement("div"); - metricWrapper.style.display = "flex"; - metricWrapper.appendChild( - makeMetricDisplay(sample.run.percentOverQ30, metrics, addTooltip) + const metricDisplay = makeMetricDisplay( + sample.run.percentOverQ30, + metrics, + addTooltip ); const contentWrapper = document.createElement("div"); - sample.run!.lanes.forEach((lane) => { if (!lane.percentOverQ30Read1) return; let text = sample.run?.lanes.length === 1 ? "" : `L${lane.laneNumber} `; @@ -736,7 +731,7 @@ function addQ30Contents( contentWrapper.appendChild(div); }); - handleCollapse(contentWrapper, metricWrapper, fragment, shouldCollapse); + handleCollapse(metricDisplay, contentWrapper, fragment, shouldCollapse); } function addClustersPfContents( @@ -746,6 +741,9 @@ function addClustersPfContents( addTooltip: boolean, shouldCollapse: boolean = true ) { + // For joined flowcells, run-level is checked + // For non-joined, each lane is checked + // Metric is sometimes specified "/lane", sometimes per run if (!sample.run || !sample.run.clustersPf) { if (sample.run && !sample.run.completionDate) { fragment.appendChild(makeSequencingIcon()); @@ -777,13 +775,6 @@ function addClustersPfContents( tooltip.addTarget(runDiv, addContents); } - // Create a wrapper for the metric and the button - const metricWrapper = document.createElement("div"); - metricWrapper.style.display = "flex"; - metricWrapper.style.alignItems = "center"; - - metricWrapper.appendChild(runDiv); - if (sample.run.lanes.length > 1) { const processLaneContents = () => { const contentWrapper = document.createElement("div"); @@ -809,10 +800,12 @@ function addClustersPfContents( } }); - handleCollapse(contentWrapper, metricWrapper, fragment, shouldCollapse); + handleCollapse(runDiv, contentWrapper, fragment, shouldCollapse); }; processLaneContents(); + } else { + fragment.appendChild(runDiv); } } @@ -905,8 +898,10 @@ function addPhixContents( sample: Sample, metrics: Metric[], fragment: DocumentFragment, + addTooltip: boolean, shouldCollapse: boolean = true ) { + // There is no run-level metric, so we check each read of each lane if ( !sample.run || !sample.run.lanes || @@ -921,6 +916,11 @@ function addPhixContents( return; } + const tooltip = Tooltip.getInstance(); + const addContents = (fragment: DocumentFragment) => { + metrics.forEach((metric) => addMetricRequirementText(metric, fragment)); + }; + const processLane = (lane: Lane, multipleLanes: boolean) => { const laneDiv = document.createElement("div"); laneDiv.classList.add("whitespace-nowrap", "print-hanging"); @@ -939,11 +939,9 @@ function addPhixContents( text += `; R2: ${lane.percentPfixRead2}`; } laneDiv.innerText = text; - const tooltip = Tooltip.getInstance(); - const addContents = (fragment: DocumentFragment) => { - metrics.forEach((metric) => addMetricRequirementText(metric, fragment)); - }; - tooltip.addTarget(laneDiv, addContents); + if (addTooltip) { + tooltip.addTarget(laneDiv, addContents); + } } return laneDiv; }; @@ -951,14 +949,10 @@ function addPhixContents( const minPhixValue = Math.min( ...sample.run.lanes.map((lane) => lane.percentPfixRead1) ); - const metricWrapper = document.createElement("div"); - metricWrapper.style.display = "flex"; // Set metricWrapper to be a flex container - metricWrapper.style.alignItems = "center"; // Vertically center contents const minPhixDiv = document.createElement("div"); minPhixDiv.classList.add("whitespace-nowrap", "print-hanging"); minPhixDiv.innerText = `${minPhixValue.toFixed(2)}+/R`; - metricWrapper.appendChild(minPhixDiv); const contentWrapper = document.createElement("div"); const multipleLanes = sample.run.lanes.length > 1; @@ -966,7 +960,7 @@ function addPhixContents( contentWrapper.appendChild(processLane(lane, multipleLanes)); }); - handleCollapse(contentWrapper, metricWrapper, fragment, shouldCollapse); + handleCollapse(minPhixDiv, contentWrapper, fragment, shouldCollapse); } function getPhixHighlight( From 32fd5a5dacfe122d4825e3f413a52af7a597fd45 Mon Sep 17 00:00:00 2001 From: wuall826 <121817690+wuall826@users.noreply.github.com> Date: Thu, 14 Sep 2023 15:41:27 +0000 Subject: [PATCH 5/8] fixups --- .../{add_GLT-3764.md => change_GLT-3764.md} | 0 ts/data/sample.ts | 131 ++++++++---------- 2 files changed, 55 insertions(+), 76 deletions(-) rename changes/{add_GLT-3764.md => change_GLT-3764.md} (100%) diff --git a/changes/add_GLT-3764.md b/changes/change_GLT-3764.md similarity index 100% rename from changes/add_GLT-3764.md rename to changes/change_GLT-3764.md diff --git a/ts/data/sample.ts b/ts/data/sample.ts index 1bbed895..c4496008 100644 --- a/ts/data/sample.ts +++ b/ts/data/sample.ts @@ -656,20 +656,13 @@ export function addMetricValueContents( function createCollapseButton(contentWrapper: HTMLElement): HTMLButtonElement { const toggleButton = document.createElement("button"); - toggleButton.classList.add("fa", "fa-caret-down", "text-sm", "icon-button"); + toggleButton.classList.add("fa-solid", "fa-caret-down", "text-sm"); toggleButton.addEventListener("click", () => { const isExpanded = contentWrapper.classList.toggle("hidden"); toggleButton.classList.toggle("fa-caret-down", isExpanded); toggleButton.classList.toggle("fa-caret-up", !isExpanded); - }); - - toggleButton.addEventListener("mouseover", () => { - toggleButton.classList.add("text-green-200"); - }); - - toggleButton.addEventListener("mouseout", () => { - toggleButton.classList.remove("text-green-200"); + toggleButton.classList.add("active:text-green-200"); }); return toggleButton; @@ -767,6 +760,7 @@ function addClustersPfContents( ); if (addTooltip && perRunMetrics.length) { + // whether originally or not, these metrics are per run const addContents = (fragment: DocumentFragment) => { perRunMetrics.forEach((metric) => addMetricRequirementText(metric, fragment) @@ -776,34 +770,31 @@ function addClustersPfContents( } if (sample.run.lanes.length > 1) { - const processLaneContents = () => { - const contentWrapper = document.createElement("div"); - const addContents = (fragment: DocumentFragment) => { - perLaneMetrics.forEach((metric) => - addMetricRequirementText(metric, fragment) - ); - }; + const contentWrapper = document.createElement("div"); + const addContents = (fragment: DocumentFragment) => { + // these metrics are per lane + perLaneMetrics.forEach((metric) => + addMetricRequirementText(metric, fragment) + ); + }; - sample.run!.lanes.forEach((lane) => { - if (lane.clustersPf) { - const laneDiv = document.createElement("div"); - laneDiv.classList.add("whitespace-nowrap", "print-hanging"); - laneDiv.innerText = `L${lane.laneNumber}: ${formatMetricValue( - lane.clustersPf, - metrics, - divisorUnit - )}`; - if (perLaneMetrics.length) { - tooltip.addTarget(laneDiv, addContents); - } - contentWrapper.appendChild(laneDiv); + sample.run!.lanes.forEach((lane) => { + if (lane.clustersPf) { + const laneDiv = document.createElement("div"); + laneDiv.classList.add("whitespace-nowrap", "print-hanging"); + laneDiv.innerText = `L${lane.laneNumber}: ${formatMetricValue( + lane.clustersPf, + metrics, + divisorUnit + )}`; + if (perLaneMetrics.length) { + tooltip.addTarget(laneDiv, addContents); } - }); - - handleCollapse(runDiv, contentWrapper, fragment, shouldCollapse); - }; + contentWrapper.appendChild(laneDiv); + } + }); - processLaneContents(); + handleCollapse(runDiv, contentWrapper, fragment, shouldCollapse); } else { fragment.appendChild(runDiv); } @@ -901,18 +892,12 @@ function addPhixContents( addTooltip: boolean, shouldCollapse: boolean = true ) { - // There is no run-level metric, so we check each read of each lane - if ( - !sample.run || - !sample.run.lanes || - !sample.run.lanes.length || - sample.run.lanes.every((lane) => nullOrUndefined(lane.percentPfixRead1)) - ) { - if (sample.run && !sample.run.completionDate) { - fragment.appendChild(makeSequencingIcon()); - } else { - fragment.appendChild(makeNotFoundIcon()); - } + if (!sample.run || !sample.run.lanes || !sample.run.lanes.length) { + fragment.appendChild( + sample.run && !sample.run.completionDate + ? makeSequencingIcon() + : makeNotFoundIcon() + ); return; } @@ -921,45 +906,39 @@ function addPhixContents( metrics.forEach((metric) => addMetricRequirementText(metric, fragment)); }; - const processLane = (lane: Lane, multipleLanes: boolean) => { + const minPhixValue = Math.min( + ...sample.run.lanes + .flatMap((lane) => [lane.percentPfixRead1, lane.percentPfixRead2]) + .filter((value) => typeof value === "number") + ); + + const contentWrapper = document.createElement("div"); + const multipleLanes = sample.run.lanes.length > 1; + + sample.run.lanes.forEach((lane) => { const laneDiv = document.createElement("div"); laneDiv.classList.add("whitespace-nowrap", "print-hanging"); - let text = multipleLanes ? `L${lane.laneNumber}` : ""; - if (nullOrUndefined(lane.percentPfixRead1)) { - const span = document.createElement("span"); - span.innerText = text + ": "; - laneDiv.appendChild(span); - laneDiv.appendChild(makeNotFoundIcon()); - } else { - if (multipleLanes) { - text += " "; - } - text += `R1: ${lane.percentPfixRead1}`; - if (!nullOrUndefined(lane.percentPfixRead2)) { - text += `; R2: ${lane.percentPfixRead2}`; - } - laneDiv.innerText = text; - if (addTooltip) { - tooltip.addTarget(laneDiv, addContents); - } + + const laneText = multipleLanes ? `L${lane.laneNumber}: ` : ""; + laneDiv.innerHTML = nullOrUndefined(lane.percentPfixRead1) + ? `${laneText}${makeNotFoundIcon()}` + : `${laneText}R1: ${lane.percentPfixRead1}${ + !nullOrUndefined(lane.percentPfixRead2) + ? `; R2: ${lane.percentPfixRead2}` + : "" + }`; + + if (addTooltip && !nullOrUndefined(lane.percentPfixRead1)) { + tooltip.addTarget(laneDiv, addContents); } - return laneDiv; - }; - const minPhixValue = Math.min( - ...sample.run.lanes.map((lane) => lane.percentPfixRead1) - ); + contentWrapper.appendChild(laneDiv); + }); const minPhixDiv = document.createElement("div"); minPhixDiv.classList.add("whitespace-nowrap", "print-hanging"); minPhixDiv.innerText = `${minPhixValue.toFixed(2)}+/R`; - const contentWrapper = document.createElement("div"); - const multipleLanes = sample.run.lanes.length > 1; - sample.run.lanes.forEach((lane) => { - contentWrapper.appendChild(processLane(lane, multipleLanes)); - }); - handleCollapse(minPhixDiv, contentWrapper, fragment, shouldCollapse); } From 4c1298bf504231c43590187c5b1c1bc4662dec1c Mon Sep 17 00:00:00 2001 From: wuall826 <121817690+wuall826@users.noreply.github.com> Date: Tue, 19 Sep 2023 14:01:33 +0000 Subject: [PATCH 6/8] more fixups --- ts/data/sample.ts | 63 ++++++++++++++++++++++++++++------------------- 1 file changed, 38 insertions(+), 25 deletions(-) diff --git a/ts/data/sample.ts b/ts/data/sample.ts index c4496008..6511f522 100644 --- a/ts/data/sample.ts +++ b/ts/data/sample.ts @@ -657,12 +657,12 @@ export function addMetricValueContents( function createCollapseButton(contentWrapper: HTMLElement): HTMLButtonElement { const toggleButton = document.createElement("button"); toggleButton.classList.add("fa-solid", "fa-caret-down", "text-sm"); + toggleButton.classList.add("active:text-green-200"); toggleButton.addEventListener("click", () => { const isExpanded = contentWrapper.classList.toggle("hidden"); toggleButton.classList.toggle("fa-caret-down", isExpanded); toggleButton.classList.toggle("fa-caret-up", !isExpanded); - toggleButton.classList.add("active:text-green-200"); }); return toggleButton; @@ -787,7 +787,7 @@ function addClustersPfContents( metrics, divisorUnit )}`; - if (perLaneMetrics.length) { + if (addTooltip && perLaneMetrics.length) { tooltip.addTarget(laneDiv, addContents); } contentWrapper.appendChild(laneDiv); @@ -892,12 +892,18 @@ function addPhixContents( addTooltip: boolean, shouldCollapse: boolean = true ) { - if (!sample.run || !sample.run.lanes || !sample.run.lanes.length) { - fragment.appendChild( - sample.run && !sample.run.completionDate - ? makeSequencingIcon() - : makeNotFoundIcon() - ); + // There is no run-level metric, so we check each read of each lane + if ( + !sample.run || + !sample.run.lanes || + !sample.run.lanes.length || + sample.run.lanes.every((lane) => nullOrUndefined(lane.percentPfixRead1)) + ) { + if (sample.run && !sample.run.completionDate) { + fragment.appendChild(makeSequencingIcon()); + } else { + fragment.appendChild(makeNotFoundIcon()); + } return; } @@ -906,6 +912,29 @@ function addPhixContents( metrics.forEach((metric) => addMetricRequirementText(metric, fragment)); }; + const processLane = (lane: Lane, multipleLanes: boolean) => { + const laneDiv = document.createElement("div"); + laneDiv.classList.add("whitespace-nowrap", "print-hanging"); + + let text = multipleLanes ? `L${lane.laneNumber}: ` : ""; + if (nullOrUndefined(lane.percentPfixRead1)) { + text += `${makeNotFoundIcon()}`; + } else { + text += `R1: ${lane.percentPfixRead1}`; + if (!nullOrUndefined(lane.percentPfixRead2)) { + text += `; R2: ${lane.percentPfixRead2}`; + } + } + + laneDiv.innerHTML = text; + + if (addTooltip && !nullOrUndefined(lane.percentPfixRead1)) { + tooltip.addTarget(laneDiv, addContents); + } + + return laneDiv; + }; + const minPhixValue = Math.min( ...sample.run.lanes .flatMap((lane) => [lane.percentPfixRead1, lane.percentPfixRead2]) @@ -916,27 +945,11 @@ function addPhixContents( const multipleLanes = sample.run.lanes.length > 1; sample.run.lanes.forEach((lane) => { - const laneDiv = document.createElement("div"); - laneDiv.classList.add("whitespace-nowrap", "print-hanging"); - - const laneText = multipleLanes ? `L${lane.laneNumber}: ` : ""; - laneDiv.innerHTML = nullOrUndefined(lane.percentPfixRead1) - ? `${laneText}${makeNotFoundIcon()}` - : `${laneText}R1: ${lane.percentPfixRead1}${ - !nullOrUndefined(lane.percentPfixRead2) - ? `; R2: ${lane.percentPfixRead2}` - : "" - }`; - - if (addTooltip && !nullOrUndefined(lane.percentPfixRead1)) { - tooltip.addTarget(laneDiv, addContents); - } - + const laneDiv = processLane(lane, multipleLanes); contentWrapper.appendChild(laneDiv); }); const minPhixDiv = document.createElement("div"); - minPhixDiv.classList.add("whitespace-nowrap", "print-hanging"); minPhixDiv.innerText = `${minPhixValue.toFixed(2)}+/R`; handleCollapse(minPhixDiv, contentWrapper, fragment, shouldCollapse); From c8ea652ef344fe2c6f8755efccbaef2096cea506 Mon Sep 17 00:00:00 2001 From: wuall826 <121817690+wuall826@users.noreply.github.com> Date: Fri, 22 Sep 2023 20:31:19 +0000 Subject: [PATCH 7/8] phixcontent fixup --- ts/data/sample.ts | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/ts/data/sample.ts b/ts/data/sample.ts index 6511f522..421f9ccc 100644 --- a/ts/data/sample.ts +++ b/ts/data/sample.ts @@ -916,22 +916,27 @@ function addPhixContents( const laneDiv = document.createElement("div"); laneDiv.classList.add("whitespace-nowrap", "print-hanging"); - let text = multipleLanes ? `L${lane.laneNumber}: ` : ""; + if (multipleLanes) { + const laneLabel = document.createTextNode(`L${lane.laneNumber}: `); + laneDiv.appendChild(laneLabel); + } + if (nullOrUndefined(lane.percentPfixRead1)) { - text += `${makeNotFoundIcon()}`; + laneDiv.appendChild(makeNotFoundIcon()); } else { - text += `R1: ${lane.percentPfixRead1}`; - if (!nullOrUndefined(lane.percentPfixRead2)) { - text += `; R2: ${lane.percentPfixRead2}`; + const text = + `R1: ${lane.percentPfixRead1}` + + (!nullOrUndefined(lane.percentPfixRead2) + ? `; R2: ${lane.percentPfixRead2}` + : ""); + const textNode = document.createTextNode(text); + laneDiv.appendChild(textNode); + + if (addTooltip) { + tooltip.addTarget(laneDiv, addContents); } } - laneDiv.innerHTML = text; - - if (addTooltip && !nullOrUndefined(lane.percentPfixRead1)) { - tooltip.addTarget(laneDiv, addContents); - } - return laneDiv; }; From 441722aca9cebb1418b1f67bb73c855b7407e7e7 Mon Sep 17 00:00:00 2001 From: wuall826 <121817690+wuall826@users.noreply.github.com> Date: Wed, 27 Sep 2023 20:28:54 +0000 Subject: [PATCH 8/8] refactoring fixed --- ts/data/sample.ts | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/ts/data/sample.ts b/ts/data/sample.ts index 421f9ccc..68cfb1cf 100644 --- a/ts/data/sample.ts +++ b/ts/data/sample.ts @@ -912,7 +912,17 @@ function addPhixContents( metrics.forEach((metric) => addMetricRequirementText(metric, fragment)); }; - const processLane = (lane: Lane, multipleLanes: boolean) => { + const multipleLanes = sample.run.lanes.length > 1; + + const minPhixValue = Math.min( + ...sample.run.lanes + .flatMap((lane) => [lane.percentPfixRead1, lane.percentPfixRead2]) + .filter((value) => typeof value === "number") + ); + + const contentWrapper = document.createElement("div"); + + sample.run.lanes.forEach((lane) => { const laneDiv = document.createElement("div"); laneDiv.classList.add("whitespace-nowrap", "print-hanging"); @@ -936,21 +946,6 @@ function addPhixContents( tooltip.addTarget(laneDiv, addContents); } } - - return laneDiv; - }; - - const minPhixValue = Math.min( - ...sample.run.lanes - .flatMap((lane) => [lane.percentPfixRead1, lane.percentPfixRead2]) - .filter((value) => typeof value === "number") - ); - - const contentWrapper = document.createElement("div"); - const multipleLanes = sample.run.lanes.length > 1; - - sample.run.lanes.forEach((lane) => { - const laneDiv = processLane(lane, multipleLanes); contentWrapper.appendChild(laneDiv); });