@genspectrum/dashboard-components 0.4.5 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/README.md +2 -2
  2. package/custom-elements.json +178 -102
  3. package/dist/dashboard-components.js +318 -147
  4. package/dist/dashboard-components.js.map +1 -1
  5. package/dist/genspectrum-components.d.ts +91 -55
  6. package/dist/style.css +34 -7
  7. package/package.json +5 -5
  8. package/src/preact/mutationComparison/mutation-comparison-venn.tsx +1 -1
  9. package/src/preact/mutationComparison/mutation-comparison.stories.tsx +18 -18
  10. package/src/preact/mutationComparison/mutation-comparison.tsx +6 -6
  11. package/src/preact/mutationComparison/queryMutationData.ts +4 -4
  12. package/src/preact/mutations/mutations.stories.tsx +3 -3
  13. package/src/preact/mutations/mutations.tsx +16 -6
  14. package/src/preact/mutations/queryMutations.ts +3 -3
  15. package/src/preact/prevalenceOverTime/__mockData__/{denominatorOneVariant.json → denominatorFilterOneDataset.json} +1 -1
  16. package/src/preact/prevalenceOverTime/__mockData__/{numeratorOneVariant.json → numeratorFilterOneDataset.json} +1 -1
  17. package/src/preact/prevalenceOverTime/prevalence-over-time-bar-chart.tsx +42 -5
  18. package/src/preact/prevalenceOverTime/prevalence-over-time-bubble-chart.tsx +26 -7
  19. package/src/preact/prevalenceOverTime/prevalence-over-time-line-chart.tsx +62 -28
  20. package/src/preact/prevalenceOverTime/prevalence-over-time.stories.tsx +26 -16
  21. package/src/preact/prevalenceOverTime/prevalence-over-time.tsx +38 -11
  22. package/src/preact/relativeGrowthAdvantage/relative-growth-advantage-chart.tsx +39 -7
  23. package/src/preact/relativeGrowthAdvantage/relative-growth-advantage.stories.tsx +10 -4
  24. package/src/preact/relativeGrowthAdvantage/relative-growth-advantage.tsx +19 -10
  25. package/src/preact/shared/charts/confideceInterval.ts +7 -2
  26. package/src/preact/shared/charts/getYAxisMax.ts +24 -0
  27. package/src/preact/shared/charts/getYAxisScale.ts +1 -3
  28. package/src/query/queryAggregateData.ts +2 -2
  29. package/src/query/queryInsertions.ts +7 -2
  30. package/src/query/querySubstitutionsOrDeletions.ts +2 -2
  31. package/src/web-components/input/gs-date-range-selector.tsx +1 -1
  32. package/src/web-components/input/gs-location-filter.tsx +1 -1
  33. package/src/web-components/input/gs-mutation-filter.tsx +1 -1
  34. package/src/web-components/input/gs-text-input.tsx +1 -1
  35. package/src/web-components/visualization/gs-aggregate.tsx +2 -2
  36. package/src/web-components/visualization/gs-mutation-comparison.stories.ts +12 -12
  37. package/src/web-components/visualization/gs-mutation-comparison.tsx +18 -19
  38. package/src/web-components/visualization/gs-mutations.stories.ts +4 -4
  39. package/src/web-components/visualization/gs-mutations.tsx +10 -11
  40. package/src/web-components/visualization/gs-prevalence-over-time.stories.ts +46 -35
  41. package/src/web-components/visualization/gs-prevalence-over-time.tsx +54 -20
  42. package/src/web-components/visualization/gs-relative-growth-advantage.stories.ts +32 -18
  43. package/src/web-components/visualization/gs-relative-growth-advantage.tsx +51 -13
  44. /package/src/preact/mutationComparison/__mockData__/{nucleotideMutationsOtherVariant.json → nucleotideMutationsOtherDataset.json} +0 -0
  45. /package/src/preact/mutationComparison/__mockData__/{nucleotideMutationsSomeVariant.json → nucleotideMutationsSomeDataset.json} +0 -0
  46. /package/src/preact/prevalenceOverTime/__mockData__/{denominator.json → denominatorFilter.json} +0 -0
  47. /package/src/preact/prevalenceOverTime/__mockData__/{numeratorEG.json → numeratorFilterEG.json} +0 -0
  48. /package/src/preact/prevalenceOverTime/__mockData__/{numeratorJN1.json → numeratorFilterJN1.json} +0 -0
  49. /package/src/preact/relativeGrowthAdvantage/__mockData__/{denominator.json → denominatorFilter.json} +0 -0
  50. /package/src/preact/relativeGrowthAdvantage/__mockData__/{numerator.json → numeratorFilter.json} +0 -0
