@genspectrum/dashboard-components 1.12.0 → 1.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/README.md +0 -7
  2. package/custom-elements.json +6 -25
  3. package/dist/components.d.ts +30 -36
  4. package/dist/components.js +943 -757
  5. package/dist/components.js.map +1 -1
  6. package/dist/util.d.ts +46 -30
  7. package/package.json +1 -5
  8. package/src/lapisApi/lapisApi.ts +21 -1
  9. package/src/lapisApi/lapisTypes.ts +36 -0
  10. package/src/preact/components/annotated-mutation.tsx +2 -2
  11. package/src/preact/{mutationsOverTime/mutations-over-time-grid.tsx → components/features-over-time-grid.tsx} +45 -52
  12. package/src/preact/genomeViewer/genome-data-viewer.tsx +2 -2
  13. package/src/preact/mutationsOverTime/MutationOverTimeData.ts +6 -4
  14. package/src/preact/mutationsOverTime/__mockData__/aminoAcidMutationsByDay/aminoAcidMutations.json +5482 -0
  15. package/src/preact/mutationsOverTime/__mockData__/aminoAcidMutationsByDay/aminoAcidMutationsOverTime.json +5496 -0
  16. package/src/preact/mutationsOverTime/__mockData__/byWeek/mutationsOverTime.json +7100 -0
  17. package/src/preact/mutationsOverTime/__mockData__/byWeek/nucleotideMutations.json +10122 -0
  18. package/src/preact/mutationsOverTime/__mockData__/defaultMockData/mutationsOverTime.json +12646 -0
  19. package/src/preact/mutationsOverTime/__mockData__/defaultMockData/nucleotideMutations.json +12632 -0
  20. package/src/preact/mutationsOverTime/__mockData__/request1800s/mutationsOverTime.json +16 -0
  21. package/src/preact/mutationsOverTime/__mockData__/request1800s/nucleotideMutations.json +11 -0
  22. package/src/preact/mutationsOverTime/__mockData__/withDisplayMutations/mutationsOverTime.json +52 -0
  23. package/src/preact/mutationsOverTime/getFilteredMutationsOverTime.spec.ts +3 -3
  24. package/src/preact/mutationsOverTime/mutations-over-time-grid-tooltip.tsx +3 -6
  25. package/src/preact/mutationsOverTime/mutations-over-time.stories.tsx +199 -12
  26. package/src/preact/mutationsOverTime/mutations-over-time.tsx +30 -35
  27. package/src/preact/wastewater/mutationsOverTime/wastewater-mutations-over-time.tsx +30 -3
  28. package/src/query/queryDatesInDataset.ts +89 -0
  29. package/src/query/queryMutationsOverTime.spec.ts +526 -548
  30. package/src/query/queryMutationsOverTime.ts +21 -232
  31. package/src/query/queryQueriesOverTime.spec.ts +432 -0
  32. package/src/query/queryQueriesOverTime.ts +125 -0
  33. package/src/utilEntrypoint.ts +3 -1
  34. package/src/utils/mutations.spec.ts +6 -0
  35. package/src/utils/mutations.ts +1 -1
  36. package/src/utils/temporalClass.ts +4 -0
  37. package/src/web-components/visualization/gs-mutations-over-time.spec-d.ts +0 -3
  38. package/src/web-components/visualization/gs-mutations-over-time.stories.ts +283 -17
  39. package/src/web-components/visualization/gs-mutations-over-time.tsx +0 -9
  40. package/standalone-bundle/dashboard-components.js +8935 -8781
  41. package/standalone-bundle/dashboard-components.js.map +1 -1
  42. package/dist/assets/mutationOverTimeWorker-f8Kp0S6V.js.map +0 -1
  43. package/src/preact/mutationsOverTime/__mockData__/aminoAcidMutationsByDay.ts +0 -47170
  44. package/src/preact/mutationsOverTime/__mockData__/byWeek.ts +0 -54026
  45. package/src/preact/mutationsOverTime/__mockData__/defaultMockData.ts +0 -108385
  46. package/src/preact/mutationsOverTime/__mockData__/mockConversion.ts +0 -54
  47. package/src/preact/mutationsOverTime/__mockData__/noDataWhenNoMutationsAreInFilter.ts +0 -23
  48. package/src/preact/mutationsOverTime/__mockData__/noDataWhenThereAreNoDatesInFilter.ts +0 -23
  49. package/src/preact/mutationsOverTime/__mockData__/showsMessageWhenTooManyMutations.ts +0 -65527
  50. package/src/preact/mutationsOverTime/__mockData__/withDisplayMutations.ts +0 -352
  51. package/src/preact/mutationsOverTime/__mockData__/withGaps.ts +0 -298
  52. package/src/preact/mutationsOverTime/mutationOverTimeWorker.mock.ts +0 -33
  53. package/src/preact/mutationsOverTime/mutationOverTimeWorker.ts +0 -29
  54. package/src/preact/webWorkers/useWebWorker.ts +0 -74
  55. package/src/preact/webWorkers/workerFunction.ts +0 -30
  56. package/src/query/queryMutationsOverTimeNewEndpoint.spec.ts +0 -988
  57. package/standalone-bundle/assets/mutationOverTimeWorker-AhhjjklP.js.map +0 -1
