@genspectrum/dashboard-components 0.6.5 → 0.6.6

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 (26) hide show
  1. package/custom-elements.json +35 -3
  2. package/dist/dashboard-components.js +142 -23
  3. package/dist/dashboard-components.js.map +1 -1
  4. package/dist/style.css +50 -2
  5. package/package.json +1 -1
  6. package/src/constants.ts +1 -0
  7. package/src/preact/components/tooltip.stories.tsx +12 -2
  8. package/src/preact/components/tooltip.tsx +37 -13
  9. package/src/preact/mutationsOverTime/__mockData__/aggregated_byDay.json +38 -0
  10. package/src/preact/mutationsOverTime/__mockData__/aggregated_byWeek.json +122 -0
  11. package/src/preact/mutationsOverTime/__mockData__/aminoAcidMutations_20_01_2024.json +6778 -0
  12. package/src/preact/mutationsOverTime/__mockData__/aminoAcidMutations_21_01_2024.json +7129 -0
  13. package/src/preact/mutationsOverTime/__mockData__/aminoAcidMutations_22_01_2024.json +4681 -0
  14. package/src/preact/mutationsOverTime/__mockData__/aminoAcidMutations_23_01_2024.json +10738 -0
  15. package/src/preact/mutationsOverTime/__mockData__/aminoAcidMutations_24_01_2024.json +11710 -0
  16. package/src/preact/mutationsOverTime/__mockData__/aminoAcidMutations_25_01_2024.json +11557 -0
  17. package/src/preact/mutationsOverTime/__mockData__/aminoAcidMutations_26_01_2024.json +8596 -0
  18. package/src/preact/mutationsOverTime/__mockData__/nucleotideMutations_week3_2024.json +8812 -0
  19. package/src/preact/mutationsOverTime/__mockData__/nucleotideMutations_week4_2024.json +9730 -0
  20. package/src/preact/mutationsOverTime/__mockData__/nucleotideMutations_week5_2024.json +9865 -0
  21. package/src/preact/mutationsOverTime/__mockData__/nucleotideMutations_week6_2024.json +11314 -0
  22. package/src/preact/mutationsOverTime/mutations-over-time-grid.tsx +26 -8
  23. package/src/preact/mutationsOverTime/mutations-over-time.tsx +32 -8
  24. package/src/utils/temporal.spec.ts +3 -4
  25. package/src/utils/temporal.ts +9 -4
  26. package/src/web-components/visualization/gs-mutations-over-time.stories.ts +254 -2
@@ -6,7 +6,7 @@ import {
6
6
  } from '../../query/queryMutationsOverTime';
7
7
  import { type Deletion, type Substitution } from '../../utils/mutations';
8
8
  import { compareTemporal, type Temporal, YearMonthDay } from '../../utils/temporal';
9
- import Tooltip from '../components/tooltip';
9
+ import Tooltip, { type TooltipPosition } from '../components/tooltip';
10
10
  import { singleGraphColorRGBByName } from '../shared/charts/colors';
11
11
  import { formatProportion } from '../shared/table/formatProportion';
12
12
 
@@ -37,23 +37,34 @@ const MutationsOverTimeGrid: FunctionComponent<MutationsOverTimeGridProps> = ({
37
37
  gridTemplateColumns: `8rem repeat(${dates.length}, minmax(1.5rem, 1fr))`,
38
38
  }}
39
39
  >
