Skip to content

Commit

Permalink
[GLT-4245] TAT trend report legend for Color by Gate (#228)
Browse files Browse the repository at this point in the history
  • Loading branch information
wuall826 authored Sep 17, 2024
1 parent 19767e0 commit 4c9044a
Show file tree
Hide file tree
Showing 8 changed files with 141 additions and 73 deletions.
1 change: 1 addition & 0 deletions changes/add_GLT-4245
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
TAT Trend report legend for colour by gate
1 change: 1 addition & 0 deletions changes/fix_GLT-4245
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
TAT Trend Report color by gate feature to work correctly with time range grouping
12 changes: 12 additions & 0 deletions src/main/resources/static/css/output.css
Original file line number Diff line number Diff line change
Expand Up @@ -658,6 +658,10 @@ Ensure the default browser behavior of the `hidden` attribute.
display: none;
}

.h-3{
height: 0.75rem;
}

.h-full{
height: 100%;
}
Expand All @@ -666,6 +670,10 @@ Ensure the default browser behavior of the `hidden` attribute.
min-height: 100vh;
}

.w-3{
width: 0.75rem;
}

.w-6{
width: 1.5rem;
}
Expand Down Expand Up @@ -800,6 +808,10 @@ Ensure the default browser behavior of the `hidden` attribute.
justify-content: space-between;
}

.gap-1{
gap: 0.25rem;
}