@@ -0,0 +1,16 @@
1
+ {
2
+ "data": {
3
+ "mutations": [],
4
+ "dateRanges": [{ "dateFrom": "1800-01-01", "dateTo": "1800-12-31" }],
5
+ "data": [],
6
+ "totalCountsByDateRange": []
7
+ },
8
+ "info": {
9
+ "dataVersion": null,
10
+ "requestId": "dff5f887-1b84-4789-93be-3bca6c8adb1c",
11
+ "requestInfo": "sars_cov-2_nextstrain_open on lapis.cov-spectrum.org at 2025-12-17T16:48:49.532134692",
12
+ "reportTo": "Please report to https://github.com/GenSpectrum/LAPIS/issues in case you encounter any unexpected issues. Please include the request ID and the requestInfo in your report.",
13
+ "lapisVersion": "12a364c66262f2f56289873faddf0552b550742e",
14
+ "siloVersion": "0.8.0"
15
+ }
16
+ }
@@ -0,0 +1,11 @@
1
+ {
2
+ "data": [],
3
+ "info": {
4
+ "dataVersion": "1765731908",
5
+ "requestId": "3bb9782f-49fa-459c-98e6-ef13e122238d",
6
+ "requestInfo": "sars_cov-2_nextstrain_open on lapis.cov-spectrum.org at 2025-12-17T16:48:49.434876617",
7
+ "reportTo": "Please report to https://github.com/GenSpectrum/LAPIS/issues in case you encounter any unexpected issues. Please include the request ID and the requestInfo in your report.",
8
+ "lapisVersion": "12a364c66262f2f56289873faddf0552b550742e",
9
+ "siloVersion": "0.8.0"
10
+ }
11
+ }
@@ -0,0 +1,52 @@
1
+ {
2
+ "data": {
3
+ "mutations": ["A13121T", "T21653-", "G24872T"],
4
+ "dateRanges": [
5
+ { "dateFrom": "2024-01-01", "dateTo": "2024-01-31" },
6
+ { "dateFrom": "2024-02-01", "dateTo": "2024-02-29" },
7
+ { "dateFrom": "2024-03-01", "dateTo": "2024-03-31" },
8
+ { "dateFrom": "2024-04-01", "dateTo": "2024-04-30" },
9
+ { "dateFrom": "2024-05-01", "dateTo": "2024-05-31" },
10
+ { "dateFrom": "2024-06-01", "dateTo": "2024-06-30" },
11
+ { "dateFrom": "2024-07-01", "dateTo": "2024-07-31" }
12
+ ],
13
+ "data": [
14
+ [
15
+ { "count": 1, "coverage": 14030 },
16
+ { "count": 1, "coverage": 17545 },
17
+ { "count": 1, "coverage": 8418 },
18
+ { "count": 0, "coverage": 0 },
19
+ { "count": 43, "coverage": 9236 },
20
+ { "count": 922, "coverage": 16466 },
21
+ { "count": 1119, "coverage": 7468 }
22
+ ],
23
+ [
24
+ { "count": 13, "coverage": 13946 },
25
+ { "count": 15, "coverage": 17353 },
26
+ { "count": 41, "coverage": 8318 },
27
+ { "count": 0, "coverage": 0 },
28
+ { "count": 1369, "coverage": 9052 },
29
+ { "count": 5080, "coverage": 16078 },
30
+ { "count": 3245, "coverage": 7342 }
31
+ ],
32
+ [
33
+ { "count": 42, "coverage": 13765 },
34
+ { "count": 203, "coverage": 17274 },
35
+ { "count": 563, "coverage": 8256 },
36
+ { "count": 0, "coverage": 0 },
37
+ { "count": 4631, "coverage": 9024 },
38
+ { "count": 11162, "coverage": 16188 },
39
+ { "count": 5451, "coverage": 7393 }
40
+ ]
41
+ ],
42
+ "totalCountsByDateRange": [14063, 17577, 8431, 0, 9250, 16513, 7508]
43
+ },
44
+ "info": {
45
+ "dataVersion": "1765731908",
46
+ "requestId": "220cae2b-f73c-42fd-a3ac-1c49e0f239ec",
47
+ "requestInfo": "sars_cov-2_nextstrain_open on lapis.cov-spectrum.org at 2025-12-17T16:59:33.639804323",
48
+ "reportTo": "Please report to https://github.com/GenSpectrum/LAPIS/issues in case you encounter any unexpected issues. Please include the request ID and the requestInfo in your report.",
49
+ "lapisVersion": "12a364c66262f2f56289873faddf0552b550742e",
50
+ "siloVersion": "0.8.0"
51
+ }
52
+ }
@@ -2,7 +2,7 @@ import { describe, expect, it } from 'vitest';
2
2
 