40
- {shownMutations.map((mutation, i) => {
40
+ {shownMutations.map((mutation, rowIndex) => {
41
41
  return (
42
42
  <Fragment key={`fragment-${mutation.toString()}`}>
43
43
  <div
44
44
  key={`mutation-${mutation.toString()}`}
45
- style={{ gridRowStart: i + 1, gridColumnStart: 1 }}
45
+ style={{ gridRowStart: rowIndex + 1, gridColumnStart: 1 }}
46
46
  >
47
47
  <MutationCell mutation={mutation} />
48
48
  </div>
49
- {dates.map((date, j) => {
49
+ {dates.map((date, columnIndex) => {
50
50
  const value = data.get(mutation, date) ?? { proportion: 0, count: 0 };
51
+ const tooltipPosition = getTooltipPosition(
52
+ rowIndex,
53
+ shownMutations.length,
54
+ columnIndex,
55
+ dates.length,
56
+ );
51
57
  return (
52
58
  <div
53
- style={{ gridRowStart: i + 1, gridColumnStart: j + 2 }}
59
+ style={{ gridRowStart: rowIndex + 1, gridColumnStart: columnIndex + 2 }}
54
60
  key={`${mutation.toString()}-${date.toString()}`}
55
61
  >
56
- <ProportionCell value={value} date={date} mutation={mutation} />
62
+ <ProportionCell
63
+ value={value}
64
+ date={date}
65
+ mutation={mutation}
66
+ tooltipPosition={tooltipPosition}
67
+ />
57
68
  </div>
58
69
  );
59
70
  })}
@@ -65,11 +76,18 @@ const MutationsOverTimeGrid: FunctionComponent<MutationsOverTimeGridProps> = ({
65
76
  );
66
77
  };
67
78
 
79
+ function getTooltipPosition(rowIndex: number, rows: number, columnIndex: number, columns: number) {
80
+ const tooltipX = rowIndex < rows / 2 ? 'bottom' : 'top';
81
+ const tooltipY = columnIndex < columns / 2 ? 'start' : 'end';
82
+ return `${tooltipX}-${tooltipY}` as const;
83
+ }
84
+
68
85
  const ProportionCell: FunctionComponent<{
69
86
  value: MutationOverTimeMutationValue;
70
87
  date: Temporal;
71
88
  mutation: Substitution | Deletion;
72
- }> = ({ value, mutation, date }) => {
89
+ tooltipPosition: TooltipPosition;
90
+ }> = ({ value, mutation, date, tooltipPosition }) => {
73
91
  const tooltipContent = (
74
92
  <div>
75
93
  <p>
@@ -84,7 +102,7 @@ const ProportionCell: FunctionComponent<{
84
102
  return (
85
103
  <>
86
104
  <div className={'py-1'}>
87
- <Tooltip content={tooltipContent}>
105
+ <Tooltip content={tooltipContent} position={tooltipPosition}>
88
106
  <div
89
107
  style={{
90
108
  backgroundColor: backgroundColor(value.proportion),
@@ -8,8 +8,10 @@ import {
8
8
  queryMutationsOverTimeData,
9
9
  } from '../../query/queryMutationsOverTime';
10
10
  import { type LapisFilter, type SequenceType, type TemporalGranularity } from '../../types';
11
+ import { compareTemporal } from '../../utils/temporal';
11
12
  import { LapisUrlContext } from '../LapisUrlContext';
12
13
  import { type DisplayedSegment, SegmentSelector, useDisplayedSegments } from '../components/SegmentSelector';
14
+ import { CsvDownloadButton } from '../components/csv-download-button';
13
15
  import { ErrorBoundary } from '../components/error-boundary';
14
16
  import { ErrorDisplay } from '../components/error-display';
15
17
  import Info from '../components/info';
@@ -126,6 +128,7 @@ const MutationsOverTimeTabs: FunctionComponent<MutationOverTimeTabsProps> = ({
126
128
  setDisplayedMutationTypes={setDisplayedMutationTypes}
127
129
  proportionInterval={proportionInterval}
128
130
  setProportionInterval={setProportionInterval}
131
+ filteredData={filteredData}
129
132
  />
130
133
  );
131
134
 
@@ -139,6 +142,7 @@ type ToolbarProps = {
139
142
  setDisplayedMutationTypes: (types: DisplayedMutationType[]) => void;
140
143
  proportionInterval: ProportionInterval;
141
144
  setProportionInterval: Dispatch<StateUpdater<ProportionInterval>>;
145
+ filteredData: MutationOverTimeDataGroupedByMutation;
142
146
  };
143
147
 
144
148
  const Toolbar: FunctionComponent<ToolbarProps> = ({
@@ -148,6 +152,7 @@ const Toolbar: FunctionComponent<ToolbarProps> = ({
148
152
  setDisplayedMutationTypes,
149
153
  proportionInterval,
150
154
  setProportionInterval,
155
+ filteredData,
151
156
  }) => {
152
157
  return (
153
158
  <>
@@ -156,15 +161,34 @@ const Toolbar: FunctionComponent<ToolbarProps> = ({
156
161
  setDisplayedMutationTypes={setDisplayedMutationTypes}
157
162
  displayedMutationTypes={displayedMutationTypes}
158
163
  />
159
- <>
160
- <ProportionSelectorDropdown
161
- proportionInterval={proportionInterval}
162
- setMinProportion={(min) => setProportionInterval((prev) => ({ ...prev, min }))}
163
- setMaxProportion={(max) => setProportionInterval((prev) => ({ ...prev, max }))}
164
- />
165
- {/* TODO(#362): Add download button */}
166
- </>
164
+ <ProportionSelectorDropdown
165
+ proportionInterval={proportionInterval}
166
+ setMinProportion={(min) => setProportionInterval((prev) => ({ ...prev, min }))}
167
+ setMaxProportion={(max) => setProportionInterval((prev) => ({ ...prev, max }))}
168
+ />
169
+ <CsvDownloadButton
170
+ className='mx-1 btn btn-xs'
171
+ getData={() => getDownloadData(filteredData)}
172
+ filename='mutations_over_time.csv'
173
+ />
167
174
  <Info height={'100px'}>Info for mutations over time</Info>
168
175
  </>
169
176
  );
170
177
  };
178
+
179
+ function getDownloadData(filteredData: MutationOverTimeDataGroupedByMutation) {
180
+ const dates = filteredData.getSecondAxisKeys().sort((a, b) => compareTemporal(a, b));
181
+
182
+ return filteredData.getFirstAxisKeys().map((mutation) => {
183
+ return dates.reduce(
184
+ (accumulated, date) => {
185
+ const proportion = filteredData.get(mutation, date)?.proportion ?? 0;
186
+ return {
187
+ ...accumulated,
188
+ [date.toString()]: proportion,
189
+ };
190
+ },
191
+ { mutation: mutation.toString() },
192
+ );
193
+ });
194
+ }
@@ -57,8 +57,7 @@ describe('YearMonthDay', () => {
57
57
  expect(underTest.yearNumber).equal(2020);
58
58
  expect(underTest.monthNumber).equal(1);
59
59
  expect(underTest.dayNumber).equal(1);
60
- // seems to be a bug in dayjs: https://github.com/iamkun/dayjs/issues/2620
61
- expect(underTest.week.text).equal('2019-01');
60
+ expect(underTest.week.text).equal('2020-W01');
62
61
  expect(underTest.text).equal('2020-01-01');
63
62
  expect(underTest.firstDay.text).equal('2020-01-01');
64
63
  expect(underTest.lastDay.text).equal('2020-01-01');
@@ -67,12 +66,12 @@ describe('YearMonthDay', () => {
67
66
 
68
67
  describe('YearWeek', () => {
69
68
  it('should parse from string', () => {
70
- const underTest = YearWeek.parse('2020-02', cache);
69
+ const underTest = YearWeek.parse('2020-W02', cache);
71
70
 
72
71
  expect(underTest.isoYearNumber).equal(2020);
73
72
  expect(underTest.isoWeekNumber).equal(2);
74
73
  expect(underTest.firstDay.text).equal('2020-01-06');
75
- expect(underTest.text).equal('2020-02');
74
+ expect(underTest.text).equal('2020-W02');
76
75
  expect(underTest.lastDay.text).equal('2020-01-12');
77
76
  });
78
77
  });
@@ -7,6 +7,11 @@ import type { TemporalGranularity } from '../types';
7
7
  dayjs.extend(isoWeek);
8
8
  dayjs.extend(advancedFormat);
9
9
 
10
+ /**
11
+ * https://day.js.org/docs/en/plugin/advanced-format
12
+ */
13
+ const FORMAT_ISO_WEEK_YEAR_WEEK = 'GGGG-[W]WW';
14
+
10
15
  export class TemporalCache {
11
16
  private yearMonthDayCache = new Map<string, YearMonthDay>();
12
17
  private yearWeekCache = new Map<string, YearWeek>();
@@ -93,7 +98,7 @@ export class YearMonthDay {
93
98
  }
94
99
 
95
100
  get week(): YearWeek {
96
- return this.cache.getYearWeek(this.dayjs.format('GGGG-WW'));
101
+ return this.cache.getYearWeek(this.dayjs.format(FORMAT_ISO_WEEK_YEAR_WEEK));
97
102
  }
98
103
 
99
104
  addDays(days: number): YearMonthDay {
@@ -120,7 +125,7 @@ export class YearWeek {
120
125
  ) {}
121
126
 
122
127
  get text(): string {
123
- return this.firstDay.dayjs.format('YYYY-WW');
128
+ return this.firstDay.dayjs.format(FORMAT_ISO_WEEK_YEAR_WEEK);
124
129
  }
125
130
 
126
131
  toString(): string {
@@ -160,7 +165,7 @@ export class YearWeek {
160
165
 
161
166
  addWeeks(weeks: number): YearWeek {
162
167
  const date = this.firstDay.dayjs.add(weeks, 'week');
163
- const s = date.format('YYYY-WW');
168
+ const s = date.format(FORMAT_ISO_WEEK_YEAR_WEEK);
164
169
  return this.cache.getYearWeek(s);
165
170
  }
166
171
 
@@ -169,7 +174,7 @@ export class YearWeek {
169
174
  }
170
175
 
171
176
  static parse(s: string, cache: TemporalCache): YearWeek {
172
- const [year, week] = s.split('-').map((s) => parseInt(s, 10));
177
+ const [year, week] = s.split('-W').map((s) => parseInt(s, 10));
173
178
  return new YearWeek(year, week, cache);
174
179
  }
175
180
  }
@@ -4,8 +4,22 @@ import { html } from 'lit';
4
4
  import './gs-mutations-over-time';
5
5
  import '../app';
6
6
  import { withComponentDocs } from '../../../.storybook/ComponentDocsBlock';
7
- import { AGGREGATED_ENDPOINT, LAPIS_URL, NUCLEOTIDE_MUTATIONS_ENDPOINT } from '../../constants';
7
+ import {
8
+ AGGREGATED_ENDPOINT,
9
+ AMINO_ACID_MUTATIONS_ENDPOINT,
10
+ LAPIS_URL,
11
+ NUCLEOTIDE_MUTATIONS_ENDPOINT,
12
+ } from '../../constants';
13
+ import aggregated_byDay from '../../preact/mutationsOverTime/__mockData__/aggregated_byDay.json';
14
+ import aggregated_byWeek from '../../preact/mutationsOverTime/__mockData__/aggregated_byWeek.json';
8
15
  import aggregated_date from '../../preact/mutationsOverTime/__mockData__/aggregated_date.json';
16
+ import aminoAcidMutations_20_01_2024 from '../../preact/mutationsOverTime/__mockData__/aminoAcidMutations_20_01_2024.json';
17
+ import aminoAcidMutations_21_01_2024 from '../../preact/mutationsOverTime/__mockData__/aminoAcidMutations_21_01_2024.json';
18
+ import aminoAcidMutations_22_01_2024 from '../../preact/mutationsOverTime/__mockData__/aminoAcidMutations_22_01_2024.json';
19
+ import aminoAcidMutations_23_01_2024 from '../../preact/mutationsOverTime/__mockData__/aminoAcidMutations_23_01_2024.json';
20
+ import aminoAcidMutations_24_01_2024 from '../../preact/mutationsOverTime/__mockData__/aminoAcidMutations_24_01_2024.json';
21
+ import aminoAcidMutations_25_01_2024 from '../../preact/mutationsOverTime/__mockData__/aminoAcidMutations_25_01_2024.json';
22
+ import aminoAcidMutations_26_01_2024 from '../../preact/mutationsOverTime/__mockData__/aminoAcidMutations_26_01_2024.json';
9
23
  import nucleotideMutation_01 from '../../preact/mutationsOverTime/__mockData__/nucleotideMutations_2024_01.json';
10
24
  import nucleotideMutation_02 from '../../preact/mutationsOverTime/__mockData__/nucleotideMutations_2024_02.json';
11
25
  import nucleotideMutation_03 from '../../preact/mutationsOverTime/__mockData__/nucleotideMutations_2024_03.json';
@@ -13,6 +27,10 @@ import nucleotideMutation_04 from '../../preact/mutationsOverTime/__mockData__/n
13
27
  import nucleotideMutation_05 from '../../preact/mutationsOverTime/__mockData__/nucleotideMutations_2024_05.json';
14
28
  import nucleotideMutation_06 from '../../preact/mutationsOverTime/__mockData__/nucleotideMutations_2024_06.json';
15
29
  import nucleotideMutation_07 from '../../preact/mutationsOverTime/__mockData__/nucleotideMutations_2024_07.json';
30
+ import nucleotideMutation_week3 from '../../preact/mutationsOverTime/__mockData__/nucleotideMutations_week3_2024.json';
31
+ import nucleotideMutation_week4 from '../../preact/mutationsOverTime/__mockData__/nucleotideMutations_week4_2024.json';
32
+ import nucleotideMutation_week5 from '../../preact/mutationsOverTime/__mockData__/nucleotideMutations_week5_2024.json';
33
+ import nucleotideMutation_week6 from '../../preact/mutationsOverTime/__mockData__/nucleotideMutations_week6_2024.json';
16
34
  import { type MutationsOverTimeProps } from '../../preact/mutationsOverTime/mutations-over-time';
17
35
 
18
36
  const codeExample = String.raw`
@@ -85,7 +103,7 @@ const Template: StoryObj<Required<MutationsOverTimeProps>> = {
85
103
  `,
86
104
  };
87
105
 
88
- export const Default: StoryObj<Required<MutationsOverTimeProps>> = {
106
+ export const ByMonth: StoryObj<Required<MutationsOverTimeProps>> = {
89
107
  ...Template,
90
108
  parameters: {
91
109
  fetchMock: {
@@ -223,3 +241,237 @@ export const Default: StoryObj<Required<MutationsOverTimeProps>> = {
223
241
  },
224
242
  },
225
243
  };
244
+
245
+ export const ByWeek: StoryObj<Required<MutationsOverTimeProps>> = {
246
+ ...Template,
247
+ args: {
248
+ ...Template.args,
249
+ lapisFilter: { pangoLineage: 'JN.1*', dateFrom: '2024-01-15', dateTo: '2024-02-11' },
250
+ granularity: 'week',
251
+ },
252
+ parameters: {
253
+ fetchMock: {
254
+ mocks: [
255
+ {
256
+ matcher: {
257
+ name: 'aggregated_dates',
258
+ url: AGGREGATED_ENDPOINT,
259
+ body: {
260
+ dateFrom: '2024-01-15',
261
+ dateTo: '2024-02-11',
262
+ fields: ['date'],
263
+ pangoLineage: 'JN.1*',
264
+ },
265
+ },
266
+ response: {
267
+ status: 200,
268
+ body: aggregated_byWeek,
269
+ },
270
+ },
271
+ {
272
+ matcher: {
273
+ name: 'nucleotideMutation_week3',
274
+ url: NUCLEOTIDE_MUTATIONS_ENDPOINT,
275
+ body: {
276
+ pangoLineage: 'JN.1*',
277
+ dateFrom: '2024-01-15',
278
+ dateTo: '2024-01-21',
279
+ minProportion: 0.001,
280
+ },
281
+ },
282
+ response: {
283
+ status: 200,
284
+ body: nucleotideMutation_week3,
285
+ },
286
+ },
287
+ {
288
+ matcher: {
289
+ name: 'nucleotideMutation_week4',
290
+ url: NUCLEOTIDE_MUTATIONS_ENDPOINT,
291
+ body: {
292
+ pangoLineage: 'JN.1*',
293
+ dateFrom: '2024-01-22',
294
+ dateTo: '2024-01-28',
295
+ minProportion: 0.001,
296
+ },
297
+ },
298
+ response: {
299
+ status: 200,
300
+ body: nucleotideMutation_week4,
301
+ },
302
+ },
303
+ {
304
+ matcher: {
305
+ name: 'nucleotideMutation_week5',
306
+ url: NUCLEOTIDE_MUTATIONS_ENDPOINT,
307
+ body: {
308
+ pangoLineage: 'JN.1*',
309
+ dateFrom: '2024-01-29',
310
+ dateTo: '2024-02-04',
311
+ minProportion: 0.001,
312
+ },
313
+ },
314
+ response: {
315
+ status: 200,
316
+ body: nucleotideMutation_week5,
317
+ },
318
+ },
319
+ {
320
+ matcher: {
321
+ name: 'nucleotideMutation_week6',
322
+ url: NUCLEOTIDE_MUTATIONS_ENDPOINT,
323
+ body: {
324
+ pangoLineage: 'JN.1*',
325
+ dateFrom: '2024-02-05',
326
+ dateTo: '2024-02-11',
327
+ minProportion: 0.001,
328
+ },
329
+ },
330
+ response: {
331
+ status: 200,
332
+ body: nucleotideMutation_week6,
333
+ },
334
+ },
335
+ ],
336
+ },
337
+ },
338
+ };
339
+
340
+ export const AminoAcidMutationsByDay: StoryObj<Required<MutationsOverTimeProps>> = {
341
+ ...Template,
342
+ args: {
343
+ ...Template.args,
344
+ lapisFilter: { pangoLineage: 'JN.1*', dateFrom: '2024-01-20', dateTo: '2024-01-26' },
345
+ granularity: 'day',
346
+ sequenceType: 'amino acid',
347
+ },
348
+ parameters: {
349
+ fetchMock: {
350
+ mocks: [
351
+ {
352
+ matcher: {
353
+ name: 'aggregated_byDay',
354
+ url: AGGREGATED_ENDPOINT,
355
+ body: { pangoLineage: 'JN.1*', dateFrom: '2024-01-20', dateTo: '2024-01-26', fields: ['date'] },
356
+ },
357
+ response: {
358
+ status: 200,
359
+ body: aggregated_byDay,
360
+ },
361
+ },
362
+ {
363
+ matcher: {
364
+ name: 'aminoAcidMutations_20_01_2024',
365
+ url: AMINO_ACID_MUTATIONS_ENDPOINT,
366
+ body: {
367
+ pangoLineage: 'JN.1*',
368
+ dateFrom: '2024-01-20',
369
+ dateTo: '2024-01-20',
370
+ minProportion: 0.001,
371
+ },
372
+ },
373
+ response: {
374
+ status: 200,
375
+ body: aminoAcidMutations_20_01_2024,
376
+ },
377
+ },
378
+ {
379
+ matcher: {
380
+ name: 'aminoAcidMutations_21_01_2024',
381
+ url: AMINO_ACID_MUTATIONS_ENDPOINT,
382
+ body: {
383
+ pangoLineage: 'JN.1*',
384
+ dateFrom: '2024-01-21',
385
+ dateTo: '2024-01-21',
386
+ minProportion: 0.001,
387
+ },
388
+ },
389
+ response: {
390
+ status: 200,
391
+ body: aminoAcidMutations_21_01_2024,
392
+ },
393
+ },
394
+ {
395
+ matcher: {
396
+ name: 'aminoAcidMutations_22_01_2024',
397
+ url: AMINO_ACID_MUTATIONS_ENDPOINT,
398
+ body: {
399
+ pangoLineage: 'JN.1*',
400
+ dateFrom: '2024-01-22',
401
+ dateTo: '2024-01-22',
402
+ minProportion: 0.001,
403
+ },
404
+ },
405
+ response: {
406
+ status: 200,
407
+ body: aminoAcidMutations_22_01_2024,
408
+ },
409
+ },
410
+ {
411
+ matcher: {
412
+ name: 'aminoAcidMutations_23_01_2024',
413
+ url: AMINO_ACID_MUTATIONS_ENDPOINT,
414
+ body: {
415
+ pangoLineage: 'JN.1*',
416
+ dateFrom: '2024-01-23',
417
+ dateTo: '2024-01-23',
418
+ minProportion: 0.001,
419
+ },
420
+ },
421
+ response: {
422
+ status: 200,
423
+ body: aminoAcidMutations_23_01_2024,
424
+ },
425
+ },
426
+ {
427
+ matcher: {
428
+ name: 'aminoAcidMutations_24_01_2024',
429
+ url: AMINO_ACID_MUTATIONS_ENDPOINT,
430
+ body: {
431
+ pangoLineage: 'JN.1*',
432
+ dateFrom: '2024-01-24',
433
+ dateTo: '2024-01-24',
434
+ minProportion: 0.001,
435
+ },
436
+ },
437
+ response: {
438
+ status: 200,
439
+ body: aminoAcidMutations_24_01_2024,
440
+ },
441
+ },
442
+ {
443
+ matcher: {
444
+ name: 'aminoAcidMutations_25_01_2024',
445
+ url: AMINO_ACID_MUTATIONS_ENDPOINT,
446
+ body: {
447
+ pangoLineage: 'JN.1*',
448
+ dateFrom: '2024-01-25',
449
+ dateTo: '2024-01-25',
450
+ minProportion: 0.001,
451
+ },
452
+ },
453
+ response: {
454
+ status: 200,
455
+ body: aminoAcidMutations_25_01_2024,
456
+ },
457
+ },
458
+ {
459
+ matcher: {
460
+ name: 'aminoAcidMutations_26_01_2024',
461
+ url: AMINO_ACID_MUTATIONS_ENDPOINT,
462
+ body: {
463
+ pangoLineage: 'JN.1*',
464
+ dateFrom: '2024-01-26',
465
+ dateTo: '2024-01-26',
466
+ minProportion: 0.001,
467
+ },
468
+ },
469
+ response: {
470
+ status: 200,
471
+ body: aminoAcidMutations_26_01_2024,
472
+ },
473
+ },
474
+ ],
475
+ },
476
+ },
477
+ };