@genspectrum/dashboard-components 1.16.0 → 1.17.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.
- package/custom-elements.json +3 -3
- package/dist/components.d.ts +4 -4
- package/dist/components.js +449 -246
- package/dist/components.js.map +1 -1
- package/dist/util.d.ts +6 -6
- package/package.json +1 -1
- package/src/preact/MutationAnnotationsContext.tsx +1 -1
- package/src/preact/components/csv-download-button.tsx +22 -14
- package/src/preact/components/features-over-time-grid.tsx +189 -43
- package/src/preact/components/mutations-over-time-mutations-filter.stories.tsx +1 -1
- package/src/preact/components/mutations-over-time-mutations-filter.tsx +1 -1
- package/src/preact/mutationsOverTime/__mockData__/aminoAcidMutationsByDay/aminoAcidMutationsOverTimePage1.json +52 -0
- package/src/preact/mutationsOverTime/__mockData__/byWeek/mutationsOverTimePage1.json +76 -0
- package/src/preact/mutationsOverTime/__mockData__/defaultMockData/mockDefaultMutationsOverTimeWithFilter.json +43 -0
- package/src/preact/mutationsOverTime/__mockData__/defaultMockData/mutationsOverTimePage1.json +126 -0
- package/src/preact/mutationsOverTime/__mockData__/defaultMockData/mutationsOverTimePage2.json +116 -0
- package/src/preact/mutationsOverTime/__mockData__/defaultMockData/mutationsOverTimePageSize20.json +216 -0
- package/src/preact/mutationsOverTime/getFilteredMutationCodes.spec.ts +236 -0
- package/src/preact/mutationsOverTime/{getFilteredMutationsOverTimeData.ts → getFilteredMutationCodes.ts} +29 -44
- package/src/preact/mutationsOverTime/mutations-over-time.stories.tsx +128 -23
- package/src/preact/mutationsOverTime/mutations-over-time.tsx +139 -74
- package/src/preact/mutationsOverTime/useMutationsOverTimePageData.ts +111 -0
- package/src/preact/shared/tanstackTable/pagination-context.tsx +5 -2
- package/src/preact/shared/tanstackTable/pagination.tsx +11 -9
- package/src/preact/shared/tanstackTable/tanstackTable.tsx +7 -4
- package/src/preact/wastewater/mutationsOverTime/wastewater-mutations-over-time.tsx +1 -1
- package/src/query/queryMutationsOverTime.spec.ts +187 -662
- package/src/query/queryMutationsOverTime.ts +46 -33
- package/src/utils/useControlledState.ts +15 -0
- package/src/web-components/visualization/gs-mutations-over-time.stories.ts +78 -22
- package/standalone-bundle/dashboard-components.js +6872 -6690
- package/standalone-bundle/dashboard-components.js.map +1 -1
- package/src/preact/mutationsOverTime/__mockData__/aminoAcidMutationsByDay/aminoAcidMutationsOverTime.json +0 -5496
- package/src/preact/mutationsOverTime/__mockData__/byWeek/mutationsOverTime.json +0 -7100
- package/src/preact/mutationsOverTime/__mockData__/defaultMockData/mutationsOverTime.json +0 -12646
- package/src/preact/mutationsOverTime/getFilteredMutationsOverTime.spec.ts +0 -417
|
@@ -11,8 +11,8 @@ import {
|
|
|
11
11
|
type TemporalGranularity,
|
|
12
12
|
} from '../types';
|
|
13
13
|
import { type Map2DContents } from '../utils/map2d';
|
|
14
|
-
import { type Deletion, type Substitution,
|
|
15
|
-
import { type Temporal } from '../utils/temporalClass';
|
|
14
|
+
import { type Deletion, DeletionClass, type Substitution, SubstitutionClass } from '../utils/mutations';
|
|
15
|
+
import { type Temporal, type TemporalClass } from '../utils/temporalClass';
|
|
16
16
|
|
|
17
17
|
export type ProportionValue =
|
|
18
18
|
| {
|
|
@@ -89,21 +89,19 @@ async function queryOverallMutationData({
|
|
|
89
89
|
lapisFilter,
|
|
90
90
|
sequenceType,
|
|
91
91
|
lapis,
|
|
92
|
-
granularity,
|
|
93
92
|
lapisDateField,
|
|
94
93
|
includeMutations,
|
|
94
|
+
requestedDateRanges,
|
|
95
95
|
signal,
|
|
96
96
|
}: {
|
|
97
97
|
lapisFilter: LapisFilter;
|
|
98
98
|
sequenceType: SequenceType;
|
|
99
99
|
lapis: string;
|
|
100
|
-
granularity: TemporalGranularity;
|
|
101
100
|
lapisDateField: string;
|
|
102
101
|
includeMutations?: string[];
|
|
102
|
+
requestedDateRanges: TemporalClass[];
|
|
103
103
|
signal?: AbortSignal;
|
|
104
104
|
}) {
|
|
105
|
-
const requestedDateRanges = await queryDatesInDataset(lapisFilter, lapis, granularity, lapisDateField, signal);
|
|
106
|
-
|
|
107
105
|
if (requestedDateRanges.length === 0) {
|
|
108
106
|
if (includeMutations) {
|
|
109
107
|
return {
|
|
@@ -142,7 +140,11 @@ async function queryOverallMutationData({
|
|
|
142
140
|
return dataPromise;
|
|
143
141
|
}
|
|
144
142
|
|
|
145
|
-
|
|
143
|
+
/**
|
|
144
|
+
* Phase 1: Fetch date buckets and the full sorted list of mutations above the threshold.
|
|
145
|
+
* This is cheap and does not depend on the current page.
|
|
146
|
+
*/
|
|
147
|
+
export async function queryMutationsOverTimeMetadata(
|
|
146
148
|
lapisFilter: LapisFilter,
|
|
147
149
|
sequenceType: SequenceType,
|
|
148
150
|
lapis: string,
|
|
@@ -168,12 +170,38 @@ export async function queryMutationsOverTimeData(
|
|
|
168
170
|
lapis,
|
|
169
171
|
lapisDateField,
|
|
170
172
|
includeMutations: displayMutations,
|
|
171
|
-
|
|
173
|
+
requestedDateRanges,
|
|
174
|
+
signal,
|
|
172
175
|
}).then((r) => r.content);
|
|
173
176
|
|
|
174
177
|
overallMutationData.sort((a, b) => sortSubstitutionsAndDeletions(a.mutation, b.mutation));
|
|
175
178
|
|
|
176
|
-
|
|
179
|
+
return { overallMutationData, requestedDateRanges };
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
export type MutationsOverTimeMetadata = Awaited<ReturnType<typeof queryMutationsOverTimeMetadata>>;
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Phase 2: Fetch the time-series matrix for a specific slice of mutations (e.g. one page).
|
|
186
|
+
* Pass `requestedDateRanges` from Phase 1, plus a slice of mutation codes to fetch.
|
|
187
|
+
*/
|
|
188
|
+
export async function queryMutationsOverTimePage(
|
|
189
|
+
lapisFilter: LapisFilter,
|
|
190
|
+
lapis: string,
|
|
191
|
+
lapisDateField: string,
|
|
192
|
+
sequenceType: SequenceType,
|
|
193
|
+
requestedDateRanges: MutationsOverTimeMetadata['requestedDateRanges'],
|
|
194
|
+
includeMutationCodes: string[],
|
|
195
|
+
signal?: AbortSignal,
|
|
196
|
+
) {
|
|
197
|
+
if (includeMutationCodes.length === 0 || requestedDateRanges.length === 0) {
|
|
198
|
+
return new BaseMutationOverTimeDataMap({
|
|
199
|
+
keysFirstAxis: new Map(),
|
|
200
|
+
keysSecondAxis: new Map(requestedDateRanges.map((date) => [date.dateString, date])),
|
|
201
|
+
data: new Map(),
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
|
|
177
205
|
const apiResult = await fetchMutationsOverTime(
|
|
178
206
|
lapis,
|
|
179
207
|
{
|
|
@@ -182,34 +210,22 @@ export async function queryMutationsOverTimeData(
|
|
|
182
210
|
dateFrom: date.firstDay.toString(),
|
|
183
211
|
dateTo: date.lastDay.toString(),
|
|
184
212
|
})),
|
|
185
|
-
includeMutations,
|
|
213
|
+
includeMutations: includeMutationCodes,
|
|
186
214
|
dateField: lapisDateField,
|
|
187
215
|
},
|
|
188
216
|
sequenceType,
|
|
189
217
|
signal,
|
|
190
218
|
);
|
|
191
219
|
|
|
220
|
+
return buildMutationOverTimeDataMap(apiResult, requestedDateRanges);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function buildMutationOverTimeDataMap(
|
|
224
|
+
apiResult: Awaited<ReturnType<typeof fetchMutationsOverTime>>,
|
|
225
|
+
requestedDateRanges: MutationsOverTimeMetadata['requestedDateRanges'],
|
|
226
|
+
) {
|
|
192
227
|
const totalCounts = apiResult.data.totalCountsByDateRange;
|
|
193
228
|
const responseMutations = apiResult.data.mutations.map(parseMutationCode);
|
|
194
|
-
const mutationEntries: SubstitutionOrDeletionEntry[] = responseMutations.map((mutation, i) => {
|
|
195
|
-
const numbers = {
|
|
196
|
-
count: overallMutationData[i].count,
|
|
197
|
-
proportion: overallMutationData[i].proportion,
|
|
198
|
-
};
|
|
199
|
-
if (mutation.type === 'deletion') {
|
|
200
|
-
return {
|
|
201
|
-
type: 'deletion',
|
|
202
|
-
mutation,
|
|
203
|
-
...numbers,
|
|
204
|
-
};
|
|
205
|
-
} else {
|
|
206
|
-
return {
|
|
207
|
-
type: 'substitution',
|
|
208
|
-
mutation,
|
|
209
|
-
...numbers,
|
|
210
|
-
};
|
|
211
|
-
}
|
|
212
|
-
});
|
|
213
229
|
|
|
214
230
|
const mutationOverTimeData: Map2DContents<Substitution | Deletion, Temporal, ProportionValue> = {
|
|
215
231
|
keysFirstAxis: new Map(responseMutations.map((mutation) => [mutation.code, mutation])),
|
|
@@ -251,10 +267,7 @@ export async function queryMutationsOverTimeData(
|
|
|
251
267
|
),
|
|
252
268
|
};
|
|
253
269
|
|
|
254
|
-
return
|
|
255
|
-
mutationOverTimeData: new BaseMutationOverTimeDataMap(mutationOverTimeData),
|
|
256
|
-
overallMutationData: mutationEntries,
|
|
257
|
-
};
|
|
270
|
+
return new BaseMutationOverTimeDataMap(mutationOverTimeData);
|
|
258
271
|
}
|
|
259
272
|
|
|
260
273
|
function parseMutationCode(code: string): SubstitutionClass | DeletionClass {
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { useEffect, useState } from 'preact/hooks';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* For when state is supplied via an attribute to the web component,
|
|
5
|
+
* and we need to update the internal state when the attribute value changes
|
|
6
|
+
*/
|
|
7
|
+
export function useControlledState<S>(initialState: S) {
|
|
8
|
+
const [state, setState] = useState(initialState);
|
|
9
|
+
|
|
10
|
+
useEffect(() => {
|
|
11
|
+
setState(initialState);
|
|
12
|
+
}, [initialState]);
|
|
13
|
+
|
|
14
|
+
return [state, setState] as const;
|
|
15
|
+
}
|
|
@@ -6,10 +6,11 @@ import '../gs-app';
|
|
|
6
6
|
import { withComponentDocs } from '../../../.storybook/ComponentDocsBlock';
|
|
7
7
|
import { LAPIS_URL } from '../../constants';
|
|
8
8
|
import mockAminoAcidMutationsByDayAminoAcidMutations from '../../preact/mutationsOverTime/__mockData__/aminoAcidMutationsByDay/aminoAcidMutations.json';
|
|
9
|
-
import
|
|
10
|
-
import
|
|
9
|
+
import mockAminoAcidMutationsByDayAminoAcidMutationsOverTimePage1 from '../../preact/mutationsOverTime/__mockData__/aminoAcidMutationsByDay/aminoAcidMutationsOverTimePage1.json';
|
|
10
|
+
import mockByWeekMutationsOverTimePage1 from '../../preact/mutationsOverTime/__mockData__/byWeek/mutationsOverTimePage1.json';
|
|
11
11
|
import mockByWeekNucleotideMutations from '../../preact/mutationsOverTime/__mockData__/byWeek/nucleotideMutations.json';
|
|
12
|
-
import
|
|
12
|
+
import mockDefaultMutationsOverTimePage1 from '../../preact/mutationsOverTime/__mockData__/defaultMockData/mutationsOverTimePage1.json';
|
|
13
|
+
import mockDefaultMutationsOverTimePageSize20 from '../../preact/mutationsOverTime/__mockData__/defaultMockData/mutationsOverTimePageSize20.json';
|
|
13
14
|
import mockDefaultNucleotideMutations from '../../preact/mutationsOverTime/__mockData__/defaultMockData/nucleotideMutations.json';
|
|
14
15
|
import mockWithDisplayMutationsMutationsOverTime from '../../preact/mutationsOverTime/__mockData__/withDisplayMutations/mutationsOverTime.json';
|
|
15
16
|
import { type MutationsOverTimeProps } from '../../preact/mutationsOverTime/mutations-over-time';
|
|
@@ -91,11 +92,7 @@ const meta: Meta<Required<MutationsOverTimeProps>> = {
|
|
|
91
92
|
matcher: {
|
|
92
93
|
url: `${LAPIS_URL}/component/nucleotideMutationsOverTime`,
|
|
93
94
|
body: {
|
|
94
|
-
filters: {
|
|
95
|
-
pangoLineage: 'JN.1*',
|
|
96
|
-
dateFrom: '2024-01-15',
|
|
97
|
-
dateTo: '2024-07-10',
|
|
98
|
-
},
|
|
95
|
+
filters: { pangoLineage: 'JN.1*', dateFrom: '2024-01-15', dateTo: '2024-07-10' },
|
|
99
96
|
dateRanges: [
|
|
100
97
|
{ dateFrom: '2024-01-01', dateTo: '2024-01-31' },
|
|
101
98
|
{ dateFrom: '2024-02-01', dateTo: '2024-02-29' },
|
|
@@ -105,12 +102,67 @@ const meta: Meta<Required<MutationsOverTimeProps>> = {
|
|
|
105
102
|
{ dateFrom: '2024-06-01', dateTo: '2024-06-30' },
|
|
106
103
|
{ dateFrom: '2024-07-01', dateTo: '2024-07-31' },
|
|
107
104
|
],
|
|
105
|
+
includeMutations: [
|
|
106
|
+
'C44T',
|
|
107
|
+
'C774T',
|
|
108
|
+
'C7113T',
|
|
109
|
+
'C12616T',
|
|
110
|
+
'A13121T',
|
|
111
|
+
'G15372T',
|
|
112
|
+
'G17334T',
|
|
113
|
+
'T18453C',
|
|
114
|
+
'A19722G',
|
|
115
|
+
'T21653-',
|
|
116
|
+
],
|
|
117
|
+
dateField: 'date',
|
|
118
|
+
},
|
|
119
|
+
response: {
|
|
120
|
+
status: 200,
|
|
121
|
+
body: mockDefaultMutationsOverTimePage1,
|
|
122
|
+
},
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
matcher: {
|
|
127
|
+
name: 'downloadData',
|
|
128
|
+
url: `${LAPIS_URL}/component/nucleotideMutationsOverTime`,
|
|
129
|
+
body: {
|
|
130
|
+
filters: { pangoLineage: 'JN.1*', dateFrom: '2024-01-15', dateTo: '2024-07-10' },
|
|
131
|
+
dateRanges: [
|
|
132
|
+
{ dateFrom: '2024-01-01', dateTo: '2024-01-31' },
|
|
133
|
+
{ dateFrom: '2024-02-01', dateTo: '2024-02-29' },
|
|
134
|
+
{ dateFrom: '2024-03-01', dateTo: '2024-03-31' },
|
|
135
|
+
{ dateFrom: '2024-04-01', dateTo: '2024-04-30' },
|
|
136
|
+
{ dateFrom: '2024-05-01', dateTo: '2024-05-31' },
|
|
137
|
+
{ dateFrom: '2024-06-01', dateTo: '2024-06-30' },
|
|
138
|
+
{ dateFrom: '2024-07-01', dateTo: '2024-07-31' },
|
|
139
|
+
],
|
|
140
|
+
includeMutations: [
|
|
141
|
+
'C44T',
|
|
142
|
+
'C774T',
|
|
143
|
+
'C7113T',
|
|
144
|
+
'C12616T',
|
|
145
|
+
'A13121T',
|
|
146
|
+
'G15372T',
|
|
147
|
+
'G17334T',
|
|
148
|
+
'T18453C',
|
|
149
|
+
'A19722G',
|
|
150
|
+
'T21653-',
|
|
151
|
+
'C21654-',
|
|
152
|
+
'T21655-',
|
|
153
|
+
'G22111T',
|
|
154
|
+
'G22599C',
|
|
155
|
+
'T22928C',
|
|
156
|
+
'T23011-',
|
|
157
|
+
'C23039G',
|
|
158
|
+
'C23277T',
|
|
159
|
+
'G24872T',
|
|
160
|
+
],
|
|
108
161
|
dateField: 'date',
|
|
109
162
|
},
|
|
110
|
-
matchPartialBody: true,
|
|
111
163
|
response: {
|
|
112
164
|
status: 200,
|
|
113
|
-
body:
|
|
165
|
+
body: mockDefaultMutationsOverTimePageSize20,
|
|
114
166
|
},
|
|
115
167
|
},
|
|
116
168
|
},
|
|
@@ -310,23 +362,30 @@ export const ByWeek: StoryObj<Required<MutationsOverTimeProps>> = {
|
|
|
310
362
|
matcher: {
|
|
311
363
|
url: `${LAPIS_URL}/component/nucleotideMutationsOverTime`,
|
|
312
364
|
body: {
|
|
313
|
-
filters: {
|
|
314
|
-
pangoLineage: 'JN.1*',
|
|
315
|
-
dateFrom: '2024-01-15',
|
|
316
|
-
dateTo: '2024-02-11',
|
|
317
|
-
},
|
|
365
|
+
filters: { pangoLineage: 'JN.1*', dateFrom: '2024-01-15', dateTo: '2024-02-11' },
|
|
318
366
|
dateRanges: [
|
|
319
367
|
{ dateFrom: '2024-01-15', dateTo: '2024-01-21' },
|
|
320
368
|
{ dateFrom: '2024-01-22', dateTo: '2024-01-28' },
|
|
321
369
|
{ dateFrom: '2024-01-29', dateTo: '2024-02-04' },
|
|
322
370
|
{ dateFrom: '2024-02-05', dateTo: '2024-02-11' },
|
|
323
371
|
],
|
|
372
|
+
includeMutations: [
|
|
373
|
+
'C44T',
|
|
374
|
+
'C774T',
|
|
375
|
+
'C1762A',
|
|
376
|
+
'C11747T',
|
|
377
|
+
'G17562T',
|
|
378
|
+
'T18453C',
|
|
379
|
+
'G21641T',
|
|
380
|
+
'C23277T',
|
|
381
|
+
'C29870A',
|
|
382
|
+
],
|
|
324
383
|
dateField: 'date',
|
|
325
384
|
},
|
|
326
385
|
matchPartialBody: true,
|
|
327
386
|
response: {
|
|
328
387
|
status: 200,
|
|
329
|
-
body:
|
|
388
|
+
body: mockByWeekMutationsOverTimePage1,
|
|
330
389
|
},
|
|
331
390
|
},
|
|
332
391
|
},
|
|
@@ -365,11 +424,7 @@ export const AminoAcidMutationsByDay: StoryObj<Required<MutationsOverTimeProps>>
|
|
|
365
424
|
matcher: {
|
|
366
425
|
url: `${LAPIS_URL}/component/aminoAcidMutationsOverTime`,
|
|
367
426
|
body: {
|
|
368
|
-
filters: {
|
|
369
|
-
pangoLineage: 'JN.1*',
|
|
370
|
-
dateFrom: '2024-01-20',
|
|
371
|
-
dateTo: '2024-01-26',
|
|
372
|
-
},
|
|
427
|
+
filters: { pangoLineage: 'JN.1*', dateFrom: '2024-01-20', dateTo: '2024-01-26' },
|
|
373
428
|
dateRanges: [
|
|
374
429
|
{ dateFrom: '2024-01-20', dateTo: '2024-01-20' },
|
|
375
430
|
{ dateFrom: '2024-01-21', dateTo: '2024-01-21' },
|
|
@@ -379,12 +434,13 @@ export const AminoAcidMutationsByDay: StoryObj<Required<MutationsOverTimeProps>>
|
|
|
379
434
|
{ dateFrom: '2024-01-25', dateTo: '2024-01-25' },
|
|
380
435
|
{ dateFrom: '2024-01-26', dateTo: '2024-01-26' },
|
|
381
436
|
],
|
|
437
|
+
includeMutations: ['ORF1a:T170I', 'ORF1a:F499L', 'S:T572I'],
|
|
382
438
|
dateField: 'date',
|
|
383
439
|
},
|
|
384
440
|
matchPartialBody: true,
|
|
385
441
|
response: {
|
|
386
442
|
status: 200,
|
|
387
|
-
body:
|
|
443
|
+
body: mockAminoAcidMutationsByDayAminoAcidMutationsOverTimePage1,
|
|
388
444
|
},
|
|
389
445
|
},
|
|
390
446
|
},
|