Skip to content

Commit

Permalink
Merge pull request #1827 from nextstrain/measurements-x-scale
Browse files Browse the repository at this point in the history
Limit x-axis by filtered measurements
  • Loading branch information
joverlee521 authored Aug 20, 2024
2 parents dddc6c9 + 6bb14a6 commit 981eb5b
Show file tree
Hide file tree
Showing 3 changed files with 38 additions and 11 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
* Fix bug where app crashed if measurements JSON did not define thresholds ([#1802](https://github.com/nextstrain/auspice/pull/1802))
* Fix bug where measurements display did not honor the default `measurements_display` ([#1802](https://github.com/nextstrain/auspice/pull/1802))
* Only display download-JSON button if the dataset name can be parsed from pathname ([#1804](https://github.com/nextstrain/auspice/pull/1804))
* Fix bug where measurements panel did not display means for measurements that had an "undefined" coloring ([#1827](https://github.com/nextstrain/auspice/pull/1827))
* Measurement panel's x-axis min/max values are now limited by visible measurements ([#1827](https://github.com/nextstrain/auspice/pull/1827))

## version 2.56.0 - 2024/07/01

Expand Down
23 changes: 17 additions & 6 deletions src/components/measurements/index.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useRef, useEffect, useMemo, useState } from "react";
import React, { useCallback, useRef, useEffect, useState } from "react";
import { useSelector } from "react-redux";
import { isEqual, orderBy } from "lodash";
import { NODE_VISIBLE } from "../../util/globals";
Expand Down Expand Up @@ -41,6 +41,17 @@ const useDeepCompareMemo = (value) => {
return ref.current;
};

/**
* A wrapper around React's useCallback hook that does a deep comparison of the
* dependencies.
* @param {function} fn
* @param {Array} dependencies
* @returns
*/
const useDeepCompareCallback = (fn, dependencies) => {
return useCallback(fn, dependencies.map(useDeepCompareMemo))

Check warning on line 52 in src/components/measurements/index.js

View workflow job for this annotation

GitHub Actions / lint (16)

React Hook useCallback has a missing dependency: 'fn'. Either include it or remove the dependency array

Check warning on line 52 in src/components/measurements/index.js

View workflow job for this annotation

GitHub Actions / lint (18)

React Hook useCallback has a missing dependency: 'fn'. Either include it or remove the dependency array

Check warning on line 52 in src/components/measurements/index.js

View workflow job for this annotation

GitHub Actions / lint (20)

React Hook useCallback has a missing dependency: 'fn'. Either include it or remove the dependency array

Check warning on line 52 in src/components/measurements/index.js

View workflow job for this annotation

GitHub Actions / lint (22)

React Hook useCallback has a missing dependency: 'fn'. Either include it or remove the dependency array
}

// Checks visibility against global NODE_VISIBLE
const isVisible = (visibility) => visibility === NODE_VISIBLE;

Expand Down Expand Up @@ -159,9 +170,9 @@ const MeasurementsPlot = ({height, width, showLegend, setPanelTitle}) => {
}
const groupedMeasurements = groupMeasurements(filteredMeasurements, groupBy, groupByValueOrder);

// Memoize D3 scale functions to allow deep comparison to work below for svgData
const xScale = useMemo(() => createXScale(width, measurements), [width, measurements]);
const yScale = useMemo(() => createYScale(), []);
// Cache D3 scale functions to allow deep comparison to work below for svgData
const xScale = useDeepCompareCallback(createXScale(width, filteredMeasurements), [width, filteredMeasurements]);
const yScale = useCallback(createYScale(), []);

Check warning on line 175 in src/components/measurements/index.js

View workflow job for this annotation

GitHub Actions / lint (16)

React Hook useCallback received a function whose dependencies are unknown. Pass an inline function instead

Check warning on line 175 in src/components/measurements/index.js

View workflow job for this annotation

GitHub Actions / lint (18)

React Hook useCallback received a function whose dependencies are unknown. Pass an inline function instead

Check warning on line 175 in src/components/measurements/index.js

View workflow job for this annotation

GitHub Actions / lint (20)

React Hook useCallback received a function whose dependencies are unknown. Pass an inline function instead

Check warning on line 175 in src/components/measurements/index.js

View workflow job for this annotation

GitHub Actions / lint (22)

React Hook useCallback received a function whose dependencies are unknown. Pass an inline function instead
// Memoize all data needed for basic SVG to avoid extra re-drawings
const svgData = useDeepCompareMemo({
containerHeight: height,
Expand All @@ -172,8 +183,8 @@ const MeasurementsPlot = ({height, width, showLegend, setPanelTitle}) => {
groupingOrderedValues,
groupedMeasurements
});
// Memoize handleHover function to avoid extra useEffect calls
const handleHover = useMemo(() => (data, dataType, mouseX, mouseY, colorByAttr=null) => {
// Cache handleHover function to avoid extra useEffect calls
const handleHover = useCallback((data, dataType, mouseX, mouseY, colorByAttr=null) => {
let newHoverData = null;
if (data !== null) {
// Set color-by attribute as title if provided
Expand Down
24 changes: 19 additions & 5 deletions src/components/measurements/measurementsD3.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,15 +58,24 @@ const getSubplotDOMId = (groupingValueIndex) => `measurement_subplot_${groupingV
/**
* Creates the D3 linear scale for the x-axis with the provided measurements'
* values as the domain and the panelWidth with hard-coded padding values as
* the range. Expected to be shared across all subplots.
* the range. The optional paddingProportion can be provided to include additional
* padding for the domain. Expected to be shared across all subplots.
* @param {number} panelWidth
* @param {Array<Object>} measurements
* @param {number} [paddingProportion=0.1]
* @returns {function}
*/
export const createXScale = (panelWidth, measurements) => {
export const createXScale = (panelWidth, measurements, paddingProportion = 0.1) => {
// Padding the xScale based on proportion
// Copied from https://github.com/d3/d3-scale/issues/150#issuecomment-561304239
function padLinear([x0, x1], k) {
const dx = (x1 - x0) * k / 2;
return [x0 - dx, x1 + dx];
}

return (
scaleLinear()
.domain(extent(measurements, (m) => m.value))
.domain(padLinear(extent(measurements, (m) => m.value), paddingProportion))
.range([layout.leftPadding, panelWidth - layout.rightPadding])
.nice()
);
Expand Down Expand Up @@ -377,8 +386,13 @@ export const drawMeansForColorBy = (ref, svgData, treeStrainColors, legendValues
const ySpacing = (layout.subplotHeight - 4 * layout.subplotPadding) / (numberOfColorByAttributes - 1);
let yValue = layout.subplotPadding;
// Order the color groups by the legend value order so that we have a stable order for the means
legendValues
.filter((attribute) => String(attribute) in colorByGroups)
const orderedColorGroups = orderBy(
Object.keys(colorByGroups),
(key) => legendValues.indexOf(key),
"asc"
);

orderedColorGroups
.forEach((attribute) => {
const {color, values} = colorByGroups[attribute];
drawMeanAndStandardDeviation(
Expand Down

0 comments on commit 981eb5b

Please sign in to comment.