.gap-2{
gap: 0.5rem;
}
Expand Down
68 changes: 34 additions & 34 deletions src/main/resources/templates/tat-trend.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,49 +11,49 @@
<div layout:fragment="content">
<h1 class="text-green-200 font-sarabun font-light text-32">TAT Trend Report</h1>
<div id="trendReportContainer">
<div id="controlsContainer">
<div id="groupingButtons" style="display: flex; gap: 10px; margin-top: 10px;">
<div id="controlsContainer" class="flex items-center justify-between gap-2 mt-2">
<div id="groupingButtons" class="flex gap-2">
<button id="weekButton" data-grouping="week"
class="font-inter font-medium text-12 text-black bg-grey-100 px-2 py-1 rounded-md hover:ring-2 ring-green-200 ring-offset-1 flex space-x-2 items-center">Week</button>
class="font-inter font-medium text-12 text-black bg-grey-100 px-2 py-1 rounded-md hover:ring-2 ring-green-200 ring-offset-1">Week</button>
<button id="monthButton" data-grouping="month"
class="font-inter font-medium text-12 text-black bg-grey-100 px-2 py-1 rounded-md hover:ring-2 ring-green-200 ring-offset-1 flex space-x-2 items-center">Month</button>
class="font-inter font-medium text-12 text-black bg-grey-100 px-2 py-1 rounded-md hover:ring-2 ring-green-200 ring-offset-1">Month</button>
<button id="quarterButton" data-grouping="fiscalQuarter"
class="font-inter font-medium text-12 text-black bg-grey-100 px-2 py-1 rounded-md hover:ring-2 ring-green-200 ring-offset-1 flex space-x-2 items-center">Fiscal
class="font-inter font-medium text-12 text-black bg-grey-100 px-2 py-1 rounded-md hover:ring-2 ring-green-200 ring-offset-1">Fiscal
Quarter</button>
<button id="yearButton" data-grouping="fiscalYear"
class="font-inter font-medium text-12 text-black bg-grey-100 px-2 py-1 rounded-md hover:ring-2 ring-green-200 ring-offset-1 flex space-x-2 items-center">Fiscal
class="font-inter font-medium text-12 text-black bg-grey-100 px-2 py-1 rounded-md hover:ring-2 ring-green-200 ring-offset-1">Fiscal
Year</button>
<div style="margin-left: auto; display: flex; align-items: center; gap: 10px;">
<label class="font-inter font-medium text-12">
<input type="checkbox" id="toggleColors"> Color by Gate
</label>
<select id="dataSelection"
class="font-inter font-medium text-12 text-black bg-grey-100 px-2 py-1 rounded-md hover:ring-2 ring-green-200 ring-offset-1 flex space-x-2 items-center">
<option value="ClinicalReport">Clinical Report (CR)</option>
<option value="DataRelease">Data Release (DR)</option>
<option value="All">All</option>
</select>
</div>
</div>
<div id="gatesCheckboxes" style="display: flex; gap: 10px; margin-top: 10px;">
<label class="font-inter font-medium text-12"><input type="checkbox" value="Receipt" checked> Receipt</label>
<label class="font-inter font-medium text-12"><input type="checkbox" value="Extraction" checked>
Extraction</label>
<label class="font-inter font-medium text-12"><input type="checkbox" value="Library Prep" checked> Library
Prep.</label>
<label class="font-inter font-medium text-12"><input type="checkbox" value="Library Qual" checked> Library
Qual.</label>
<label class="font-inter font-medium text-12"><input type="checkbox" value="Full-Depth" checked>
Full-Depth</label>
<label class="font-inter font-medium text-12"><input type="checkbox" value="Analysis Review" checked> Analysis
Review</label>
<label class="font-inter font-medium text-12"><input type="checkbox" value="Release Approval" checked> Release
Approval</label>
<label class="font-inter font-medium text-12"><input type="checkbox" value="Release" checked> Release</label>
<label class="font-inter font-medium text-12"><input type="checkbox" value="Full Case" checked> All
Completed</label>
<div class="flex items-center gap-2">
<button id="legendButton"
class="noprint bg-green-200 rounded-md hover:ring-2 ring-offset-1 ring-green-200 text-white font-inter font-medium text-12 px-2 py-1 hidden">Legend</button>
<label class="font-inter font-medium text-12 flex items-center gap-1">
<input type="checkbox" id="toggleColors"> Colour by Gate
</label>
<select id="dataSelection"
class="font-inter font-medium text-12 text-black bg-gray-100 px-2 py-1 rounded-md hover:ring-2 ring-green-200 ring-offset-1">
<option value="ClinicalReport">Clinical Report (CR)</option>
<option value="DataRelease">Data Release (DR)</option>
<option value="All">All</option>
</select>
</div>
</div>
<div id="gatesCheckboxes" class="flex gap-2 mt-2 font-inter font-medium text-12 items-center">
<label class="flex items-center gap-1"><input type="checkbox" value="Receipt" checked> Receipt</label>
<label class="flex items-center gap-1"><input type="checkbox" value="Extraction" checked> Extraction</label>
<label class="flex items-center gap-1"><input type="checkbox" value="Library Prep" checked> Library
Prep.</label>
<label class="flex items-center gap-1"><input type="checkbox" value="Library Qual" checked> Library
Qual.</label>
<label class="flex items-center gap-1"><input type="checkbox" value="Full-Depth" checked> Full-Depth</label>
<label class="flex items-center gap-1"><input type="checkbox" value="Analysis Review" checked> Analysis
Review</label>
<label class="flex items-center gap-1"><input type="checkbox" value="Release Approval" checked> Release
Approval</label>
<label class="flex items-center gap-1"><input type="checkbox" value="Release" checked> Release</label>
<label class="flex items-center gap-1"><input type="checkbox" value="Full Case" checked> Full Case</label>
</div>

<div id="plotContainer"></div>
</div>
<script src="/js/tatTrend.js"></script>
Expand Down
41 changes: 35 additions & 6 deletions ts/component/legend.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { qcStatuses } from "../data/qc-status";
import { makeIcon } from "../util/html-utils";
import { GATE_COLOR_MAPPING, getColorForGate } from "../util/color-mapping";

const legendId = "legend-container";

