Skip to content

Commit

Permalink
refactor(components): memoize some chart configs
Browse files Browse the repository at this point in the history
  • Loading branch information
fengelniederhammer committed Feb 11, 2025
1 parent e058cb4 commit 9aafe73
Show file tree
Hide file tree
Showing 4 changed files with 197 additions and 163 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Chart, type ChartConfiguration, type ChartDataset, registerables, type TooltipItem } from 'chart.js';
import { BarWithErrorBar, BarWithErrorBarsController } from 'chartjs-chart-error-bars';
import { useMemo } from 'preact/hooks';

import { maxInData } from './prevalence-over-time';
import {
Expand All @@ -26,52 +27,62 @@ interface PrevalenceOverTimeBarChartProps {

Chart.register(...registerables, LogitScale, BarWithErrorBarsController, BarWithErrorBar);

const NO_DATA = 'noData';

const PrevalenceOverTimeBarChart = ({
data,
yAxisScaleType,
confidenceIntervalMethod,
yAxisMaxConfig,
maintainAspectRatio,
}: PrevalenceOverTimeBarChartProps) => {
const nullFirstData = data
.filter((prevalenceOverTimeData) => prevalenceOverTimeData.content.length > 0)
.map((variantData) => {
return {
content: variantData.content.sort(sortNullToBeginningThenByDate),
displayName: variantData.displayName,
};
});
const config = useMemo<ChartConfiguration | typeof NO_DATA>(() => {
const nullFirstData = data
.filter((prevalenceOverTimeData) => prevalenceOverTimeData.content.length > 0)
.map((variantData) => {
return {
content: variantData.content.sort(sortNullToBeginningThenByDate),
displayName: variantData.displayName,
};
});

if (nullFirstData.length === 0) {
return NO_DATA;
}

const datasets = nullFirstData.map((graphData, index) =>
getDataset(graphData, index, confidenceIntervalMethod),
);

const maxY =
yAxisScaleType !== 'logit'
? getYAxisMax(maxInData(nullFirstData), yAxisMaxConfig?.[yAxisScaleType])
: undefined;

if (nullFirstData.length === 0) {
return <NoDataDisplay />;
}

const datasets = nullFirstData.map((graphData, index) => getDataset(graphData, index, confidenceIntervalMethod));

const maxY =
yAxisScaleType !== 'logit'
? getYAxisMax(maxInData(nullFirstData), yAxisMaxConfig?.[yAxisScaleType])
: undefined;

const config: ChartConfiguration = {
type: BarWithErrorBarsController.id,
data: {
datasets,
},
options: {
maintainAspectRatio,
animation: false,
scales: {
y: { ...getYAxisScale(yAxisScaleType), max: maxY },
return {
type: BarWithErrorBarsController.id,
data: {
datasets,
},
plugins: {
legend: {
display: false,
options: {
maintainAspectRatio,
animation: false,
scales: {
y: { ...getYAxisScale(yAxisScaleType), max: maxY },
},
plugins: {
legend: {
display: false,
},
tooltip: tooltip(confidenceIntervalMethod),
},
tooltip: tooltip(confidenceIntervalMethod),
},
},
};
};
}, [data, yAxisScaleType, confidenceIntervalMethod, yAxisMaxConfig, maintainAspectRatio]);

if (config === NO_DATA) {
return <NoDataDisplay />;
}

return <GsChart configuration={config} />;
};
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Chart, type ChartConfiguration, registerables } from 'chart.js';
import { useMemo } from 'preact/hooks';

import { maxInData } from './prevalence-over-time';
import { type PrevalenceOverTimeData } from '../../query/queryPrevalenceOverTime';
Expand All @@ -20,94 +21,104 @@ interface PrevalenceOverTimeBubbleChartProps {

Chart.register(...registerables, LogitScale);

const NO_DATA = 'noData';

const PrevalenceOverTimeBubbleChart = ({
data,
yAxisScaleType,
yAxisMaxConfig,
maintainAspectRatio,
}: PrevalenceOverTimeBubbleChartProps) => {
const nonNullDateRangeData = data
.filter((prevalenceOverTimeData) => prevalenceOverTimeData.content.length > 0)
.map((variantData) => {
return {
content: variantData.content.filter((dataPoint) => dataPoint.dateRange !== null),
displayName: variantData.displayName,
};
});
const config = useMemo<ChartConfiguration | typeof NO_DATA>(() => {
const nonNullDateRangeData = data
.filter((prevalenceOverTimeData) => prevalenceOverTimeData.content.length > 0)
.map((variantData) => {
return {
content: variantData.content.filter((dataPoint) => dataPoint.dateRange !== null),
displayName: variantData.displayName,
};
});

if (nonNullDateRangeData.length === 0) {
return <NoDataDisplay />;
}
if (nonNullDateRangeData.length === 0) {
return NO_DATA;
}

const firstDate = nonNullDateRangeData[0].content[0].dateRange!;
const total = nonNullDateRangeData.map((graphData) => graphData.content.map((dataPoint) => dataPoint.total)).flat();
const [minTotal, maxTotal] = getMinMaxNumber(total)!;
const scaleBubble = (value: number) => {
return ((value - minTotal) / (maxTotal - minTotal)) * 4.5 + 0.5;
};
const firstDate = nonNullDateRangeData[0].content[0].dateRange!;
const total = nonNullDateRangeData
.map((graphData) => graphData.content.map((dataPoint) => dataPoint.total))
.flat();
const [minTotal, maxTotal] = getMinMaxNumber(total)!;
const scaleBubble = (value: number) => {
return ((value - minTotal) / (maxTotal - minTotal)) * 4.5 + 0.5;
};

const maxY =
yAxisScaleType !== 'logit'
? getYAxisMax(maxInData(nonNullDateRangeData), yAxisMaxConfig?.[yAxisScaleType])
: undefined;
const maxY =
yAxisScaleType !== 'logit'
? getYAxisMax(maxInData(nonNullDateRangeData), yAxisMaxConfig?.[yAxisScaleType])
: undefined;

const config: ChartConfiguration = {
type: 'bubble',
data: {
datasets: nonNullDateRangeData.map((graphData, index) => ({
label: graphData.displayName,
data: graphData.content
.filter((dataPoint) => dataPoint.dateRange !== null)
.map((dataPoint) => ({
x: minusTemporal(dataPoint.dateRange!, firstDate),
y: dataPoint.prevalence,
r: scaleBubble(dataPoint.total),
})),
borderWidth: 1,
pointRadius: 0,
backgroundColor: singleGraphColorRGBAById(index, 0.3),
borderColor: singleGraphColorRGBAById(index),
})),
},
options: {
animation: false,
maintainAspectRatio,
scales: {
x: {
ticks: {
callback: (value) => addUnit(firstDate, value as number).toString(),
},
},
y: { ...getYAxisScale(yAxisScaleType), max: maxY },
return {
type: 'bubble',
data: {
datasets: nonNullDateRangeData.map((graphData, index) => ({
label: graphData.displayName,
data: graphData.content
.filter((dataPoint) => dataPoint.dateRange !== null)
.map((dataPoint) => ({
x: minusTemporal(dataPoint.dateRange!, firstDate),
y: dataPoint.prevalence,
r: scaleBubble(dataPoint.total),
})),
borderWidth: 1,
pointRadius: 0,
backgroundColor: singleGraphColorRGBAById(index, 0.3),
borderColor: singleGraphColorRGBAById(index),
})),
},
plugins: {
legend: {
display: false,
},
tooltip: {
mode: 'index',
intersect: false,
callbacks: {
title: (context) => {
const dataset = nonNullDateRangeData[context[0].datasetIndex];
const dataPoint = dataset.content[context[0].dataIndex];
return dataPoint.dateRange?.toString();
options: {
animation: false,
maintainAspectRatio,
scales: {
x: {
ticks: {
callback: (value) => addUnit(firstDate, value as number).toString(),
},
label: (context) => {
const dataset = nonNullDateRangeData[context.datasetIndex];
const dataPoint = dataset.content[context.dataIndex];
},
y: { ...getYAxisScale(yAxisScaleType), max: maxY },
},
plugins: {
legend: {
display: false,
},
tooltip: {
mode: 'index',
intersect: false,
callbacks: {
title: (context) => {
const dataset = nonNullDateRangeData[context[0].datasetIndex];
const dataPoint = dataset.content[context[0].dataIndex];
return dataPoint.dateRange?.toString();
},
label: (context) => {
const dataset = nonNullDateRangeData[context.datasetIndex];
const dataPoint = dataset.content[context.dataIndex];

const percentage = (dataPoint.prevalence * 100).toFixed(2);
const count = dataPoint.count.toFixed(0);
const total = dataPoint.total.toFixed(0);
const percentage = (dataPoint.prevalence * 100).toFixed(2);
const count = dataPoint.count.toFixed(0);
const total = dataPoint.total.toFixed(0);

return `${dataset.displayName}: ${percentage}%, ${count}/${total} samples`;
return `${dataset.displayName}: ${percentage}%, ${count}/${total} samples`;
},
},
},
},
},
},
};
} satisfies ChartConfiguration;
}, [data, maintainAspectRatio, yAxisMaxConfig, yAxisScaleType]);

if (config === NO_DATA) {
return <NoDataDisplay />;
}

return <GsChart configuration={config} />;
};
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Chart, type ChartConfiguration, registerables } from 'chart.js';
import { type TooltipItem } from 'chart.js/dist/types';
import { useMemo } from 'preact/hooks';

import { maxInData } from './prevalence-over-time';
import { type PrevalenceOverTimeData, type PrevalenceOverTimeVariantData } from '../../query/queryPrevalenceOverTime';
Expand All @@ -25,54 +26,62 @@ interface PrevalenceOverTimeLineChartProps {

Chart.register(...registerables, LogitScale);

const NO_DATA = 'noData';

const PrevalenceOverTimeLineChart = ({
data,
yAxisScaleType,
confidenceIntervalMethod,
yAxisMaxConfig,
maintainAspectRatio,
}: PrevalenceOverTimeLineChartProps) => {
const nonNullDateRangeData = data
.filter((prevalenceOverTimeData) => prevalenceOverTimeData.content.length > 0)
.map((variantData) => {
return {
content: variantData.content.filter((dataPoint) => dataPoint.dateRange !== null),
displayName: variantData.displayName,
};
});
const config = useMemo<ChartConfiguration | typeof NO_DATA>(() => {
const nonNullDateRangeData = data
.filter((prevalenceOverTimeData) => prevalenceOverTimeData.content.length > 0)
.map((variantData) => {
return {
content: variantData.content.filter((dataPoint) => dataPoint.dateRange !== null),
displayName: variantData.displayName,
};
});

if (nonNullDateRangeData.length === 0) {
return NO_DATA;
}

const datasets = nonNullDateRangeData
.map((graphData, index) => getDataset(graphData, index, confidenceIntervalMethod))
.flat();

const maxY =
yAxisScaleType !== 'logit'
? getYAxisMax(maxInData(nonNullDateRangeData), yAxisMaxConfig?.[yAxisScaleType])
: undefined;

if (nonNullDateRangeData.length === 0) {
return <NoDataDisplay />;
}

const datasets = nonNullDateRangeData
.map((graphData, index) => getDataset(graphData, index, confidenceIntervalMethod))
.flat();

const maxY =
yAxisScaleType !== 'logit'
? getYAxisMax(maxInData(nonNullDateRangeData), yAxisMaxConfig?.[yAxisScaleType])
: undefined;

const config: ChartConfiguration = {
type: 'line',
data: {
datasets,
},
options: {
animation: false,
maintainAspectRatio,
scales: {
y: { ...getYAxisScale(yAxisScaleType), max: maxY },
return {
type: 'line',
data: {
datasets,
},
plugins: {
legend: {
display: false,
options: {
animation: false,
maintainAspectRatio,
scales: {
y: { ...getYAxisScale(yAxisScaleType), max: maxY },
},
plugins: {
legend: {
display: false,
},
tooltip: tooltip(confidenceIntervalMethod),
},
tooltip: tooltip(confidenceIntervalMethod),
},
},
};
};
}, [data, yAxisScaleType, confidenceIntervalMethod, yAxisMaxConfig, maintainAspectRatio]);

if (config === NO_DATA) {
return <NoDataDisplay />;
}

return <GsChart configuration={config} />;
};
Expand Down
Loading

0 comments on commit 9aafe73

Please sign in to comment.