From daf2f1e43ac906c0b8bc08b0f788700768f8ce99 Mon Sep 17 00:00:00 2001 From: Jover Date: Wed, 1 Feb 2023 14:02:51 -0800 Subject: [PATCH] measurements: Allow multiple thresholds The measurements panel now expects each collection to have an array of threshold values. The first value is displayed as a solid line and all others are displayed as dashed lines. This change is backwards compatible because single value thresholds are converted to an array during the loading of the measurements JSON. Note `collection.thresholds` takes precedence over the deprecated `collection.threshold`, so the single value will be ignored if `collection.thresholds` exists. The toggle for showing thresholds applies to all threshold values. We can add functionality for individual toggles in the future. --- src/actions/measurements.js | 10 +++++++ .../controls/measurementsOptions.js | 7 +++-- src/components/measurements/index.js | 4 +-- src/components/measurements/measurementsD3.js | 30 ++++++++++--------- 4 files changed, 33 insertions(+), 18 deletions(-) diff --git a/src/actions/measurements.js b/src/actions/measurements.js index 325f87392..c7dd4e191 100644 --- a/src/actions/measurements.js +++ b/src/actions/measurements.js @@ -109,6 +109,16 @@ export const loadMeasurements = (json) => (dispatch, getState) => { } collections.forEach((collection) => { + /** + * Keep backwards compatibility with single value threshold. + * Make sure thresholds are an array of values so that we don't have to + * check the data type in the D3 drawing process + * `collection.thresholds` takes precedence over the deprecated `collection.threshold` + */ + if (typeof collection.threshold === "number") { + collection.thresholds = collection.thresholds || [collection.threshold]; + delete collection.threshold; + } /* * Create fields Map for easier access of titles and to keep ordering * First add fields from JSON to keep user's ordering diff --git a/src/components/controls/measurementsOptions.js b/src/components/controls/measurementsOptions.js index 5afd0b7cb..dee0fa21d 100644 --- a/src/components/controls/measurementsOptions.js +++ b/src/components/controls/measurementsOptions.js @@ -112,9 +112,12 @@ const MeasurementsOptions = () => { /> typeof threshold === "number") + } on={showThreshold} - label="Show measurement threshold" + label="Show measurement threshold(s)" callback={() => dispatch({type: TOGGLE_MEASUREMENTS_THRESHOLD, data: !showThreshold})} /> diff --git a/src/components/measurements/index.js b/src/components/measurements/index.js index 25da25353..0f40e39a1 100644 --- a/src/components/measurements/index.js +++ b/src/components/measurements/index.js @@ -137,7 +137,7 @@ const MeasurementsPlot = ({height, width, showLegend, setPanelTitle}) => { const showOverallMean = useSelector((state) => state.controls.measurementsShowOverallMean); const showThreshold = useSelector((state) => state.controls.measurementsShowThreshold); const collection = useSelector((state) => state.measurements.collectionToDisplay, isEqual); - const { title, x_axis_label, threshold, fields, measurements, groupings } = collection; + const { title, x_axis_label, thresholds, fields, measurements, groupings } = collection; // Ref to access the D3 SVG const d3Ref = useRef(null); @@ -166,7 +166,7 @@ const MeasurementsPlot = ({height, width, showLegend, setPanelTitle}) => { xScale, yScale, x_axis_label, - threshold, + thresholds, groupingOrderedValues, groupedMeasurements }); diff --git a/src/components/measurements/measurementsD3.js b/src/components/measurements/measurementsD3.js index 9d79cdfba..cd46e2cd9 100644 --- a/src/components/measurements/measurementsD3.js +++ b/src/components/measurements/measurementsD3.js @@ -179,7 +179,7 @@ const drawStickyXAxis = (ref, containerHeight, svgHeight, xScale, x_axis_label) }; export const drawMeasurementsSVG = (ref, xAxisRef, svgData) => { - const {containerHeight, xScale, yScale, x_axis_label, threshold, groupingOrderedValues, groupedMeasurements} = svgData; + const {containerHeight, xScale, yScale, x_axis_label, thresholds, groupingOrderedValues, groupedMeasurements} = svgData; // Do not draw SVG if there are no measurements if (groupedMeasurements && groupedMeasurements.length === 0) return; @@ -194,19 +194,21 @@ export const drawMeasurementsSVG = (ref, xAxisRef, svgData) => { // x-axis is in a different SVG element to allow sticky positioning drawStickyXAxis(xAxisRef, containerHeight, svgHeight, xScale, x_axis_label); - // Add threshold if provided - if (threshold !== null) { - const thresholdXValue = xScale(threshold); - svg.append("line") - .attr("class", classes.threshold) - .attr("x1", thresholdXValue) - .attr("x2", thresholdXValue) - .attr("y1", layout.topPadding) - .attr("y2", svgHeight) - .attr("stroke-width", layout.thresholdStrokeWidth) - .attr("stroke", layout.thresholdStroke) - // Hide threshold by default since another function will toggle display - .attr("display", "none"); + // Add threshold(s) if provided + if (thresholds !== null) { + thresholds.forEach((threshold, index) => { + const thresholdXValue = xScale(threshold); + svg.append("line") + .attr("class", classes.threshold) + .attr("x1", thresholdXValue) + .attr("x2", thresholdXValue) + .attr("y1", layout.topPadding) + .attr("y2", svgHeight) + .attr("stroke-width", layout.thresholdStrokeWidth) + .attr("stroke", layout.thresholdStroke) + // Hide threshold by default since another function will toggle display + .attr("display", "none"); + }); } // Create a subplot for each grouping