@genspectrum/dashboard-components 0.6.9 → 0.6.11

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 (27) hide show
  1. package/custom-elements.json +3 -3
  2. package/dist/dashboard-components.js +366 -75
  3. package/dist/dashboard-components.js.map +1 -1
  4. package/dist/genspectrum-components.d.ts +2 -2
  5. package/dist/style.css +65 -1
  6. package/package.json +4 -1
  7. package/src/preact/aggregatedData/aggregate.tsx +14 -4
  8. package/src/preact/components/chart.tsx +14 -1
  9. package/src/preact/components/fullscreen.tsx +57 -0
  10. package/src/preact/components/info.tsx +88 -1
  11. package/src/preact/components/resize-container.tsx +5 -1
  12. package/src/preact/mutationComparison/mutation-comparison.tsx +2 -0
  13. package/src/preact/mutations/mutations.tsx +34 -2
  14. package/src/preact/mutationsOverTime/__mockData__/aminoAcidMutations_byDayOverall.json +4726 -0
  15. package/src/preact/mutationsOverTime/__mockData__/nucleotideMutations_byMonthOverall.json +11143 -0
  16. package/src/preact/mutationsOverTime/__mockData__/nucleotideMutations_byWeekOverall.json +9154 -0
  17. package/src/preact/mutationsOverTime/getFilteredMutationsOverTime.spec.ts +66 -22
  18. package/src/preact/mutationsOverTime/getFilteredMutationsOverTimeData.ts +16 -7
  19. package/src/preact/mutationsOverTime/mutations-over-time.stories.tsx +17 -0
  20. package/src/preact/mutationsOverTime/mutations-over-time.tsx +28 -4
  21. package/src/preact/numberSequencesOverTime/number-sequences-over-time.tsx +42 -6
  22. package/src/preact/prevalenceOverTime/prevalence-over-time.stories.tsx +2 -1
  23. package/src/preact/prevalenceOverTime/prevalence-over-time.tsx +46 -53
  24. package/src/preact/relativeGrowthAdvantage/relative-growth-advantage.tsx +2 -0
  25. package/src/query/queryMutationsOverTime.ts +10 -1
  26. package/src/web-components/visualization/gs-mutations-over-time.stories.ts +51 -1
  27. package/src/web-components/visualization/gs-prevalence-over-time.tsx +2 -4
@@ -61,38 +61,82 @@ describe('getFilteredMutationOverTimeData', () => {
61
61
  });
62
62
 