class Legend {
private container: HTMLElement;
constructor() {
constructor(type: "qc" | "gate" = "qc") {
// outer container
this.container = document.createElement("div");
this.container.className =
Expand Down Expand Up @@ -38,10 +40,20 @@ class Legend {
// grid of legend labels
const body = document.createElement("div");
body.className = "m-4 grid grid-rows-5 grid-flow-col gap-2";
for (const qcStatus of Object.values(qcStatuses)) {
body.appendChild(makeLegendEntry(qcStatus.icon, qcStatus.label));
// populate legend based on type
if (type === "qc") {
for (const qcStatus of Object.values(qcStatuses)) {
body.appendChild(makeLegendEntry(qcStatus.icon, qcStatus.label));
}
body.appendChild(makeLegendEntry("pen-ruler", "Preliminary value"));
} else if (type === "gate") {
for (const gate of Object.keys(GATE_COLOR_MAPPING)) {
const color = getColorForGate(gate);
body.appendChild(makeLegendEntryColor(color, gate));
}
} else {
throw new Error(`Unsupported legend type: ${type}`);
}
body.appendChild(makeLegendEntry("pen-ruler", "Preliminary value"));

this.container.append(header);
this.container.append(body);
Expand Down Expand Up @@ -152,10 +164,10 @@ class Legend {
}
}

export function toggleLegend() {
export function toggleLegend(type: "qc" | "gate" = "qc") {
const legendWindow = document.getElementById(legendId);
if (!legendWindow) {
const legendContainer = new Legend();
const legendContainer = new Legend(type);
document.body.appendChild(legendContainer.getTag());
} else {
legendWindow.remove();
Expand All @@ -174,3 +186,20 @@ function makeLegendEntry(iconName: string, text: string) {
labelContainer.appendChild(label);
return labelContainer;
}

function makeLegendEntryColor(color: string, text: string) {
const labelContainer = document.createElement("div");
labelContainer.className =
"flex items-center space-x-2 bg-grey-100 rounded-md font-inter font-medium p-2 text-12";
// create the color box
const colorBox = document.createElement("div");
colorBox.className = "w-3 h-3 inline-block border-2";
colorBox.style.backgroundColor = `${color}B3`;
colorBox.style.borderColor = color;
const label = document.createElement("span");
label.innerHTML = text;
// append the color box and label to the container
labelContainer.appendChild(colorBox);
labelContainer.appendChild(label);
return labelContainer;
}
2 changes: 1 addition & 1 deletion ts/component/table-builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1056,5 +1056,5 @@ function getElement<Type>(element?: Type) {

export const legendAction: StaticAction = {
title: "Legend",
handler: toggleLegend,
handler: () => toggleLegend("qc"),
};
74 changes: 42 additions & 32 deletions ts/tat-trend.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import Plotly from "plotly.js-dist-min";
import { post } from "./util/requests";
import { getRequiredElementById } from "./util/html-utils";
import { toggleLegend } from "./component/legend";
import { getColorForGate } from "./util/color-mapping";

let jsonData: any[] = [];
const uirevision = "true";
Expand Down Expand Up @@ -259,7 +261,7 @@ function plotData(
pointpos: 0,
marker: {
size: 6,
color: colorByGate ? gateColors[gate] : assayColors[assay],
color: colorByGate ? getColorForGate(gate) : assayColors[assay],
},
boxmean: true,
legendgroup: assay,
Expand Down Expand Up @@ -344,6 +346,31 @@ function updatePlot(
Plotly.react("plotContainer", newPlot, layout);
}

function updatePlotWithLegend(
selectedGrouping: string,
jsonData: any[],
selectedGates: string[],
selectedDataType: string
) {
updatePlot(selectedGrouping, jsonData, selectedGates, selectedDataType);
const legendButton = document.getElementById("legendButton");
if (getColorByGate()) {
// show the Legend button
if (legendButton) {
legendButton.classList.remove("hidden");
}
} else {
// hide the Legend button and close the legend if it's open
if (legendButton) {
legendButton.classList.add("hidden");
}
const legendElement = document.getElementById("legend-container");
if (legendElement) {
legendElement.remove(); // hide the legend if 'color by gate' is deselected
}
}
}

function parseUrlParams(): { key: string; value: string }[] {
const params: { key: string; value: string }[] = [];
const searchParams = new URLSearchParams(window.location.search);
Expand All @@ -364,9 +391,7 @@ function getSelectedGrouping(): string {
const selectedButton = document.querySelector<HTMLButtonElement>(
"#groupingButtons button.active"
);
return selectedButton
? selectedButton.id.replace("Button", "")
: "fiscalQuarter";
return selectedButton ? selectedButton.dataset.grouping! : "fiscalQuarter";
}

function getSelectedDataType(): string {
Expand All @@ -376,7 +401,7 @@ function getSelectedDataType(): string {
return dataSelection.value;
}

document.addEventListener("DOMContentLoaded", () => {
window.addEventListener("load", () => {
const params = parseUrlParams();
const requestData = { filters: params.length > 0 ? params : [] };

Expand All @@ -395,10 +420,11 @@ document.addEventListener("DOMContentLoaded", () => {
});

const handlePlotUpdate = () => {
const selectedGrouping = getSelectedGrouping();
const selectedGates = getSelectedGates();
const selectedDataType = getSelectedDataType();
updatePlot(
getSelectedGrouping(),
updatePlotWithLegend(
selectedGrouping,
jsonData,
selectedGates,
selectedDataType
Expand All @@ -410,33 +436,17 @@ document.addEventListener("DOMContentLoaded", () => {
buttons.forEach((button) => button.classList.remove("active"));
(event.currentTarget as HTMLButtonElement).classList.add("active");

const selectedGrouping = (event.currentTarget as HTMLButtonElement).dataset
.grouping;
const selectedGates = getSelectedGates();
const selectedDataType = getSelectedDataType();
newPlot(selectedGrouping!, jsonData, selectedGates, selectedDataType);
handlePlotUpdate();
};

const weekButton = getRequiredElementById("weekButton");
const monthButton = getRequiredElementById("monthButton");
const quarterButton = getRequiredElementById("quarterButton");
const yearButton = getRequiredElementById("yearButton");
["weekButton", "monthButton", "quarterButton", "yearButton"].forEach((id) => {
getRequiredElementById(id).addEventListener("click", handleNewPlot);
});

weekButton.addEventListener("click", handleNewPlot);
monthButton.addEventListener("click", handleNewPlot);
quarterButton.addEventListener("click", handleNewPlot);
yearButton.addEventListener("click", handleNewPlot);
["dataSelection", "gatesCheckboxes", "toggleColors"].forEach((id) => {
getRequiredElementById(id).addEventListener("change", handlePlotUpdate);
});

getRequiredElementById("dataSelection").addEventListener(
"change",
handlePlotUpdate
);
getRequiredElementById("gatesCheckboxes").addEventListener(
"change",
handlePlotUpdate
);
getRequiredElementById("toggleColors").addEventListener(
"change",
handlePlotUpdate
);
const legendButton = getRequiredElementById("legendButton");
legendButton.addEventListener("click", () => toggleLegend("gate"));
});
15 changes: 15 additions & 0 deletions ts/util/color-mapping.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
export const GATE_COLOR_MAPPING: { [gate: string]: string } = {
"Receipt": "#4477AA",
"Extraction": "#66CCEE",
"Library Prep": "#228833",
"Library Qual": "#CCBB44",
"Full-Depth": "#EE6677",
"Analysis Review": "#44AA99",
"Release Approval": "#AA3377",
"Release": "#BBBBBB",
"Full Case": "#000000",
};

export function getColorForGate(gate: string): string {
return GATE_COLOR_MAPPING[gate] || "#DDDDDD";
}

0 comments on commit 4c9044a

Please sign in to comment.