@genspectrum/dashboard-components 0.7.0 → 0.7.2
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/assets/mutationOverTimeWorker-BOCXtKzd.js.map +1 -1
- package/dist/dashboard-components.js +216 -207
- package/dist/dashboard-components.js.map +1 -1
- package/dist/genspectrum-components.d.ts +40 -40
- package/package.json +2 -2
- package/src/operator/SlidingOperator.ts +3 -0
- package/src/preact/aggregatedData/aggregate.tsx +41 -33
- package/src/preact/components/error-display.tsx +1 -0
- package/src/preact/mutationComparison/mutation-comparison.tsx +32 -34
- package/src/preact/mutations/mutations.tsx +63 -56
- package/src/preact/mutationsOverTime/mutations-over-time.tsx +33 -25
- package/src/preact/numberSequencesOverTime/number-sequences-over-time.tsx +46 -64
- package/src/preact/prevalenceOverTime/__mockData__/numeratorFilterNoData.json +11 -0
- package/src/preact/prevalenceOverTime/prevalence-over-time-bar-chart.tsx +13 -6
- package/src/preact/prevalenceOverTime/prevalence-over-time-bubble-chart.tsx +13 -6
- package/src/preact/prevalenceOverTime/prevalence-over-time-line-chart.tsx +13 -6
- package/src/preact/prevalenceOverTime/prevalence-over-time.stories.tsx +74 -11
- package/src/preact/prevalenceOverTime/prevalence-over-time.tsx +1 -1
- package/src/preact/relativeGrowthAdvantage/relative-growth-advantage.stories.tsx +12 -5
- package/src/preact/relativeGrowthAdvantage/relative-growth-advantage.tsx +29 -36
- package/standalone-bundle/dashboard-components.js +9842 -9847
- package/standalone-bundle/dashboard-components.js.map +1 -1
|
@@ -18,7 +18,7 @@ import { CsvDownloadButton } from '../components/csv-download-button';
|
|
|
18
18
|
import { ErrorBoundary } from '../components/error-boundary';
|
|
19
19
|
import { ErrorDisplay } from '../components/error-display';
|
|
20
20
|
import { Fullscreen } from '../components/fullscreen';
|
|
21
|
-
import Info, { InfoHeadline1, InfoHeadline2, InfoLink, InfoParagraph } from '../components/info';
|
|
21
|
+
import Info, { InfoComponentCode, InfoHeadline1, InfoHeadline2, InfoLink, InfoParagraph } from '../components/info';
|
|
22
22
|
import { LoadingDisplay } from '../components/loading-display';
|
|
23
23
|
import { type DisplayedMutationType, MutationTypeSelector } from '../components/mutation-type-selector';
|
|
24
24
|
import { NoDataDisplay } from '../components/no-data-display';
|
|
@@ -31,37 +31,32 @@ import { useQuery } from '../useQuery';
|
|
|
31
31
|
|
|
32
32
|
export type View = 'table' | 'grid' | 'insertions';
|
|
33
33
|
|
|
34
|
-
export interface
|
|
34
|
+
export interface MutationsProps {
|
|
35
|
+
width: string;
|
|
36
|
+
height: string;
|
|
35
37
|
lapisFilter: LapisFilter;
|
|
36
38
|
sequenceType: SequenceType;
|
|
37
39
|
views: View[];
|
|
38
40
|
pageSize: boolean | number;
|
|
39
41
|
}
|
|
40
42
|
|
|
41
|
-
export
|
|
42
|
-
width
|
|
43
|
-
height: string;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
export const Mutations: FunctionComponent<MutationsProps> = ({ width, height, ...innerProps }) => {
|
|
43
|
+
export const Mutations: FunctionComponent<MutationsProps> = (componentProps) => {
|
|
44
|
+
const { width, height } = componentProps;
|
|
47
45
|
const size = { height, width };
|
|
48
46
|
|
|
49
47
|
return (
|
|
50
48
|
<ErrorBoundary size={size}>
|
|
51
49
|
<ResizeContainer size={size}>
|
|
52
|
-
<MutationsInner {...
|
|
50
|
+
<MutationsInner {...componentProps} />
|
|
53
51
|
</ResizeContainer>
|
|
54
52
|
</ErrorBoundary>
|
|
55
53
|
);
|
|
56
54
|
};
|
|
57
55
|
|
|
58
|
-
export const MutationsInner: FunctionComponent<
|
|
59
|
-
lapisFilter,
|
|
60
|
-
sequenceType,
|
|
61
|
-
views,
|
|
62
|
-
pageSize,
|
|
63
|
-
}) => {
|
|
56
|
+
export const MutationsInner: FunctionComponent<MutationsProps> = (componentProps) => {
|
|
64
57
|
const lapis = useContext(LapisUrlContext);
|
|
58
|
+
const { lapisFilter, sequenceType } = componentProps;
|
|
59
|
+
|
|
65
60
|
const { data, error, isLoading } = useQuery(async () => {
|
|
66
61
|
return queryMutationsData(lapisFilter, sequenceType, lapis);
|
|
67
62
|
}, [lapisFilter, sequenceType, lapis]);
|
|
@@ -78,20 +73,18 @@ export const MutationsInner: FunctionComponent<MutationsInnerProps> = ({
|
|
|
78
73
|
return <NoDataDisplay />;
|
|
79
74
|
}
|
|
80
75
|
|
|
81
|
-
return <MutationsTabs mutationsData={data}
|
|
76
|
+
return <MutationsTabs mutationsData={data} originalComponentProps={componentProps} />;
|
|
82
77
|
};
|
|
83
78
|
|
|
84
79
|
type MutationTabsProps = {
|
|
85
80
|
mutationsData: { insertions: InsertionEntry[]; substitutionsOrDeletions: SubstitutionOrDeletionEntry[] };
|
|
86
|
-
|
|
87
|
-
views: View[];
|
|
88
|
-
pageSize: boolean | number;
|
|
81
|
+
originalComponentProps: MutationsProps;
|
|
89
82
|
};
|
|
90
83
|
|
|
91
|
-
const MutationsTabs: FunctionComponent<MutationTabsProps> = ({ mutationsData,
|
|
84
|
+
const MutationsTabs: FunctionComponent<MutationTabsProps> = ({ mutationsData, originalComponentProps }) => {
|
|
92
85
|
const [proportionInterval, setProportionInterval] = useState({ min: 0.05, max: 1 });
|
|
93
86
|
|
|
94
|
-
const [displayedSegments, setDisplayedSegments] = useDisplayedSegments(sequenceType);
|
|
87
|
+
const [displayedSegments, setDisplayedSegments] = useDisplayedSegments(originalComponentProps.sequenceType);
|
|
95
88
|
const [displayedMutationTypes, setDisplayedMutationTypes] = useState<DisplayedMutationType[]>([
|
|
96
89
|
{ label: 'Substitutions', checked: true, type: 'substitution' },
|
|
97
90
|
{ label: 'Deletions', checked: true, type: 'deletion' },
|
|
@@ -108,7 +101,7 @@ const MutationsTabs: FunctionComponent<MutationTabsProps> = ({ mutationsData, se
|
|
|
108
101
|
<MutationsTable
|
|
109
102
|
data={filteredData.tableData}
|
|
110
103
|
proportionInterval={proportionInterval}
|
|
111
|
-
pageSize={pageSize}
|
|
104
|
+
pageSize={originalComponentProps.pageSize}
|
|
112
105
|
/>
|
|
113
106
|
),
|
|
114
107
|
};
|
|
@@ -118,21 +111,23 @@ const MutationsTabs: FunctionComponent<MutationTabsProps> = ({ mutationsData, se
|
|
|
118
111
|
content: (
|
|
119
112
|
<MutationsGrid
|
|
120
113
|
data={filteredData.gridData}
|
|
121
|
-
sequenceType={sequenceType}
|
|
114
|
+
sequenceType={originalComponentProps.sequenceType}
|
|
122
115
|
proportionInterval={proportionInterval}
|
|
123
|
-
pageSize={pageSize}
|
|
116
|
+
pageSize={originalComponentProps.pageSize}
|
|
124
117
|
/>
|
|
125
118
|
),
|
|
126
119
|
};
|
|
127
120
|
case 'insertions':
|
|
128
121
|
return {
|
|
129
122
|
title: 'Insertions',
|
|
130
|
-
content:
|
|
123
|
+
content: (
|
|
124
|
+
<InsertionsTable data={filteredData.insertions} pageSize={originalComponentProps.pageSize} />
|
|
125
|
+
),
|
|
131
126
|
};
|
|
132
127
|
}
|
|
133
128
|
};
|
|
134
129
|
|
|
135
|
-
const tabs = views.map((view) => getTab(view));
|
|
130
|
+
const tabs = originalComponentProps.views.map((view) => getTab(view));
|
|
136
131
|
|
|
137
132
|
const toolbar = (activeTab: string) => (
|
|
138
133
|
<Toolbar
|
|
@@ -144,6 +139,7 @@ const MutationsTabs: FunctionComponent<MutationTabsProps> = ({ mutationsData, se
|
|
|
144
139
|
filteredData={filteredData}
|
|
145
140
|
proportionInterval={proportionInterval}
|
|
146
141
|
setProportionInterval={setProportionInterval}
|
|
142
|
+
originalComponentProps={originalComponentProps}
|
|
147
143
|
/>
|
|
148
144
|
);
|
|
149
145
|
|
|
@@ -159,6 +155,7 @@ type ToolbarProps = {
|
|
|
159
155
|
filteredData: { tableData: SubstitutionOrDeletionEntry[]; insertions: InsertionEntry[] };
|
|
160
156
|
proportionInterval: ProportionInterval;
|
|
161
157
|
setProportionInterval: Dispatch<StateUpdater<ProportionInterval>>;
|
|
158
|
+
originalComponentProps: MutationsProps;
|
|
162
159
|
};
|
|
163
160
|
|
|
164
161
|
const Toolbar: FunctionComponent<ToolbarProps> = ({
|
|
@@ -170,6 +167,7 @@ const Toolbar: FunctionComponent<ToolbarProps> = ({
|
|
|
170
167
|
filteredData,
|
|
171
168
|
proportionInterval,
|
|
172
169
|
setProportionInterval,
|
|
170
|
+
originalComponentProps,
|
|
173
171
|
}) => {
|
|
174
172
|
return (
|
|
175
173
|
<>
|
|
@@ -208,38 +206,47 @@ const Toolbar: FunctionComponent<ToolbarProps> = ({
|
|
|
208
206
|
filename='insertions.csv'
|
|
209
207
|
/>
|
|
210
208
|
)}
|
|
211
|
-
<MutationsInfo />
|
|
209
|
+
<MutationsInfo originalComponentProps={originalComponentProps} />
|
|
212
210
|
<Fullscreen />
|
|
213
211
|
</>
|
|
214
212
|
);
|
|
215
213
|
};
|
|
216
214
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
<
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
<
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
215
|
+
type MutationsInfoProps = {
|
|
216
|
+
originalComponentProps: MutationsProps;
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
const MutationsInfo: FunctionComponent<MutationsInfoProps> = ({ originalComponentProps }) => {
|
|
220
|
+
const lapis = useContext(LapisUrlContext);
|
|
221
|
+
|
|
222
|
+
return (
|
|
223
|
+
<Info>
|
|
224
|
+
<InfoHeadline1>Mutations</InfoHeadline1>
|
|
225
|
+
<InfoParagraph>
|
|
226
|
+
This shows mutations of a variant. There are three types of mutations:{' '}
|
|
227
|
+
<InfoLink href='https://www.genome.gov/genetics-glossary/Substitution'>substitutions</InfoLink>,{' '}
|
|
228
|
+
<InfoLink href='https://www.genome.gov/genetics-glossary/Deletion'>deletions</InfoLink> and{' '}
|
|
229
|
+
<InfoLink href='https://www.genome.gov/genetics-glossary/Insertion'>insertions</InfoLink>.
|
|
230
|
+
</InfoParagraph>
|
|
231
|
+
<InfoHeadline2>Proportion calculation</InfoHeadline2>
|
|
232
|
+
<InfoParagraph>
|
|
233
|
+
The proportion of a mutation is calculated by dividing the number of sequences with the mutation by the
|
|
234
|
+
total number of sequences with a non-ambiguous symbol at the position.
|
|
235
|
+
</InfoParagraph>
|
|
236
|
+
<InfoParagraph>
|
|
237
|
+
<b>Example:</b> Assume we look at nucleotide mutations at position 5 where the reference has a T and
|
|
238
|
+
assume there are 10 sequences in total:
|
|
239
|
+
<ul className='list-disc list-inside ml-2'>
|
|
240
|
+
<li>3 sequences have a C,</li>
|
|
241
|
+
<li>2 sequences have a T,</li>
|
|
242
|
+
<li>1 sequence has a G,</li>
|
|
243
|
+
<li>3 sequences have an N,</li>
|
|
244
|
+
<li>1 sequence has a Y (which means T or C),</li>
|
|
245
|
+
</ul>
|
|
246
|
+
then the proportion of the T5C mutation is 50%. The 4 sequences that have an N or Y are excluded from
|
|
247
|
+
the calculation.
|
|
248
|
+
</InfoParagraph>
|
|
249
|
+
<InfoComponentCode componentName='mutations' params={originalComponentProps} lapisUrl={lapis} />
|
|
250
|
+
</Info>
|
|
251
|
+
);
|
|
252
|
+
};
|
|
@@ -23,7 +23,7 @@ import { CsvDownloadButton } from '../components/csv-download-button';
|
|
|
23
23
|
import { ErrorBoundary } from '../components/error-boundary';
|
|
24
24
|
import { ErrorDisplay } from '../components/error-display';
|
|
25
25
|
import { Fullscreen } from '../components/fullscreen';
|
|
26
|
-
import Info from '../components/info';
|
|
26
|
+
import Info, { InfoComponentCode, InfoHeadline1, InfoParagraph } from '../components/info';
|
|
27
27
|
import { LoadingDisplay } from '../components/loading-display';
|
|
28
28
|
import { type DisplayedMutationType, MutationTypeSelector } from '../components/mutation-type-selector';
|
|
29
29
|
import { NoDataDisplay } from '../components/no-data-display';
|
|
@@ -36,7 +36,9 @@ import { useWebWorker } from '../webWorkers/useWebWorker';
|
|
|
36
36
|
|
|
37
37
|
export type View = 'grid';
|
|
38
38
|
|
|
39
|
-
export interface
|
|
39
|
+
export interface MutationsOverTimeProps {
|
|
40
|
+
width: string;
|
|
41
|
+
height: string;
|
|
40
42
|
lapisFilter: LapisFilter;
|
|
41
43
|
sequenceType: SequenceType;
|
|
42
44
|
views: View[];
|
|
@@ -44,31 +46,22 @@ export interface MutationsOverTimeInnerProps {
|
|
|
44
46
|
lapisDateField: string;
|
|
45
47
|
}
|
|
46
48
|
|
|
47
|
-
export
|
|
48
|
-
width
|
|
49
|
-
height: string;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
export const MutationsOverTime: FunctionComponent<MutationsOverTimeProps> = ({ width, height, ...innerProps }) => {
|
|
49
|
+
export const MutationsOverTime: FunctionComponent<MutationsOverTimeProps> = (componentProps) => {
|
|
50
|
+
const { width, height } = componentProps;
|
|
53
51
|
const size = { height, width };
|
|
54
52
|
|
|
55
53
|
return (
|
|
56
54
|
<ErrorBoundary size={size}>
|
|
57
55
|
<ResizeContainer size={size}>
|
|
58
|
-
<MutationsOverTimeInner {...
|
|
56
|
+
<MutationsOverTimeInner {...componentProps} />
|
|
59
57
|
</ResizeContainer>
|
|
60
58
|
</ErrorBoundary>
|
|
61
59
|
);
|
|
62
60
|
};
|
|
63
61
|
|
|
64
|
-
export const MutationsOverTimeInner: FunctionComponent<
|
|
65
|
-
lapisFilter,
|
|
66
|
-
sequenceType,
|
|
67
|
-
views,
|
|
68
|
-
granularity,
|
|
69
|
-
lapisDateField,
|
|
70
|
-
}) => {
|
|
62
|
+
export const MutationsOverTimeInner: FunctionComponent<MutationsOverTimeProps> = (componentProps) => {
|
|
71
63
|
const lapis = useContext(LapisUrlContext);
|
|
64
|
+
const { lapisFilter, sequenceType, granularity, lapisDateField } = componentProps;
|
|
72
65
|
|
|
73
66
|
const { data, error, isLoading } = useWebWorker<MutationOverTimeQuery, MutationOverTimeWorkerResponse>(
|
|
74
67
|
{
|
|
@@ -99,29 +92,26 @@ export const MutationsOverTimeInner: FunctionComponent<MutationsOverTimeInnerPro
|
|
|
99
92
|
<MutationsOverTimeTabs
|
|
100
93
|
overallMutationData={overallMutationData}
|
|
101
94
|
mutationOverTimeData={mutationOverTimeData}
|
|
102
|
-
|
|
103
|
-
views={views}
|
|
95
|
+
originalComponentProps={componentProps}
|
|
104
96
|
/>
|
|
105
97
|
);
|
|
106
98
|
};
|
|
107
99
|
|
|
108
100
|
type MutationOverTimeTabsProps = {
|
|
109
101
|
mutationOverTimeData: BaseMutationOverTimeDataMap;
|
|
110
|
-
|
|
111
|
-
views: View[];
|
|
102
|
+
originalComponentProps: MutationsOverTimeProps;
|
|
112
103
|
overallMutationData: SubstitutionOrDeletionEntry<Substitution, Deletion>[];
|
|
113
104
|
};
|
|
114
105
|
|
|
115
106
|
const MutationsOverTimeTabs: FunctionComponent<MutationOverTimeTabsProps> = ({
|
|
116
107
|
mutationOverTimeData,
|
|
117
|
-
|
|
118
|
-
views,
|
|
108
|
+
originalComponentProps,
|
|
119
109
|
overallMutationData,
|
|
120
110
|
}) => {
|
|
121
111
|
const [proportionInterval, setProportionInterval] = useState({ min: 0.05, max: 0.9 });
|
|
122
112
|
const [colorScale, setColorScale] = useState<ColorScale>({ min: 0, max: 1, color: 'indigo' });
|
|
123
113
|
|
|
124
|
-
const [displayedSegments, setDisplayedSegments] = useDisplayedSegments(sequenceType);
|
|
114
|
+
const [displayedSegments, setDisplayedSegments] = useDisplayedSegments(originalComponentProps.sequenceType);
|
|
125
115
|
const [displayedMutationTypes, setDisplayedMutationTypes] = useState<DisplayedMutationType[]>([
|
|
126
116
|
{ label: 'Substitutions', checked: true, type: 'substitution' },
|
|
127
117
|
{ label: 'Deletions', checked: true, type: 'deletion' },
|
|
@@ -153,7 +143,7 @@ const MutationsOverTimeTabs: FunctionComponent<MutationOverTimeTabsProps> = ({
|
|
|
153
143
|
}
|
|
154
144
|
};
|
|
155
145
|
|
|
156
|
-
const tabs = views.map((view) => getTab(view));
|
|
146
|
+
const tabs = originalComponentProps.views.map((view) => getTab(view));
|
|
157
147
|
|
|
158
148
|
const toolbar = (activeTab: string) => (
|
|
159
149
|
<Toolbar
|
|
@@ -167,6 +157,7 @@ const MutationsOverTimeTabs: FunctionComponent<MutationOverTimeTabsProps> = ({
|
|
|
167
157
|
filteredData={filteredData}
|
|
168
158
|
colorScale={colorScale}
|
|
169
159
|
setColorScale={setColorScale}
|
|
160
|
+
originalComponentProps={originalComponentProps}
|
|
170
161
|
/>
|
|
171
162
|
);
|
|
172
163
|
|
|
@@ -184,6 +175,7 @@ type ToolbarProps = {
|
|
|
184
175
|
filteredData: MutationOverTimeDataMap;
|
|
185
176
|
colorScale: ColorScale;
|
|
186
177
|
setColorScale: Dispatch<StateUpdater<ColorScale>>;
|
|
178
|
+
originalComponentProps: MutationsOverTimeProps;
|
|
187
179
|
};
|
|
188
180
|
|
|
189
181
|
const Toolbar: FunctionComponent<ToolbarProps> = ({
|
|
@@ -197,6 +189,7 @@ const Toolbar: FunctionComponent<ToolbarProps> = ({
|
|
|
197
189
|
filteredData,
|
|
198
190
|
colorScale,
|
|
199
191
|
setColorScale,
|
|
192
|
+
originalComponentProps,
|
|
200
193
|
}) => {
|
|
201
194
|
return (
|
|
202
195
|
<>
|
|
@@ -219,12 +212,27 @@ const Toolbar: FunctionComponent<ToolbarProps> = ({
|
|
|
219
212
|
getData={() => getDownloadData(filteredData)}
|
|
220
213
|
filename='mutations_over_time.csv'
|
|
221
214
|
/>
|
|
222
|
-
<
|
|
215
|
+
<MutationsOverTimeInfo originalComponentProps={originalComponentProps} />
|
|
223
216
|
<Fullscreen />
|
|
224
217
|
</>
|
|
225
218
|
);
|
|
226
219
|
};
|
|
227
220
|
|
|
221
|
+
type MutationsOverTimeInfoProps = {
|
|
222
|
+
originalComponentProps: MutationsOverTimeProps;
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
const MutationsOverTimeInfo: FunctionComponent<MutationsOverTimeInfoProps> = ({ originalComponentProps }) => {
|
|
226
|
+
const lapis = useContext(LapisUrlContext);
|
|
227
|
+
return (
|
|
228
|
+
<Info>
|
|
229
|
+
<InfoHeadline1>Info for mutations over time</InfoHeadline1>
|
|
230
|
+
<InfoParagraph>TODO: https://github.com/GenSpectrum/dashboard-components/issues/441</InfoParagraph>
|
|
231
|
+
<InfoComponentCode componentName='mutations-over-time' params={originalComponentProps} lapisUrl={lapis} />
|
|
232
|
+
</Info>
|
|
233
|
+
);
|
|
234
|
+
};
|
|
235
|
+
|
|
228
236
|
function getDownloadData(filteredData: MutationOverTimeDataMap) {
|
|
229
237
|
const dates = filteredData.getSecondAxisKeys().map((date) => toTemporalClass(date));
|
|
230
238
|
|
|
@@ -15,7 +15,7 @@ import { CsvDownloadButton } from '../components/csv-download-button';
|
|
|
15
15
|
import { ErrorBoundary } from '../components/error-boundary';
|
|
16
16
|
import { ErrorDisplay } from '../components/error-display';
|
|
17
17
|
import { Fullscreen } from '../components/fullscreen';
|
|
18
|
-
import Info, { InfoHeadline1, InfoParagraph } from '../components/info';
|
|
18
|
+
import Info, { InfoComponentCode, InfoHeadline1, InfoParagraph } from '../components/info';
|
|
19
19
|
import { LoadingDisplay } from '../components/loading-display';
|
|
20
20
|
import { NoDataDisplay } from '../components/no-data-display';
|
|
21
21
|
import { ResizeContainer } from '../components/resize-container';
|
|
@@ -26,12 +26,9 @@ import { useQuery } from '../useQuery';
|
|
|
26
26
|
|
|
27
27
|
type NumberSequencesOverTimeView = 'bar' | 'line' | 'table';
|
|
28
28
|
|
|
29
|
-
export interface NumberSequencesOverTimeProps
|
|
29
|
+
export interface NumberSequencesOverTimeProps {
|
|
30
30
|
width: string;
|
|
31
31
|
height: string;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
interface NumberSequencesOverTimeInnerProps {
|
|
35
32
|
lapisFilter: NamedLapisFilter | NamedLapisFilter[];
|
|
36
33
|
lapisDateField: string;
|
|
37
34
|
views: NumberSequencesOverTimeView[];
|
|
@@ -40,26 +37,21 @@ interface NumberSequencesOverTimeInnerProps {
|
|
|
40
37
|
pageSize: boolean | number;
|
|
41
38
|
}
|
|
42
39
|
|
|
43
|
-
export const NumberSequencesOverTime = (
|
|
40
|
+
export const NumberSequencesOverTime = (componentProps: NumberSequencesOverTimeProps) => {
|
|
41
|
+
const { width, height } = componentProps;
|
|
44
42
|
const size = { height, width };
|
|
45
43
|
|
|
46
44
|
return (
|
|
47
45
|
<ErrorBoundary size={size}>
|
|
48
46
|
<ResizeContainer size={size}>
|
|
49
|
-
<NumberSequencesOverTimeInner {...
|
|
47
|
+
<NumberSequencesOverTimeInner {...componentProps} />
|
|
50
48
|
</ResizeContainer>
|
|
51
49
|
</ErrorBoundary>
|
|
52
50
|
);
|
|
53
51
|
};
|
|
54
52
|
|
|
55
|
-
const NumberSequencesOverTimeInner = ({
|
|
56
|
-
lapisFilter,
|
|
57
|
-
granularity,
|
|
58
|
-
smoothingWindow,
|
|
59
|
-
lapisDateField,
|
|
60
|
-
views,
|
|
61
|
-
pageSize,
|
|
62
|
-
}: NumberSequencesOverTimeInnerProps) => {
|
|
53
|
+
const NumberSequencesOverTimeInner = (componentProps: NumberSequencesOverTimeProps) => {
|
|
54
|
+
const { lapisFilter, lapisDateField, granularity, smoothingWindow } = componentProps;
|
|
63
55
|
const lapis = useContext(LapisUrlContext);
|
|
64
56
|
|
|
65
57
|
const { data, error, isLoading } = useQuery(
|
|
@@ -79,32 +71,15 @@ const NumberSequencesOverTimeInner = ({
|
|
|
79
71
|
return <NoDataDisplay />;
|
|
80
72
|
}
|
|
81
73
|
|
|
82
|
-
return
|
|
83
|
-
<NumberSequencesOverTimeTabs
|
|
84
|
-
views={views}
|
|
85
|
-
data={data}
|
|
86
|
-
granularity={granularity}
|
|
87
|
-
smoothingWindow={smoothingWindow}
|
|
88
|
-
pageSize={pageSize}
|
|
89
|
-
/>
|
|
90
|
-
);
|
|
74
|
+
return <NumberSequencesOverTimeTabs data={data} originalComponentProps={componentProps} />;
|
|
91
75
|
};
|
|
92
76
|
|
|
93
77
|
interface NumberSequencesOverTimeTabsProps {
|
|
94
|
-
views: NumberSequencesOverTimeView[];
|
|
95
78
|
data: NumberOfSequencesDatasets;
|
|
96
|
-
|
|
97
|
-
smoothingWindow: number;
|
|
98
|
-
pageSize: boolean | number;
|
|
79
|
+
originalComponentProps: NumberSequencesOverTimeProps;
|
|
99
80
|
}
|
|
100
81
|
|
|
101
|
-
const NumberSequencesOverTimeTabs = ({
|
|
102
|
-
views,
|
|
103
|
-
data,
|
|
104
|
-
granularity,
|
|
105
|
-
smoothingWindow,
|
|
106
|
-
pageSize,
|
|
107
|
-
}: NumberSequencesOverTimeTabsProps) => {
|
|
82
|
+
const NumberSequencesOverTimeTabs = ({ data, originalComponentProps }: NumberSequencesOverTimeTabsProps) => {
|
|
108
83
|
const [yAxisScaleType, setYAxisScaleType] = useState<ScaleType>('linear');
|
|
109
84
|
|
|
110
85
|
const getTab = (view: NumberSequencesOverTimeView) => {
|
|
@@ -122,7 +97,13 @@ const NumberSequencesOverTimeTabs = ({
|
|
|
122
97
|
case 'table':
|
|
123
98
|
return {
|
|
124
99
|
title: 'Table',
|
|
125
|
-
content:
|
|
100
|
+
content: (
|
|
101
|
+
<NumberSequencesOverTimeTable
|
|
102
|
+
data={data}
|
|
103
|
+
granularity={originalComponentProps.granularity}
|
|
104
|
+
pageSize={originalComponentProps.pageSize}
|
|
105
|
+
/>
|
|
106
|
+
),
|
|
126
107
|
};
|
|
127
108
|
default:
|
|
128
109
|
throw new Error(`Unknown view: ${view}`);
|
|
@@ -131,15 +112,14 @@ const NumberSequencesOverTimeTabs = ({
|
|
|
131
112
|
|
|
132
113
|
return (
|
|
133
114
|
<Tabs
|
|
134
|
-
tabs={views.map((view) => getTab(view))}
|
|
115
|
+
tabs={originalComponentProps.views.map((view) => getTab(view))}
|
|
135
116
|
toolbar={(activeTab) => (
|
|
136
117
|
<Toolbar
|
|
137
118
|
activeTab={activeTab}
|
|
138
119
|
data={data}
|
|
139
|
-
granularity={granularity}
|
|
140
|
-
smoothingWindow={smoothingWindow}
|
|
141
120
|
yAxisScaleType={yAxisScaleType}
|
|
142
121
|
setYAxisScaleType={setYAxisScaleType}
|
|
122
|
+
originalComponentProps={originalComponentProps}
|
|
143
123
|
/>
|
|
144
124
|
)}
|
|
145
125
|
/>
|
|
@@ -149,20 +129,12 @@ const NumberSequencesOverTimeTabs = ({
|
|
|
149
129
|
interface ToolbarProps {
|
|
150
130
|
activeTab: string;
|
|
151
131
|
data: NumberOfSequencesDatasets;
|
|
152
|
-
granularity: TemporalGranularity;
|
|
153
132
|
yAxisScaleType: ScaleType;
|
|
154
133
|
setYAxisScaleType: (scaleType: ScaleType) => void;
|
|
155
|
-
|
|
134
|
+
originalComponentProps: NumberSequencesOverTimeProps;
|
|
156
135
|
}
|
|
157
136
|
|
|
158
|
-
const Toolbar = ({
|
|
159
|
-
activeTab,
|
|
160
|
-
data,
|
|
161
|
-
granularity,
|
|
162
|
-
yAxisScaleType,
|
|
163
|
-
setYAxisScaleType,
|
|
164
|
-
smoothingWindow,
|
|
165
|
-
}: ToolbarProps) => {
|
|
137
|
+
const Toolbar = ({ activeTab, data, yAxisScaleType, setYAxisScaleType, originalComponentProps }: ToolbarProps) => {
|
|
166
138
|
return (
|
|
167
139
|
<>
|
|
168
140
|
{activeTab !== 'Table' && (
|
|
@@ -174,29 +146,39 @@ const Toolbar = ({
|
|
|
174
146
|
)}
|
|
175
147
|
<CsvDownloadButton
|
|
176
148
|
className='mx-1 btn btn-xs'
|
|
177
|
-
getData={() => getNumberOfSequencesOverTimeTableData(data, granularity)}
|
|
149
|
+
getData={() => getNumberOfSequencesOverTimeTableData(data, originalComponentProps.granularity)}
|
|
178
150
|
filename='number_of_sequences_over_time.csv'
|
|
179
151
|
/>
|
|
180
|
-
<NumberSequencesOverTimeInfo
|
|
152
|
+
<NumberSequencesOverTimeInfo originalComponentProps={originalComponentProps} />
|
|
181
153
|
<Fullscreen />
|
|
182
154
|
</>
|
|
183
155
|
);
|
|
184
156
|
};
|
|
185
157
|
|
|
186
158
|
type NumberSequencesOverTimeInfoProps = {
|
|
187
|
-
|
|
188
|
-
smoothingWindow: number;
|
|
159
|
+
originalComponentProps: NumberSequencesOverTimeProps;
|
|
189
160
|
};
|
|
190
161
|
|
|
191
162
|
const NumberSequencesOverTimeInfo: FunctionComponent<NumberSequencesOverTimeInfoProps> = ({
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
<
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
163
|
+
originalComponentProps,
|
|
164
|
+
}) => {
|
|
165
|
+
const lapis = useContext(LapisUrlContext);
|
|
166
|
+
|
|
167
|
+
return (
|
|
168
|
+
<Info>
|
|
169
|
+
<InfoHeadline1>Number of sequences over time</InfoHeadline1>
|
|
170
|
+
<InfoParagraph>
|
|
171
|
+
This presents the number of available sequences of a variant per{' '}
|
|
172
|
+
<b>{originalComponentProps.granularity}</b>
|
|
173
|
+
{originalComponentProps.smoothingWindow > 0 &&
|
|
174
|
+
`, smoothed using a ${originalComponentProps.smoothingWindow}-${originalComponentProps.granularity} sliding window`}
|
|
175
|
+
.
|
|
176
|
+
</InfoParagraph>
|
|
177
|
+
<InfoComponentCode
|
|
178
|
+
componentName='number-sequences-over-time'
|
|
179
|
+
params={originalComponentProps}
|
|
180
|
+
lapisUrl={lapis}
|
|
181
|
+
/>
|
|
182
|
+
</Info>
|
|
183
|
+
);
|
|
184
|
+
};
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
} from '../../query/queryPrevalenceOverTime';
|
|
10
10
|
import { sortNullToBeginningThenByDate } from '../../utils/sort';
|
|
11
11
|
import GsChart from '../components/chart';
|
|
12
|
+
import { NoDataDisplay } from '../components/no-data-display';
|
|
12
13
|
import { LogitScale } from '../shared/charts/LogitScale';
|
|
13
14
|
import { singleGraphColorRGBAById } from '../shared/charts/colors';
|
|
14
15
|
import { type ConfidenceIntervalMethod, wilson95PercentConfidenceInterval } from '../shared/charts/confideceInterval';
|
|
@@ -30,12 +31,18 @@ const PrevalenceOverTimeBarChart = ({
|
|
|
30
31
|
confidenceIntervalMethod,
|
|
31
32
|
yAxisMaxConfig,
|
|
32
33
|
}: PrevalenceOverTimeBarChartProps) => {
|
|
33
|
-
const nullFirstData = data
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
34
|
+
const nullFirstData = data
|
|
35
|
+
.filter((prevalenceOverTimeData) => prevalenceOverTimeData.content.length > 0)
|
|
36
|
+
.map((variantData) => {
|
|
37
|
+
return {
|
|
38
|
+
content: variantData.content.sort(sortNullToBeginningThenByDate),
|
|
39
|
+
displayName: variantData.displayName,
|
|
40
|
+
};
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
if (nullFirstData.length === 0) {
|
|
44
|
+
return <NoDataDisplay />;
|
|
45
|
+
}
|
|
39
46
|
|
|
40
47
|
const datasets = nullFirstData.map((graphData, index) => getDataset(graphData, index, confidenceIntervalMethod));
|
|
41
48
|
|
|
@@ -5,6 +5,7 @@ import { type PrevalenceOverTimeData } from '../../query/queryPrevalenceOverTime
|
|
|
5
5
|
import { addUnit, minusTemporal } from '../../utils/temporalClass';
|
|
6
6
|
import { getMinMaxNumber } from '../../utils/utils';
|
|
7
7
|
import GsChart from '../components/chart';
|
|
8
|
+
import { NoDataDisplay } from '../components/no-data-display';
|
|
8
9
|
import { LogitScale } from '../shared/charts/LogitScale';
|
|
9
10
|
import { singleGraphColorRGBAById } from '../shared/charts/colors';
|
|
10
11
|
import { getYAxisMax, type YAxisMaxConfig } from '../shared/charts/getYAxisMax';
|
|
@@ -23,12 +24,18 @@ const PrevalenceOverTimeBubbleChart = ({
|
|
|
23
24
|
yAxisScaleType,
|
|
24
25
|
yAxisMaxConfig,
|
|
25
26
|
}: PrevalenceOverTimeBubbleChartProps) => {
|
|
26
|
-
const nonNullDateRangeData = data
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
27
|
+
const nonNullDateRangeData = data
|
|
28
|
+
.filter((prevalenceOverTimeData) => prevalenceOverTimeData.content.length > 0)
|
|
29
|
+
.map((variantData) => {
|
|
30
|
+
return {
|
|
31
|
+
content: variantData.content.filter((dataPoint) => dataPoint.dateRange !== null),
|
|
32
|
+
displayName: variantData.displayName,
|
|
33
|
+
};
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
if (nonNullDateRangeData.length === 0) {
|
|
37
|
+
return <NoDataDisplay />;
|
|
38
|
+
}
|
|
32
39
|
|
|
33
40
|
const firstDate = nonNullDateRangeData[0].content[0].dateRange!;
|
|
34
41
|
const total = nonNullDateRangeData.map((graphData) => graphData.content.map((dataPoint) => dataPoint.total)).flat();
|