63
63
  describe('filterProportion', () => {
64
- it('should filter by proportion', () => {
65
- const data = new Map2d<Substitution | Deletion, Temporal, MutationOverTimeMutationValue>();
64
+ const belowFilter = 0.1;
65
+ const atFilterMin = 0.2;
66
+ const inFilter = 0.5;
67
+ const atFilterMax = 0.9;
68
+ const aboveFilter = 0.99;
69
+ const proportionInterval = { min: atFilterMin, max: atFilterMax };
66
70
 
67
- const belowFilter = { count: 1, proportion: 0.1 };
68
- const aboveFilter = { count: 99, proportion: 0.99 };
69
- const proportionInterval = { min: 0.2, max: 0.9 };
71
+ const someSubstitution = new Substitution('someSegment', 'A', 'T', 123);
72
+ const someOtherMutation = new Substitution('someOtherSegment', 'A', 'G', 9);
70
73
 
71
- const someSubstitution = new Substitution('someSegment', 'A', 'T', 123);
72
- data.set(someSubstitution, yearMonthDay('2021-01-01'), belowFilter);
73
- data.set(someSubstitution, yearMonthDay('2021-02-02'), aboveFilter);
74
+ it('should remove mutations where overall proportion is below filter', () => {
75
+ const data = getMutationOverTimeData();
74
76
 
75
- filterProportion(data, proportionInterval);
77
+ filterProportion(data, getOverallMutationData(belowFilter), proportionInterval);
76
78
 
77
- expect(data.getAsArray({ count: 0, proportion: 0 }).length).to.equal(0);
79
+ expect(data.getAsArray({ count: 0, proportion: 0 })).to.toHaveLength(0);
78
80
  });
79
81
 
80
- it('should not filter if one proportion is within the interval', () => {
81
- const data = new Map2d<Substitution | Deletion, Temporal, MutationOverTimeMutationValue>();
82
+ it('should remove mutations where overall proportion is above filter', () => {
83
+ const data = getMutationOverTimeData();
84
+
85
+ filterProportion(data, getOverallMutationData(aboveFilter), proportionInterval);
86
+
87
+ expect(data.getAsArray({ count: 0, proportion: 0 })).to.toHaveLength(0);
88
+ });
82
89
 
83
- const belowFilter = { count: 1, proportion: 0.1 };
84
- const aboveFilter = { count: 99, proportion: 0.99 };
85
- const inFilter = { count: 5, proportion: 0.5 };
86
- const proportionInterval = { min: 0.2, max: 0.9 };
90
+ it('should remove mutations where overall proportion is missing', () => {
91
+ const data = getMutationOverTimeData();
87
92
 
88
- const someSubstitution = new Substitution('someSegment', 'A', 'T', 123);
89
- data.set(someSubstitution, yearMonthDay('2021-01-01'), belowFilter);
90
- data.set(someSubstitution, yearMonthDay('2021-02-02'), aboveFilter);
91
- data.set(someSubstitution, yearMonthDay('2021-03-03'), inFilter);
93
+ filterProportion(data, getOverallMutationData(aboveFilter, someOtherMutation), proportionInterval);
92
94
 
93
- filterProportion(data, proportionInterval);
95
+ expect(data.getAsArray({ count: 0, proportion: 0 })).to.toHaveLength(0);
96
+ });
97
+
98
+ it('should not remove mutation where overall proportion is at lower border of filter', () => {
99
+ const data = getMutationOverTimeData();
100
+
101
+ filterProportion(data, getOverallMutationData(inFilter), proportionInterval);
94
102
 
95
- expect(data.getRow(someSubstitution, { count: 0, proportion: 0 }).length).to.equal(3);
103
+ expect(data.getRow(someSubstitution, { count: 0, proportion: 0 })).to.toHaveLength(2);
96
104
  });
105
+
106
+ it('should not remove mutation where overall proportion is within filter', () => {
107
+ const data = getMutationOverTimeData();
108
+
109
+ filterProportion(data, getOverallMutationData(inFilter), proportionInterval);
110
+
111
+ expect(data.getRow(someSubstitution, { count: 0, proportion: 0 })).to.toHaveLength(2);
112
+ });
113
+
114
+ it('should not remove mutation where overall proportion is at upper border of filter', () => {
115
+ const data = getMutationOverTimeData();
116
+
117
+ filterProportion(data, getOverallMutationData(inFilter), proportionInterval);
118
+
119
+ expect(data.getRow(someSubstitution, { count: 0, proportion: 0 })).to.toHaveLength(2);
120
+ });
121
+
122
+ function getMutationOverTimeData() {
123
+ const data = new Map2d<Substitution | Deletion, Temporal, MutationOverTimeMutationValue>();
124
+ data.set(someSubstitution, yearMonthDay('2021-01-01'), { count: 1, proportion: 0.1 });
125
+ data.set(someSubstitution, yearMonthDay('2021-02-02'), { count: 99, proportion: 0.99 });
126
+ return data;
127
+ }
128
+
129
+ function getOverallMutationData(proportion: number = 0.1, mutation: Substitution = someSubstitution) {
130
+ return {
131
+ content: [
132
+ {
133
+ type: 'substitution' as const,
134
+ count: -1,
135
+ mutation,
136
+ proportion,
137
+ },
138
+ ],
139
+ };
140
+ }
97
141
  });
98
142
  });
@@ -1,9 +1,12 @@
1
+ import { type Dataset } from '../../operator/Dataset';
1
2
  import { type MutationOverTimeDataGroupedByMutation } from '../../query/queryMutationsOverTime';
3
+ import { type DeletionEntry, type SubstitutionEntry } from '../../types';
2
4
  import type { DisplayedSegment } from '../components/SegmentSelector';
3
5
  import type { DisplayedMutationType } from '../components/mutation-type-selector';
4
6
 
5
7
  export function getFilteredMutationOverTimeData(
6
8
  data: MutationOverTimeDataGroupedByMutation,
9
+ overallMutationData: Dataset<SubstitutionEntry | DeletionEntry>,
7
10
  displayedSegments: DisplayedSegment[],
8
11
  displayedMutationTypes: DisplayedMutationType[],
9
12
  proportionInterval: { min: number; max: number },
@@ -11,7 +14,7 @@ export function getFilteredMutationOverTimeData(
11
14
  const filteredData = data.copy();
12
15
  filterDisplayedSegments(displayedSegments, filteredData);
13
16
  filterMutationTypes(displayedMutationTypes, filteredData);
14
- filterProportion(filteredData, proportionInterval);
17
+ filterProportion(filteredData, overallMutationData, proportionInterval);
15
18
 
16
19
  return filteredData;
17
20
  }
@@ -48,18 +51,24 @@ export function filterMutationTypes(
48
51
 
49
52
  export function filterProportion(
50
53
  data: MutationOverTimeDataGroupedByMutation,
54
+ overallMutationData: Dataset<SubstitutionEntry | DeletionEntry>,
51
55
  proportionInterval: {
52
56
  min: number;
53
57
  max: number;
54
58
  },
55
59
  ) {
60
+ const overallProportionsByMutation = overallMutationData.content.reduce(
61
+ (acc, { mutation, proportion }) => ({
62
+ ...acc,
63
+ [mutation.toString()]: proportion,
64
+ }),
65
+ {} as Record<string, number>,
66
+ );
67
+
56
68
  data.getFirstAxisKeys().forEach((mutation) => {
57
- const row = data.getRow(mutation, { count: 0, proportion: 0 });
58
- if (
59
- !row.some(
60
- (value) => value.proportion >= proportionInterval.min && value.proportion <= proportionInterval.max,
61
- )
62
- ) {
69
+ const overallProportion = overallProportionsByMutation[mutation.toString()] || -1;
70
+
71
+ if (overallProportion < proportionInterval.min || overallProportion > proportionInterval.max) {
63
72
  data.deleteRow(mutation);
64
73
  }
65
74
  });
@@ -14,6 +14,7 @@ import nucleotideMutation_tooManyMutations from './__mockData__/nucleotideMutati
14
14
  import { MutationsOverTime, type MutationsOverTimeProps } from './mutations-over-time';
15
15
  import { AGGREGATED_ENDPOINT, LAPIS_URL, NUCLEOTIDE_MUTATIONS_ENDPOINT } from '../../constants';
16
16
  import referenceGenome from '../../lapisApi/__mockData__/referenceGenome.json';
17
+ import nucleotideMutations_byMonthOverall from '../../preact/mutationsOverTime/__mockData__/nucleotideMutations_byMonthOverall.json';
17
18
  import { LapisUrlContext } from '../LapisUrlContext';
18
19
  import { ReferenceGenomeContext } from '../ReferenceGenomeContext';
19
20
 
@@ -90,6 +91,22 @@ export const Default: StoryObj<MutationsOverTimeProps> = {
90
91
  body: aggregated_date,
91
92
  },
92
93
  },
94
+ {
95
+ matcher: {
96
+ name: 'nucleotideMutations_overall',
97
+ url: NUCLEOTIDE_MUTATIONS_ENDPOINT,
98
+ body: {
99
+ pangoLineage: 'JN.1*',
100
+ dateFrom: '2024-01-15',
101
+ dateTo: '2024-07-10',
102
+ minProportion: 0.001,
103
+ },
104
+ },
105
+ response: {
106
+ status: 200,
107
+ body: nucleotideMutations_byMonthOverall,
108
+ },
109
+ },
93
110
  {
94
111
  matcher: {
95
112
  name: 'nucleotideMutations_01',
@@ -3,11 +3,19 @@ import { type Dispatch, type StateUpdater, useContext, useMemo, useState } from
3
3
 
4
4
  import { getFilteredMutationOverTimeData } from './getFilteredMutationsOverTimeData';
5
5
  import MutationsOverTimeGrid from './mutations-over-time-grid';
6
+ import { type Dataset } from '../../operator/Dataset';
6
7
  import {
7
8
  type MutationOverTimeDataGroupedByMutation,
8
9
  queryMutationsOverTimeData,
10
+ queryOverallMutationData,
9
11
  } from '../../query/queryMutationsOverTime';
10
- import { type LapisFilter, type SequenceType, type TemporalGranularity } from '../../types';
12
+ import {
13
+ type DeletionEntry,
14
+ type LapisFilter,
15
+ type SequenceType,
16
+ type SubstitutionEntry,
17
+ type TemporalGranularity,
18
+ } from '../../types';
11
19
  import { compareTemporal } from '../../utils/temporal';
12
20
  import { LapisUrlContext } from '../LapisUrlContext';
13
21
  import { type DisplayedSegment, SegmentSelector, useDisplayedSegments } from '../components/SegmentSelector';
@@ -16,6 +24,7 @@ import { ColorScaleSelectorDropdown } from '../components/color-scale-selector-d
16
24
  import { CsvDownloadButton } from '../components/csv-download-button';
17
25
  import { ErrorBoundary } from '../components/error-boundary';
18
26
  import { ErrorDisplay } from '../components/error-display';
27
+ import { Fullscreen } from '../components/fullscreen';
19
28
  import Info from '../components/info';
20
29
  import { LoadingDisplay } from '../components/loading-display';
21
30
  import { type DisplayedMutationType, MutationTypeSelector } from '../components/mutation-type-selector';
@@ -63,7 +72,11 @@ export const MutationsOverTimeInner: FunctionComponent<MutationsOverTimeInnerPro
63
72
  }) => {
64
73
  const lapis = useContext(LapisUrlContext);
65
74
  const { data, error, isLoading } = useQuery(async () => {
66
- return queryMutationsOverTimeData(lapisFilter, sequenceType, lapis, lapisDateField, granularity);
75
+ const [mutationOverTimeData, overallMutationData] = await Promise.all([
76
+ queryMutationsOverTimeData(lapisFilter, sequenceType, lapis, lapisDateField, granularity),
77
+ queryOverallMutationData(lapisFilter, sequenceType, lapis),
78
+ ]);
79
+ return { mutationOverTimeData, overallMutationData };
67
80
  }, [lapisFilter, sequenceType, lapis, granularity, lapisDateField]);
68
81
 
69
82
  if (isLoading) {
@@ -78,19 +91,28 @@ export const MutationsOverTimeInner: FunctionComponent<MutationsOverTimeInnerPro
78
91
  return <NoDataDisplay />;
79
92
  }
80
93
 
81
- return <MutationsOverTimeTabs mutationOverTimeData={data} sequenceType={sequenceType} views={views} />;
94
+ return (
95
+ <MutationsOverTimeTabs
96
+ overallMutationData={data.overallMutationData}
97
+ mutationOverTimeData={data.mutationOverTimeData}
98
+ sequenceType={sequenceType}
99
+ views={views}
100
+ />
101
+ );
82
102
  };
83
103
 
84
104
  type MutationOverTimeTabsProps = {
85
105
  mutationOverTimeData: MutationOverTimeDataGroupedByMutation;
86
106
  sequenceType: SequenceType;
87
107
  views: View[];
108
+ overallMutationData: Dataset<SubstitutionEntry | DeletionEntry>;
88
109
  };
89
110
 
90
111
  const MutationsOverTimeTabs: FunctionComponent<MutationOverTimeTabsProps> = ({
91
112
  mutationOverTimeData,
92
113
  sequenceType,
93
114
  views,
115
+ overallMutationData,
94
116
  }) => {
95
117
  const [proportionInterval, setProportionInterval] = useState({ min: 0.05, max: 0.9 });
96
118
  const [colorScale, setColorScale] = useState<ColorScale>({ min: 0, max: 1, color: 'indigo' });
@@ -105,11 +127,12 @@ const MutationsOverTimeTabs: FunctionComponent<MutationOverTimeTabsProps> = ({
105
127
  () =>
106
128
  getFilteredMutationOverTimeData(
107
129
  mutationOverTimeData,
130
+ overallMutationData,
108
131
  displayedSegments,
109
132
  displayedMutationTypes,
110
133
  proportionInterval,
111
134
  ),
112
- [mutationOverTimeData, displayedSegments, displayedMutationTypes, proportionInterval],
135
+ [mutationOverTimeData, overallMutationData, displayedSegments, displayedMutationTypes, proportionInterval],
113
136
  );
114
137
 
115
138
  const getTab = (view: View) => {
@@ -188,6 +211,7 @@ const Toolbar: FunctionComponent<ToolbarProps> = ({
188
211
  filename='mutations_over_time.csv'
189
212
  />
190
213
  <Info>Info for mutations over time</Info>
214
+ <Fullscreen />
191
215
  </>
192
216
  );
193
217
  };
@@ -1,3 +1,4 @@
1
+ import { type FunctionComponent } from 'preact';
1
2
  import { useContext, useState } from 'preact/hooks';
2
3
 
3
4
  import { getNumberOfSequencesOverTimeTableData } from './getNumberOfSequencesOverTimeTableData';
@@ -13,6 +14,7 @@ import { LapisUrlContext } from '../LapisUrlContext';
13
14
  import { CsvDownloadButton } from '../components/csv-download-button';
14
15
  import { ErrorBoundary } from '../components/error-boundary';
15
16
  import { ErrorDisplay } from '../components/error-display';
17
+ import { Fullscreen } from '../components/fullscreen';
16
18
  import Info, { InfoHeadline1, InfoParagraph } from '../components/info';
17
19
  import { LoadingDisplay } from '../components/loading-display';
18
20
  import { NoDataDisplay } from '../components/no-data-display';
@@ -76,17 +78,32 @@ const NumberSequencesOverTimeInner = ({
76
78
  return <NoDataDisplay />;
77
79
  }
78
80
 
79
- return <NumberSequencesOverTimeTabs views={views} data={data} granularity={granularity} pageSize={pageSize} />;
81
+ return (
82
+ <NumberSequencesOverTimeTabs
83
+ views={views}
84
+ data={data}
85
+ granularity={granularity}
86
+ smoothingWindow={smoothingWindow}
87
+ pageSize={pageSize}
88
+ />
89
+ );
80
90
  };
81
91
 
82
92
  interface NumberSequencesOverTimeTabsProps {
83
93
  views: NumberSequencesOverTimeView[];
84
94
  data: NumberOfSequencesDatasets;
85
95
  granularity: TemporalGranularity;
96
+ smoothingWindow: number;
86
97
  pageSize: boolean | number;
87
98
  }
88
99
 
89
- const NumberSequencesOverTimeTabs = ({ views, data, granularity, pageSize }: NumberSequencesOverTimeTabsProps) => {
100
+ const NumberSequencesOverTimeTabs = ({
101
+ views,
102
+ data,
103
+ granularity,
104
+ smoothingWindow,
105
+ pageSize,
106
+ }: NumberSequencesOverTimeTabsProps) => {
90
107
  const [yAxisScaleType, setYAxisScaleType] = useState<ScaleType>('linear');
91
108
 
92
109
  const getTab = (view: NumberSequencesOverTimeView) => {
@@ -119,6 +136,7 @@ const NumberSequencesOverTimeTabs = ({ views, data, granularity, pageSize }: Num
119
136
  activeTab={activeTab}
120
137
  data={data}
121
138
  granularity={granularity}
139
+ smoothingWindow={smoothingWindow}
122
140
  yAxisScaleType={yAxisScaleType}
123
141
  setYAxisScaleType={setYAxisScaleType}
124
142
  />
@@ -133,9 +151,17 @@ interface ToolbarProps {
133
151
  granularity: TemporalGranularity;
134
152
  yAxisScaleType: ScaleType;
135
153
  setYAxisScaleType: (scaleType: ScaleType) => void;
154
+ smoothingWindow: number;
136
155
  }
137
156
 
138
- const Toolbar = ({ activeTab, data, granularity, yAxisScaleType, setYAxisScaleType }: ToolbarProps) => {
157
+ const Toolbar = ({
158
+ activeTab,
159
+ data,
160
+ granularity,
161
+ yAxisScaleType,
162
+ setYAxisScaleType,
163
+ smoothingWindow,
164
+ }: ToolbarProps) => {
139
165
  return (
140
166
  <>
141
167
  {activeTab !== 'Table' && (
@@ -150,16 +176,26 @@ const Toolbar = ({ activeTab, data, granularity, yAxisScaleType, setYAxisScaleTy
150
176
  getData={() => getNumberOfSequencesOverTimeTableData(data, granularity)}
151
177
  filename='number_of_sequences_over_time.csv'
152
178
  />
153
- <NumberSequencesOverTimeInfo />
179
+ <NumberSequencesOverTimeInfo granularity={granularity} smoothingWindow={smoothingWindow} />
180
+ <Fullscreen />
154
181
  </>
155
182
  );
156
183
  };
157
184
 
158
- const NumberSequencesOverTimeInfo = () => (
185
+ type NumberSequencesOverTimeInfoProps = {
186
+ granularity: TemporalGranularity;
187
+ smoothingWindow: number;
188
+ };
189
+
190
+ const NumberSequencesOverTimeInfo: FunctionComponent<NumberSequencesOverTimeInfoProps> = ({
191
+ granularity,
192
+ smoothingWindow,
193
+ }) => (
159
194
  <Info>
160
195
  <InfoHeadline1>Number of sequences over time</InfoHeadline1>
161
196
  <InfoParagraph>
162
- <a href='https://github.com/GenSpectrum/dashboard-components/issues/315'>TODO</a>
197
+ This presents the number of available sequences of a variant per <b>{granularity}</b>
198
+ {smoothingWindow > 0 && `, smoothed using a ${smoothingWindow}-${granularity} sliding window`}.
163
199
  </InfoParagraph>
164
200
  </Info>
165
201
  );
@@ -51,7 +51,8 @@ const Template = {
51
51
  height={args.height}
52
52
  lapisDateField={args.lapisDateField}
53
53
  pageSize={args.pageSize}
54
- yAxisMaxConfig={args.yAxisMaxConfig}
54
+ yAxisMaxLinear={args.yAxisMaxLinear}
55
+ yAxisMaxLogarithmic={args.yAxisMaxLogarithmic}
55
56
  />
56
57
  </LapisUrlContext.Provider>
57
58
  ),
@@ -13,25 +13,23 @@ import { ConfidenceIntervalSelector } from '../components/confidence-interval-se
13
13
  import { CsvDownloadButton } from '../components/csv-download-button';
14
14
  import { ErrorBoundary } from '../components/error-boundary';
15
15
  import { ErrorDisplay } from '../components/error-display';
16
- import Info, { InfoHeadline1, InfoParagraph } from '../components/info';
16
+ import { Fullscreen } from '../components/fullscreen';
17
+ import Info, { InfoComponentCode, InfoHeadline1, InfoHeadline2, InfoParagraph } from '../components/info';
17
18
  import { LoadingDisplay } from '../components/loading-display';
18
19
  import { NoDataDisplay } from '../components/no-data-display';
19
20
  import { ResizeContainer } from '../components/resize-container';
20
21
  import { ScalingSelector } from '../components/scaling-selector';
21
22
  import Tabs from '../components/tabs';
22
23
  import { type ConfidenceIntervalMethod } from '../shared/charts/confideceInterval';
23
- import type { YAxisMaxConfig } from '../shared/charts/getYAxisMax';
24
+ import { type AxisMax } from '../shared/charts/getYAxisMax';
24
25
  import { type ScaleType } from '../shared/charts/getYAxisScale';
25
26
  import { useQuery } from '../useQuery';
26
27
 
27
28
  export type View = 'bar' | 'line' | 'bubble' | 'table';
28
29
 
29
- export interface PrevalenceOverTimeProps extends PrevalenceOverTimeInnerProps {
30
+ export interface PrevalenceOverTimeProps {
30
31
  width: string;
31
32
  height: string;
32
- }
33
-
34
- export interface PrevalenceOverTimeInnerProps {
35
33
  numeratorFilter: NamedLapisFilter | NamedLapisFilter[];
36
34
  denominatorFilter: LapisFilter;
37
35
  granularity: TemporalGranularity;
@@ -40,32 +38,25 @@ export interface PrevalenceOverTimeInnerProps {
40
38
  confidenceIntervalMethods: ConfidenceIntervalMethod[];
41
39
  lapisDateField: string;
42
40
  pageSize: boolean | number;
43
- yAxisMaxConfig: YAxisMaxConfig;
41
+ yAxisMaxLinear?: AxisMax;
42
+ yAxisMaxLogarithmic?: AxisMax;
44
43
  }
45
44
 
46
- export const PrevalenceOverTime: FunctionComponent<PrevalenceOverTimeProps> = ({ width, height, ...innerProps }) => {
45
+ export const PrevalenceOverTime: FunctionComponent<PrevalenceOverTimeProps> = (componentProps) => {
46
+ const { width, height } = componentProps;
47
47
  const size = { height, width };
48
48
 
49
49
  return (
50
50
  <ErrorBoundary size={size}>
51
51
  <ResizeContainer size={size}>
52
- <PrevalenceOverTimeInner {...innerProps} />
52
+ <PrevalenceOverTimeInner {...componentProps} />
53
53
  </ResizeContainer>
54
54
  </ErrorBoundary>
55
55
  );
56
56
  };
57
57
 
58
- export const PrevalenceOverTimeInner: FunctionComponent<PrevalenceOverTimeInnerProps> = ({
59
- numeratorFilter,
60
- denominatorFilter,
61
- granularity,
62
- smoothingWindow,
63
- views,
64
- confidenceIntervalMethods,
65
- lapisDateField,
66
- pageSize,
67
- yAxisMaxConfig,
68
- }) => {
58
+ export const PrevalenceOverTimeInner: FunctionComponent<PrevalenceOverTimeProps> = (componentProps) => {
59
+ const { numeratorFilter, denominatorFilter, granularity, smoothingWindow, lapisDateField } = componentProps;
69
60
  const lapis = useContext(LapisUrlContext);
70
61
 
71
62
  const { data, error, isLoading } = useQuery(
@@ -93,39 +84,21 @@ export const PrevalenceOverTimeInner: FunctionComponent<PrevalenceOverTimeInnerP
93
84
  return <NoDataDisplay />;
94
85
  }
95
86
 
96
- return (
97
- <PrevalenceOverTimeTabs
98
- views={views}
99
- data={data}
100
- granularity={granularity}
101
- confidenceIntervalMethods={confidenceIntervalMethods}
102
- pageSize={pageSize}
103
- yAxisMaxConfig={yAxisMaxConfig}
104
- />
105
- );
87
+ return <PrevalenceOverTimeTabs data={data} {...componentProps} />;
106
88
  };
107
89
 
108
- type PrevalenceOverTimeTabsProps = {
109
- views: View[];
90
+ type PrevalenceOverTimeTabsProps = PrevalenceOverTimeProps & {
110
91
  data: PrevalenceOverTimeData;
111
- granularity: TemporalGranularity;
112
- confidenceIntervalMethods: ConfidenceIntervalMethod[];
113
- pageSize: boolean | number;
114
- yAxisMaxConfig: YAxisMaxConfig;
115
92
  };
116
93
 
117
- const PrevalenceOverTimeTabs: FunctionComponent<PrevalenceOverTimeTabsProps> = ({
118
- views,
119
- data,
120
- granularity,
121
- confidenceIntervalMethods,
122
- pageSize,
123
- yAxisMaxConfig,
124
- }) => {
94
+ const PrevalenceOverTimeTabs: FunctionComponent<PrevalenceOverTimeTabsProps> = ({ data, ...componentProps }) => {
95
+ const { views, granularity, confidenceIntervalMethods, pageSize, yAxisMaxLinear, yAxisMaxLogarithmic } =
96
+ componentProps;
125
97
  const [yAxisScaleType, setYAxisScaleType] = useState<ScaleType>('linear');
126
98
  const [confidenceIntervalMethod, setConfidenceIntervalMethod] = useState<ConfidenceIntervalMethod>(
127
99
  confidenceIntervalMethods.length > 0 ? confidenceIntervalMethods[0] : 'none',
128
100
  );
101
+ const yAxisMaxConfig = { linear: yAxisMaxLinear, logarithmic: yAxisMaxLogarithmic };
129
102
 
130
103
  const getTab = (view: View) => {
131
104
  switch (view) {
@@ -180,20 +153,18 @@ const PrevalenceOverTimeTabs: FunctionComponent<PrevalenceOverTimeTabsProps> = (
180
153
  yAxisScaleType={yAxisScaleType}
181
154
  setYAxisScaleType={setYAxisScaleType}
182
155
  data={data}
183
- granularity={granularity}
184
- confidenceIntervalMethods={confidenceIntervalMethods}
185
156
  confidenceIntervalMethod={confidenceIntervalMethod}
186
157
  setConfidenceIntervalMethod={setConfidenceIntervalMethod}
158
+ {...componentProps}
187
159
  />
188
160
  );
189
161
 
190
162
  return <Tabs tabs={tabs} toolbar={toolbar} />;
191
163
  };
192
164
 
193
- type ToolbarProps = {
165
+ type ToolbarProps = PrevalenceOverTimeProps & {
194
166
  activeTab: string;
195
167
  data: PrevalenceOverTimeData;
196
- granularity: TemporalGranularity;
197
168
  yAxisScaleType: ScaleType;
198
169
  setYAxisScaleType: (scaleType: ScaleType) => void;
199
170
  confidenceIntervalMethods: ConfidenceIntervalMethod[];
@@ -205,12 +176,12 @@ const Toolbar: FunctionComponent<ToolbarProps> = ({
205
176
  activeTab,
206
177
  yAxisScaleType,
207
178
  setYAxisScaleType,
208
- confidenceIntervalMethods,
209
179
  confidenceIntervalMethod,
210
180
  setConfidenceIntervalMethod,
211
181
  data,
212
- granularity,
182
+ ...componentProps
213
183
  }) => {
184
+ const { confidenceIntervalMethods, granularity } = componentProps;
214
185
  return (
215
186
  <>
216
187
  {activeTab !== 'Table' && (
@@ -229,16 +200,38 @@ const Toolbar: FunctionComponent<ToolbarProps> = ({
229
200
  filename='prevalence_over_time.csv'
230
201
  />
231
202
 
232
- <PrevalenceOverTimeInfo />
203
+ <PrevalenceOverTimeInfo {...componentProps} />
204
+ <Fullscreen />
233
205
  </>
234
206
  );
235
207
  };
236
208
 
237
- const PrevalenceOverTimeInfo: FunctionComponent = () => {
209
+ const PrevalenceOverTimeInfo: FunctionComponent<PrevalenceOverTimeProps> = (componentProps) => {
210
+ const { granularity, smoothingWindow, views } = componentProps;
211
+ const lapis = useContext(LapisUrlContext);
238
212
  return (
239
213
  <Info>
240
214
  <InfoHeadline1>Prevalence over time</InfoHeadline1>
241
- <InfoParagraph>Prevalence over time info.</InfoParagraph>
215
+ <InfoParagraph>
216
+ This presents the proportion of one or more variants per <b>{granularity}</b>
217
+ {smoothingWindow > 0 && `, smoothed using a ${smoothingWindow}-${granularity} sliding window`}. The
218
+ proportion is calculated as the number of sequences of the variant(s) divided by the number of sequences
219
+ that match the <code>denominatorFilter</code> (see below).
220
+ </InfoParagraph>
221
+ <InfoParagraph>
222
+ Sequences that have no assigned date will not be shown in the line and bubble chart. They will show up
223
+ in the bar and table view with date "unknown".
224
+ </InfoParagraph>
225
+ {views.includes('bubble') && (
226
+ <>
227
+ <InfoHeadline2>Bubble chart</InfoHeadline2>
228
+ <InfoParagraph>
229
+ The size of the bubble scales with the total number of available sequences from the{' '}
230
+ {granularity}.
231
+ </InfoParagraph>
232
+ </>
233
+ )}
234
+ <InfoComponentCode componentName='prevalence-over-time' params={componentProps} lapisUrl={lapis} />
242
235
  </Info>
243
236
  );
244
237
  };
@@ -10,6 +10,7 @@ import { type LapisFilter } from '../../types';
10
10
  import { LapisUrlContext } from '../LapisUrlContext';
11
11
  import { ErrorBoundary } from '../components/error-boundary';
12
12
  import { ErrorDisplay } from '../components/error-display';
13
+ import { Fullscreen } from '../components/fullscreen';
13
14
  import Info, { InfoHeadline1, InfoHeadline2, InfoLink, InfoParagraph } from '../components/info';
14
15
  import { LoadingDisplay } from '../components/loading-display';
15
16
  import { NoDataDisplay } from '../components/no-data-display';
@@ -156,6 +157,7 @@ const RelativeGrowthAdvantageToolbar: FunctionComponent<RelativeGrowthAdvantageT
156
157
  <>
157
158
  <ScalingSelector yAxisScaleType={yAxisScaleType} setYAxisScaleType={setYAxisScaleType} />
158
159
  <RelativeGrowthAdvantageInfo generationTime={generationTime} />
160
+ <Fullscreen />
159
161
  </>
160
162
  );
161
163
  };
@@ -36,9 +36,18 @@ export type MutationOverTimeDataGroupedByMutation = Map2d<
36
36
 
37
37
  const MAX_NUMBER_OF_GRID_COLUMNS = 200;
38
38
 
39
+ export async function queryOverallMutationData(
40
+ lapisFilter: LapisFilter,
41
+ sequenceType: SequenceType,
42
+ lapis: string,
43
+ signal?: AbortSignal,
44
+ ) {
45
+ return fetchAndPrepareSubstitutionsOrDeletions(lapisFilter, sequenceType).evaluate(lapis, signal);
46
+ }
47
+
39
48
  export async function queryMutationsOverTimeData(
40
49
  lapisFilter: LapisFilter,
41
- sequenceType: 'nucleotide' | 'amino acid',
50
+ sequenceType: SequenceType,
42
51
  lapis: string,
43
52
  lapisDateField: string,
44
53
  granularity: TemporalGranularity,