diff --git a/.github/workflows/pr-check.yml b/.github/workflows/pr-check.yml index 8844511145..a68bdcb2f2 100644 --- a/.github/workflows/pr-check.yml +++ b/.github/workflows/pr-check.yml @@ -17,3 +17,4 @@ jobs: - uses: nrwl/nx-set-shas@v3 - run: yarn - run: yarn nx affected --target=build-npm-modules --parallel=3 + - run: yarn nx affected --target=compile:check diff --git a/packages/libs/components/package.json b/packages/libs/components/package.json index 039a1f8448..dd20ef2f9b 100755 --- a/packages/libs/components/package.json +++ b/packages/libs/components/package.json @@ -19,7 +19,7 @@ "@visx/text": "^1.3.0", "@visx/tooltip": "^1.3.0", "@visx/visx": "^1.1.0", - "@visx/xychart": "^3.1.0", + "@visx/xychart": "https://github.com/jernestmyers/visx.git#visx-xychart", "bootstrap": "^4.5.2", "color-math": "^1.1.3", "d3": "^7.1.1", diff --git a/packages/libs/components/src/components/plotControls/AxisRangeControl.tsx b/packages/libs/components/src/components/plotControls/AxisRangeControl.tsx index 0f52658666..ccffa18c62 100755 --- a/packages/libs/components/src/components/plotControls/AxisRangeControl.tsx +++ b/packages/libs/components/src/components/plotControls/AxisRangeControl.tsx @@ -22,6 +22,8 @@ export interface AxisRangeControlProps disabled?: boolean; /** is this for a log scale axis? If so, we'll validate the min value to be > 0 */ logScale?: boolean; + /** specify step for increment/decrement buttons in MUI number inputs; MUI's default is 1 */ + step?: number; } export default function AxisRangeControl({ @@ -33,6 +35,7 @@ export default function AxisRangeControl({ // add disabled prop to disable input fields: default is false disabled = false, logScale = false, + step = undefined, }: AxisRangeControlProps) { const validator = useCallback( ( @@ -87,6 +90,7 @@ export default function AxisRangeControl({ validator={validator} // add disabled prop to disable input fields disabled={disabled} + step={step} /> ) ) : null; diff --git a/packages/libs/components/src/components/widgets/NumberAndDateRangeInputs.tsx b/packages/libs/components/src/components/widgets/NumberAndDateRangeInputs.tsx index 50f1558b52..cdc625cb07 100755 --- a/packages/libs/components/src/components/widgets/NumberAndDateRangeInputs.tsx +++ b/packages/libs/components/src/components/widgets/NumberAndDateRangeInputs.tsx @@ -6,6 +6,7 @@ import { NumberInput, DateInput } from './NumberAndDateInputs'; import Button from './Button'; import Notification from './Notification'; import { NumberRange, DateRange, NumberOrDateRange } from '../../types/general'; +import { propTypes } from 'react-bootstrap/esm/Image'; export type BaseProps = { /** Externally controlled range. */ @@ -44,7 +45,7 @@ export type BaseProps = { disabled?: boolean; }; -export type NumberRangeInputProps = BaseProps; +export type NumberRangeInputProps = BaseProps & { step?: number }; export function NumberRangeInput(props: NumberRangeInputProps) { return ; @@ -85,6 +86,7 @@ function BaseInput({ clearButtonLabel = 'Clear', // add disabled prop to disable input fields disabled = false, + ...props }: BaseInputProps) { if (validator && required) console.log( @@ -161,6 +163,7 @@ function BaseInput({ ]); const { min, max } = localRange ?? {}; + const step = 'step' in props ? props.step : undefined; return (
@@ -188,6 +191,7 @@ function BaseInput({ }} // add disabled prop to disable input fields disabled={disabled} + step={step} /> ) : ( ) : ( void; + /** Should the label be drawn to the left or right of the node? */ + labelPosition?: 'right' | 'left'; + /** Font size for the label. Ex. "1em" */ + fontSize?: string; + /** Font weight for the label */ + fontWeight?: number; + /** Color for the label */ + labelColor?: string; +} + +// NodeWithLabel draws one node and an optional label for the node. Both the node and +// label can be styled. +export function NodeWithLabel(props: NodeWithLabelProps) { + const DEFAULT_NODE_RADIUS = 4; + const DEFAULT_NODE_COLOR = '#aaa'; + const DEFAULT_STROKE_WIDTH = 1; + + const { + node, + onClick, + labelPosition = 'right', + fontSize = '1em', + fontWeight = 200, + labelColor = '#000', + } = props; + + const { color, label, stroke, strokeWidth } = node; + + const nodeRadius = node.r ?? DEFAULT_NODE_RADIUS; + + // Calculate where the label should be posiitoned based on + // total size of the node. + let textXOffset: number; + let textAnchor: 'start' | 'end'; + + if (labelPosition === 'right') { + textXOffset = 4 + nodeRadius; + if (strokeWidth) textXOffset = textXOffset + strokeWidth; + textAnchor = 'start'; + } else { + textXOffset = -4 - nodeRadius; + if (strokeWidth) textXOffset = textXOffset - strokeWidth; + textAnchor = 'end'; + } + + return ( + <> + + {/* Note that Text becomes a tspan */} + + {label} + + + ); +} + +export interface LinkProps { + link: LinkData; + // onClick?: () => void; To add in the future, maybe also some hover action +} + +// Link component draws a linear edge between two nodes. +// Eventually can grow into drawing directed edges (edges with arrows) when the time comes. +export function Link(props: LinkProps) { + const DEFAULT_LINK_WIDTH = 1; + const DEFAULT_COLOR = '#222'; + const DEFAULT_OPACITY = 0.95; + + const { link } = props; + + return ( + + ); +} diff --git a/packages/libs/components/src/plots/VolcanoPlot.css b/packages/libs/components/src/plots/VolcanoPlot.css new file mode 100644 index 0000000000..3316c0cb7f --- /dev/null +++ b/packages/libs/components/src/plots/VolcanoPlot.css @@ -0,0 +1,28 @@ +.visx-tooltip { + z-index: 1; +} + +.VolcanoPlotTooltip { + padding: 5px 10px; + font-size: 12px; + border-radius: 2px; + box-shadow: 2px 2px 7px rgba(0, 0, 0, 0.5); +} + +.VolcanoPlotTooltip > .pseudo-hr { + margin: 5px auto; + height: 1px; + width: 100%; +} + +.VolcanoPlotTooltip > ul { + margin: 0; + padding: 0; + list-style: none; + line-height: 1.5em; + font-weight: normal; +} + +.VolcanoPlotTooltip > ul > li > span { + font-weight: bold; +} diff --git a/packages/libs/components/src/plots/VolcanoPlot.tsx b/packages/libs/components/src/plots/VolcanoPlot.tsx index 3873ae07ff..68288f233c 100755 --- a/packages/libs/components/src/plots/VolcanoPlot.tsx +++ b/packages/libs/components/src/plots/VolcanoPlot.tsx @@ -10,7 +10,7 @@ import { VolcanoPlotDataPoint, } from '../types/plots/volcanoplot'; import { NumberRange } from '../types/general'; -import { SignificanceColors } from '../types/plots'; +import { SignificanceColors, significanceColors } from '../types/plots'; import { XYChart, Axis, @@ -20,7 +20,9 @@ import { AnnotationLineSubject, DataContext, AnnotationLabel, + Tooltip, } from '@visx/xychart'; +import findNearestDatumXY from '@visx/xychart/lib/utils/findNearestDatumXY'; import { Group } from '@visx/group'; import { gridStyles, @@ -36,6 +38,7 @@ import Spinner from '../components/Spinner'; import { ToImgopts } from 'plotly.js'; import { DEFAULT_CONTAINER_HEIGHT } from './PlotlyPlot'; import domToImage from 'dom-to-image'; +import './VolcanoPlot.css'; export interface RawDataMinMaxValues { x: NumberRange; @@ -65,7 +68,7 @@ export interface VolcanoPlotProps { /** Title of the plot */ plotTitle?: string; /** marker fill opacity: range from 0 to 1 */ - markerBodyOpacity?: number; + markerBodyOpacity: number; /** Truncation bar fill color. If no color provided, truncation bars will be filled with a black and white pattern */ truncationBarFill?: string; /** container name */ @@ -120,8 +123,8 @@ function TruncationRectangle(props: TruncationRectangleProps) { function VolcanoPlot(props: VolcanoPlotProps, ref: Ref) { const { data = EmptyVolcanoPlotData, - independentAxisRange, // not yet implemented - expect this to be set by user - dependentAxisRange, // not yet implemented - expect this to be set by user + independentAxisRange, + dependentAxisRange, significanceThreshold, log2FoldChangeThreshold, markerBodyOpacity, @@ -190,6 +193,18 @@ function VolcanoPlot(props: VolcanoPlotProps, ref: Ref) { const showYMaxTruncationBar = Number(-Math.log10(dataYMin) > yAxisMax); const yTruncationBarHeight = 0.02 * (yAxisMax - yAxisMin); + /** + * Check whether each threshold line is within the graph's axis ranges so we can + * prevent the line from rendering outside the graph. + */ + const showNegativeFoldChangeThresholdLine = + -log2FoldChangeThreshold > xAxisMin; + const showPositiveFoldChangeThresholdLine = + log2FoldChangeThreshold < xAxisMax; + const showSignificanceThresholdLine = + -Math.log10(Number(significanceThreshold)) > yAxisMin && + -Math.log10(Number(significanceThreshold)) < yAxisMax; + return ( // Relative positioning so that tooltips are positioned correctly (tooltips are positioned absolutely)
) { style={{ ...containerStyles, position: 'relative' }} >
{/* The XYChart takes care of laying out the chart elements (children) appropriately. It uses modularized React.context layers for data, events, etc. The following all becomes an svg, so use caution when ordering the children (ex. draw axes before data). */} - ) { ], zero: false, }} + findNearestDatumOverride={findNearestDatumXY} > {/* Set up the axes and grid lines. XYChart magically lays them out correctly */} @@ -261,7 +276,7 @@ function VolcanoPlot(props: VolcanoPlotProps, ref: Ref) { is on the points instead of the line connecting them. */} {/* Draw horizontal significance threshold */} - {significanceThreshold && ( + {significanceThreshold && showSignificanceThresholdLine && ( ) { {/* Draw both vertical log2 fold change threshold lines */} {log2FoldChangeThreshold && ( <> - - - - - - + {showNegativeFoldChangeThresholdLine && ( + + + + )} + {showPositiveFoldChangeThresholdLine && ( + + + + )} )} @@ -303,14 +322,73 @@ function VolcanoPlot(props: VolcanoPlotProps, ref: Ref) { {/* Wrapping in a group in order to change the opacity of points. The GlyphSeries is somehow a bunch of glyphs which are so there should be a way to pass opacity down to those elements, but I haven't found it yet */} - + d.significanceColor} + colorAccessor={(d: VolcanoPlotDataPoint) => d.significanceColor} + findNearestDatumOverride={findNearestDatumXY} /> + + snapTooltipToDatumX + snapTooltipToDatumY + showVerticalCrosshair + showHorizontalCrosshair + horizontalCrosshairStyle={{ stroke: 'red' }} + verticalCrosshairStyle={{ stroke: 'red' }} + unstyled + applyPositionStyle + renderTooltip={(d) => { + const data = d.tooltipData?.nearestDatum?.datum; + /** + * Notes regarding colors in the tooltips: + * 1. We use the data point's significanceColor property for background color + * 2. For color contrast reasons, color for text and hr's border is set conditionally: + * - if significanceColor matches the 'inconclusive' color (grey), we use black + * - else, we use white + * (white font meets contrast ratio threshold (min 3:1 for UI-y things) w/ #AC3B4E (red) and #0E8FAB (blue)) + */ + const color = + data?.significanceColor === significanceColors['inconclusive'] + ? 'black' + : 'white'; + return ( +
+
    + {data?.pointIDs?.map((id) => ( +
  • + {id} +
  • + ))} +
+
+
    +
  • + log2 Fold Change: {data?.log2foldChange} +
  • +
  • + P Value: {data?.pValue} +
  • +
  • + Adjusted P Value:{' '} + {data?.adjustedPValue ?? 'n/a'} +
  • +
+
+ ); + }} + /> {/* Truncation indicators */} {/* Example from https://airbnb.io/visx/docs/pattern */} diff --git a/packages/libs/components/src/stories/plots/Network.stories.tsx b/packages/libs/components/src/stories/plots/Network.stories.tsx new file mode 100755 index 0000000000..fa4d41c9b8 --- /dev/null +++ b/packages/libs/components/src/stories/plots/Network.stories.tsx @@ -0,0 +1,90 @@ +import { Story, Meta } from '@storybook/react/types-6-0'; +import { Graph } from '@visx/network'; +import { NodeData, LinkData, NetworkData } from '../../types/plots/network'; +import { Link, NodeWithLabel } from '../../plots/Network'; + +export default { + title: 'Plots/Network', + component: NodeWithLabel, +} as Meta; + +// For simplicity, make square svgs with the following height and width +const DEFAULT_PLOT_SIZE = 500; + +interface TemplateProps { + data: NetworkData; +} + +// This template is a simple network that highlights our NodeWithLabel and Link components. +const Template: Story = (args) => { + return ( + + } + // The node components are already transformed using x and y. + // So inside the node component all coords should be relative to this + // initial transform. + nodeComponent={({ node }) => { + const nodeWithLabelProps = { + node: node, + }; + return ; + }} + /> + + ); +}; + +/** + * Stories + */ + +// A simple network with node labels +const simpleData = genNetwork(20, true, DEFAULT_PLOT_SIZE, DEFAULT_PLOT_SIZE); +export const Simple = Template.bind({}); +Simple.args = { + data: simpleData, +}; + +// A network with lots and lots of points! +const manyPointsData = genNetwork( + 100, + false, + DEFAULT_PLOT_SIZE, + DEFAULT_PLOT_SIZE +); +export const ManyPoints = Template.bind({}); +ManyPoints.args = { + data: manyPointsData, +}; + +// Gerenate a network with a given number of nodes and random edges +function genNetwork( + nNodes: number, + addNodeLabel: boolean, + height: number, + width: number +) { + // Create nodes with random positioning, an id, and optionally a label + const nodes: NodeData[] = [...Array(nNodes).keys()].map((i) => { + return { + x: Math.floor(Math.random() * width), + y: Math.floor(Math.random() * height), + id: String(i), + label: addNodeLabel ? 'Node ' + String(i) : undefined, + }; + }); + + // Create {nNodes} links. Just basic links no weighting or colors for now. + const links: LinkData[] = [...Array(nNodes).keys()].map(() => { + return { + source: nodes[Math.floor(Math.random() * nNodes)], + target: nodes[Math.floor(Math.random() * nNodes)], + }; + }); + + return { nodes, links } as NetworkData; +} diff --git a/packages/libs/components/src/stories/plots/NodeWithLabel.stories.tsx b/packages/libs/components/src/stories/plots/NodeWithLabel.stories.tsx new file mode 100755 index 0000000000..f69368b550 --- /dev/null +++ b/packages/libs/components/src/stories/plots/NodeWithLabel.stories.tsx @@ -0,0 +1,86 @@ +import { Story, Meta } from '@storybook/react/types-6-0'; +import { NodeData } from '../../types/plots/network'; +import { NodeWithLabel } from '../../plots/Network'; +import { Group } from '@visx/group'; + +export default { + title: 'Plots/Network', + component: NodeWithLabel, +} as Meta; + +interface TemplateProps { + data: NodeData; + onClick: () => void; + labelPosition?: 'right' | 'left'; + fontWeight?: number; + labelColor?: string; +} + +// Simply draw a node! +const Template: Story = (args) => { + const { data, labelPosition, fontWeight, labelColor, onClick } = args; + + const nodeWithLabelProps = { + node: data, + onClick: onClick, + labelPosition: labelPosition, + fontWeight: fontWeight, + labelColor: labelColor, + }; + + return ( + + + + + + ); +}; + +/** + * Stories + */ + +// Basic node with a label +const myNode = { + x: 100, + y: 100, + id: 'id', + label: 'label', +}; + +export const NodeWithALabel = Template.bind({}); +NodeWithALabel.args = { + data: myNode, + labelPosition: 'left', +}; + +const myFancyNode = { + x: 100, + y: 100, + id: 'id', + label: 'a fancy long label', + r: 9, + color: '#118899', + stroke: '#000', + strokeWidth: 3, +}; + +export const FancyNodeWithLabel = Template.bind({}); +FancyNodeWithLabel.args = { + data: myFancyNode, + labelPosition: 'right', + labelColor: '#008822', + fontWeight: 600, +}; + +export const ClickNodeOrLabel = Template.bind({}); +ClickNodeOrLabel.args = { + data: myNode, + labelPosition: 'right', + labelColor: '#008822', + fontWeight: 600, + onClick: () => { + console.log('clicked!'); + }, +}; diff --git a/packages/libs/components/src/stories/plots/VolcanoPlot.stories.tsx b/packages/libs/components/src/stories/plots/VolcanoPlot.stories.tsx index e5c36623fa..5eca64288d 100755 --- a/packages/libs/components/src/stories/plots/VolcanoPlot.stories.tsx +++ b/packages/libs/components/src/stories/plots/VolcanoPlot.stories.tsx @@ -112,6 +112,7 @@ const Template: Story = (args) => { }) .map((d) => ({ ...d, + pointID: d.pointID ? [d.pointID] : undefined, significanceColor: assignSignificanceColor( Number(d.log2foldChange), Number(d.pValue), diff --git a/packages/libs/components/src/stories/plots/VolcanoPlotRef.stories.tsx b/packages/libs/components/src/stories/plots/VolcanoPlotRef.stories.tsx index aacca4e493..65bd5e9de8 100644 --- a/packages/libs/components/src/stories/plots/VolcanoPlotRef.stories.tsx +++ b/packages/libs/components/src/stories/plots/VolcanoPlotRef.stories.tsx @@ -76,6 +76,7 @@ const Template: Story = (args) => { }) .map((d) => ({ ...d, + pointID: d.pointID ? [d.pointID] : undefined, significanceColor: assignSignificanceColor( Number(d.log2foldChange), Number(d.pValue), diff --git a/packages/libs/components/src/types/plots/network.ts b/packages/libs/components/src/types/plots/network.ts new file mode 100755 index 0000000000..fadf279582 --- /dev/null +++ b/packages/libs/components/src/types/plots/network.ts @@ -0,0 +1,49 @@ +// Types required for creating networks +export type NodeData = { + /** For now x and y are required. Eventually the network should have a default layout so that + * these become unnecessary in certain situations. + */ + /** The x coordinate of the node */ + x: number; + /** The y coordinate of the node */ + y: number; + /** Node ID. Must be unique in the network! */ + id: string; + /** Node color */ + color?: string; + /** Node radius */ + r?: number; + /** User-friendly node label */ + label?: string; + /** Color for the stroke of the node */ + stroke?: string; + /** Width of node stroke */ + strokeWidth?: number; +}; + +export type LinkData = { + /** The beginning node of the link */ + source: NodeData; + /** The ending node of the link */ + target: NodeData; + /** Link stroke width */ + strokeWidth?: number; + /** Link color */ + color?: string; + /** Link opacity. Must be between 0 and 1 */ + opacity?: number; +}; + +/** NetworkData is the same format accepted by visx's Graph component. */ +export type NetworkData = { + nodes: NodeData[]; + links: LinkData[]; +}; + +/** Bipartite network data is a regular network with addiitonal declarations of + * nodes in each of the two columns. IDs in columnXNodeIDs must match node ids exactly. + */ +export type BipartiteNetworkData = { + column1NodeIDs: string[]; + column2NodeIDs: string[]; +} & NetworkData; diff --git a/packages/libs/components/src/types/plots/volcanoplot.ts b/packages/libs/components/src/types/plots/volcanoplot.ts index 4e40169265..2fa2c8dbf4 100755 --- a/packages/libs/components/src/types/plots/volcanoplot.ts +++ b/packages/libs/components/src/types/plots/volcanoplot.ts @@ -8,7 +8,7 @@ export type VolcanoPlotDataPoint = { // Used for thresholding and tooltip adjustedPValue?: string; // Used for tooltip - pointID?: string; + pointIDs?: string[]; // Used to determine color of data point in the plot significanceColor?: string; }; diff --git a/packages/libs/eda/src/lib/core/components/visualizations/implementations/ScatterplotVisualization.tsx b/packages/libs/eda/src/lib/core/components/visualizations/implementations/ScatterplotVisualization.tsx index d2d2d0e102..f45617e6f9 100755 --- a/packages/libs/eda/src/lib/core/components/visualizations/implementations/ScatterplotVisualization.tsx +++ b/packages/libs/eda/src/lib/core/components/visualizations/implementations/ScatterplotVisualization.tsx @@ -136,7 +136,7 @@ import { ResetButtonCoreUI } from '../../ResetButton'; // add Slider and SliderWidgetProps import SliderWidget, { - SliderWidgetProps, + plotsSliderOpacityGradientColorSpec, } from '@veupathdb/components/lib/components/widgets/Slider'; import { FloatingScatterplotExtraProps } from '../../../../map/analysis/hooks/plugins/scatterplot'; @@ -164,6 +164,14 @@ const modalPlotContainerStyles = { margin: 'auto', }; +// slider settings +const markerBodyOpacityContainerStyles = { + height: '4em', + width: '20em', + marginLeft: '1em', + marginBottom: '0.5em', +}; + // define ScatterPlotDataWithCoverage and export export interface ScatterPlotDataWithCoverage extends CoverageStatistics { dataSetProcess: ScatterPlotData | FacetedData; @@ -1257,24 +1265,6 @@ function ScatterplotViz(props: VisualizationProps) { setTruncatedDependentAxisWarning, ]); - // slider settings - const markerBodyOpacityContainerStyles = { - height: '4em', - width: '20em', - marginLeft: '1em', - marginBottom: '0.5em', - }; - - // implement gradient color for slider opacity - const colorSpecProps: SliderWidgetProps['colorSpec'] = { - type: 'gradient', - tooltip: '#aaa', - knobColor: '#aaa', - // normal slider color: e.g., from 0 to 1 - trackGradientStart: '#fff', - trackGradientEnd: '#000', - }; - const scatterplotProps: ScatterPlotProps = { interactive: !isFaceted(data.value?.dataSetProcess) ? true : false, showSpinner: filteredCounts.pending || data.pending, @@ -1583,7 +1573,7 @@ function ScatterplotViz(props: VisualizationProps) { containerStyles={markerBodyOpacityContainerStyles} showLimits={true} label={'Marker opacity'} - colorSpec={colorSpecProps} + colorSpec={plotsSliderOpacityGradientColorSpec} /> {/* axis range control UIs */} diff --git a/packages/libs/eda/src/lib/core/components/visualizations/implementations/VolcanoPlotVisualization.tsx b/packages/libs/eda/src/lib/core/components/visualizations/implementations/VolcanoPlotVisualization.tsx index 8200b9a446..db2e229fd6 100755 --- a/packages/libs/eda/src/lib/core/components/visualizations/implementations/VolcanoPlotVisualization.tsx +++ b/packages/libs/eda/src/lib/core/components/visualizations/implementations/VolcanoPlotVisualization.tsx @@ -6,7 +6,7 @@ import VolcanoPlot, { } from '@veupathdb/components/lib/plots/VolcanoPlot'; import * as t from 'io-ts'; -import { useCallback, useState, useMemo } from 'react'; +import { useCallback, useMemo } from 'react'; import { usePromise } from '../../../hooks/promise'; import { useUpdateThumbnailEffect } from '../../../hooks/thumbnails'; @@ -33,18 +33,30 @@ import DataClient, { VolcanoPlotRequestParams, VolcanoPlotResponse, } from '../../../api/DataClient'; +import { + VolcanoPlotData, + VolcanoPlotDataPoint, +} from '@veupathdb/components/lib/types/plots/volcanoplot'; import VolcanoSVG from './selectorIcons/VolcanoSVG'; import { NumberOrDate } from '@veupathdb/components/lib/types/general'; import { DifferentialAbundanceConfig } from '../../computations/plugins/differentialabundance'; import { yellow } from '@material-ui/core/colors'; import PlotLegend from '@veupathdb/components/lib/components/plotControls/PlotLegend'; import { significanceColors } from '@veupathdb/components/lib/types/plots'; -import { NumberRange } from '../../../types/general'; +import { NumberOrDateRange, NumberRange } from '../../../types/general'; import { max, min } from 'lodash'; + +// plot controls +import SliderWidget, { + plotsSliderOpacityGradientColorSpec, +} from '@veupathdb/components/lib/components/widgets/Slider'; +import { ResetButtonCoreUI } from '../../ResetButton'; +import AxisRangeControl from '@veupathdb/components/lib/components/plotControls/AxisRangeControl'; // end imports const DEFAULT_SIG_THRESHOLD = 0.05; const DEFAULT_FC_THRESHOLD = 2; +const DEFAULT_MARKER_OPACITY = 0.7; /** * The padding ensures we don't clip off part of the glyphs that represent the most extreme points. * We could have also used d3.scale.nice but then we dont have precise control of where the extremes @@ -74,16 +86,20 @@ function createDefaultConfig(): VolcanoPlotConfig { return { log2FoldChangeThreshold: DEFAULT_FC_THRESHOLD, significanceThreshold: DEFAULT_SIG_THRESHOLD, - markerBodyOpacity: 0.5, + markerBodyOpacity: DEFAULT_MARKER_OPACITY, + independentAxisRange: undefined, + dependentAxisRange: undefined, }; } export type VolcanoPlotConfig = t.TypeOf; - +// eslint-disable-next-line @typescript-eslint/no-redeclare export const VolcanoPlotConfig = t.partial({ log2FoldChangeThreshold: t.number, significanceThreshold: t.number, markerBodyOpacity: t.number, + independentAxisRange: NumberRange, + dependentAxisRange: NumberRange, }); interface Options @@ -93,7 +109,7 @@ interface Options // Volcano Plot Visualization // The volcano plot visualization takes no input variables. The received data populates all parts of the plot. // The user can control the threshold lines, which affect the marker colors. Additional controls -// will include axis ranges. +// include axis ranges and marker opacity slider. function VolcanoPlotViz(props: VisualizationProps) { const { options, @@ -185,13 +201,10 @@ function VolcanoPlotViz(props: VisualizationProps) { // Determine mins, maxes of axes in the plot. These are different than the data mins/maxes because // of the log transform and the little bit of padding, or because axis ranges are supplied. - // NOTE: this state may be unnecessary depending on how we implement user-controlled axis ranges - const [xAxisRange, setXAxisRange] = - useState(undefined); const independentAxisRange = useMemo(() => { if (!data.value) return undefined; - if (xAxisRange) { - return xAxisRange; + if (vizConfig.independentAxisRange) { + return vizConfig.independentAxisRange; } else { const { x: { min: dataXMin, max: dataXMax }, @@ -199,19 +212,16 @@ function VolcanoPlotViz(props: VisualizationProps) { // We can use the dataMin and dataMax here because we don't have a further transform // Add a little padding to prevent clipping the glyph representing the extreme points return { - min: dataXMin - (dataXMax - dataXMin) * AXIS_PADDING_FACTOR, - max: dataXMax + (dataXMax - dataXMin) * AXIS_PADDING_FACTOR, + min: Math.floor(dataXMin - (dataXMax - dataXMin) * AXIS_PADDING_FACTOR), + max: Math.ceil(dataXMax + (dataXMax - dataXMin) * AXIS_PADDING_FACTOR), }; } - }, [data.value, xAxisRange, rawDataMinMaxValues]); + }, [data.value, vizConfig.independentAxisRange, rawDataMinMaxValues]); - // NOTE: this state may be unnecessary depending on how we implement user-controlled axis ranges - const [yAxisRange, setYAxisRange] = - useState(undefined); const dependentAxisRange = useMemo(() => { if (!data.value) return undefined; - if (yAxisRange) { - return yAxisRange; + if (vizConfig.dependentAxisRange) { + return vizConfig.dependentAxisRange; } else { const { y: { min: dataYMin, max: dataYMax }, @@ -221,11 +231,11 @@ function VolcanoPlotViz(props: VisualizationProps) { const yAxisMax = -Math.log10(dataYMin); // Add a little padding to prevent clipping the glyph representing the extreme points return { - min: yAxisMin - (yAxisMax - yAxisMin) * AXIS_PADDING_FACTOR, - max: yAxisMax + (yAxisMax - yAxisMin) * AXIS_PADDING_FACTOR, + min: Math.floor(yAxisMin - (yAxisMax - yAxisMin) * AXIS_PADDING_FACTOR), + max: Math.ceil(yAxisMax + (yAxisMax - yAxisMin) * AXIS_PADDING_FACTOR), }; } - }, [data.value, yAxisRange, rawDataMinMaxValues]); + }, [data.value, vizConfig.dependentAxisRange, rawDataMinMaxValues]); const significanceThreshold = vizConfig.significanceThreshold ?? DEFAULT_SIG_THRESHOLD; @@ -233,14 +243,12 @@ function VolcanoPlotViz(props: VisualizationProps) { vizConfig.log2FoldChangeThreshold ?? DEFAULT_FC_THRESHOLD; /** - * Let's filter out data that falls outside of the plot axis ranges and then - * assign a significance color to the visible data * This version of the data will get passed to the VolcanoPlot component */ const finalData = useMemo(() => { if (data.value && independentAxisRange && dependentAxisRange) { - // Only return data if the points fall within the specified range! Otherwise they'll show up on the plot. - return data.value + const cleanedData = data.value + // Only return data if the points fall within the specified range! Otherwise they'll show up on the plot. .filter((d) => { const log2foldChange = Number(d?.log2foldChange); const transformedPValue = -Math.log10(Number(d?.pValue)); @@ -251,16 +259,64 @@ function VolcanoPlotViz(props: VisualizationProps) { transformedPValue >= dependentAxisRange.min ); }) - .map((d) => ({ - ...d, - significanceColor: assignSignificanceColor( - Number(d.log2foldChange), - Number(d.pValue), - significanceThreshold, - log2FoldChangeThreshold, - significanceColors - ), - })); + /** + * Okay, this map function is doing a number of things. + * 1. We're going to remove the pointID property and replace it with a pointIDs property that is an array of strings. + * Some data share coordinates but correspond to a different pointID. By converting pointID to pointIDs, we can + * later aggregate data that share coordinates and then render one tooltip that lists all pointIDs corresponding + * to the point on the plot + * 2. We also add a significanceColor property that is assigned a value that gets used in VolcanoPlot when rendering + * the data point and the data point's tooltip. The property is also used in the countsData logic. + */ + .map((d) => { + const { pointID, ...remainingProperties } = d; + return { + ...remainingProperties, + pointIDs: pointID ? [pointID] : undefined, + significanceColor: assignSignificanceColor( + Number(d.log2foldChange), + Number(d.pValue), + significanceThreshold, + log2FoldChangeThreshold, + significanceColors + ), + }; + }) + // Sort data in ascending order for tooltips to work most effectively + .sort((a, b) => Number(a.log2foldChange) - Number(b.log2foldChange)); + + // Here we're going to loop through the cleanedData to aggregate any data with shared coordinates. + // For each entry, we'll check if our aggregatedData includes an item with the same coordinates: + // Yes? => update the matched aggregatedData element's pointID array to include the pointID of the matching entry + // No? => just push the entry onto the aggregatedData array since no match was found + const aggregatedData: VolcanoPlotData = []; + for (const entry of cleanedData) { + const foundIndex = aggregatedData.findIndex( + (d: VolcanoPlotDataPoint) => + d.log2foldChange === entry.log2foldChange && + d.pValue === entry.pValue + ); + if (foundIndex === -1) { + aggregatedData.push(entry); + } else { + const { pointIDs } = aggregatedData[foundIndex]; + if (pointIDs) { + aggregatedData[foundIndex] = { + ...aggregatedData[foundIndex], + pointIDs: [ + ...pointIDs, + ...(entry.pointIDs ? entry.pointIDs : []), + ], + }; + } else { + aggregatedData[foundIndex] = { + ...aggregatedData[foundIndex], + pointIDs: entry.pointIDs, + }; + } + } + } + return aggregatedData; } }, [ data.value, @@ -270,7 +326,7 @@ function VolcanoPlotViz(props: VisualizationProps) { log2FoldChangeThreshold, ]); - // For the legend, we need the counts of each assigned significance value + // For the legend, we need the counts of the data const countsData = useMemo(() => { if (!finalData) return; const counts = { @@ -279,7 +335,14 @@ function VolcanoPlotViz(props: VisualizationProps) { [significanceColors['low']]: 0, }; for (const entry of finalData) { - counts[entry.significanceColor]++; + if (entry.significanceColor) { + // Recall that finalData combines data with shared coords into one point in order to display a + // single tooltip that lists all the pointIDs for that shared point. This means we need to use + // the length of the pointID array to accurately reflect the counts of unique data (not unique coords). + const addend = entry.pointIDs?.length ?? 1; + counts[entry.significanceColor] = + addend + counts[entry.significanceColor]; + } } return counts; }, [finalData]); @@ -288,10 +351,10 @@ function VolcanoPlotViz(props: VisualizationProps) { updateThumbnail, plotContainerStyles, [ - data, + finalData, // vizConfig.checkedLegendItems, TODO - // vizConfig.independentAxisRange, TODO - // vizConfig.dependentAxisRange, TODO + vizConfig.independentAxisRange, + vizConfig.dependentAxisRange, vizConfig.markerBodyOpacity, ] ); @@ -321,7 +384,9 @@ function VolcanoPlotViz(props: VisualizationProps) { * Since we are rendering a single point in order to display an empty viz, let's hide the data point * by setting the marker opacity to 0 when data.value doesn't exist */ - markerBodyOpacity: data.value ? vizConfig.markerBodyOpacity ?? 0.5 : 0, + markerBodyOpacity: data.value + ? vizConfig.markerBodyOpacity ?? DEFAULT_MARKER_OPACITY + : 0, containerStyles: plotContainerStyles, /** * Let's not display comparisonLabels before we have data for the viz. This prevents what may be @@ -342,8 +407,130 @@ function VolcanoPlotViz(props: VisualizationProps) { // @ts-ignore const plotNode = ; - // TODO - const controlsNode = <> ; + const controlsNode = ( +
+ + { + updateVizConfig({ markerBodyOpacity: newValue }); + }} + containerStyles={{ width: '20em', marginTop: '0.5em' }} + showLimits={true} + label={'Marker opacity'} + colorSpec={plotsSliderOpacityGradientColorSpec} + /> + +
+
+
+ } + containerStyles={{ + marginRight: 0, + paddingLeft: 0, + }} + /> + + updateVizConfig({ independentAxisRange: undefined }) + } + /> +
+ { + const typeCheckedNewRange = + typeof newRange?.min === 'number' && + typeof newRange?.max === 'number' + ? { + min: newRange.min, + max: newRange.max, + } + : undefined; + updateVizConfig({ + independentAxisRange: typeCheckedNewRange, + }); + }} + step={0.01} + /> +
+ {/** vertical line to separate x from y range controls */} +
+
+
+ } + containerStyles={{ + marginRight: 0, + paddingLeft: 0, + }} + /> + updateVizConfig({ dependentAxisRange: undefined })} + /> +
+ { + const typeCheckedNewRange = + typeof newRange?.min === 'number' && + typeof newRange?.max === 'number' + ? { + min: newRange.min, + max: newRange.max, + } + : undefined; + updateVizConfig({ + dependentAxisRange: typeCheckedNewRange, + }); + }} + step={0.01} + /> +
+
+
+ ); const legendNode = finalData && countsData && ( + + + + + + + + + + + + + + + + + ); +} diff --git a/packages/libs/eda/src/lib/map/analysis/hooks/standaloneVizPlugins.ts b/packages/libs/eda/src/lib/map/analysis/hooks/standaloneVizPlugins.ts index 78dcad9c78..2abe58a43d 100644 --- a/packages/libs/eda/src/lib/map/analysis/hooks/standaloneVizPlugins.ts +++ b/packages/libs/eda/src/lib/map/analysis/hooks/standaloneVizPlugins.ts @@ -27,8 +27,8 @@ import { barplotRequest } from './plugins/barplot'; import { lineplotRequest } from './plugins/lineplot'; import { histogramRequest } from './plugins/histogram'; import { scatterplotRequest } from './plugins/scatterplot'; -//TO DO import timeline SVGIcon -import LineSVG from '../../../core/components/visualizations/implementations/selectorIcons/LineSVG'; + +import TimeSeriesSVG from '../../../core/components/visualizations/implementations/selectorIcons/TimeSeriesSVG'; import _ from 'lodash'; interface Props { @@ -123,7 +123,7 @@ export function useStandaloneVizPlugins({ .withOptions({ showMarginalHistogram: true, }) - .withSelectorIcon(LineSVG) + .withSelectorIcon(TimeSeriesSVG) ), lineplotRequest ), diff --git a/packages/libs/eda/src/lib/map/analysis/utils/defaultOverlayConfig.ts b/packages/libs/eda/src/lib/map/analysis/utils/defaultOverlayConfig.ts index 4a52be0971..9c44d8a44c 100644 --- a/packages/libs/eda/src/lib/map/analysis/utils/defaultOverlayConfig.ts +++ b/packages/libs/eda/src/lib/map/analysis/utils/defaultOverlayConfig.ts @@ -65,7 +65,8 @@ export async function getDefaultOverlayConfig( overlayVariable: overlayVariableDescriptor, aggregationConfig: { overlayType: 'categorical', - numeratorValues: numeratorValues ?? [], + numeratorValues: + numeratorValues ?? overlayVariable.vocabulary ?? [], denominatorValues: denominatorValues ?? overlayVariable.vocabulary ?? [], }, diff --git a/packages/libs/user-datasets/src/lib/Components/Detail/UserDatasetDetail.jsx b/packages/libs/user-datasets/src/lib/Components/Detail/UserDatasetDetail.jsx index 2dafec8c72..b9fef4bc0d 100644 --- a/packages/libs/user-datasets/src/lib/Components/Detail/UserDatasetDetail.jsx +++ b/packages/libs/user-datasets/src/lib/Components/Detail/UserDatasetDetail.jsx @@ -519,6 +519,11 @@ class UserDatasetDetail extends React.Component { -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= */ + // This is needed to resolve downstream typescript errors. + // TypeScript infers that this method returns JSX.Element[]. + // Some classes extending this will return (JSX.Element | null)[]. + // The ReactNode type is better suited, here, since it allows for null values. + /** @return {import("react").ReactNode[]} */ getPageSections() { return [ this.renderHeaderSection, diff --git a/packages/libs/wdk-client/src/Components/DataTable/DataTable.css b/packages/libs/wdk-client/src/Components/DataTable/DataTable.css index d85a3d3d0f..d3e902b5cc 100644 --- a/packages/libs/wdk-client/src/Components/DataTable/DataTable.css +++ b/packages/libs/wdk-client/src/Components/DataTable/DataTable.css @@ -136,3 +136,8 @@ top: -3em; margin-left: 310px; } + +.wdk-DataTableCountsContainer { + margin: auto 0 auto 1em; + font-size: 0.9em; +} diff --git a/packages/libs/wdk-client/src/Components/DataTable/DataTable.tsx b/packages/libs/wdk-client/src/Components/DataTable/DataTable.tsx index d37f251754..ad7abecb55 100644 --- a/packages/libs/wdk-client/src/Components/DataTable/DataTable.tsx +++ b/packages/libs/wdk-client/src/Components/DataTable/DataTable.tsx @@ -124,6 +124,8 @@ interface State { childRows: [HTMLElement, ChildRowProps][]; selectedColumnFilters: string[]; showFieldSelector: boolean; + /** Used to display row counts to user */ + numberOfRowsVisible: number | null; } /** @@ -158,6 +160,7 @@ class DataTable extends PureComponent { childRows: [], selectedColumnFilters: [], showFieldSelector: false, + numberOfRowsVisible: null, }; _childRowContainers: Map = new Map(); @@ -430,6 +433,11 @@ class DataTable extends PureComponent { .search(searchTermRegex, true, false, true) .draw(); } + /** set row count after .draw() for correct count */ + this.setState((state) => ({ + ...state, + numberOfRowsVisible: dataTable.page.info().recordsDisplay, + })); } _updateSorting(dataTable: DataTables.Api) { @@ -662,6 +670,21 @@ class DataTable extends PureComponent {
+
+ + + {this.state.numberOfRowsVisible ?? this.props.data.length} + {' '} + rows + {' '} + {(this.state.numberOfRowsVisible === 0 || + this.state.numberOfRowsVisible) && + this.state.numberOfRowsVisible < this.props.data.length && ( + + (filtered from a total of {this.props.data.length}) + + )} +
{this.state.showFieldSelector && ( ) => - (props: Exclude) => + (options: Pick) => + (props: Exclude) => , }); diff --git a/packages/libs/wdk-client/src/StoreModules/QuestionStoreModule.ts b/packages/libs/wdk-client/src/StoreModules/QuestionStoreModule.ts index 418bae05ff..6c026a4ab9 100644 --- a/packages/libs/wdk-client/src/StoreModules/QuestionStoreModule.ts +++ b/packages/libs/wdk-client/src/StoreModules/QuestionStoreModule.ts @@ -1,4 +1,4 @@ -import { keyBy, mapValues, pick, toString } from 'lodash'; +import { isEmpty, keyBy, mapValues, pick, toString } from 'lodash'; import { Seq } from '../Utils/IterableUtils'; import { combineEpics, @@ -1101,7 +1101,7 @@ async function fetchInitialParams( ) { if (step != null) { return initialParamDataFromStep(step); - } else if (initialParamData != null) { + } else if (!isEmpty(initialParamData)) { return extracParamValues(initialParamData, question.paramNames); } else if (prepopulateWithLastParamValues) { return ( diff --git a/packages/libs/wdk-client/src/Utils/ComponentUtils.tsx b/packages/libs/wdk-client/src/Utils/ComponentUtils.tsx index cef9e517fd..8f93c9ac94 100644 --- a/packages/libs/wdk-client/src/Utils/ComponentUtils.tsx +++ b/packages/libs/wdk-client/src/Utils/ComponentUtils.tsx @@ -261,7 +261,12 @@ export function safeHtml

( Component: any = 'span' ): JSX.Element { str = str ?? ''; - if (str.indexOf('<') === -1) { + /** + * To improve performance, let's skip the element creation and innerHTML magic + * when we detect neither HTML nor an HTML entity in the string + */ + const isHtmlEntityFound = /(\&(.+?);)/.test(str); + if (str.indexOf('<') === -1 && !isHtmlEntityFound) { return {str}; } // Use innerHTML to auto close tags diff --git a/packages/libs/wdk-client/src/Views/ResultTableSummaryView/ResultTable.tsx b/packages/libs/wdk-client/src/Views/ResultTableSummaryView/ResultTable.tsx index 06a5dce46b..a7227eef52 100644 --- a/packages/libs/wdk-client/src/Views/ResultTableSummaryView/ResultTable.tsx +++ b/packages/libs/wdk-client/src/Views/ResultTableSummaryView/ResultTable.tsx @@ -36,6 +36,7 @@ export interface Props { resultType: ResultType; viewId: string; actions?: Action[]; + showCount?: boolean; selectedIds?: string[]; showIdAttributeColumn: boolean; activeAttributeAnalysisName: string | undefined; @@ -72,6 +73,7 @@ function ResultTable(props: Props) { showHideAddColumnsDialog, requestAddStepToBasket, actions, + showCount, selectedIds, userIsGuest, showLoginWarning, @@ -106,6 +108,7 @@ function ResultTable(props: Props) { recordInstance.attributes[recordClass.recordIdAttributeName] as string ); }, + showCount, }; const tableState = MesaState.create({ options, diff --git a/packages/libs/wdk-client/src/Views/ResultTableSummaryView/ResultTableSummaryView.tsx b/packages/libs/wdk-client/src/Views/ResultTableSummaryView/ResultTableSummaryView.tsx index 280cf7db13..0d8e46adf2 100644 --- a/packages/libs/wdk-client/src/Views/ResultTableSummaryView/ResultTableSummaryView.tsx +++ b/packages/libs/wdk-client/src/Views/ResultTableSummaryView/ResultTableSummaryView.tsx @@ -48,6 +48,7 @@ interface Props { columnsDialogSearchString?: string; columnsDialogExpandedNodes?: string[]; columnsTree?: CategoryTreeNode; + showCount?: boolean; requestSortingUpdate: RequestSortingUpdate; requestColumnsChoiceUpdate: RequestColumnsChoiceUpdate; requestUpdateBasket: RequestUpdateBasket; @@ -80,6 +81,7 @@ export default function ResultTableSummaryView({ question, userIsGuest, basketStatusArray, + showCount, requestColumnsChoiceUpdate, requestSortingUpdate, requestUpdateBasket, @@ -130,6 +132,7 @@ export default function ResultTableSummaryView({ answer={answer} viewId={viewId} actions={actions} + showCount={showCount} selectedIds={selectedIds} showIdAttributeColumn={showIdAttributeColumn} activeAttributeAnalysisName={activeAttributeAnalysisName} diff --git a/packages/sites/clinepi-site/package.json b/packages/sites/clinepi-site/package.json index b25056f5a0..d7ddfb01a6 100644 --- a/packages/sites/clinepi-site/package.json +++ b/packages/sites/clinepi-site/package.json @@ -8,6 +8,7 @@ "clean": "rm -rf dist && mkdir dist", "copy:webapp": "cp -r webapp/* dist", "copy:images": "cp -r ../../../node_modules/@veupathdb/web-common/images dist", + "compile:check": "tsc --noEmit", "bundle:dev": "npm-run-all clean copy:webapp copy:images && BROWSERSLIST_ENV=modern webpack --mode=development", "bundle:npm": "npm-run-all clean copy:webapp copy:images && BROWSERSLIST_ENV=modern webpack --mode=production && BROWSERSLIST_ENV=legacy webpack --mode=production" }, diff --git a/packages/sites/genomics-site/package.json b/packages/sites/genomics-site/package.json index a811a8e1ae..376369bc26 100644 --- a/packages/sites/genomics-site/package.json +++ b/packages/sites/genomics-site/package.json @@ -8,6 +8,7 @@ "clean": "rm -rf dist && mkdir dist", "copy:webapp": "cp -r webapp/* dist", "copy:images": "cp -r ../../../node_modules/@veupathdb/web-common/images dist", + "compile:check": "tsc --noEmit", "bundle:dev": "npm-run-all clean copy:webapp copy:images && BROWSERSLIST_ENV=modern webpack --mode=development", "bundle:npm": "npm-run-all clean copy:webapp copy:images && BROWSERSLIST_ENV=modern webpack --mode=production && BROWSERSLIST_ENV=legacy webpack --mode=production" }, diff --git a/packages/sites/genomics-site/webapp/wdkCustomization/js/client/components/Downloads/Downloads.scss b/packages/sites/genomics-site/webapp/wdkCustomization/js/client/components/Downloads/Downloads.scss index ff4d2384ea..bdb2477c50 100644 --- a/packages/sites/genomics-site/webapp/wdkCustomization/js/client/components/Downloads/Downloads.scss +++ b/packages/sites/genomics-site/webapp/wdkCustomization/js/client/components/Downloads/Downloads.scss @@ -10,13 +10,26 @@ } &-Filter-Container { - outline: 2px solid #acacac; - margin: 2em auto 3em; - width: 60vw; - border-radius: 8px; - // height: 25em; + .wdk-QuestionFormParameterList { + width: 60vw; + margin: auto; + } + + .wdk-QuestionFormParameterHeading h2 { + font-size: 1.3em; + } + + .OrganismParam { + max-width: 47em; + } .filter-param { + outline: 2px solid #acacac; + // margin: 2em auto 3em; + // width: 60vw; + border-radius: 8px; + // height: 25em; + h3 { padding: 0.25em 0; } @@ -64,6 +77,10 @@ } .TableToolbar { + order: -3; + } + + .TableToolbar-Children { display: none; } diff --git a/packages/sites/genomics-site/webapp/wdkCustomization/js/client/components/Downloads/DownloadsFilter.tsx b/packages/sites/genomics-site/webapp/wdkCustomization/js/client/components/Downloads/DownloadsFilter.tsx index 9602b6c152..a51d55a0bc 100644 --- a/packages/sites/genomics-site/webapp/wdkCustomization/js/client/components/Downloads/DownloadsFilter.tsx +++ b/packages/sites/genomics-site/webapp/wdkCustomization/js/client/components/Downloads/DownloadsFilter.tsx @@ -2,9 +2,13 @@ import { SubmissionMetadata } from '@veupathdb/wdk-client/lib/Actions/QuestionAc import { QuestionController } from '@veupathdb/wdk-client/lib/Controllers'; import { RootState } from '@veupathdb/wdk-client/lib/Core/State/Types'; import { SearchConfig } from '@veupathdb/wdk-client/lib/Utils/WdkModel'; -import { Props as FormProps } from '@veupathdb/wdk-client/lib/Views/Question/DefaultQuestionForm'; -import React, { useEffect } from 'react'; +import { + Props as FormProps, + renderDefaultParamGroup, +} from '@veupathdb/wdk-client/lib/Views/Question/DefaultQuestionForm'; +import React, { useEffect, useMemo } from 'react'; import { useSelector } from 'react-redux'; +import { mapValues } from 'lodash'; interface Props { recordName: string; @@ -38,11 +42,43 @@ export function DownloadsFilter(props: Props) { submissionMetadata={submissionMetadata} FormComponent={FormComponent} initialParamData={initialParamData} + prepopulateWithLastParamValues /> ); } function FormComponent(props: FormProps) { - const { parameterElements } = props; - return <>{Object.values(parameterElements)}; + const { state } = props; + // Need to add `isSearchPage` prop so that organism prefs are used + const parameterElements = useMemo( + () => + mapValues(props.parameterElements, (parameterElement) => { + return React.isValidElement(parameterElement) + ? React.cloneElement( + parameterElement, + { + pluginProps: { + ...parameterElement.props.pluginProps, + isSearchPage: true, + }, + } as any, + parameterElement.props.chilren + ) + : parameterElement; + }), + [props.parameterElements] + ); + + const updatedProps = useMemo( + () => ({ ...props, parameterElements }), + [props, parameterElements] + ); + + return ( + <> + {state.question.groups + .filter((group) => group.displayType !== 'hidden') + .map((group) => renderDefaultParamGroup(group, updatedProps))} + + ); } diff --git a/packages/sites/genomics-site/webapp/wdkCustomization/js/client/components/Downloads/DownloadsTable.tsx b/packages/sites/genomics-site/webapp/wdkCustomization/js/client/components/Downloads/DownloadsTable.tsx index d561252369..3057342bca 100644 --- a/packages/sites/genomics-site/webapp/wdkCustomization/js/client/components/Downloads/DownloadsTable.tsx +++ b/packages/sites/genomics-site/webapp/wdkCustomization/js/client/components/Downloads/DownloadsTable.tsx @@ -18,6 +18,8 @@ interface Props { bulkSearchName: string; } +const R = ResultTableSummaryViewController.withOptions({ showCount: true }); + export function DownloadsTable(props: Props) { const { searchConfig, tableSearchName, bulkSearchName } = props; const { wdkService } = useNonNullableContext(WdkDependenciesContext); @@ -78,11 +80,13 @@ export function DownloadsTable(props: Props) { }, [bulkSearchName, wdkService]); return ( - + <> + + ); } diff --git a/packages/sites/mbio-site/package.json b/packages/sites/mbio-site/package.json index 17d044c693..47a898421c 100644 --- a/packages/sites/mbio-site/package.json +++ b/packages/sites/mbio-site/package.json @@ -8,6 +8,7 @@ "clean": "rm -rf dist && mkdir dist", "copy:webapp": "cp -r webapp/* dist", "copy:images": "cp -r ../../../node_modules/@veupathdb/web-common/images dist", + "compile:check": "tsc --noEmit", "bundle:dev": "npm-run-all clean copy:webapp copy:images && BROWSERSLIST_ENV=modern webpack --mode=development", "bundle:npm": "npm-run-all clean copy:webapp copy:images && BROWSERSLIST_ENV=modern webpack --mode=production && BROWSERSLIST_ENV=legacy webpack --mode=production" }, diff --git a/packages/sites/ortho-site/package.json b/packages/sites/ortho-site/package.json index f1c3b6c283..ad20423e94 100644 --- a/packages/sites/ortho-site/package.json +++ b/packages/sites/ortho-site/package.json @@ -9,6 +9,7 @@ "clean": "rm -rf dist && mkdir dist", "copy:webapp": "cp -r webapp/* dist", "copy:images": "cp -r ../../../node_modules/@veupathdb/web-common/images dist", + "compile:check": "tsc --noEmit", "bundle:dev": "npm-run-all clean copy:webapp copy:images && BROWSERSLIST_ENV=modern webpack --mode=development", "bundle:npm": "npm-run-all clean copy:webapp copy:images && BROWSERSLIST_ENV=modern webpack --mode=production && BROWSERSLIST_ENV=legacy webpack --mode=production" }, diff --git a/yarn.lock b/yarn.lock index e2ce4e4e63..bff2e0e8ea 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6272,6 +6272,13 @@ __metadata: languageName: node linkType: hard +"@types/d3-array@npm:3.0.3": + version: 3.0.3 + resolution: "@types/d3-array@npm:3.0.3" + checksum: 972c8ff009c97c9093883d7597aac7185d4e8c2832d7f25420d8312aa1ebd60182f85fe768615cbc9d73d8819add52eea7ce640e57f5b1668ef389f1dc9569bc + languageName: node + linkType: hard + "@types/d3-axis@npm:*": version: 3.0.2 resolution: "@types/d3-axis@npm:3.0.2" @@ -6297,7 +6304,7 @@ __metadata: languageName: node linkType: hard -"@types/d3-color@npm:*": +"@types/d3-color@npm:*, @types/d3-color@npm:3.1.0": version: 3.1.0 resolution: "@types/d3-color@npm:3.1.0" checksum: b1856f17d6366559a68eaba0164f30727e9dc5eaf1b3a6c8844354da228860240423d19fa4de65bff9da26b4ead8843eab14b1566962665412e8fd82c3810554 @@ -6321,7 +6328,7 @@ __metadata: languageName: node linkType: hard -"@types/d3-delaunay@npm:*": +"@types/d3-delaunay@npm:*, @types/d3-delaunay@npm:6.0.1": version: 6.0.1 resolution: "@types/d3-delaunay@npm:6.0.1" checksum: c46fd6f399ed604e9f40841851c432c936c4408af9e61b235a7f6030e93faa9b5c4f6c33a62be221e1d33f48a9162e9c4bbfa173746c0e4787483310da9a03b2 @@ -6374,7 +6381,7 @@ __metadata: languageName: node linkType: hard -"@types/d3-format@npm:*": +"@types/d3-format@npm:*, @types/d3-format@npm:3.0.1": version: 3.0.1 resolution: "@types/d3-format@npm:3.0.1" checksum: 6819fae7e7c3fce1e44cd56e9b6d8ea5508f708cb7a42793edf82ff914b120bf6cddc35208b1b33ec82615ab82b843c284709f9cea5c3fe1ea5f012106b3d32c @@ -6413,7 +6420,7 @@ __metadata: languageName: node linkType: hard -"@types/d3-interpolate@npm:*, @types/d3-interpolate@npm:^3.0.1": +"@types/d3-interpolate@npm:*, @types/d3-interpolate@npm:3.0.1, @types/d3-interpolate@npm:^3.0.1": version: 3.0.1 resolution: "@types/d3-interpolate@npm:3.0.1" dependencies: @@ -6489,6 +6496,15 @@ __metadata: languageName: node linkType: hard +"@types/d3-scale@npm:4.0.2": + version: 4.0.2 + resolution: "@types/d3-scale@npm:4.0.2" + dependencies: + "@types/d3-time": "*" + checksum: 6b3c0337f38f82b582d9f3190fde82edfce7ffafb371e5b2464c443137c2660bac644099d259f1f5cc829085eb5688bed0bea2b336a957d0845433bd07bf2ddd + languageName: node + linkType: hard + "@types/d3-scale@npm:^3.3.0": version: 3.3.2 resolution: "@types/d3-scale@npm:3.3.2" @@ -6530,7 +6546,14 @@ __metadata: languageName: node linkType: hard -"@types/d3-time@npm:*": +"@types/d3-time-format@npm:2.1.0": + version: 2.1.0 + resolution: "@types/d3-time-format@npm:2.1.0" + checksum: 8493cc4676d7656becf0c1195abb1213827e4cf42a85849c5e0159fa89b1b80c5ff6ad8af54e54d0e8c76815b866adf68a948da9b966405bd12c2070f57a7efe + languageName: node + linkType: hard + +"@types/d3-time@npm:*, @types/d3-time@npm:3.0.0": version: 3.0.0 resolution: "@types/d3-time@npm:3.0.0" checksum: e76adb056daccf80107f4db190ac6deb77e8774f00362bb6c76f178e67f2f217422fe502b654edbc9ac6451f6619045b9f6f5fe0db1ec5520e2ada377af7c72e @@ -8066,7 +8089,7 @@ __metadata: "@visx/text": ^1.3.0 "@visx/tooltip": ^1.3.0 "@visx/visx": ^1.1.0 - "@visx/xychart": ^3.1.0 + "@visx/xychart": "https://github.com/jernestmyers/visx.git#visx-xychart" babel-loader: ^8.3.0 bootstrap: ^4.5.2 color-math: ^1.1.3 @@ -8955,20 +8978,20 @@ __metadata: languageName: node linkType: hard -"@visx/annotation@npm:3.0.1": - version: 3.0.1 - resolution: "@visx/annotation@npm:3.0.1" +"@visx/annotation@npm:3.3.0": + version: 3.3.0 + resolution: "@visx/annotation@npm:3.3.0" dependencies: "@types/react": "*" - "@visx/drag": 3.0.1 - "@visx/group": 3.0.0 - "@visx/text": 3.0.0 + "@visx/drag": 3.3.0 + "@visx/group": 3.3.0 + "@visx/text": 3.3.0 classnames: ^2.3.1 prop-types: ^15.5.10 react-use-measure: ^2.0.4 peerDependencies: react: ^16.0.0-0 || ^17.0.0-0 || ^18.0.0-0 - checksum: e1a3016b8b00c843b56a41c0b07c5509ea195948408c856f09fd2db3268fbc4641f1e6adf0022e60a042cd25d7f0334e7ac6128a367c92a27e42770573eef5f6 + checksum: 1b073c7e4608c03f3200eb17dee325ad4ced21292171c7b774001e94f61e02237fbbbda96abf24de453230e4cb2209571e8fffe7bd9270cda58511389efc9ada languageName: node linkType: hard @@ -8990,7 +9013,25 @@ __metadata: languageName: node linkType: hard -"@visx/axis@npm:3.1.0, @visx/axis@npm:^3.1.0": +"@visx/axis@npm:3.3.0": + version: 3.3.0 + resolution: "@visx/axis@npm:3.3.0" + dependencies: + "@types/react": "*" + "@visx/group": 3.3.0 + "@visx/point": 3.3.0 + "@visx/scale": 3.3.0 + "@visx/shape": 3.3.0 + "@visx/text": 3.3.0 + classnames: ^2.3.1 + prop-types: ^15.6.0 + peerDependencies: + react: ^16.3.0-0 || ^17.0.0-0 || ^18.0.0-0 + checksum: b82d5017ca01c23b0f5c3dbf23d99ea28dcfd37bac8f15d0397d6ba0ac3d569dd935f7cc90588a6c1c1bee185af444539ecde1d653484c0922131f7ab43c5928 + languageName: node + linkType: hard + +"@visx/axis@npm:^3.1.0": version: 3.1.0 resolution: "@visx/axis@npm:3.1.0" dependencies: @@ -9022,9 +9063,9 @@ __metadata: languageName: node linkType: hard -"@visx/bounds@npm:3.0.0": - version: 3.0.0 - resolution: "@visx/bounds@npm:3.0.0" +"@visx/bounds@npm:3.3.0": + version: 3.3.0 + resolution: "@visx/bounds@npm:3.3.0" dependencies: "@types/react": "*" "@types/react-dom": "*" @@ -9032,7 +9073,7 @@ __metadata: peerDependencies: react: ^16.0.0-0 || ^17.0.0-0 || ^18.0.0-0 react-dom: ^16.0.0-0 || ^17.0.0-0 || ^18.0.0-0 - checksum: 3c50dad1c78b526bebe20a690360ae5ad3dd2a31c86150553071671e49309a9fcf3bca3303eab7f7ac2e28be8d7987ac9ce5daa28b8df32c647b0aa97f118fa9 + checksum: e682b343735f53d7eed04cc9e4bc40308583724faa0577f752449d59daea57030f05cb72a9711d7e7f46db932691fea6bb7a545b204b4df24b80ffff0ed04e78 languageName: node linkType: hard @@ -9084,6 +9125,16 @@ __metadata: languageName: node linkType: hard +"@visx/curve@npm:3.3.0": + version: 3.3.0 + resolution: "@visx/curve@npm:3.3.0" + dependencies: + "@types/d3-shape": ^1.3.1 + d3-shape: ^1.0.6 + checksum: 36db95911c567408de78851eee6d98b60065c9d07b5abc7652cef072eaf5040444d8d84e73255f91bca0cfb50837f3b47db7209183211c20bc4bc1adcf13794d + languageName: node + linkType: hard + "@visx/drag@npm:1.18.1": version: 1.18.1 resolution: "@visx/drag@npm:1.18.1" @@ -9097,17 +9148,17 @@ __metadata: languageName: node linkType: hard -"@visx/drag@npm:3.0.1": - version: 3.0.1 - resolution: "@visx/drag@npm:3.0.1" +"@visx/drag@npm:3.3.0": + version: 3.3.0 + resolution: "@visx/drag@npm:3.3.0" dependencies: "@types/react": "*" - "@visx/event": 3.0.1 - "@visx/point": 3.0.1 + "@visx/event": 3.3.0 + "@visx/point": 3.3.0 prop-types: ^15.5.10 peerDependencies: react: ^16.8.0-0 || ^17.0.0-0 || ^18.0.0-0 - checksum: 256e4a7b649e29b2112f77a57a657cd3aedb76b605ebc8b49e6c3d4b152fea5d81de7820060dc569dda0c81a0e7b6668ce9fe071536198b26cf2ebd655a6e6a9 + checksum: 98a352249e4009e6de83bb229d3093706766d205eaf21b3d330a087d4b310e603b25759836645a605f1b7ba462cc3fd7e2d94c65ea40752d3d4f72fa4da27d9c languageName: node linkType: hard @@ -9121,13 +9172,13 @@ __metadata: languageName: node linkType: hard -"@visx/event@npm:3.0.1": - version: 3.0.1 - resolution: "@visx/event@npm:3.0.1" +"@visx/event@npm:3.3.0": + version: 3.3.0 + resolution: "@visx/event@npm:3.3.0" dependencies: "@types/react": "*" - "@visx/point": 3.0.1 - checksum: 0cb4dd578bbe54bd428a0cfae9e64db141b039c2c6c1412d1cd1d04e1613d193212b031124948ca3b7eed877cdea87d161cc3a008b973c6f217e21a8a2dd27b0 + "@visx/point": 3.3.0 + checksum: 3cdb86848d9ae7cc8b6a14a0cc4efd7fc2186fca582ee374bb1f2dfe104ec5cd04dbdbbfc41ccb8c94e1885e2b6e6060a488f3a0a0554ad9e831ecc1b28339de languageName: node linkType: hard @@ -9164,19 +9215,19 @@ __metadata: languageName: node linkType: hard -"@visx/glyph@npm:3.0.0": - version: 3.0.0 - resolution: "@visx/glyph@npm:3.0.0" +"@visx/glyph@npm:3.3.0": + version: 3.3.0 + resolution: "@visx/glyph@npm:3.3.0" dependencies: "@types/d3-shape": ^1.3.1 "@types/react": "*" - "@visx/group": 3.0.0 + "@visx/group": 3.3.0 classnames: ^2.3.1 d3-shape: ^1.2.0 prop-types: ^15.6.2 peerDependencies: react: ^16.3.0-0 || ^17.0.0-0 || ^18.0.0-0 - checksum: 0a944f2594c519e66e66dbc806e9ff2e53bbc64bfd8c2106420d97f0c47824d22b6e733f7bed3591dd94fb00b32ead68c34fd3d342fc42968182d511ac9996c5 + checksum: d9218f3a9ea410c43c7a6f3831deda456e7c746f7b3ed883ec6cf4e2ebe7a20d7f89b617c4782564d88c5c5e05e33184132735dafc7ae19ab8f974fc9a90dd50 languageName: node linkType: hard @@ -9210,21 +9261,21 @@ __metadata: languageName: node linkType: hard -"@visx/grid@npm:3.0.1": - version: 3.0.1 - resolution: "@visx/grid@npm:3.0.1" +"@visx/grid@npm:3.3.0": + version: 3.3.0 + resolution: "@visx/grid@npm:3.3.0" dependencies: "@types/react": "*" - "@visx/curve": 3.0.0 - "@visx/group": 3.0.0 - "@visx/point": 3.0.1 - "@visx/scale": 3.0.0 - "@visx/shape": 3.0.0 + "@visx/curve": 3.3.0 + "@visx/group": 3.3.0 + "@visx/point": 3.3.0 + "@visx/scale": 3.3.0 + "@visx/shape": 3.3.0 classnames: ^2.3.1 prop-types: ^15.6.2 peerDependencies: react: ^16.0.0-0 || ^17.0.0-0 || ^18.0.0-0 - checksum: 2ff960b180f0ac1960170f3628a49667f3da58751242638af7844eb49a30273e52bba380103c14bb5b4829ca4bef0c072a8a78696abf2b41d4a49fcdbc02a263 + checksum: 9265ab7c0c9ca9caddbe2c4413ef8e2641a181f6e3a72492d940c3c4e62d28be8e3da52c5f0d723f9921ac455e653414cb8b660af7014af0371bf0d60387fb6e languageName: node linkType: hard @@ -9254,6 +9305,19 @@ __metadata: languageName: node linkType: hard +"@visx/group@npm:3.3.0": + version: 3.3.0 + resolution: "@visx/group@npm:3.3.0" + dependencies: + "@types/react": "*" + classnames: ^2.3.1 + prop-types: ^15.6.2 + peerDependencies: + react: ^16.0.0-0 || ^17.0.0-0 || ^18.0.0-0 + checksum: a2f950664f43f1f1700a329f3a66bc3921ce8be35a93095a9ea28334ae4f9943c1082f21ba0f57f2bda913d2a40da8b7124235c2ae666c0e5d58a900c7148a8b + languageName: node + linkType: hard + "@visx/heatmap@npm:1.17.1": version: 1.17.1 resolution: "@visx/heatmap@npm:1.17.1" @@ -9365,6 +9429,13 @@ __metadata: languageName: node linkType: hard +"@visx/point@npm:3.3.0": + version: 3.3.0 + resolution: "@visx/point@npm:3.3.0" + checksum: 79950f49828d12c67c856ac0b345292df03a154b50f739aab3dbab37abb73ba40f9cbdaa46d698b50691984e81b94afdafe64dd4fa18c6d2895298cf25f1a450 + languageName: node + linkType: hard + "@visx/react-spring@npm:1.17.1": version: 1.17.1 resolution: "@visx/react-spring@npm:1.17.1" @@ -9383,21 +9454,21 @@ __metadata: languageName: node linkType: hard -"@visx/react-spring@npm:3.1.0": - version: 3.1.0 - resolution: "@visx/react-spring@npm:3.1.0" +"@visx/react-spring@npm:3.3.0": + version: 3.3.0 + resolution: "@visx/react-spring@npm:3.3.0" dependencies: "@types/react": "*" - "@visx/axis": 3.1.0 - "@visx/grid": 3.0.1 - "@visx/scale": 3.0.0 - "@visx/text": 3.0.0 + "@visx/axis": 3.3.0 + "@visx/grid": 3.3.0 + "@visx/scale": 3.3.0 + "@visx/text": 3.3.0 classnames: ^2.3.1 prop-types: ^15.6.2 peerDependencies: "@react-spring/web": ^9.4.5 react: ^16.3.0-0 || ^17.0.0 || ^18.0.0 - checksum: 5d727dd8053c101d6473580feba5b8c05d70fa6311a0dc2f9c1d420c6fce4ce497f7f191b89bb10c53eedd986aa3006cd7a7f389035c02a2a847f8c3c38fcafb + checksum: 07693c055ed6de780e002f9060cad416017030a784078edd3ccbb6c377a671b2cbc18688861abcd6b41984df4721975c5df9c5a8a6dcf561c7110e6e4faebf34 languageName: node linkType: hard @@ -9416,9 +9487,9 @@ __metadata: languageName: node linkType: hard -"@visx/responsive@npm:3.0.0": - version: 3.0.0 - resolution: "@visx/responsive@npm:3.0.0" +"@visx/responsive@npm:3.3.0": + version: 3.3.0 + resolution: "@visx/responsive@npm:3.3.0" dependencies: "@types/lodash": ^4.14.172 "@types/react": "*" @@ -9426,7 +9497,7 @@ __metadata: prop-types: ^15.6.1 peerDependencies: react: ^16.0.0-0 || ^17.0.0-0 || ^18.0.0-0 - checksum: 0a547c7cec9f2f61931d1de6ee8955b76904ea0bcc1c55d18d991067f63579c6ca1d1b976a0ada21d3b3818551b3d5749d8b11ab50dc472f38bd94f21b56b8ec + checksum: 8f23d09bdb66d1deb35a393ff731438e8f9329a0dba89cffe00cc7aadd7ffd6af021eed1452f91a8cc0566b1f025b59d32f060f9070ef2bc66c7a41f8a06c9fb languageName: node linkType: hard @@ -9458,6 +9529,15 @@ __metadata: languageName: node linkType: hard +"@visx/scale@npm:3.3.0": + version: 3.3.0 + resolution: "@visx/scale@npm:3.3.0" + dependencies: + "@visx/vendor": 3.3.0 + checksum: a6a5589fc86c72df84d8d11bab255fa3bf0481ca13ff15c66228368afae60937bb0553d942202b007a5d50b93dfc62359b06fd53c00da0eeb472db09d662440d + languageName: node + linkType: hard + "@visx/shape@npm:1.17.1, @visx/shape@npm:^1.4.0": version: 1.17.1 resolution: "@visx/shape@npm:1.17.1" @@ -9502,6 +9582,28 @@ __metadata: languageName: node linkType: hard +"@visx/shape@npm:3.3.0": + version: 3.3.0 + resolution: "@visx/shape@npm:3.3.0" + dependencies: + "@types/d3-path": ^1.0.8 + "@types/d3-shape": ^1.3.1 + "@types/lodash": ^4.14.172 + "@types/react": "*" + "@visx/curve": 3.3.0 + "@visx/group": 3.3.0 + "@visx/scale": 3.3.0 + classnames: ^2.3.1 + d3-path: ^1.0.5 + d3-shape: ^1.2.0 + lodash: ^4.17.21 + prop-types: ^15.5.10 + peerDependencies: + react: ^16.3.0-0 || ^17.0.0-0 || ^18.0.0-0 + checksum: ab53910f6a2b5c2a36bfceed92ebafaf3bfa0c20272e3b86e7ec7e782628021fca4fb377edfe86b76d38df4f5ad9b846056ba358f63112988ff7346f23bad739 + languageName: node + linkType: hard + "@visx/text@npm:1.17.1, @visx/text@npm:^1.3.0": version: 1.17.1 resolution: "@visx/text@npm:1.17.1" @@ -9534,6 +9636,22 @@ __metadata: languageName: node linkType: hard +"@visx/text@npm:3.3.0": + version: 3.3.0 + resolution: "@visx/text@npm:3.3.0" + dependencies: + "@types/lodash": ^4.14.172 + "@types/react": "*" + classnames: ^2.3.1 + lodash: ^4.17.21 + prop-types: ^15.7.2 + reduce-css-calc: ^1.3.0 + peerDependencies: + react: ^16.3.0-0 || ^17.0.0-0 || ^18.0.0-0 + checksum: 22e2b10a22fa708f59a8a93c9e3610162c4bb2aed8fc4be624f3277ceead858c83a46f72f9940ce9b28d9b6847e561a6aa977a35d9363c08dd7ea5d5a62748b3 + languageName: node + linkType: hard + "@visx/tooltip@npm:1.17.1, @visx/tooltip@npm:^1.3.0": version: 1.17.1 resolution: "@visx/tooltip@npm:1.17.1" @@ -9550,19 +9668,44 @@ __metadata: languageName: node linkType: hard -"@visx/tooltip@npm:3.1.2": - version: 3.1.2 - resolution: "@visx/tooltip@npm:3.1.2" +"@visx/tooltip@npm:3.3.0": + version: 3.3.0 + resolution: "@visx/tooltip@npm:3.3.0" dependencies: "@types/react": "*" - "@visx/bounds": 3.0.0 + "@visx/bounds": 3.3.0 classnames: ^2.3.1 prop-types: ^15.5.10 react-use-measure: ^2.0.4 peerDependencies: react: ^16.8.0-0 || ^17.0.0-0 || ^18.0.0-0 react-dom: ^16.8.0-0 || ^17.0.0-0 || ^18.0.0-0 - checksum: 4c2127068f22766e984ed3ed1452dcece919bacb0297b17ba862a6815bb7f04c4fc462a100d3934e0cb56a9c7962b82f7c4184748ef4b2dbef3b4b0d5b717fdd + checksum: eb525afd34bf108f9343e7998a8752e0002190afa76d2e9e7f47ec83f89139f611def8fe59ba55af4ab2d15bb7d31b836d372ef338e8aa4ca2e7e4cd68567c7f + languageName: node + linkType: hard + +"@visx/vendor@npm:3.3.0": + version: 3.3.0 + resolution: "@visx/vendor@npm:3.3.0" + dependencies: + "@types/d3-array": 3.0.3 + "@types/d3-color": 3.1.0 + "@types/d3-delaunay": 6.0.1 + "@types/d3-format": 3.0.1 + "@types/d3-interpolate": 3.0.1 + "@types/d3-scale": 4.0.2 + "@types/d3-time": 3.0.0 + "@types/d3-time-format": 2.1.0 + d3-array: 3.2.1 + d3-color: 3.1.0 + d3-delaunay: 6.0.2 + d3-format: 3.1.0 + d3-interpolate: 3.0.1 + d3-scale: 4.0.2 + d3-time: 3.1.0 + d3-time-format: 4.1.0 + internmap: 2.0.3 + checksum: eaab876b3898d44930e75a95ea1f6f1db490822fd9c93f2806f5673875f8b63bde08f41f43ab0c2c291301796b52d612e671d3f81dfaa20c986adc90d95d09cf languageName: node linkType: hard @@ -9620,9 +9763,9 @@ __metadata: languageName: node linkType: hard -"@visx/voronoi@npm:3.0.0": - version: 3.0.0 - resolution: "@visx/voronoi@npm:3.0.0" +"@visx/voronoi@npm:3.3.0": + version: 3.3.0 + resolution: "@visx/voronoi@npm:3.3.0" dependencies: "@types/d3-voronoi": ^1.1.9 "@types/react": "*" @@ -9631,7 +9774,39 @@ __metadata: prop-types: ^15.6.1 peerDependencies: react: ^16.3.0-0 || ^17.0.0-0 || ^18.0.0-0 - checksum: 7c0f4ff846401bae337bf3e961e3807a1ae7b6e50da3817dd7bfba0c8b40017079ef81baa6145bab61b29368d27d2852b5fcf17ee52b372ab17ff2d81a087b9a + checksum: 9640a77f270f940aed0e2a44fcb8768083abaae18dbc343edcd7d7ea9b40aca775d080b7cabbe72cdf9ada425f567e05aca6867b29b3407593b14f789110cf61 + languageName: node + linkType: hard + +"@visx/xychart@https://github.com/jernestmyers/visx.git#visx-xychart": + version: 3.3.0 + resolution: "@visx/xychart@https://github.com/jernestmyers/visx.git#commit=fb1fa95a48e35a27a54ec9ad5de149ad8cd1e0c5" + dependencies: + "@types/lodash": ^4.14.172 + "@types/react": "*" + "@visx/annotation": 3.3.0 + "@visx/axis": 3.3.0 + "@visx/event": 3.3.0 + "@visx/glyph": 3.3.0 + "@visx/grid": 3.3.0 + "@visx/react-spring": 3.3.0 + "@visx/responsive": 3.3.0 + "@visx/scale": 3.3.0 + "@visx/shape": 3.3.0 + "@visx/text": 3.3.0 + "@visx/tooltip": 3.3.0 + "@visx/vendor": 3.3.0 + "@visx/voronoi": 3.3.0 + classnames: ^2.3.1 + d3-interpolate-path: 2.2.1 + d3-shape: ^2.0.0 + lodash: ^4.17.21 + mitt: ^2.1.0 + prop-types: ^15.6.2 + peerDependencies: + "@react-spring/web": ^9.4.5 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + checksum: 491f82103ceb4ebfc75cb82b8606e8130d22b6e6ef3132664f69b847e4131444d6ccc8014ab7a521619e14fdf489b47fb65c4adee747ef2423d9fe3d926ad46c languageName: node linkType: hard @@ -9667,38 +9842,6 @@ __metadata: languageName: node linkType: hard -"@visx/xychart@npm:^3.1.0": - version: 3.1.2 - resolution: "@visx/xychart@npm:3.1.2" - dependencies: - "@types/lodash": ^4.14.172 - "@types/react": "*" - "@visx/annotation": 3.0.1 - "@visx/axis": 3.1.0 - "@visx/event": 3.0.1 - "@visx/glyph": 3.0.0 - "@visx/grid": 3.0.1 - "@visx/react-spring": 3.1.0 - "@visx/responsive": 3.0.0 - "@visx/scale": 3.0.0 - "@visx/shape": 3.0.0 - "@visx/text": 3.0.0 - "@visx/tooltip": 3.1.2 - "@visx/voronoi": 3.0.0 - classnames: ^2.3.1 - d3-array: ^2.6.0 - d3-interpolate-path: 2.2.1 - d3-shape: ^2.0.0 - lodash: ^4.17.21 - mitt: ^2.1.0 - prop-types: ^15.6.2 - peerDependencies: - "@react-spring/web": ^9.4.5 - react: ^16.8.0 || ^17.0.0 || ^ 18.0.0 - checksum: f6776a4da8000811c7f71e2285610dfe26b159f43904ba8e124225219a6791a89b9ded2f71c4f04f38da03f10b59e5f0d63a6b2b7f8c688aebc9107be4ec2e57 - languageName: node - linkType: hard - "@visx/zoom@npm:1.14.1": version: 1.14.1 resolution: "@visx/zoom@npm:1.14.1" @@ -15338,6 +15481,15 @@ __metadata: languageName: node linkType: hard +"d3-array@npm:3.2.1": + version: 3.2.1 + resolution: "d3-array@npm:3.2.1" + dependencies: + internmap: 1 - 2 + checksum: 0bed33cc33b70f9d48ccef3e7a5956e134862e09179bf14df0bf9c8fc0ec02b8f847d4f5e1d32729cd5e02032af1d0a32bcc968ff1333795028455a749994623 + languageName: node + linkType: hard + "d3-axis@npm:3": version: 3.0.0 resolution: "d3-axis@npm:3.0.0" @@ -15388,7 +15540,7 @@ __metadata: languageName: node linkType: hard -"d3-color@npm:1 - 3, d3-color@npm:3": +"d3-color@npm:1 - 3, d3-color@npm:3, d3-color@npm:3.1.0": version: 3.1.0 resolution: "d3-color@npm:3.1.0" checksum: 4931fbfda5d7c4b5cfa283a13c91a954f86e3b69d75ce588d06cde6c3628cebfc3af2069ccf225e982e8987c612aa7948b3932163ce15eb3c11cd7c003f3ee3b @@ -15404,7 +15556,7 @@ __metadata: languageName: node linkType: hard -"d3-delaunay@npm:6": +"d3-delaunay@npm:6, d3-delaunay@npm:6.0.2": version: 6.0.2 resolution: "d3-delaunay@npm:6.0.2" dependencies: @@ -15514,7 +15666,7 @@ __metadata: languageName: node linkType: hard -"d3-format@npm:1 - 3, d3-format@npm:3": +"d3-format@npm:1 - 3, d3-format@npm:3, d3-format@npm:3.1.0": version: 3.1.0 resolution: "d3-format@npm:3.1.0" checksum: f345ec3b8ad3cab19bff5dead395bd9f5590628eb97a389b1dd89f0b204c7c4fc1d9520f13231c2c7cf14b7c9a8cf10f8ef15bde2befbab41454a569bd706ca2 @@ -15585,7 +15737,7 @@ __metadata: languageName: node linkType: hard -"d3-interpolate@npm:1 - 3, d3-interpolate@npm:1.2.0 - 3, d3-interpolate@npm:3, d3-interpolate@npm:^3.0.1": +"d3-interpolate@npm:1 - 3, d3-interpolate@npm:1.2.0 - 3, d3-interpolate@npm:3, d3-interpolate@npm:3.0.1, d3-interpolate@npm:^3.0.1": version: 3.0.1 resolution: "d3-interpolate@npm:3.0.1" dependencies: @@ -15678,7 +15830,7 @@ __metadata: languageName: node linkType: hard -"d3-scale@npm:4, d3-scale@npm:^4.0.2": +"d3-scale@npm:4, d3-scale@npm:4.0.2, d3-scale@npm:^4.0.2": version: 4.0.2 resolution: "d3-scale@npm:4.0.2" dependencies: @@ -15754,7 +15906,7 @@ __metadata: languageName: node linkType: hard -"d3-time-format@npm:2 - 4, d3-time-format@npm:4": +"d3-time-format@npm:2 - 4, d3-time-format@npm:4, d3-time-format@npm:4.1.0": version: 4.1.0 resolution: "d3-time-format@npm:4.1.0" dependencies: @@ -15781,7 +15933,7 @@ __metadata: languageName: node linkType: hard -"d3-time@npm:1 - 3, d3-time@npm:2.1.1 - 3, d3-time@npm:3": +"d3-time@npm:1 - 3, d3-time@npm:2.1.1 - 3, d3-time@npm:3, d3-time@npm:3.1.0": version: 3.1.0 resolution: "d3-time@npm:3.1.0" dependencies: @@ -21130,7 +21282,7 @@ __metadata: languageName: node linkType: hard -"internmap@npm:1 - 2": +"internmap@npm:1 - 2, internmap@npm:2.0.3": version: 2.0.3 resolution: "internmap@npm:2.0.3" checksum: 7ca41ec6aba8f0072fc32fa8a023450a9f44503e2d8e403583c55714b25efd6390c38a87161ec456bf42d7bc83aab62eb28f5aef34876b1ac4e60693d5e1d241