3
3
  import { BaseMutationOverTimeDataMap } from './MutationOverTimeData';
4
4
  import { getFilteredMutationOverTimeData, type MutationFilter } from './getFilteredMutationsOverTimeData';
5
- import { type MutationOverTimeMutationValue } from '../../query/queryMutationsOverTime';
5
+ import { type ProportionValue } from '../../query/queryMutationsOverTime';
6
6
  import { type DeletionEntry, type SubstitutionEntry } from '../../types';
7
7
  import { type Deletion, type Substitution } from '../../utils/mutations';
8
8
  import { type TemporalClass } from '../../utils/temporalClass';
@@ -392,13 +392,13 @@ describe('getFilteredMutationOverTimeData', () => {
392
392
  count: 1,
393
393
  proportion: inFilter,
394
394
  totalCount: 10,
395
- } satisfies MutationOverTimeMutationValue;
395
+ } satisfies ProportionValue;
396
396
  const emptyMutationOverTimeValue = {
397
397
  type: 'value',
398
398
  count: 0,
399
399
  proportion: NaN,
400
400
  totalCount: 0,
401
- } satisfies MutationOverTimeMutationValue;
401
+ } satisfies ProportionValue;
402
402
 
403
403
  function prepareMutationOverTimeData(
404
404
  mutationEntries: (SubstitutionEntry<Substitution> | DeletionEntry<Deletion>)[],
@@ -1,9 +1,6 @@
1
1
  import type { FunctionComponent } from 'preact';
2
2
 
3
- import {
4
- type MutationOverTimeMutationValue,
5
- MUTATIONS_OVER_TIME_MIN_PROPORTION,
6
- } from '../../query/queryMutationsOverTime';
3
+ import { type ProportionValue, MUTATIONS_OVER_TIME_MIN_PROPORTION } from '../../query/queryMutationsOverTime';
7
4
  import type { Deletion, Substitution } from '../../utils/mutations';
8
5
  import { type Temporal, type TemporalClass, toTemporalClass, YearMonthDayClass } from '../../utils/temporalClass';
9
6
  import { formatProportion } from '../shared/table/formatProportion';
@@ -11,7 +8,7 @@ import { formatProportion } from '../shared/table/formatProportion';
11
8
  export type MutationsOverTimeGridTooltipProps = {
12
9
  mutation: Substitution | Deletion;
13
10
  date: Temporal;
14
- value: MutationOverTimeMutationValue;
11
+ value: ProportionValue;
15
12
  };
16
13
 
17
14
  export const MutationsOverTimeGridTooltip: FunctionComponent<MutationsOverTimeGridTooltipProps> = ({
@@ -66,7 +63,7 @@ export const MutationsOverTimeGridTooltip: FunctionComponent<MutationsOverTimeGr
66
63
  };
67
64
 
68
65
  const TooltipValueCountsDescription: FunctionComponent<{
69
- value: NonNullable<MutationOverTimeMutationValue>;
66
+ value: NonNullable<ProportionValue>;
70
67
  mutationCode: string;
71
68
  mutationPosition: number;
72
69
  }> = ({ value, mutationCode, mutationPosition }) => {
@@ -4,11 +4,16 @@ import { type Canvas } from '@storybook/types';
4
4
 
5
5
  import { MutationsOverTime, type MutationsOverTimeProps } from './mutations-over-time';
6
6
  import { LAPIS_URL } from '../../constants';
7
- import referenceGenome from '../../lapisApi/__mockData__/referenceGenome.json';
8
7
  import { type MutationAnnotations } from '../../web-components/mutation-annotations-context';
9
8
  import { LapisUrlContextProvider } from '../LapisUrlContext';
10
9
  import { MutationAnnotationsContextProvider } from '../MutationAnnotationsContext';
11
10
  import { ReferenceGenomeContext } from '../ReferenceGenomeContext';
11
+ import mockDefaultMutationsOverTime from './__mockData__/defaultMockData/mutationsOverTime.json';
12
+ import mockDefaultNucleotideMutations from './__mockData__/defaultMockData/nucleotideMutations.json';
13
+ import mock1800sMutationsOverTime from './__mockData__/request1800s/mutationsOverTime.json';
14
+ import mock1800sNucleotideMutations from './__mockData__/request1800s/nucleotideMutations.json';
15
+ import mockWithDisplayMutationsMutationsOverTime from './__mockData__/withDisplayMutations/mutationsOverTime.json';
16
+ import referenceGenome from '../../lapisApi/__mockData__/referenceGenome.json';
12
17
  import { expectInvalidAttributesErrorMessage } from '../shared/stories/expectErrorMessage';
13
18
  import { playThatExpectsFinishedLoadingEvent } from '../shared/stories/expectFinishedLoadingEvent';
14
19
  import { expectMutationAnnotation } from '../shared/stories/expectMutationAnnotation';
@@ -37,11 +42,55 @@ const meta: Meta<MutationsOverTimeProps> = {
37
42
  initialMeanProportionInterval: { control: 'object' },
38
43
  hideGaps: { control: 'boolean' },
39
44
  pageSizes: { control: 'object' },
40
- useNewEndpoint: { control: 'boolean' },
41
45
  customColumns: { control: 'object' },
42
46
  },
43
47
  parameters: {
44
- fetchMock: {},
48
+ fetchMock: {
49
+ mocks: [
50
+ {
51
+ matcher: {
52
+ url: `${LAPIS_URL}/sample/nucleotideMutations`,
53
+ body: {
54
+ pangoLineage: 'JN.1*',
55
+ dateFrom: '2024-01-01',
56
+ dateTo: '2024-07-31',
57
+ minProportion: 0.001,
58
+ },
59
+ response: {
60
+ status: 200,
61
+ body: mockDefaultNucleotideMutations,
62
+ },
63
+ },
64
+ },
65
+ {
66
+ matcher: {
67
+ url: `${LAPIS_URL}/component/nucleotideMutationsOverTime`,
68
+ body: {
69
+ filters: {
70
+ pangoLineage: 'JN.1*',
71
+ dateFrom: '2024-01-15',
72
+ dateTo: '2024-07-10',
73
+ },
74
+ dateRanges: [
75
+ { dateFrom: '2024-01-01', dateTo: '2024-01-31' },
76
+ { dateFrom: '2024-02-01', dateTo: '2024-02-29' },
77
+ { dateFrom: '2024-03-01', dateTo: '2024-03-31' },
78
+ { dateFrom: '2024-04-01', dateTo: '2024-04-30' },
79
+ { dateFrom: '2024-05-01', dateTo: '2024-05-31' },
80
+ { dateFrom: '2024-06-01', dateTo: '2024-06-30' },
81
+ { dateFrom: '2024-07-01', dateTo: '2024-07-31' },
82
+ ],
83
+ dateField: 'date',
84
+ },
85
+ matchPartialBody: true, // includeMutations left out
86
+ response: {
87
+ status: 200,
88
+ body: mockDefaultMutationsOverTime,
89
+ },
90
+ },
91
+ },
92
+ ],
93
+ },
45
94
  },
46
95
  };
47
96
 
@@ -84,7 +133,6 @@ export const Default: StoryObj<MutationsOverTimeProps> = {
84
133
  lapisDateField: 'date',
85
134
  initialMeanProportionInterval: { min: 0.05, max: 0.9 },
86
135
  hideGaps: false,
87
- useNewEndpoint: false,
88
136
  pageSizes: [10, 20, 30, 40, 50],
89
137
  },
90
138
  };
@@ -109,6 +157,38 @@ export const ShowsNoDataWhenNoMutationsAreInFilter: StoryObj<MutationsOverTimePr
109
157
  height: '700px',
110
158
  granularity: 'year',
111
159
  },
