@genspectrum/dashboard-components 0.6.10 → 0.6.12
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.
- package/dist/dashboard-components.js +1006 -618
- package/dist/dashboard-components.js.map +1 -1
- package/dist/genspectrum-components.d.ts +7 -7
- package/dist/style.css +147 -1
- package/package.json +2 -2
- package/src/preact/aggregatedData/aggregate.tsx +12 -4
- package/src/preact/components/checkbox-selector.stories.tsx +93 -11
- package/src/preact/components/checkbox-selector.tsx +19 -0
- package/src/preact/components/color-scale-selector-dropdown.tsx +5 -3
- package/src/preact/components/dropdown.tsx +3 -3
- package/src/preact/components/info.tsx +88 -1
- package/src/preact/components/mutation-type-selector.stories.tsx +115 -0
- package/src/preact/components/mutation-type-selector.tsx +33 -8
- package/src/preact/components/percent-input.stories.tsx +93 -0
- package/src/preact/components/percent-intput.tsx +4 -0
- package/src/preact/components/proportion-selector-dropdown.stories.tsx +2 -2
- package/src/preact/components/proportion-selector-dropdown.tsx +9 -7
- package/src/preact/components/proportion-selector.stories.tsx +4 -4
- package/src/preact/components/proportion-selector.tsx +46 -12
- package/src/preact/components/segment-selector.stories.tsx +151 -0
- package/src/preact/components/{SegmentSelector.tsx → segment-selector.tsx} +29 -20
- package/src/preact/mutationComparison/mutation-comparison.stories.tsx +1 -1
- package/src/preact/mutationComparison/mutation-comparison.tsx +1 -1
- package/src/preact/mutationComparison/queryMutationData.ts +1 -1
- package/src/preact/mutations/mutations-grid.tsx +5 -1
- package/src/preact/mutations/mutations.tsx +33 -3
- package/src/preact/mutations/queryMutations.ts +1 -1
- package/src/preact/mutationsOverTime/getFilteredMutationsOverTime.spec.ts +4 -4
- package/src/preact/mutationsOverTime/getFilteredMutationsOverTimeData.ts +3 -2
- package/src/preact/mutationsOverTime/mutations-over-time.tsx +1 -1
- package/src/preact/numberSequencesOverTime/number-sequences-over-time.tsx +43 -8
- package/src/preact/prevalenceOverTime/prevalence-over-time.stories.tsx +2 -1
- package/src/preact/prevalenceOverTime/prevalence-over-time.tsx +44 -53
- package/src/preact/useQuery.ts +1 -1
- package/src/query/queryMutationsOverTime.ts +3 -3
- package/src/utils/map2d.spec.ts +83 -22
- package/src/utils/map2d.ts +158 -0
- package/src/web-components/visualization/gs-prevalence-over-time.tsx +2 -4
- package/src/utils/Map2d.ts +0 -75
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type Row } from 'gridjs';
|
|
2
2
|
import { type FunctionComponent } from 'preact';
|
|
3
|
+
import { useMemo } from 'preact/hooks';
|
|
3
4
|
|
|
4
5
|
import { getMutationsGridData } from './getMutationsGridData';
|
|
5
6
|
import { type SequenceType, type SubstitutionOrDeletionEntry } from '../../types';
|
|
@@ -84,7 +85,10 @@ export const MutationsGrid: FunctionComponent<MutationsGridProps> = ({
|
|
|
84
85
|
return {};
|
|
85
86
|
};
|
|
86
87
|
|
|
87
|
-
const tableData =
|
|
88
|
+
const tableData = useMemo(
|
|
89
|
+
() => getMutationsGridData(data, sequenceType, proportionInterval).map((row) => Object.values(row)),
|
|
90
|
+
[data, proportionInterval, sequenceType],
|
|
91
|
+
);
|
|
88
92
|
|
|
89
93
|
return <Table data={tableData} columns={getHeaders()} pageSize={pageSize} />;
|
|
90
94
|
};
|
|
@@ -14,18 +14,18 @@ import {
|
|
|
14
14
|
type SubstitutionOrDeletionEntry,
|
|
15
15
|
} from '../../types';
|
|
16
16
|
import { LapisUrlContext } from '../LapisUrlContext';
|
|
17
|
-
import { type DisplayedSegment, SegmentSelector, useDisplayedSegments } from '../components/SegmentSelector';
|
|
18
17
|
import { CsvDownloadButton } from '../components/csv-download-button';
|
|
19
18
|
import { ErrorBoundary } from '../components/error-boundary';
|
|
20
19
|
import { ErrorDisplay } from '../components/error-display';
|
|
21
20
|
import { Fullscreen } from '../components/fullscreen';
|
|
22
|
-
import Info from '../components/info';
|
|
21
|
+
import Info, { InfoHeadline1, InfoHeadline2, InfoLink, InfoParagraph } from '../components/info';
|
|
23
22
|
import { LoadingDisplay } from '../components/loading-display';
|
|
24
23
|
import { type DisplayedMutationType, MutationTypeSelector } from '../components/mutation-type-selector';
|
|
25
24
|
import { NoDataDisplay } from '../components/no-data-display';
|
|
26
25
|
import type { ProportionInterval } from '../components/proportion-selector';
|
|
27
26
|
import { ProportionSelectorDropdown } from '../components/proportion-selector-dropdown';
|
|
28
27
|
import { ResizeContainer } from '../components/resize-container';
|
|
28
|
+
import { type DisplayedSegment, SegmentSelector, useDisplayedSegments } from '../components/segment-selector';
|
|
29
29
|
import Tabs from '../components/tabs';
|
|
30
30
|
import { useQuery } from '../useQuery';
|
|
31
31
|
|
|
@@ -208,8 +208,38 @@ const Toolbar: FunctionComponent<ToolbarProps> = ({
|
|
|
208
208
|
filename='insertions.csv'
|
|
209
209
|
/>
|
|
210
210
|
)}
|
|
211
|
-
<
|
|
211
|
+
<MutationsInfo />
|
|
212
212
|
<Fullscreen />
|
|
213
213
|
</>
|
|
214
214
|
);
|
|
215
215
|
};
|
|
216
|
+
|
|
217
|
+
const MutationsInfo = () => (
|
|
218
|
+
<Info>
|
|
219
|
+
<InfoHeadline1>Mutations</InfoHeadline1>
|
|
220
|
+
<InfoParagraph>
|
|
221
|
+
This shows mutations of a variant. There are three types of mutations:{' '}
|
|
222
|
+
<InfoLink href='https://www.genome.gov/genetics-glossary/Substitution'>substitutions</InfoLink>,{' '}
|
|
223
|
+
<InfoLink href='https://www.genome.gov/genetics-glossary/Deletion'>deletions</InfoLink> and{' '}
|
|
224
|
+
<InfoLink href='https://www.genome.gov/genetics-glossary/Insertion'>insertions</InfoLink>.
|
|
225
|
+
</InfoParagraph>
|
|
226
|
+
<InfoHeadline2>Proportion calculation</InfoHeadline2>
|
|
227
|
+
<InfoParagraph>
|
|
228
|
+
The proportion of a mutation is calculated by dividing the number of sequences with the mutation by the
|
|
229
|
+
total number of sequences with a non-ambiguous symbol at the position.
|
|
230
|
+
</InfoParagraph>
|
|
231
|
+
<InfoParagraph>
|
|
232
|
+
<b>Example:</b> Assume we look at nucleotide mutations at position 5 where the reference has a T and assume
|
|
233
|
+
there are 10 sequences in total:
|
|
234
|
+
<ul className='list-disc list-inside ml-2'>
|
|
235
|
+
<li>3 sequences have a C,</li>
|
|
236
|
+
<li>2 sequences have a T,</li>
|
|
237
|
+
<li>1 sequence has a G,</li>
|
|
238
|
+
<li>3 sequences have an N,</li>
|
|
239
|
+
<li>1 sequence has a Y (which means T or C),</li>
|
|
240
|
+
</ul>
|
|
241
|
+
then the proportion of the T5C mutation is 50%. The 4 sequences that have an N or Y are excluded from the
|
|
242
|
+
calculation.
|
|
243
|
+
</InfoParagraph>
|
|
244
|
+
</Info>
|
|
245
|
+
);
|
|
@@ -6,8 +6,8 @@ import {
|
|
|
6
6
|
type MutationEntry,
|
|
7
7
|
type SubstitutionOrDeletionEntry,
|
|
8
8
|
} from '../../types';
|
|
9
|
-
import { type DisplayedSegment } from '../components/SegmentSelector';
|
|
10
9
|
import { type DisplayedMutationType } from '../components/mutation-type-selector';
|
|
10
|
+
import { type DisplayedSegment } from '../components/segment-selector';
|
|
11
11
|
|
|
12
12
|
export async function queryMutationsData(
|
|
13
13
|
lapisFilter: LapisFilter,
|
|
@@ -2,7 +2,7 @@ import { describe, expect, it } from 'vitest';
|
|
|
2
2
|
|
|
3
3
|
import { filterDisplayedSegments, filterMutationTypes, filterProportion } from './getFilteredMutationsOverTimeData';
|
|
4
4
|
import { type MutationOverTimeMutationValue } from '../../query/queryMutationsOverTime';
|
|
5
|
-
import {
|
|
5
|
+
import { Map2dBase } from '../../utils/map2d';
|
|
6
6
|
import { Deletion, Substitution } from '../../utils/mutations';
|
|
7
7
|
import { type Temporal } from '../../utils/temporal';
|
|
8
8
|
import { yearMonthDay } from '../../utils/temporalTestHelpers';
|
|
@@ -10,7 +10,7 @@ import { yearMonthDay } from '../../utils/temporalTestHelpers';
|
|
|
10
10
|
describe('getFilteredMutationOverTimeData', () => {
|
|
11
11
|
describe('filterDisplayedSegments', () => {
|
|
12
12
|
it('should filter by displayed segments', () => {
|
|
13
|
-
const data = new
|
|
13
|
+
const data = new Map2dBase<Substitution | Deletion, Temporal, MutationOverTimeMutationValue>();
|
|
14
14
|
|
|
15
15
|
data.set(new Substitution('someSegment', 'A', 'T', 123), yearMonthDay('2021-01-01'), {
|
|
16
16
|
count: 1,
|
|
@@ -36,7 +36,7 @@ describe('getFilteredMutationOverTimeData', () => {
|
|
|
36
36
|
|
|
37
37
|
describe('filterMutationTypes', () => {
|
|
38
38
|
it('should filter by mutation types', () => {
|
|
39
|
-
const data = new
|
|
39
|
+
const data = new Map2dBase<Substitution | Deletion, Temporal, MutationOverTimeMutationValue>();
|
|
40
40
|
|
|
41
41
|
data.set(new Substitution('someSegment', 'A', 'T', 123), yearMonthDay('2021-01-01'), {
|
|
42
42
|
count: 1,
|
|
@@ -120,7 +120,7 @@ describe('getFilteredMutationOverTimeData', () => {
|
|
|
120
120
|
});
|
|
121
121
|
|
|
122
122
|
function getMutationOverTimeData() {
|
|
123
|
-
const data = new
|
|
123
|
+
const data = new Map2dBase<Substitution | Deletion, Temporal, MutationOverTimeMutationValue>();
|
|
124
124
|
data.set(someSubstitution, yearMonthDay('2021-01-01'), { count: 1, proportion: 0.1 });
|
|
125
125
|
data.set(someSubstitution, yearMonthDay('2021-02-02'), { count: 99, proportion: 0.99 });
|
|
126
126
|
return data;
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { type Dataset } from '../../operator/Dataset';
|
|
2
2
|
import { type MutationOverTimeDataGroupedByMutation } from '../../query/queryMutationsOverTime';
|
|
3
3
|
import { type DeletionEntry, type SubstitutionEntry } from '../../types';
|
|
4
|
-
import
|
|
4
|
+
import { Map2dView } from '../../utils/map2d';
|
|
5
5
|
import type { DisplayedMutationType } from '../components/mutation-type-selector';
|
|
6
|
+
import type { DisplayedSegment } from '../components/segment-selector';
|
|
6
7
|
|
|
7
8
|
export function getFilteredMutationOverTimeData(
|
|
8
9
|
data: MutationOverTimeDataGroupedByMutation,
|
|
@@ -11,7 +12,7 @@ export function getFilteredMutationOverTimeData(
|
|
|
11
12
|
displayedMutationTypes: DisplayedMutationType[],
|
|
12
13
|
proportionInterval: { min: number; max: number },
|
|
13
14
|
) {
|
|
14
|
-
const filteredData = data
|
|
15
|
+
const filteredData = new Map2dView(data);
|
|
15
16
|
filterDisplayedSegments(displayedSegments, filteredData);
|
|
16
17
|
filterMutationTypes(displayedMutationTypes, filteredData);
|
|
17
18
|
filterProportion(filteredData, overallMutationData, proportionInterval);
|
|
@@ -18,7 +18,6 @@ import {
|
|
|
18
18
|
} from '../../types';
|
|
19
19
|
import { compareTemporal } from '../../utils/temporal';
|
|
20
20
|
import { LapisUrlContext } from '../LapisUrlContext';
|
|
21
|
-
import { type DisplayedSegment, SegmentSelector, useDisplayedSegments } from '../components/SegmentSelector';
|
|
22
21
|
import { type ColorScale } from '../components/color-scale-selector';
|
|
23
22
|
import { ColorScaleSelectorDropdown } from '../components/color-scale-selector-dropdown';
|
|
24
23
|
import { CsvDownloadButton } from '../components/csv-download-button';
|
|
@@ -32,6 +31,7 @@ import { NoDataDisplay } from '../components/no-data-display';
|
|
|
32
31
|
import type { ProportionInterval } from '../components/proportion-selector';
|
|
33
32
|
import { ProportionSelectorDropdown } from '../components/proportion-selector-dropdown';
|
|
34
33
|
import { ResizeContainer } from '../components/resize-container';
|
|
34
|
+
import { type DisplayedSegment, SegmentSelector, useDisplayedSegments } from '../components/segment-selector';
|
|
35
35
|
import Tabs from '../components/tabs';
|
|
36
36
|
import { sortSubstitutionsAndDeletions } from '../shared/sort/sortSubstitutionsAndDeletions';
|
|
37
37
|
import { useQuery } from '../useQuery';
|
|
@@ -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';
|
|
@@ -61,8 +62,9 @@ const NumberSequencesOverTimeInner = ({
|
|
|
61
62
|
}: NumberSequencesOverTimeInnerProps) => {
|
|
62
63
|
const lapis = useContext(LapisUrlContext);
|
|
63
64
|
|
|
64
|
-
const { data, error, isLoading } = useQuery(
|
|
65
|
-
queryNumberOfSequencesOverTime(lapis, lapisFilter, lapisDateField, granularity, smoothingWindow),
|
|
65
|
+
const { data, error, isLoading } = useQuery(
|
|
66
|
+
() => queryNumberOfSequencesOverTime(lapis, lapisFilter, lapisDateField, granularity, smoothingWindow),
|
|
67
|
+
[lapis, lapisFilter, lapisDateField, granularity, smoothingWindow],
|
|
66
68
|
);
|
|
67
69
|
|
|
68
70
|
if (isLoading) {
|
|
@@ -77,17 +79,32 @@ const NumberSequencesOverTimeInner = ({
|
|
|
77
79
|
return <NoDataDisplay />;
|
|
78
80
|
}
|
|
79
81
|
|
|
80
|
-
return
|
|
82
|
+
return (
|
|
83
|
+
<NumberSequencesOverTimeTabs
|
|
84
|
+
views={views}
|
|
85
|
+
data={data}
|
|
86
|
+
granularity={granularity}
|
|
87
|
+
smoothingWindow={smoothingWindow}
|
|
88
|
+
pageSize={pageSize}
|
|
89
|
+
/>
|
|
90
|
+
);
|
|
81
91
|
};
|
|
82
92
|
|
|
83
93
|
interface NumberSequencesOverTimeTabsProps {
|
|
84
94
|
views: NumberSequencesOverTimeView[];
|
|
85
95
|
data: NumberOfSequencesDatasets;
|
|
86
96
|
granularity: TemporalGranularity;
|
|
97
|
+
smoothingWindow: number;
|
|
87
98
|
pageSize: boolean | number;
|
|
88
99
|
}
|
|
89
100
|
|
|
90
|
-
const NumberSequencesOverTimeTabs = ({
|
|
101
|
+
const NumberSequencesOverTimeTabs = ({
|
|
102
|
+
views,
|
|
103
|
+
data,
|
|
104
|
+
granularity,
|
|
105
|
+
smoothingWindow,
|
|
106
|
+
pageSize,
|
|
107
|
+
}: NumberSequencesOverTimeTabsProps) => {
|
|
91
108
|
const [yAxisScaleType, setYAxisScaleType] = useState<ScaleType>('linear');
|
|
92
109
|
|
|
93
110
|
const getTab = (view: NumberSequencesOverTimeView) => {
|
|
@@ -120,6 +137,7 @@ const NumberSequencesOverTimeTabs = ({ views, data, granularity, pageSize }: Num
|
|
|
120
137
|
activeTab={activeTab}
|
|
121
138
|
data={data}
|
|
122
139
|
granularity={granularity}
|
|
140
|
+
smoothingWindow={smoothingWindow}
|
|
123
141
|
yAxisScaleType={yAxisScaleType}
|
|
124
142
|
setYAxisScaleType={setYAxisScaleType}
|
|
125
143
|
/>
|
|
@@ -134,9 +152,17 @@ interface ToolbarProps {
|
|
|
134
152
|
granularity: TemporalGranularity;
|
|
135
153
|
yAxisScaleType: ScaleType;
|
|
136
154
|
setYAxisScaleType: (scaleType: ScaleType) => void;
|
|
155
|
+
smoothingWindow: number;
|
|
137
156
|
}
|
|
138
157
|
|
|
139
|
-
const Toolbar = ({
|
|
158
|
+
const Toolbar = ({
|
|
159
|
+
activeTab,
|
|
160
|
+
data,
|
|
161
|
+
granularity,
|
|
162
|
+
yAxisScaleType,
|
|
163
|
+
setYAxisScaleType,
|
|
164
|
+
smoothingWindow,
|
|
165
|
+
}: ToolbarProps) => {
|
|
140
166
|
return (
|
|
141
167
|
<>
|
|
142
168
|
{activeTab !== 'Table' && (
|
|
@@ -151,17 +177,26 @@ const Toolbar = ({ activeTab, data, granularity, yAxisScaleType, setYAxisScaleTy
|
|
|
151
177
|
getData={() => getNumberOfSequencesOverTimeTableData(data, granularity)}
|
|
152
178
|
filename='number_of_sequences_over_time.csv'
|
|
153
179
|
/>
|
|
154
|
-
<NumberSequencesOverTimeInfo />
|
|
180
|
+
<NumberSequencesOverTimeInfo granularity={granularity} smoothingWindow={smoothingWindow} />
|
|
155
181
|
<Fullscreen />
|
|
156
182
|
</>
|
|
157
183
|
);
|
|
158
184
|
};
|
|
159
185
|
|
|
160
|
-
|
|
186
|
+
type NumberSequencesOverTimeInfoProps = {
|
|
187
|
+
granularity: TemporalGranularity;
|
|
188
|
+
smoothingWindow: number;
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
const NumberSequencesOverTimeInfo: FunctionComponent<NumberSequencesOverTimeInfoProps> = ({
|
|
192
|
+
granularity,
|
|
193
|
+
smoothingWindow,
|
|
194
|
+
}) => (
|
|
161
195
|
<Info>
|
|
162
196
|
<InfoHeadline1>Number of sequences over time</InfoHeadline1>
|
|
163
197
|
<InfoParagraph>
|
|
164
|
-
|
|
198
|
+
This presents the number of available sequences of a variant per <b>{granularity}</b>
|
|
199
|
+
{smoothingWindow > 0 && `, smoothed using a ${smoothingWindow}-${granularity} sliding window`}.
|
|
165
200
|
</InfoParagraph>
|
|
166
201
|
</Info>
|
|
167
202
|
);
|
|
@@ -51,7 +51,8 @@ const Template = {
|
|
|
51
51
|
height={args.height}
|
|
52
52
|
lapisDateField={args.lapisDateField}
|
|
53
53
|
pageSize={args.pageSize}
|
|
54
|
-
|
|
54
|
+
yAxisMaxLinear={args.yAxisMaxLinear}
|
|
55
|
+
yAxisMaxLogarithmic={args.yAxisMaxLogarithmic}
|
|
55
56
|
/>
|
|
56
57
|
</LapisUrlContext.Provider>
|
|
57
58
|
),
|
|
@@ -14,25 +14,22 @@ import { CsvDownloadButton } from '../components/csv-download-button';
|
|
|
14
14
|
import { ErrorBoundary } from '../components/error-boundary';
|
|
15
15
|
import { ErrorDisplay } from '../components/error-display';
|
|
16
16
|
import { Fullscreen } from '../components/fullscreen';
|
|
17
|
-
import Info, { InfoHeadline1, InfoParagraph } from '../components/info';
|
|
17
|
+
import Info, { InfoComponentCode, InfoHeadline1, InfoHeadline2, InfoParagraph } from '../components/info';
|
|
18
18
|
import { LoadingDisplay } from '../components/loading-display';
|
|
19
19
|
import { NoDataDisplay } from '../components/no-data-display';
|
|
20
20
|
import { ResizeContainer } from '../components/resize-container';
|
|
21
21
|
import { ScalingSelector } from '../components/scaling-selector';
|
|
22
22
|
import Tabs from '../components/tabs';
|
|
23
23
|
import { type ConfidenceIntervalMethod } from '../shared/charts/confideceInterval';
|
|
24
|
-
import type
|
|
24
|
+
import { type AxisMax } from '../shared/charts/getYAxisMax';
|
|
25
25
|
import { type ScaleType } from '../shared/charts/getYAxisScale';
|
|
26
26
|
import { useQuery } from '../useQuery';
|
|
27
27
|
|
|
28
28
|
export type View = 'bar' | 'line' | 'bubble' | 'table';
|
|
29
29
|
|
|
30
|
-
export interface PrevalenceOverTimeProps
|
|
30
|
+
export interface PrevalenceOverTimeProps {
|
|
31
31
|
width: string;
|
|
32
32
|
height: string;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export interface PrevalenceOverTimeInnerProps {
|
|
36
33
|
numeratorFilter: NamedLapisFilter | NamedLapisFilter[];
|
|
37
34
|
denominatorFilter: LapisFilter;
|
|
38
35
|
granularity: TemporalGranularity;
|
|
@@ -41,32 +38,25 @@ export interface PrevalenceOverTimeInnerProps {
|
|
|
41
38
|
confidenceIntervalMethods: ConfidenceIntervalMethod[];
|
|
42
39
|
lapisDateField: string;
|
|
43
40
|
pageSize: boolean | number;
|
|
44
|
-
|
|
41
|
+
yAxisMaxLinear?: AxisMax;
|
|
42
|
+
yAxisMaxLogarithmic?: AxisMax;
|
|
45
43
|
}
|
|
46
44
|
|
|
47
|
-
export const PrevalenceOverTime: FunctionComponent<PrevalenceOverTimeProps> = (
|
|
45
|
+
export const PrevalenceOverTime: FunctionComponent<PrevalenceOverTimeProps> = (componentProps) => {
|
|
46
|
+
const { width, height } = componentProps;
|
|
48
47
|
const size = { height, width };
|
|
49
48
|
|
|
50
49
|
return (
|
|
51
50
|
<ErrorBoundary size={size}>
|
|
52
51
|
<ResizeContainer size={size}>
|
|
53
|
-
<PrevalenceOverTimeInner {...
|
|
52
|
+
<PrevalenceOverTimeInner {...componentProps} />
|
|
54
53
|
</ResizeContainer>
|
|
55
54
|
</ErrorBoundary>
|
|
56
55
|
);
|
|
57
56
|
};
|
|
58
57
|
|
|
59
|
-
export const PrevalenceOverTimeInner: FunctionComponent<
|
|
60
|
-
numeratorFilter,
|
|
61
|
-
denominatorFilter,
|
|
62
|
-
granularity,
|
|
63
|
-
smoothingWindow,
|
|
64
|
-
views,
|
|
65
|
-
confidenceIntervalMethods,
|
|
66
|
-
lapisDateField,
|
|
67
|
-
pageSize,
|
|
68
|
-
yAxisMaxConfig,
|
|
69
|
-
}) => {
|
|
58
|
+
export const PrevalenceOverTimeInner: FunctionComponent<PrevalenceOverTimeProps> = (componentProps) => {
|
|
59
|
+
const { numeratorFilter, denominatorFilter, granularity, smoothingWindow, lapisDateField } = componentProps;
|
|
70
60
|
const lapis = useContext(LapisUrlContext);
|
|
71
61
|
|
|
72
62
|
const { data, error, isLoading } = useQuery(
|
|
@@ -94,39 +84,21 @@ export const PrevalenceOverTimeInner: FunctionComponent<PrevalenceOverTimeInnerP
|
|
|
94
84
|
return <NoDataDisplay />;
|
|
95
85
|
}
|
|
96
86
|
|
|
97
|
-
return
|
|
98
|
-
<PrevalenceOverTimeTabs
|
|
99
|
-
views={views}
|
|
100
|
-
data={data}
|
|
101
|
-
granularity={granularity}
|
|
102
|
-
confidenceIntervalMethods={confidenceIntervalMethods}
|
|
103
|
-
pageSize={pageSize}
|
|
104
|
-
yAxisMaxConfig={yAxisMaxConfig}
|
|
105
|
-
/>
|
|
106
|
-
);
|
|
87
|
+
return <PrevalenceOverTimeTabs data={data} {...componentProps} />;
|
|
107
88
|
};
|
|
108
89
|
|
|
109
|
-
type PrevalenceOverTimeTabsProps = {
|
|
110
|
-
views: View[];
|
|
90
|
+
type PrevalenceOverTimeTabsProps = PrevalenceOverTimeProps & {
|
|
111
91
|
data: PrevalenceOverTimeData;
|
|
112
|
-
granularity: TemporalGranularity;
|
|
113
|
-
confidenceIntervalMethods: ConfidenceIntervalMethod[];
|
|
114
|
-
pageSize: boolean | number;
|
|
115
|
-
yAxisMaxConfig: YAxisMaxConfig;
|
|
116
92
|
};
|
|
117
93
|
|
|
118
|
-
const PrevalenceOverTimeTabs: FunctionComponent<PrevalenceOverTimeTabsProps> = ({
|
|
119
|
-
views,
|
|
120
|
-
|
|
121
|
-
granularity,
|
|
122
|
-
confidenceIntervalMethods,
|
|
123
|
-
pageSize,
|
|
124
|
-
yAxisMaxConfig,
|
|
125
|
-
}) => {
|
|
94
|
+
const PrevalenceOverTimeTabs: FunctionComponent<PrevalenceOverTimeTabsProps> = ({ data, ...componentProps }) => {
|
|
95
|
+
const { views, granularity, confidenceIntervalMethods, pageSize, yAxisMaxLinear, yAxisMaxLogarithmic } =
|
|
96
|
+
componentProps;
|
|
126
97
|
const [yAxisScaleType, setYAxisScaleType] = useState<ScaleType>('linear');
|
|
127
98
|
const [confidenceIntervalMethod, setConfidenceIntervalMethod] = useState<ConfidenceIntervalMethod>(
|
|
128
99
|
confidenceIntervalMethods.length > 0 ? confidenceIntervalMethods[0] : 'none',
|
|
129
100
|
);
|
|
101
|
+
const yAxisMaxConfig = { linear: yAxisMaxLinear, logarithmic: yAxisMaxLogarithmic };
|
|
130
102
|
|
|
131
103
|
const getTab = (view: View) => {
|
|
132
104
|
switch (view) {
|
|
@@ -181,20 +153,18 @@ const PrevalenceOverTimeTabs: FunctionComponent<PrevalenceOverTimeTabsProps> = (
|
|
|
181
153
|
yAxisScaleType={yAxisScaleType}
|
|
182
154
|
setYAxisScaleType={setYAxisScaleType}
|
|
183
155
|
data={data}
|
|
184
|
-
granularity={granularity}
|
|
185
|
-
confidenceIntervalMethods={confidenceIntervalMethods}
|
|
186
156
|
confidenceIntervalMethod={confidenceIntervalMethod}
|
|
187
157
|
setConfidenceIntervalMethod={setConfidenceIntervalMethod}
|
|
158
|
+
{...componentProps}
|
|
188
159
|
/>
|
|
189
160
|
);
|
|
190
161
|
|
|
191
162
|
return <Tabs tabs={tabs} toolbar={toolbar} />;
|
|
192
163
|
};
|
|
193
164
|
|
|
194
|
-
type ToolbarProps = {
|
|
165
|
+
type ToolbarProps = PrevalenceOverTimeProps & {
|
|
195
166
|
activeTab: string;
|
|
196
167
|
data: PrevalenceOverTimeData;
|
|
197
|
-
granularity: TemporalGranularity;
|
|
198
168
|
yAxisScaleType: ScaleType;
|
|
199
169
|
setYAxisScaleType: (scaleType: ScaleType) => void;
|
|
200
170
|
confidenceIntervalMethods: ConfidenceIntervalMethod[];
|
|
@@ -206,12 +176,12 @@ const Toolbar: FunctionComponent<ToolbarProps> = ({
|
|
|
206
176
|
activeTab,
|
|
207
177
|
yAxisScaleType,
|
|
208
178
|
setYAxisScaleType,
|
|
209
|
-
confidenceIntervalMethods,
|
|
210
179
|
confidenceIntervalMethod,
|
|
211
180
|
setConfidenceIntervalMethod,
|
|
212
181
|
data,
|
|
213
|
-
|
|
182
|
+
...componentProps
|
|
214
183
|
}) => {
|
|
184
|
+
const { confidenceIntervalMethods, granularity } = componentProps;
|
|
215
185
|
return (
|
|
216
186
|
<>
|
|
217
187
|
{activeTab !== 'Table' && (
|
|
@@ -230,17 +200,38 @@ const Toolbar: FunctionComponent<ToolbarProps> = ({
|
|
|
230
200
|
filename='prevalence_over_time.csv'
|
|
231
201
|
/>
|
|
232
202
|
|
|
233
|
-
<PrevalenceOverTimeInfo />
|
|
203
|
+
<PrevalenceOverTimeInfo {...componentProps} />
|
|
234
204
|
<Fullscreen />
|
|
235
205
|
</>
|
|
236
206
|
);
|
|
237
207
|
};
|
|
238
208
|
|
|
239
|
-
const PrevalenceOverTimeInfo: FunctionComponent = () => {
|
|
209
|
+
const PrevalenceOverTimeInfo: FunctionComponent<PrevalenceOverTimeProps> = (componentProps) => {
|
|
210
|
+
const { granularity, smoothingWindow, views } = componentProps;
|
|
211
|
+
const lapis = useContext(LapisUrlContext);
|
|
240
212
|
return (
|
|
241
213
|
<Info>
|
|
242
214
|
<InfoHeadline1>Prevalence over time</InfoHeadline1>
|
|
243
|
-
<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} />
|
|
244
235
|
</Info>
|
|
245
236
|
);
|
|
246
237
|
};
|
package/src/preact/useQuery.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useEffect, useState } from 'preact/hooks';
|
|
2
2
|
|
|
3
|
-
export function useQuery<Data>(fetchDataCallback: () => Promise<Data>, dependencies: unknown[]
|
|
3
|
+
export function useQuery<Data>(fetchDataCallback: () => Promise<Data>, dependencies: unknown[]) {
|
|
4
4
|
const [data, setData] = useState<Data | null>(null);
|
|
5
5
|
const [error, setError] = useState<Error | null>(null);
|
|
6
6
|
const [isLoading, setIsLoading] = useState(true);
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
type SubstitutionOrDeletionEntry,
|
|
13
13
|
type TemporalGranularity,
|
|
14
14
|
} from '../types';
|
|
15
|
-
import { Map2d } from '../utils/
|
|
15
|
+
import { Map2dBase, type Map2d } from '../utils/map2d';
|
|
16
16
|
import { type Deletion, type Substitution } from '../utils/mutations';
|
|
17
17
|
import {
|
|
18
18
|
dateRangeCompare,
|
|
@@ -154,7 +154,7 @@ function fetchAndPrepareSubstitutionsOrDeletions(filter: LapisFilter, sequenceTy
|
|
|
154
154
|
}
|
|
155
155
|
|
|
156
156
|
export function groupByMutation(data: MutationOverTimeData[]) {
|
|
157
|
-
const dataArray = new
|
|
157
|
+
const dataArray = new Map2dBase<Substitution | Deletion, Temporal, MutationOverTimeMutationValue>(
|
|
158
158
|
(mutation) => mutation.code,
|
|
159
159
|
(date) => date.toString(),
|
|
160
160
|
);
|
|
@@ -174,7 +174,7 @@ export function groupByMutation(data: MutationOverTimeData[]) {
|
|
|
174
174
|
}
|
|
175
175
|
|
|
176
176
|
function addZeroValuesForDatesWithNoMutationData(
|
|
177
|
-
dataArray:
|
|
177
|
+
dataArray: Map2dBase<Substitution | Deletion, Temporal, MutationOverTimeMutationValue>,
|
|
178
178
|
data: MutationOverTimeData[],
|
|
179
179
|
) {
|
|
180
180
|
if (dataArray.getFirstAxisKeys().length !== 0) {
|