@@ -31,14 +31,14 @@ export interface MutationComparisonProps extends MutationComparisonInnerProps {
31
31
  }
32
32
 
33
33
  export interface MutationComparisonInnerProps {
34
- variants: NamedLapisFilter[];
34
+ lapisFilters: NamedLapisFilter[];
35
35
  sequenceType: SequenceType;
36
36
  views: View[];
37
37
  pageSize: boolean | number;
38
38
  }
39
39
 
40
40
  export const MutationComparison: FunctionComponent<MutationComparisonProps> = ({
41
- variants,
41
+ lapisFilters,
42
42
  sequenceType,
43
43
  views,
44
44
  width,
@@ -53,7 +53,7 @@ export const MutationComparison: FunctionComponent<MutationComparisonProps> = ({
53
53
  <ResizeContainer size={size}>
54
54
  <Headline heading={headline}>
55
55
  <MutationComparisonInner
56
- variants={variants}
56
+ lapisFilters={lapisFilters}
57
57
  sequenceType={sequenceType}
58
58
  views={views}
59
59
  pageSize={pageSize}
@@ -65,7 +65,7 @@ export const MutationComparison: FunctionComponent<MutationComparisonProps> = ({
65
65
  };
66
66
 
67
67
  export const MutationComparisonInner: FunctionComponent<MutationComparisonInnerProps> = ({
68
- variants,
68
+ lapisFilters,
69
69
  sequenceType,
70
70
  views,
71
71
  pageSize,
@@ -73,8 +73,8 @@ export const MutationComparisonInner: FunctionComponent<MutationComparisonInnerP
73
73
  const lapis = useContext(LapisUrlContext);
74
74
 
75
75
  const { data, error, isLoading } = useQuery(async () => {
76
- return queryMutationData(variants, sequenceType, lapis);
77
- }, [variants, sequenceType, lapis]);
76
+ return queryMutationData(lapisFilters, sequenceType, lapis);
77
+ }, [lapisFilters, sequenceType, lapis]);
78
78
 
79
79
  if (isLoading) {
80
80
  return <LoadingDisplay />;
@@ -9,15 +9,15 @@ export type MutationData = {
9
9
  };
10
10
 
11
11
  export async function queryMutationData(
12
- variants: NamedLapisFilter[],
12
+ lapisFilters: NamedLapisFilter[],
13
13
  sequenceType: 'nucleotide' | 'amino acid',
14
14
  lapis: string,
15
15
  ) {
16
16
  const mutationData = await Promise.all(
17
- variants.map(async (variant) => {
17
+ lapisFilters.map(async (filter) => {
18
18
  return {
19
- displayName: variant.displayName,
20
- data: (await querySubstitutionsOrDeletions(variant.lapisFilter, sequenceType, lapis)).content,
19
+ displayName: filter.displayName,
20
+ data: (await querySubstitutionsOrDeletions(filter.lapisFilter, sequenceType, lapis)).content,
21
21
  };
22
22
  }),
23
23
  );
@@ -13,7 +13,7 @@ const meta: Meta<MutationsProps> = {
13
13
  title: 'Visualization/Mutations',
14
14
  component: Mutations,
15
15
  argTypes: {
16
- variant: { control: 'object' },
16
+ lapisFilter: { control: 'object' },
17
17
  sequenceType: {
18
18
  options: ['nucleotide', 'amino acid'],
19
19
  control: { type: 'radio' },
@@ -36,7 +36,7 @@ const Template = {
36
36
  <LapisUrlContext.Provider value={LAPIS_URL}>
37
37
  <ReferenceGenomeContext.Provider value={referenceGenome}>
38
38
  <Mutations
39
- variant={args.variant}
39
+ lapisFilter={args.lapisFilter}
40
40
  sequenceType={args.sequenceType}
41
41
  views={args.views}
42
42
  width={args.width}
@@ -52,7 +52,7 @@ const Template = {
52
52
  export const Default: StoryObj<MutationsProps> = {
53
53
  ...Template,
54
54
  args: {
55
- variant: { country: 'Switzerland', pangoLineage: 'B.1.1.7', dateTo: '2022-01-01' },
55
+ lapisFilter: { country: 'Switzerland', pangoLineage: 'B.1.1.7', dateTo: '2022-01-01' },
56
56
  sequenceType: 'nucleotide',
57
57
  views: ['grid', 'table', 'insertions'],
58
58
  width: '100%',
@@ -32,7 +32,7 @@ import { useQuery } from '../useQuery';
32
32
  export type View = 'table' | 'grid' | 'insertions';
33
33
 
34
34
  export interface MutationsInnerProps {
35
- variant: LapisFilter;
35
+ lapisFilter: LapisFilter;
36
36
  sequenceType: SequenceType;
37
37
  views: View[];
38
38
  pageSize: boolean | number;
@@ -45,7 +45,7 @@ export interface MutationsProps extends MutationsInnerProps {
45
45
  }
46
46
 
47
47
  export const Mutations: FunctionComponent<MutationsProps> = ({
48
- variant,
48
+ lapisFilter,
49
49
  sequenceType,
50
50
  views,
51
51
  width,
@@ -59,18 +59,28 @@ export const Mutations: FunctionComponent<MutationsProps> = ({
59
59
  <ErrorBoundary size={size} headline={headline}>
60
60
  <ResizeContainer size={size}>
61
61
  <Headline heading={headline}>
62
- <MutationsInner variant={variant} sequenceType={sequenceType} views={views} pageSize={pageSize} />
62
+ <MutationsInner
63
+ lapisFilter={lapisFilter}
64
+ sequenceType={sequenceType}
65
+ views={views}
66
+ pageSize={pageSize}
67
+ />
63
68
  </Headline>
64
69
  </ResizeContainer>
65
70
  </ErrorBoundary>
66
71
  );
67
72
  };
68
73
 
69
- export const MutationsInner: FunctionComponent<MutationsInnerProps> = ({ variant, sequenceType, views, pageSize }) => {
74
+ export const MutationsInner: FunctionComponent<MutationsInnerProps> = ({
75
+ lapisFilter,
76
+ sequenceType,
77
+ views,
78
+ pageSize,
79
+ }) => {
70
80
  const lapis = useContext(LapisUrlContext);
71
81
  const { data, error, isLoading } = useQuery(async () => {
72
- return queryMutationsData(variant, sequenceType, lapis);
73
- }, [variant, sequenceType, lapis]);
82
+ return queryMutationsData(lapisFilter, sequenceType, lapis);
83
+ }, [lapisFilter, sequenceType, lapis]);
74
84
 
75
85
  if (isLoading) {
76
86
  return <LoadingDisplay />;
@@ -10,12 +10,12 @@ import { type DisplayedSegment } from '../components/SegmentSelector';
10
10
  import { type DisplayedMutationType } from '../components/mutation-type-selector';
11
11
 
12
12
  export async function queryMutationsData(
13
- variant: LapisFilter,
13
+ lapisFilter: LapisFilter,
14
14
  sequenceType: 'nucleotide' | 'amino acid',
15
15
  lapis: string,
16
16
  ) {
17
- const substitutionsOrDeletions = (await querySubstitutionsOrDeletions(variant, sequenceType, lapis)).content;
18
- const insertions = (await queryInsertions(variant, sequenceType, lapis)).content;
17
+ const substitutionsOrDeletions = (await querySubstitutionsOrDeletions(lapisFilter, sequenceType, lapis)).content;
18
+ const insertions = (await queryInsertions(lapisFilter, sequenceType, lapis)).content;
19
19
 
20
20
  return {
21
21
  substitutionsOrDeletions,
@@ -25,7 +25,7 @@
25
25
  "count": 289
26
26
  },
27
27
  {
28
- "date": "2023-11-24",
28
+ "date": null,
29
29
  "count": 516
30
30
  },
31
31
  {
@@ -25,7 +25,7 @@
25
25
  "count": 27
26
26
  },
27
27
  {
28
- "date": "2023-11-24",
28
+ "date": null,
29
29
  "count": 62
30
30
  },
31
31
  {
@@ -1,17 +1,21 @@
1
1
  import { Chart, type ChartConfiguration, registerables, type TooltipItem } from 'chart.js';
2
2
  import { BarWithErrorBar, BarWithErrorBarsController } from 'chartjs-chart-error-bars';
3
3
 
4
+ import { maxInData } from './prevalence-over-time';
4
5
  import { type PrevalenceOverTimeData, type PrevalenceOverTimeVariantData } from '../../query/queryPrevalenceOverTime';
6
+ import type { Temporal } from '../../utils/temporal';
5
7
  import GsChart from '../components/chart';
6
8
  import { LogitScale } from '../shared/charts/LogitScale';
7
9
  import { singleGraphColorRGBAById } from '../shared/charts/colors';
8
10
  import { type ConfidenceIntervalMethod, wilson95PercentConfidenceInterval } from '../shared/charts/confideceInterval';
11
+ import { getYAxisMax, type YAxisMaxConfig } from '../shared/charts/getYAxisMax';
9
12
  import { getYAxisScale, type ScaleType } from '../shared/charts/getYAxisScale';
10
13
 
11
14
  interface PrevalenceOverTimeBarChartProps {
12
15
  data: PrevalenceOverTimeData;
13
16
  yAxisScaleType: ScaleType;
14
17
  confidenceIntervalMethod: ConfidenceIntervalMethod;
18
+ yAxisMaxConfig: YAxisMaxConfig;
15
19
  }
16
20
 
17
21
  Chart.register(...registerables, LogitScale, BarWithErrorBarsController, BarWithErrorBar);
@@ -20,18 +24,32 @@ const PrevalenceOverTimeBarChart = ({
20
24
  data,
21
25
  yAxisScaleType,
22
26
  confidenceIntervalMethod,
27
+ yAxisMaxConfig,
23
28
  }: PrevalenceOverTimeBarChartProps) => {
29
+ const nullFirstData = data.map((variantData) => {
30
+ return {
31
+ content: variantData.content.sort(sortNullToBeginningThenByDate),
32
+ displayName: variantData.displayName,
33
+ };
34
+ });
35
+
36
+ const datasets = nullFirstData.map((graphData, index) => getDataset(graphData, index, confidenceIntervalMethod));
37
+
38
+ const maxY =
39
+ yAxisScaleType !== 'logit'
40
+ ? getYAxisMax(maxInData(nullFirstData), yAxisMaxConfig?.[yAxisScaleType])
41
+ : undefined;
42
+
24
43
  const config: ChartConfiguration = {
25
44
  type: BarWithErrorBarsController.id,
26
45
  data: {
27
- labels: data[0]?.content.map((dateRange) => dateRange.dateRange?.toString() ?? 'Unknown') || [],
28
- datasets: data.map((graphData, index) => datasets(graphData, index, confidenceIntervalMethod)),
46
+ datasets,
29
47
  },
30
48
  options: {
31
49
  maintainAspectRatio: false,
32
50
  animation: false,
33
51
  scales: {
34
- y: getYAxisScale(yAxisScaleType),
52
+ y: { ...getYAxisScale(yAxisScaleType), max: maxY },
35
53
  },
36
54
  plugins: {
37
55
  legend: {
@@ -45,7 +63,23 @@ const PrevalenceOverTimeBarChart = ({
45
63
  return <GsChart configuration={config} />;
46
64
  };
47
65
 
48
- const datasets = (
66
+ function sortNullToBeginningThenByDate(
67
+ a: { count: number; prevalence: number; total: number; dateRange: Temporal | null },
68
+ b: {
69
+ count: number;
70
+ prevalence: number;
71
+ total: number;
72
+ dateRange: Temporal | null;
73
+ },
74
+ ) {
75
+ return a.dateRange === null
76
+ ? -1
77
+ : b.dateRange === null
78
+ ? 1
79
+ : a.dateRange.toString().localeCompare(b.dateRange.toString());
80
+ }
81
+
82
+ const getDataset = (
49
83
  prevalenceOverTimeVariant: PrevalenceOverTimeVariantData,
50
84
  index: number,
51
85
  confidenceIntervalMethod: ConfidenceIntervalMethod,
@@ -66,12 +100,15 @@ const datasets = (
66
100
  y: dataPoint.prevalence,
67
101
  yMin: wilson95PercentConfidenceInterval(dataPoint.count, dataPoint.total).lowerLimit,
68
102
  yMax: wilson95PercentConfidenceInterval(dataPoint.count, dataPoint.total).upperLimit,
103
+ x: dataPoint.dateRange?.toString() ?? 'Unknown',
69
104
  })),
70
105
  };
71
106
  default:
72
107
  return {
73
108
  ...generalConfig,
74
- data: prevalenceOverTimeVariant.content.map((dataPoint) => dataPoint.prevalence),
109
+ data: prevalenceOverTimeVariant.content.map((dataPoint) => {
110
+ return { y: dataPoint.prevalence, x: dataPoint.dateRange };
111
+ }),
75
112
  };
76
113
  }
77
114
  };
@@ -1,32 +1,51 @@
1
1
  import { Chart, type ChartConfiguration, registerables } from 'chart.js';
2
2
 
3
+ import { maxInData } from './prevalence-over-time';
3
4
  import { type PrevalenceOverTimeData } from '../../query/queryPrevalenceOverTime';
4
5
  import { addUnit, minusTemporal } from '../../utils/temporal';
5
6
  import { getMinMaxNumber } from '../../utils/utils';
6
7
  import GsChart from '../components/chart';
7
8
  import { LogitScale } from '../shared/charts/LogitScale';
8
9
  import { singleGraphColorRGBAById } from '../shared/charts/colors';
10
+ import { getYAxisMax, type YAxisMaxConfig } from '../shared/charts/getYAxisMax';
9
11
  import { getYAxisScale, type ScaleType } from '../shared/charts/getYAxisScale';
10
12
 
11
13
  interface PrevalenceOverTimeBubbleChartProps {
12
14
  data: PrevalenceOverTimeData;
13
15
  yAxisScaleType: ScaleType;
16
+ yAxisMaxConfig: YAxisMaxConfig;
14
17
  }
15
18
 
16
19
  Chart.register(...registerables, LogitScale);
17
20
 
18
- const PrevalenceOverTimeBubbleChart = ({ data, yAxisScaleType }: PrevalenceOverTimeBubbleChartProps) => {
19
- const firstDate = data[0].content[0].dateRange!;
20
- const total = data.map((graphData) => graphData.content.map((dataPoint) => dataPoint.total)).flat();
21
+ const PrevalenceOverTimeBubbleChart = ({
22
+ data,
23
+ yAxisScaleType,
24
+ yAxisMaxConfig,
25
+ }: PrevalenceOverTimeBubbleChartProps) => {
26
+ const nonNullDateRangeData = data.map((variantData) => {
27
+ return {
28
+ content: variantData.content.filter((dataPoint) => dataPoint.dateRange !== null),
29
+ displayName: variantData.displayName,
30
+ };
31
+ });
32
+
33
+ const firstDate = nonNullDateRangeData[0].content[0].dateRange!;
34
+ const total = nonNullDateRangeData.map((graphData) => graphData.content.map((dataPoint) => dataPoint.total)).flat();
21
35
  const [minTotal, maxTotal] = getMinMaxNumber(total)!;
22
36
  const scaleBubble = (value: number) => {
23
37
  return ((value - minTotal) / (maxTotal - minTotal)) * 4.5 + 0.5;
24
38
  };
25
39
 
40
+ const maxY =
41
+ yAxisScaleType !== 'logit'
42
+ ? getYAxisMax(maxInData(nonNullDateRangeData), yAxisMaxConfig?.[yAxisScaleType])
43
+ : undefined;
44
+
26
45
  const config: ChartConfiguration = {
27
46
  type: 'bubble',
28
47
  data: {
29
- datasets: data.map((graphData, index) => ({
48
+ datasets: nonNullDateRangeData.map((graphData, index) => ({
30
49
  label: graphData.displayName,
31
50
  data: graphData.content
32
51
  .filter((dataPoint) => dataPoint.dateRange !== null)
@@ -50,7 +69,7 @@ const PrevalenceOverTimeBubbleChart = ({ data, yAxisScaleType }: PrevalenceOverT
50
69
  callback: (value) => addUnit(firstDate, value as number).toString(),
51
70
  },
52
71
  },
53
- y: getYAxisScale(yAxisScaleType),
72
+ y: { ...getYAxisScale(yAxisScaleType), max: maxY },
54
73
  },
55
74
  plugins: {
56
75
  legend: {
@@ -61,12 +80,12 @@ const PrevalenceOverTimeBubbleChart = ({ data, yAxisScaleType }: PrevalenceOverT
61
80
  intersect: false,
62
81
  callbacks: {
63
82
  title: (context) => {
64
- const dataset = data[context[0].datasetIndex!];
83
+ const dataset = nonNullDateRangeData[context[0].datasetIndex!];
65
84
  const dataPoint = dataset.content[context[0].dataIndex!];
66
85
  return dataPoint.dateRange?.toString();
67
86
  },
68
87
  label: (context) => {
69
- const dataset = data[context.datasetIndex!];
88
+ const dataset = nonNullDateRangeData[context.datasetIndex!];
70
89
  const dataPoint = dataset.content[context.dataIndex!];
71
90
 
72
91
  const percentage = (dataPoint.prevalence * 100).toFixed(2);
@@ -1,6 +1,7 @@
1
1
  import { Chart, type ChartConfiguration, registerables } from 'chart.js';
2
2
  import { type TooltipItem } from 'chart.js/dist/types';
3
3
 
4
+ import { maxInData } from './prevalence-over-time';
4
5
  import { type PrevalenceOverTimeData, type PrevalenceOverTimeVariantData } from '../../query/queryPrevalenceOverTime';
5
6
  import GsChart from '../components/chart';
6
7
  import { LogitScale } from '../shared/charts/LogitScale';
@@ -10,12 +11,14 @@ import {
10
11
  type ConfidenceIntervalMethod,
11
12
  wilson95PercentConfidenceInterval,
12
13
  } from '../shared/charts/confideceInterval';
14
+ import { getYAxisMax, type YAxisMaxConfig } from '../shared/charts/getYAxisMax';
13
15
  import { getYAxisScale, type ScaleType } from '../shared/charts/getYAxisScale';
14
16
 
15
17
  interface PrevalenceOverTimeLineChartProps {
16
18
  data: PrevalenceOverTimeData;
17
19
  yAxisScaleType: ScaleType;
18
20
  confidenceIntervalMethod: ConfidenceIntervalMethod;
21
+ yAxisMaxConfig: YAxisMaxConfig;
19
22
  }
20
23
 
21
24
  Chart.register(...registerables, LogitScale);
@@ -24,21 +27,34 @@ const PrevalenceOverTimeLineChart = ({
24
27
  data,
25
28
  yAxisScaleType,
26
29
  confidenceIntervalMethod,
30
+ yAxisMaxConfig,
27
31
  }: PrevalenceOverTimeLineChartProps) => {
28
- const datasets = data.map((graphData, index) => getDataset(graphData, index, confidenceIntervalMethod)).flat();
29
- const labels = data[0]?.content.map((dateRange) => dateRange.dateRange?.toString() ?? 'Unknown') || [];
32
+ const nonNullDateRangeData = data.map((variantData) => {
33
+ return {
34
+ content: variantData.content.filter((dataPoint) => dataPoint.dateRange !== null),
35
+ displayName: variantData.displayName,
36
+ };
37
+ });
38
+
39
+ const datasets = nonNullDateRangeData
40
+ .map((graphData, index) => getDataset(graphData, index, confidenceIntervalMethod))
41
+ .flat();
42
+
43
+ const maxY =
44
+ yAxisScaleType !== 'logit'
45
+ ? getYAxisMax(maxInData(nonNullDateRangeData), yAxisMaxConfig?.[yAxisScaleType])
46
+ : undefined;
30
47
 
31
48
  const config: ChartConfiguration = {
32
49
  type: 'line',
33
50
  data: {
34
- labels,
35
51
  datasets,
36
52
  },
37
53
  options: {
38
54
  animation: false,
39
55
  maintainAspectRatio: false,
40
56
  scales: {
41
- y: getYAxisScale(yAxisScaleType),
57
+ y: { ...getYAxisScale(yAxisScaleType), max: maxY },
42
58
  },
43
59
  plugins: {
44
60
  legend: {
@@ -71,9 +87,12 @@ const getDataset = (
71
87
 
72
88
  const getDatasetCIUpper = (prevalenceOverTimeVariant: PrevalenceOverTimeVariantData, dataIndex: number) => ({
73
89
  label: `${prevalenceOverTimeVariant.displayName} CI upper`,
74
- data: prevalenceOverTimeVariant.content.map(
75
- (dataPoint) => wilson95PercentConfidenceInterval(dataPoint.count, dataPoint.total).upperLimit,
76
- ),
90
+ data: prevalenceOverTimeVariant.content.map((dataPoint): Datapoint => {
91
+ return {
92
+ y: wilson95PercentConfidenceInterval(dataPoint.count, dataPoint.total).upperLimit,
93
+ x: dataPoint.dateRange?.toString(),
94
+ };
95
+ }),
77
96
  borderWidth: 0,
78
97
  pointRadius: 0,
79
98
  fill: '+1',
@@ -82,9 +101,12 @@ const getDatasetCIUpper = (prevalenceOverTimeVariant: PrevalenceOverTimeVariantD
82
101
 
83
102
  const getDatasetCILower = (prevalenceOverTimeVariant: PrevalenceOverTimeVariantData, dataIndex: number) => ({
84
103
  label: `${prevalenceOverTimeVariant.displayName} CI lower`,
85
- data: prevalenceOverTimeVariant.content.map(
86
- (dataPoint) => wilson95PercentConfidenceInterval(dataPoint.count, dataPoint.total).lowerLimit,
87
- ),
104
+ data: prevalenceOverTimeVariant.content.map((dataPoint): Datapoint => {
105
+ return {
106
+ y: wilson95PercentConfidenceInterval(dataPoint.count, dataPoint.total).lowerLimit,
107
+ x: dataPoint.dateRange?.toString(),
108
+ };
109
+ }),
88
110
  borderWidth: 0,
89
111
  pointRadius: 0,
90
112
  fill: '-1',
@@ -93,13 +115,29 @@ const getDatasetCILower = (prevalenceOverTimeVariant: PrevalenceOverTimeVariantD
93
115
 
94
116
  const getDatasetLine = (prevalenceOverTimeVariant: PrevalenceOverTimeVariantData, dataIndex: number) => ({
95
117
  label: prevalenceOverTimeVariant.displayName,
96
- data: prevalenceOverTimeVariant.content.map((dataPoint) => dataPoint.prevalence),
118
+ data: prevalenceOverTimeVariant.content.map((dataPoint): Datapoint => {
119
+ const ciLimits = wilson95PercentConfidenceInterval(dataPoint.count, dataPoint.total);
120
+
121
+ return {
122
+ y: dataPoint.prevalence,
123
+ x: dataPoint.dateRange?.toString(),
124
+ yCiUpper: ciLimits.upperLimit,
125
+ yCiLower: ciLimits.lowerLimit,
126
+ };
127
+ }),
97
128
  borderWidth: 1,
98
129
  pointRadius: 0,
99
130
  borderColor: singleGraphColorRGBAById(dataIndex),
100
131
  backgroundColor: singleGraphColorRGBAById(dataIndex),
101
132
  });
102
133
 
134
+ interface Datapoint {
135
+ y: number;
136
+ yCiLower?: number;
137
+ yCiUpper?: number;
138
+ x?: string;
139
+ }
140
+
103
141
  const tooltip = (confidenceIntervalMethod?: ConfidenceIntervalMethod) => {
104
142
  const generalConfig = {
105
143
  mode: 'index' as const,
@@ -111,26 +149,18 @@ const tooltip = (confidenceIntervalMethod?: ConfidenceIntervalMethod) => {
111
149
  return {
112
150
  ...generalConfig,
113
151
  filter: ({ datasetIndex }: TooltipItem<'line'>) => {
114
- return datasetIndex % 3 === 1;
152
+ return isNotCiIndex(datasetIndex);
115
153
  },
116
154
  callbacks: {
117
155
  label: (context: TooltipItem<'line'>) => {
118
- if (context.datasetIndex % 3 === 1) {
119
- const value = context.dataset.data[context.dataIndex];
120
- const ciLower = context.dataset.data[context.dataIndex - 1];
121
- const ciUpper = context.dataset.data[context.dataIndex + 1];
122
-
123
- if (
124
- typeof value !== 'number' ||
125
- typeof ciLower !== 'number' ||
126
- typeof ciUpper !== 'number'
127
- ) {
128
- return '';
129
- }
130
-
131
- return confidenceIntervalDataLabel(value, ciLower, ciUpper, context.dataset.label);
132
- }
133
- return context.dataset.label;
156
+ const dataPoint = context.dataset.data[context.dataIndex] as unknown as Datapoint;
157
+
158
+ return confidenceIntervalDataLabel(
159
+ dataPoint.y,
160
+ dataPoint.yCiLower,
161
+ dataPoint.yCiUpper,
162
+ context.dataset.label,
163
+ );
134
164
  },
135
165
  },
136
166
  };
@@ -139,4 +169,8 @@ const tooltip = (confidenceIntervalMethod?: ConfidenceIntervalMethod) => {
139
169
  }
140
170
  };
141
171
 
172
+ function isNotCiIndex(datasetIndex: number) {
173
+ return datasetIndex % 3 === 1;
174
+ }
175
+
142
176
  export default PrevalenceOverTimeLineChart;
@@ -1,8 +1,8 @@
1
- import denominator from './__mockData__/denominator.json';
2
- import denominatorOneVariant from './__mockData__/denominatorOneVariant.json';
3
- import numeratorEG from './__mockData__/numeratorEG.json';
4
- import numeratorJN1 from './__mockData__/numeratorJN1.json';
5
- import numeratorOneVariant from './__mockData__/numeratorOneVariant.json';
1
+ import denominatorFilter from './__mockData__/denominatorFilter.json';
2
+ import denominatorOneDataset from './__mockData__/denominatorFilterOneDataset.json';
3
+ import numeratorFilterEG from './__mockData__/numeratorFilterEG.json';
4
+ import numeratorFilterJN1 from './__mockData__/numeratorFilterJN1.json';
5
+ import numeratorOneDataset from './__mockData__/numeratorFilterOneDataset.json';
6
6
  import { PrevalenceOverTime, type PrevalenceOverTimeProps } from './prevalence-over-time';
7
7
  import { AGGREGATED_ENDPOINT, LAPIS_URL } from '../../constants';
8
8
  import { LapisUrlContext } from '../LapisUrlContext';
@@ -33,6 +33,7 @@ export default {
33
33
  height: { control: 'text' },
34
34
  headline: { control: 'text' },
35
35
  pageSize: { control: 'object' },
36
+ yAxisMaxConfig: { control: 'object' },
36
37
  },
37
38
  };
38
39
 
@@ -40,8 +41,8 @@ const Template = {
40
41
  render: (args: PrevalenceOverTimeProps) => (
41
42
  <LapisUrlContext.Provider value={LAPIS_URL}>
42
43
  <PrevalenceOverTime
43
- numerator={args.numerator}
44
- denominator={args.denominator}
44
+ numeratorFilter={args.numeratorFilter}
45
+ denominatorFilter={args.denominatorFilter}
45
46
  granularity={args.granularity}
46
47
  smoothingWindow={args.smoothingWindow}
47
48
  views={args.views}
@@ -51,6 +52,7 @@ const Template = {
51
52
  headline={args.headline}
52
53
  lapisDateField={args.lapisDateField}
53
54
  pageSize={args.pageSize}
55
+ yAxisMaxConfig={args.yAxisMaxConfig}
54
56
  />
55
57
  </LapisUrlContext.Provider>
56
58
  ),
@@ -59,11 +61,11 @@ const Template = {
59
61
  export const TwoVariants = {
60
62
  ...Template,
61
63
  args: {
62
- numerator: [
64
+ numeratorFilter: [
63
65
  { displayName: 'EG', lapisFilter: { country: 'USA', pangoLineage: 'EG*', dateFrom: '2023-01-01' } },
64
66
  { displayName: 'JN.1', lapisFilter: { country: 'USA', pangoLineage: 'JN.1*', dateFrom: '2023-01-01' } },
65
67
  ],
66
- denominator: { country: 'USA', dateFrom: '2023-01-01' },
68
+ denominatorFilter: { country: 'USA', dateFrom: '2023-01-01' },
67
69
  granularity: 'month',
68
70
  smoothingWindow: 0,
69
71
  views: ['bar', 'line', 'bubble', 'table'],
@@ -73,6 +75,10 @@ export const TwoVariants = {
73
75
  headline: 'Prevalence over time',
74
76
  lapisDateField: 'date',
75
77
  pageSize: 10,
78
+ yAxisMaxConfig: {
79
+ linear: 1,
80
+ logarithmic: 1,
81
+ },
76
82
  },
77
83
  parameters: {
78
84
  fetchMock: {
@@ -90,7 +96,7 @@ export const TwoVariants = {
90
96
  },
91
97
  response: {
92
98
  status: 200,
93
- body: numeratorEG,
99
+ body: numeratorFilterEG,
94
100
  },
95
101
  },
96
102
  {
@@ -106,7 +112,7 @@ export const TwoVariants = {
106
112
  },
107
113
  response: {
108
114
  status: 200,
109
- body: numeratorJN1,
115
+ body: numeratorFilterJN1,
110
116
  },
111
117
  },
112
118
  {
@@ -121,7 +127,7 @@ export const TwoVariants = {
121
127
  },
122
128
  response: {
123
129
  status: 200,
124
- body: denominator,
130
+ body: denominatorFilter,
125
131
  },
126
132
  },
127
133
  ],
@@ -132,11 +138,11 @@ export const TwoVariants = {
132
138
  export const OneVariant = {
133
139
  ...Template,
134
140
  args: {
135
- numerator: {
141
+ numeratorFilter: {
136
142
  displayName: 'EG',
137
143
  lapisFilter: { country: 'USA', pangoLineage: 'BA.2.86*', dateFrom: '2023-10-01' },
138
144
  },
139
- denominator: { country: 'USA', dateFrom: '2023-10-01' },
145
+ denominatorFilter: { country: 'USA', dateFrom: '2023-10-01' },
140
146
  granularity: 'day',
141
147
  smoothingWindow: 7,
142
148
  views: ['bar', 'line', 'bubble', 'table'],
@@ -146,6 +152,10 @@ export const OneVariant = {
146
152
  headline: 'Prevalence over time',
147
153
  lapisDateField: 'date',
148
154
  pageSize: 10,
155
+ yAxisMaxConfig: {
156
+ linear: 1,
157
+ logarithmic: 1,
158
+ },
149
159
  },
150
160
  parameters: {
151
161
  fetchMock: {
@@ -163,7 +173,7 @@ export const OneVariant = {
163
173
  },
164
174
  response: {
165
175
  status: 200,
166
- body: numeratorOneVariant,
176
+ body: numeratorOneDataset,
167
177
  },
168
178
  },
169
179
  {
@@ -178,7 +188,7 @@ export const OneVariant = {
178
188
  },
179
189
  response: {
180
190
  status: 200,
181
- body: denominatorOneVariant,
191
+ body: denominatorOneDataset,
182
192
  },
183
193
  },
184
194
  ],