160
+ parameters: {
161
+ fetchMock: {
162
+ mocks: [
163
+ {
164
+ matcher: {
165
+ url: `${LAPIS_URL}/sample/nucleotideMutations`,
166
+ body: { dateFrom: '1800-01-01', dateTo: '1800-12-31', minProportion: 0.001 },
167
+ response: {
168
+ status: 200,
169
+ body: mock1800sNucleotideMutations,
170
+ },
171
+ },
172
+ },
173
+ {
174
+ matcher: {
175
+ name: 'mockDefaultMutationsOverTime',
176
+ url: `${LAPIS_URL}/component/nucleotideMutationsOverTime`,
177
+ body: {
178
+ filters: { dateFrom: '1800-01-01', dateTo: '1800-01-02' },
179
+ dateRanges: [{ dateFrom: '1800-01-01', dateTo: '1800-12-31' }],
180
+ includeMutations: [],
181
+ dateField: 'date',
182
+ },
183
+ response: {
184
+ status: 200,
185
+ body: mock1800sMutationsOverTime,
186
+ },
187
+ },
188
+ },
189
+ ],
190
+ },
191
+ },
112
192
  play: async ({ canvas }) => {
113
193
  await waitFor(() => expect(canvas.getByText('No data available.', { exact: false })).toBeVisible(), {
114
194
  timeout: 10000,
@@ -120,7 +200,51 @@ export const UsesHideGaps: StoryObj<MutationsOverTimeProps> = {
120
200
  ...Default,
121
201
  args: {
122
202
  ...Default.args,
123
- displayMutations: ['A19722G', 'G21641T', 'T21652-'],
203
+ displayMutations: ['A13121T', 'G24872T', 'T21653-'],
204
+ },
205
+ parameters: {
206
+ fetchMock: {
207
+ mocks: [
208
+ {
209
+ matcher: {
210
+ url: `${LAPIS_URL}/sample/nucleotideMutations`,
211
+ body: {
212
+ pangoLineage: 'JN.1*',
213
+ dateFrom: '2024-01-01',
214
+ dateTo: '2024-07-31',
215
+ minProportion: 0.001,
216
+ },
217
+ response: {
218
+ status: 200,
219
+ body: mockDefaultNucleotideMutations,
220
+ },
221
+ },
222
+ },
223
+ {
224
+ matcher: {
225
+ url: `${LAPIS_URL}/component/nucleotideMutationsOverTime`,
226
+ body: {
227
+ filters: { pangoLineage: 'JN.1*', dateFrom: '2024-01-15', dateTo: '2024-07-10' },
228
+ dateRanges: [
229
+ { dateFrom: '2024-01-01', dateTo: '2024-01-31' },
230
+ { dateFrom: '2024-02-01', dateTo: '2024-02-29' },
231
+ { dateFrom: '2024-03-01', dateTo: '2024-03-31' },
232
+ { dateFrom: '2024-04-01', dateTo: '2024-04-30' },
233
+ { dateFrom: '2024-05-01', dateTo: '2024-05-31' },
234
+ { dateFrom: '2024-06-01', dateTo: '2024-06-30' },
235
+ { dateFrom: '2024-07-01', dateTo: '2024-07-31' },
236
+ ],
237
+ includeMutations: ['A13121T', 'T21653-', 'G24872T'],
238
+ dateField: 'date',
239
+ },
240
+ response: {
241
+ status: 200,
242
+ body: mockWithDisplayMutationsMutationsOverTime,
243
+ },
244
+ },
245
+ },
246
+ ],
247
+ },
124
248
  },
125
249
  play: async ({ canvas, step }) => {
126
250
  await expectDateRangeOnPage(canvas, '2024-04');
@@ -201,17 +325,61 @@ export const WithCustomColumns: StoryObj<MutationsOverTimeProps> = {
201
325
  ...Default,
202
326
  args: {
203
327
  ...Default.args,
204
- displayMutations: ['A19722G', 'G21641T', 'T21653-'],
328
+ displayMutations: ['A13121T', 'G24872T', 'T21653-'],
205
329
  customColumns: [
206
330
  {
207
331
  header: 'Jaccard Index',
208
332
  values: {
209
- A19722G: 0.75,
210
- G21641T: 'Foobar',
333
+ A13121T: 0.75,
334
+ G24872T: 'Foobar',
211
335
  },
212
336
  },
213
337
  ],
214
338
  },
339
+ parameters: {
340
+ fetchMock: {
341
+ mocks: [
342
+ {
343
+ matcher: {
344
+ url: `${LAPIS_URL}/sample/nucleotideMutations`,
345
+ body: {
346
+ pangoLineage: 'JN.1*',
347
+ dateFrom: '2024-01-01',
348
+ dateTo: '2024-07-31',
349
+ minProportion: 0.001,
350
+ },
351
+ response: {
352
+ status: 200,
353
+ body: mockDefaultNucleotideMutations,
354
+ },
355
+ },
356
+ },
357
+ {
358
+ matcher: {
359
+ url: `${LAPIS_URL}/component/nucleotideMutationsOverTime`,
360
+ body: {
361
+ filters: { pangoLineage: 'JN.1*', dateFrom: '2024-01-15', dateTo: '2024-07-10' },
362
+ dateRanges: [
363
+ { dateFrom: '2024-01-01', dateTo: '2024-01-31' },
364
+ { dateFrom: '2024-02-01', dateTo: '2024-02-29' },
365
+ { dateFrom: '2024-03-01', dateTo: '2024-03-31' },
366
+ { dateFrom: '2024-04-01', dateTo: '2024-04-30' },
367
+ { dateFrom: '2024-05-01', dateTo: '2024-05-31' },
368
+ { dateFrom: '2024-06-01', dateTo: '2024-06-30' },
369
+ { dateFrom: '2024-07-01', dateTo: '2024-07-31' },
370
+ ],
371
+ includeMutations: ['A13121T', 'T21653-', 'G24872T'],
372
+ dateField: 'date',
373
+ },
374
+ response: {
375
+ status: 200,
376
+ body: mockWithDisplayMutationsMutationsOverTime,
377
+ },
378
+ },
379
+ },
380
+ ],
381
+ },
382
+ },
215
383
  play: async ({ canvas }) => {
216
384
  await waitFor(() => expect(canvas.getByText('Jaccard Index')).toBeVisible(), {
217
385
  timeout: 5000,
@@ -245,6 +413,29 @@ export const ShowsNoDataMessageWhenThereAreNoDatesInFilter: StoryObj<MutationsOv
245
413
  height: '700px',
246
414
  granularity: 'year',
247
415
  },
416
+ parameters: {
417
+ fetchMock: {
418
+ mocks: [
419
+ {
420
+ matcher: {
421
+ url: `${LAPIS_URL}/sample/nucleotideMutations`,
422
+ body: {
423
+ filters: { dateFrom: '2345-01-01', dateTo: '2020-01-02' },
424
+ dateRanges: [],
425
+ includeMutations: [],
426
+ dateField: 'date',
427
+ },
428
+ response: {
429
+ status: 200,
430
+ body: {
431
+ data: { mutations: [], dateRanges: [], data: [], totalCountsByDateRange: [] },
432
+ },
433
+ },
434
+ },
435
+ },
436
+ ],
437
+ },
438
+ },
248
439
  play: async ({ canvas }) => {
249
440
  await waitFor(() => expect(canvas.getByText('No data available.', { exact: false })).toBeVisible(), {
250
441
  timeout: 10000,
@@ -254,10 +445,6 @@ export const ShowsNoDataMessageWhenThereAreNoDatesInFilter: StoryObj<MutationsOv
254
445
 
255
446
  export const ShowsNoDataMessageForStrictFilters: StoryObj<MutationsOverTimeProps> = {
256
447
  ...Default,
257
- args: {
258
- ...Default.args,
259
- lapisFilter: { pangoLineage: 'JN.1*', dateFrom: '2024-01-15', dateTo: '2024-07-10' },
260
- },
261
448
  play: async ({ canvas }) => {
262
449
  await waitFor(() => expect(canvas.getByText('Grid')).toBeVisible(), { timeout: 10000 });
263
450
 
@@ -2,17 +2,14 @@ import { type FunctionComponent } from 'preact';
2
2
  import { type Dispatch, type StateUpdater, useMemo, useState, useEffect, useLayoutEffect, useRef } from 'preact/hooks';
3
3
  import z from 'zod';
4
4
 
5
- // @ts-expect-error -- uses subpath imports and vite worker import
6
- import MutationOverTimeWorker from '#mutationOverTime?worker&inline';
7
- import { BaseMutationOverTimeDataMap, type MutationOverTimeDataMap } from './MutationOverTimeData';
5
+ import { type BaseMutationOverTimeDataMap, type MutationOverTimeDataMap } from './MutationOverTimeData';
8
6
  import {
9
7
  displayMutationsSchema,
10
8
  getFilteredMutationOverTimeData,
11
9
  type MutationFilter,
12
10
  } from './getFilteredMutationsOverTimeData';
13
- import { type MutationOverTimeWorkerResponse } from './mutationOverTimeWorker';
14
- import MutationsOverTimeGrid, { customColumnSchema } from './mutations-over-time-grid';
15
- import { getProportion, type MutationOverTimeQuery } from '../../query/queryMutationsOverTime';
11
+ import { MutationsOverTimeGridTooltip } from './mutations-over-time-grid-tooltip';
12
+ import { type ProportionValue, getProportion, queryMutationsOverTimeData } from '../../query/queryMutationsOverTime';
16
13
  import {
17
14
  lapisFilterSchema,
18
15
  sequenceTypeSchema,
@@ -21,14 +18,16 @@ import {
21
18
  views,
22
19
  } from '../../types';
23
20
  import { type Deletion, type Substitution } from '../../utils/mutations';
24
- import { toTemporalClass } from '../../utils/temporalClass';
21
+ import { type Temporal, toTemporalClass } from '../../utils/temporalClass';
25
22
  import { useDispatchFinishedLoadingEvent } from '../../utils/useDispatchFinishedLoadingEvent';
26
23
  import { useLapisUrl } from '../LapisUrlContext';
27
24
  import { useMutationAnnotationsProvider } from '../MutationAnnotationsContext';
25
+ import { AnnotatedMutation } from '../components/annotated-mutation';
28
26
  import { type ColorScale } from '../components/color-scale-selector';
29
27
  import { ColorScaleSelectorDropdown } from '../components/color-scale-selector-dropdown';
30
28
  import { CsvDownloadButton } from '../components/csv-download-button';
31
29
  import { ErrorBoundary } from '../components/error-boundary';
30
+ import FeaturesOverTimeGrid, { type FeatureRenderer, customColumnSchema } from '../components/features-over-time-grid';
32
31
  import { Fullscreen } from '../components/fullscreen';
33
32
  import Info, { InfoComponentCode, InfoHeadline1, InfoParagraph } from '../components/info';
34
33
  import { LoadingDisplay } from '../components/loading-display';
@@ -42,7 +41,7 @@ import { type DisplayedSegment, SegmentSelector, useDisplayedSegments } from '..
42
41
  import Tabs from '../components/tabs';
43
42
  import { pageSizesSchema } from '../shared/tanstackTable/pagination';
44
43
  import { PageSizeContextProvider } from '../shared/tanstackTable/pagination-context';
45
- import { useWebWorker } from '../webWorkers/useWebWorker';
44
+ import { useQuery } from '../useQuery';
46
45
 
47
46
  const mutationsOverTimeViewSchema = z.literal(views.grid);
48
47
  export type MutationsOverTimeView = z.infer<typeof mutationsOverTimeViewSchema>;
@@ -59,7 +58,6 @@ const mutationOverTimeSchema = z.object({
59
58
  views: z.array(mutationsOverTimeViewSchema),
60
59
  granularity: temporalGranularitySchema,
61
60
  lapisDateField: z.string().min(1),
62
- useNewEndpoint: z.boolean().optional(),
63
61
  displayMutations: displayMutationsSchema.optional(),
64
62
  initialMeanProportionInterval: meanProportionIntervalSchema,
65
63
  hideGaps: z.boolean().optional(),
@@ -83,45 +81,29 @@ export const MutationsOverTime: FunctionComponent<MutationsOverTimeProps> = (com
83
81
  );
84
82
  };
85
83
 
86
- export const MutationsOverTimeInner: FunctionComponent<MutationsOverTimeProps> = ({
87
- useNewEndpoint = false,
88
- ...componentProps
89
- }) => {
84
+ export const MutationsOverTimeInner: FunctionComponent<MutationsOverTimeProps> = ({ ...componentProps }) => {
90
85
  const lapis = useLapisUrl();
91
86
  const { lapisFilter, sequenceType, granularity, lapisDateField, displayMutations } = componentProps;
92
87
 
93
- const messageToWorker: MutationOverTimeQuery = useMemo(() => {
94
- return {
95
- lapisFilter,
96
- sequenceType,
97
- granularity,
98
- lapisDateField,
99
- lapis,
100
- displayMutations,
101
- useNewEndpoint,
102
- };
103
- }, [granularity, lapis, lapisDateField, lapisFilter, sequenceType, displayMutations, useNewEndpoint]);
104
-
105
- const { data, error, isLoading } = useWebWorker<MutationOverTimeWorkerResponse>(
106
- messageToWorker,
107
- // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
108
- MutationOverTimeWorker,
88
+ const { data, error, isLoading } = useQuery(
89
+ () =>
90
+ queryMutationsOverTimeData(lapisFilter, sequenceType, lapis, lapisDateField, granularity, displayMutations),
91
+ [granularity, lapis, lapisDateField, lapisFilter, sequenceType, displayMutations],
109
92
  );
110
93
 
111
94
  if (isLoading) {
112
95
  return <LoadingDisplay />;
113
96
  }
114
97
 
115
- if (error !== undefined) {
98
+ if (error !== null) {
116
99
  throw error;
117
100
  }
118
101
 
119
- if (data === undefined || data.overallMutationData.length === 0) {
102
+ if (data.overallMutationData.length === 0) {
120
103
  return <NoDataDisplay />;
121
104
  }
122
105
 
123
- const { overallMutationData, mutationOverTimeSerialized } = data;
124
- const mutationOverTimeData = new BaseMutationOverTimeDataMap(mutationOverTimeSerialized);
106
+ const { overallMutationData, mutationOverTimeData } = data;
125
107
  return (
126
108
  <MutationsOverTimeTabs
127
109
  overallMutationData={overallMutationData}
@@ -193,6 +175,18 @@ const MutationsOverTimeTabs: FunctionComponent<MutationOverTimeTabsProps> = ({
193
175
  annotationProvider,
194
176
  ]);
195
177
 
178
+ const mutationRenderer: FeatureRenderer<Substitution | Deletion> = {
179
+ asString: (value: Substitution | Deletion) => value.code,
180
+ renderRowLabel: (value: Substitution | Deletion) => (
181
+ <div className={'text-center'}>
182
+ <AnnotatedMutation mutation={value} sequenceType={originalComponentProps.sequenceType} />
183
+ </div>
184
+ ),
185
+ renderTooltip: (value: Substitution | Deletion, temporal: Temporal, proportionValue: ProportionValue) => (
186
+ <MutationsOverTimeGridTooltip mutation={value} date={temporal} value={proportionValue} />
187
+ ),
188
+ };
189
+
196
190
  const getTab = (view: MutationsOverTimeView) => {
197
191
  switch (view) {
198
192
  // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- for extensibility
@@ -200,12 +194,13 @@ const MutationsOverTimeTabs: FunctionComponent<MutationOverTimeTabsProps> = ({
200
194
  return {
201
195
  title: 'Grid',
202
196
  content: (
203
- <MutationsOverTimeGrid
197
+ <FeaturesOverTimeGrid
198
+ rowLabelHeader='Mutation'
204
199
  data={filteredData}
205
200
  colorScale={colorScale}
206
- sequenceType={originalComponentProps.sequenceType}
207
201
  pageSizes={originalComponentProps.pageSizes}
208
202
  customColumns={originalComponentProps.customColumns}
203
+ featureRenderer={mutationRenderer}
209
204
  tooltipPortalTarget={tooltipPortalTarget}
210
205
  />
211
206
  ),
@@ -3,14 +3,19 @@ import { type Dispatch, type StateUpdater, useMemo, useState, useRef } from 'pre
3
3
  import z from 'zod';
4
4
 
5
5
  import { computeWastewaterMutationsOverTimeDataPerLocation } from './computeWastewaterMutationsOverTimeDataPerLocation';
6
+ import { type ProportionValue } from '../../../query/queryMutationsOverTime';
6
7
  import { lapisFilterSchema, type SequenceType, sequenceTypeSchema } from '../../../types';
7
8
  import { Map2dView } from '../../../utils/map2d';
9
+ import { type Deletion, type Substitution } from '../../../utils/mutations';
10
+ import { type Temporal } from '../../../utils/temporalClass';
8
11
  import { useDispatchFinishedLoadingEvent } from '../../../utils/useDispatchFinishedLoadingEvent';
9
12
  import { useLapisUrl } from '../../LapisUrlContext';
10
13
  import { useMutationAnnotationsProvider } from '../../MutationAnnotationsContext';
14
+ import { AnnotatedMutation } from '../../components/annotated-mutation';
11
15
  import { type ColorScale } from '../../components/color-scale-selector';
12
16
  import { ColorScaleSelectorDropdown } from '../../components/color-scale-selector-dropdown';
13
17
  import { ErrorBoundary } from '../../components/error-boundary';
18
+ import FeaturesOverTimeGrid from '../../components/features-over-time-grid';
14
19
  import { Fullscreen } from '../../components/fullscreen';
15
20
  import Info, { InfoComponentCode, InfoHeadline1, InfoParagraph } from '../../components/info';
16
21
  import { LoadingDisplay } from '../../components/loading-display';
@@ -24,7 +29,7 @@ import {
24
29
  type MutationFilter,
25
30
  mutationOrAnnotationDoNotMatchFilter,
26
31
  } from '../../mutationsOverTime/getFilteredMutationsOverTimeData';
27
- import MutationsOverTimeGrid from '../../mutationsOverTime/mutations-over-time-grid';
32
+ import { MutationsOverTimeGridTooltip } from '../../mutationsOverTime/mutations-over-time-grid-tooltip';
28
33
  import { pageSizesSchema } from '../../shared/tanstackTable/pagination';
29
34
  import { PageSizeContextProvider } from '../../shared/tanstackTable/pagination-context';
30
35
  import { useQuery } from '../../useQuery';
@@ -166,7 +171,8 @@ const MutationsOverTimeTabs: FunctionComponent<MutationOverTimeTabsProps> = ({
166
171
  mutationOverTimeDataPerLocation.map(({ location, data }) => ({
167
172
  title: location,
168
173
  content: (
169
- <MutationsOverTimeGrid
174
+ <FeaturesOverTimeGrid
175
+ rowLabelHeader='Mutation'
170
176
  data={getFilteredMutationOverTimeData({
171
177
  data,
172
178
  displayedSegments,
@@ -176,7 +182,28 @@ const MutationsOverTimeTabs: FunctionComponent<MutationOverTimeTabsProps> = ({
176
182
  })}
177
183
  colorScale={colorScale}
178
184
  pageSizes={originalComponentProps.pageSizes}
179
- sequenceType={originalComponentProps.sequenceType}
185
+ featureRenderer={{
186
+ asString: (value: Substitution | Deletion) => value.code,
187
+ renderRowLabel: (value: Substitution | Deletion) => (
188
+ <div className={'text-center'}>
189
+ <AnnotatedMutation
190
+ mutation={value}
191
+ sequenceType={originalComponentProps.sequenceType}
192
+ />
193
+ </div>
194
+ ),
195
+ renderTooltip: (
196
+ value: Substitution | Deletion,
197
+ temporal: Temporal,
198
+ proportionValue: ProportionValue,
199
+ ) => (
200
+ <MutationsOverTimeGridTooltip
201
+ mutation={value}
202
+ date={temporal}
203
+ value={proportionValue}
204
+ />
205
+ ),
206
+ }}
180
207
  tooltipPortalTarget={tooltipPortalTargetRef.current}
181
208
  />
182
209
  ),