@genspectrum/dashboard-components 0.6.2 → 0.6.4

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 (42) hide show
  1. package/custom-elements.json +220 -0
  2. package/dist/dashboard-components.js +675 -178
  3. package/dist/dashboard-components.js.map +1 -1
  4. package/dist/genspectrum-components.d.ts +70 -2
  5. package/dist/style.css +10 -4
  6. package/package.json +3 -1
  7. package/src/constants.ts +1 -1
  8. package/src/lapisApi/lapisTypes.ts +1 -0
  9. package/src/operator/FillMissingOperator.spec.ts +3 -1
  10. package/src/operator/FillMissingOperator.ts +4 -2
  11. package/src/preact/components/tooltip.stories.tsx +54 -0
  12. package/src/preact/components/tooltip.tsx +31 -0
  13. package/src/preact/mutationComparison/queryMutationData.ts +12 -4
  14. package/src/preact/mutationsOverTime/__mockData__/aggregated_date.json +642 -0
  15. package/src/preact/mutationsOverTime/__mockData__/nucleotideMutations_2024_01.json +1747 -0
  16. package/src/preact/mutationsOverTime/__mockData__/nucleotideMutations_2024_02.json +1774 -0
  17. package/src/preact/mutationsOverTime/__mockData__/nucleotideMutations_2024_03.json +1819 -0
  18. package/src/preact/mutationsOverTime/__mockData__/nucleotideMutations_2024_04.json +1864 -0
  19. package/src/preact/mutationsOverTime/__mockData__/nucleotideMutations_2024_05.json +1927 -0
  20. package/src/preact/mutationsOverTime/__mockData__/nucleotideMutations_2024_06.json +1864 -0
  21. package/src/preact/mutationsOverTime/__mockData__/nucleotideMutations_2024_07.json +9 -0
  22. package/src/preact/mutationsOverTime/getFilteredMutationsOverTime.spec.ts +98 -0
  23. package/src/preact/mutationsOverTime/getFilteredMutationsOverTimeData.ts +66 -0
  24. package/src/preact/mutationsOverTime/mutations-over-time-grid.tsx +127 -0
  25. package/src/preact/mutationsOverTime/mutations-over-time.stories.tsx +206 -0
  26. package/src/preact/mutationsOverTime/mutations-over-time.tsx +170 -0
  27. package/src/preact/numberSequencesOverTime/getNumberOfSequencesOverTimeTableData.ts +1 -1
  28. package/src/preact/prevalenceOverTime/prevalence-over-time.stories.tsx +1 -0
  29. package/src/preact/shared/table/formatProportion.ts +2 -2
  30. package/src/query/queryAggregatedDataOverTime.ts +8 -33
  31. package/src/query/queryMutationsOverTime.spec.ts +378 -0
  32. package/src/query/queryMutationsOverTime.ts +179 -0
  33. package/src/query/queryNumberOfSequencesOverTime.ts +0 -1
  34. package/src/query/queryRelativeGrowthAdvantage.ts +3 -3
  35. package/src/utils/Map2d.ts +75 -0
  36. package/src/utils/map2d.spec.ts +94 -0
  37. package/src/utils/mutations.ts +5 -1
  38. package/src/utils/temporal.spec.ts +5 -0
  39. package/src/utils/temporal.ts +88 -5
  40. package/src/web-components/visualization/gs-mutations-over-time.stories.ts +225 -0
  41. package/src/web-components/visualization/gs-mutations-over-time.tsx +112 -0
  42. package/src/web-components/visualization/index.ts +1 -0
@@ -0,0 +1,75 @@
1
+ import hash from 'object-hash';
2
+
3
+ export class Map2d<Key1 extends object | string, Key2 extends object | string, Value> {
4
+ readonly data: Map<string, Map<string, Value>> = new Map<string, Map<string, Value>>();
5
+ readonly keysFirstAxis = new Map<string, Key1>();
6
+ readonly keysSecondAxis = new Map<string, Key2>();
7
+
8
+ constructor(
9
+ readonly serializeFirstAxis: (key: Key1) => string = (key) => (typeof key === 'string' ? key : hash(key)),
10
+ readonly serializeSecondAxis: (key: Key2) => string = (key) => (typeof key === 'string' ? key : hash(key)),
11
+ ) {}
12
+
13
+ get(keyFirstAxis: Key1, keySecondAxis: Key2) {
14
+ const serializedKeyFirstAxis = this.serializeFirstAxis(keyFirstAxis);
15
+ const serializedKeySecondAxis = this.serializeSecondAxis(keySecondAxis);
16
+ return this.data.get(serializedKeyFirstAxis)?.get(serializedKeySecondAxis);
17
+ }
18
+
19
+ getRow(key: Key1, fillEmptyWith: Value) {
20
+ const serializedKeyFirstAxis = this.serializeFirstAxis(key);
21
+ const row = this.data.get(serializedKeyFirstAxis);
22
+ if (row === undefined) {
23
+ return [];
24
+ }
25
+ return Array.from(this.keysSecondAxis.keys()).map((key) => row.get(key) ?? fillEmptyWith);
26
+ }
27
+
28
+ set(keyFirstAxis: Key1, keySecondAxis: Key2, value: Value) {
29
+ const serializedKeyFirstAxis = this.serializeFirstAxis(keyFirstAxis);
30
+ const serializedKeySecondAxis = this.serializeSecondAxis(keySecondAxis);
31
+
32
+ if (!this.data.has(serializedKeyFirstAxis)) {
33
+ this.data.set(serializedKeyFirstAxis, new Map<string, Value>());
34
+ }
35
+
36
+ this.data.get(serializedKeyFirstAxis)!.set(serializedKeySecondAxis, value);
37
+
38
+ this.keysFirstAxis.set(serializedKeyFirstAxis, keyFirstAxis);
39
+ this.keysSecondAxis.set(serializedKeySecondAxis, keySecondAxis);
40
+ }
41
+
42
+ deleteRow(key: Key1) {
43
+ const serializedKeyFirstAxis = this.serializeFirstAxis(key);
44
+ this.data.delete(serializedKeyFirstAxis);
45
+ this.keysFirstAxis.delete(serializedKeyFirstAxis);
46
+ }
47
+
48
+ getFirstAxisKeys() {
49
+ return Array.from(this.keysFirstAxis.values());
50
+ }
51
+
52
+ getSecondAxisKeys() {
53
+ return Array.from(this.keysSecondAxis.values());
54
+ }
55
+
56
+ getAsArray(fillEmptyWith: Value) {
57
+ return this.getFirstAxisKeys().map((firstAxisKey) => {
58
+ return this.getSecondAxisKeys().map((secondAxisKey) => {
59
+ return this.get(firstAxisKey, secondAxisKey) ?? fillEmptyWith;
60
+ });
61
+ });
62
+ }
63
+
64
+ copy() {
65
+ const copy = new Map2d<Key1, Key2, Value>(this.serializeFirstAxis, this.serializeSecondAxis);
66
+ this.data.forEach((value, key) => {
67
+ const keyFirstAxis = this.keysFirstAxis.get(key);
68
+ value.forEach((value, key) => {
69
+ const keySecondAxis = this.keysSecondAxis.get(key);
70
+ copy.set(keyFirstAxis!, keySecondAxis!, value);
71
+ });
72
+ });
73
+ return copy;
74
+ }
75
+ }
@@ -0,0 +1,94 @@
1
+ import { describe, expect, it } from 'vitest';
2
+
3
+ import { Map2d } from './Map2d';
4
+
5
+ describe('Map2d', () => {
6
+ it('should add a value and return it', () => {
7
+ const map2d = new Map2d<string, string, number>();
8
+ map2d.set('a', 'b', 2);
9
+ expect(map2d.get('a', 'b')).toBe(2);
10
+ });
11
+
12
+ it('should update a value', () => {
13
+ const map2d = new Map2d<string, string, number>();
14
+ map2d.set('a', 'b', 2);
15
+ map2d.set('a', 'b', 3);
16
+ expect(map2d.get('a', 'b')).toBe(3);
17
+ });
18
+
19
+ it('should return the data as an array', () => {
20
+ const map2d = new Map2d<string, string, number>();
21
+ map2d.set('a', 'b', 1);
22
+ map2d.set('a', 'd', 2);
23
+ map2d.set('c', 'b', 3);
24
+ map2d.set('c', 'd', 4);
25
+
26
+ expect(map2d.getAsArray(0)).toEqual([
27
+ [1, 2],
28
+ [3, 4],
29
+ ]);
30
+ });
31
+
32
+ it('should fill empty values with the given value', () => {
33
+ const map2d = new Map2d<string, string, number>();
34
+ map2d.set('a', 'b', 2);
35
+ map2d.set('c', 'd', 4);
36
+ expect(map2d.getAsArray(0)).toEqual([
37
+ [2, 0],
38
+ [0, 4],
39
+ ]);
40
+ });
41
+
42
+ it('should return the keys from the first axis', () => {
43
+ const map2d = new Map2d<string, string, number>();
44
+ map2d.set('a', 'b', 2);
45
+ map2d.set('c', 'd', 4);
46
+
47
+ expect(map2d.getFirstAxisKeys()).toEqual(['a', 'c']);
48
+ });
49
+
50
+ it('should return the keys from the second axis', () => {
51
+ const map2d = new Map2d<string, string, number>();
52
+ map2d.set('a', 'b', 2);
53
+ map2d.set('c', 'd', 4);
54
+
55
+ expect(map2d.getSecondAxisKeys()).toEqual(['b', 'd']);
56
+ });
57
+
58
+ it('should work with objects as keys', () => {
59
+ const map2d = new Map2d<{ a: string }, { b: string }, number>();
60
+ map2d.set({ a: 'a' }, { b: 'b' }, 2);
61
+ map2d.set({ a: 'second' }, { b: 'second' }, 3);
62
+
63
+ expect(map2d.get({ a: 'a' }, { b: 'b' })).toBe(2);
64
+ expect(map2d.get({ a: 'second' }, { b: 'second' })).toBe(3);
65
+ });
66
+
67
+ it('should update a value with objects as keys', () => {
68
+ const map2d = new Map2d<{ a: string }, { b: string }, number>();
69
+ map2d.set({ a: 'a' }, { b: 'b' }, 2);
70
+ map2d.set({ a: 'a' }, { b: 'b' }, 3);
71
+ expect(map2d.get({ a: 'a' }, { b: 'b' })).toBe(3);
72
+ });
73
+
74
+ it('should create a deep copy of the map', () => {
75
+ const map2d = new Map2d<string, string, number>();
76
+ map2d.set('a', 'b', 2);
77
+ expect(map2d.get('a', 'b')).toBe(2);
78
+
79
+ const copy = map2d.copy();
80
+ expect(copy.get('a', 'b')).toBe(2);
81
+
82
+ map2d.deleteRow('a');
83
+ expect(map2d.get('a', 'b')).toBe(undefined);
84
+ });
85
+
86
+ it('should return a row by key', () => {
87
+ const map2d = new Map2d<string, string, number>();
88
+ map2d.set('a', 'b', 2);
89
+ map2d.set('c', 'd', 4);
90
+
91
+ expect(map2d.getRow('a', 0)).toEqual([2, 0]);
92
+ expect(map2d.getRow('c', 0)).toEqual([0, 4]);
93
+ });
94
+ });
@@ -1,9 +1,10 @@
1
- import { type SequenceType } from '../types';
1
+ import { type MutationType, type SequenceType } from '../types';
2
2
 
3
3
  export interface Mutation {
4
4
  readonly segment: string | undefined;
5
5
  readonly position: number;
6
6
  readonly code: string;
7
+ readonly type: MutationType;
7
8
 
8
9
  equals(other: Mutation): boolean;
9
10
 
@@ -15,6 +16,7 @@ export const substitutionRegex =
15
16
 
16
17
  export class Substitution implements Mutation {
17
18
  readonly code;
19
+ readonly type = 'substitution';
18
20
 
19
21
  constructor(
20
22
  readonly segment: string | undefined,
@@ -62,6 +64,7 @@ export const deletionRegex = /^((?<segment>[A-Za-z0-9_-]+)(?=:):)?(?<valueAtRefe
62
64
 
63
65
  export class Deletion implements Mutation {
64
66
  readonly code;
67
+ readonly type = 'deletion';
65
68
 
66
69
  constructor(
67
70
  readonly segment: string | undefined,
@@ -103,6 +106,7 @@ export const insertionRegexp =
103
106
 
104
107
  export class Insertion implements Mutation {
105
108
  readonly code;
109
+ readonly type = 'insertion';
106
110
 
107
111
  constructor(
108
112
  readonly segment: string | undefined,
@@ -60,6 +60,8 @@ describe('YearMonthDay', () => {
60
60
  // seems to be a bug in dayjs: https://github.com/iamkun/dayjs/issues/2620
61
61
  expect(underTest.week.text).equal('2019-01');
62
62
  expect(underTest.text).equal('2020-01-01');
63
+ expect(underTest.firstDay.text).equal('2020-01-01');
64
+ expect(underTest.lastDay.text).equal('2020-01-01');
63
65
  });
64
66
  });
65
67
 
@@ -71,6 +73,7 @@ describe('YearWeek', () => {
71
73
  expect(underTest.isoWeekNumber).equal(2);
72
74
  expect(underTest.firstDay.text).equal('2020-01-06');
73
75
  expect(underTest.text).equal('2020-02');
76
+ expect(underTest.lastDay.text).equal('2020-01-12');
74
77
  });
75
78
  });
76
79
 
@@ -82,6 +85,7 @@ describe('YearMonth', () => {
82
85
  expect(underTest.monthNumber).equal(1);
83
86
  expect(underTest.text).equal('2020-01');
84
87
  expect(underTest.firstDay.text).equal('2020-01-01');
88
+ expect(underTest.lastDay.text).equal('2020-01-31');
85
89
  });
86
90
  });
87
91
 
@@ -93,5 +97,6 @@ describe('Year', () => {
93
97
  expect(underTest.text).equal('2020');
94
98
  expect(underTest.firstDay.text).equal('2020-01-01');
95
99
  expect(underTest.firstMonth.text).equal('2020-01');
100
+ expect(underTest.lastDay.text).equal('2020-12-31');
96
101
  });
97
102
  });
@@ -2,6 +2,8 @@ import dayjs from 'dayjs/esm';
2
2
  import advancedFormat from 'dayjs/esm/plugin/advancedFormat';
3
3
  import isoWeek from 'dayjs/esm/plugin/isoWeek';
4
4
 
5
+ import type { TemporalGranularity } from '../types';
6
+
5
7
  dayjs.extend(isoWeek);
6
8
  dayjs.extend(advancedFormat);
7
9
 
@@ -70,6 +72,18 @@ export class YearMonthDay {
70
72
  return this.text;
71
73
  }
72
74
 
75
+ englishName(): string {
76
+ return this.dayjs.format('dddd, MMMM D, YYYY');
77
+ }
78
+
79
+ get firstDay(): YearMonthDay {
80
+ return this;
81
+ }
82
+
83
+ get lastDay(): YearMonthDay {
84
+ return this;
85
+ }
86
+
73
87
  get year(): Year {
74
88
  return this.cache.getYear(`${this.yearNumber}`);
75
89
  }
@@ -113,6 +127,10 @@ export class YearWeek {
113
127
  return this.text;
114
128
  }
115
129
 
130
+ englishName(): string {
131
+ return `Week ${this.isoWeekNumber}, ${this.isoYearNumber}`;
132
+ }
133
+
116
134
  get firstDay(): YearMonthDay {
117
135
  // "The first week of the year, hence, always contains 4 January." https://en.wikipedia.org/wiki/ISO_week_date
118
136
  const firstDay = dayjs()
@@ -124,6 +142,18 @@ export class YearWeek {
124
142
  return this.cache.getYearMonthDay(firstDay.format('YYYY-MM-DD'));
125
143
  }
126
144
 
145
+ get lastDay(): YearMonthDay {
146
+ const firstDay = dayjs()
147
+ .year(this.isoYearNumber)
148
+ .startOf('year')
149
+ .add((this.isoWeekNumber - 1) * 7, 'day')
150
+ .startOf('week')
151
+ .add(1, 'day');
152
+ const lastDay = firstDay.add(6, 'day');
153
+
154
+ return this.cache.getYearMonthDay(lastDay.format('YYYY-MM-DD'));
155
+ }
156
+
127
157
  get year(): Year {
128
158
  return this.cache.getYear(`${this.isoYearNumber}`);
129
159
  }
@@ -159,10 +189,20 @@ export class YearMonth {
159
189
  return this.text;
160
190
  }
161
191
 
192
+ englishName(): string {
193
+ return `${monthName(this.monthNumber)} ${this.yearNumber}`;
194
+ }
195
+
162
196
  get firstDay(): YearMonthDay {
163
197
  return this.cache.getYearMonthDay(dayjs(`${this.yearNumber}-${this.monthNumber}-01`).format('YYYY-MM-DD'));
164
198
  }
165
199
 
200
+ get lastDay(): YearMonthDay {
201
+ return this.cache.getYearMonthDay(
202
+ dayjs(`${this.yearNumber}-${this.monthNumber}-01`).endOf('month').format('YYYY-MM-DD'),
203
+ );
204
+ }
205
+
166
206
  get year(): Year {
167
207
  return this.cache.getYear(`${this.yearNumber}`);
168
208
  }
@@ -197,14 +237,26 @@ export class Year {
197
237
  return this.text;
198
238
  }
199
239
 
240
+ englishName(): string {
241
+ return this.year.toString();
242
+ }
243
+
200
244
  get firstMonth(): YearMonth {
201
245
  return this.cache.getYearMonth(`${this.year}-01`);
202
246
  }
203
247
 
248
+ get lastMonth(): YearMonth {
249
+ return this.cache.getYearMonth(`${this.year}-12`);
250
+ }
251
+
204
252
  get firstDay(): YearMonthDay {
205
253
  return this.firstMonth.firstDay;
206
254
  }
207
255
 
256
+ get lastDay(): YearMonthDay {
257
+ return this.lastMonth.lastDay;
258
+ }
259
+
208
260
  addYears(years: number): Year {
209
261
  const date = this.firstDay.dayjs.add(years, 'year');
210
262
  const s = date.format('YYYY');
@@ -221,6 +273,12 @@ export class Year {
221
273
  }
222
274
  }
223
275
 
276
+ function monthName(month: number): string {
277
+ return dayjs()
278
+ .month(month - 1)
279
+ .format('MMMM');
280
+ }
281
+
224
282
  export type Temporal = YearMonthDay | YearWeek | YearMonth | Year;
225
283
 
226
284
  export function generateAllDaysInRange(start: YearMonthDay, end: YearMonthDay): YearMonthDay[] {
@@ -311,9 +369,9 @@ export function compareTemporal(a: Temporal | null, b: Temporal | null): number
311
369
  return 0;
312
370
  }
313
371
 
314
- export function getMinMaxTemporal(values: Iterable<Temporal | null>): [Temporal, Temporal] | null {
315
- let min = null;
316
- let max = null;
372
+ export function getMinMaxTemporal<T extends Temporal>(values: Iterable<T | null>) {
373
+ let min: T | null = null;
374
+ let max: T | null = null;
317
375
  for (const value of values) {
318
376
  if (value === null) {
319
377
  continue;
@@ -326,9 +384,9 @@ export function getMinMaxTemporal(values: Iterable<Temporal | null>): [Temporal,
326
384
  }
327
385
  }
328
386
  if (min === null || max === null) {
329
- return null;
387
+ return { min: null, max: null };
330
388
  }
331
- return [min, max];
389
+ return { min, max };
332
390
  }
333
391
 
334
392
  export function addUnit(temporal: Temporal, amount: number): Temporal {
@@ -346,3 +404,28 @@ export function addUnit(temporal: Temporal, amount: number): Temporal {
346
404
  }
347
405
  throw new Error(`Invalid argument: ${temporal}`);
348
406
  }
407
+
408
+ export function parseDateStringToTemporal(date: string, granularity: TemporalGranularity) {
409
+ const cache = TemporalCache.getInstance();
410
+ const day = cache.getYearMonthDay(date);
411
+ switch (granularity) {
412
+ case 'day':
413
+ return day;
414
+ case 'week':
415
+ return day.week;
416
+ case 'month':
417
+ return day.month;
418
+ case 'year':
419
+ return day.year;
420
+ }
421
+ }
422
+
423
+ export function dateRangeCompare(a: { dateRange: Temporal | null }, b: { dateRange: Temporal | null }) {
424
+ if (a.dateRange === null) {
425
+ return 1;
426
+ }
427
+ if (b.dateRange === null) {
428
+ return -1;
429
+ }
430
+ return compareTemporal(a.dateRange, b.dateRange);
431
+ }
@@ -0,0 +1,225 @@
1
+ import type { Meta, StoryObj } from '@storybook/web-components';
2
+ import { html } from 'lit';
3
+
4
+ import './gs-mutations-over-time';
5
+ import '../app';
6
+ import { withComponentDocs } from '../../../.storybook/ComponentDocsBlock';
7
+ import { AGGREGATED_ENDPOINT, LAPIS_URL, NUCLEOTIDE_MUTATIONS_ENDPOINT } from '../../constants';
8
+ import aggregated_date from '../../preact/mutationsOverTime/__mockData__/aggregated_date.json';
9
+ import nucleotideMutation_01 from '../../preact/mutationsOverTime/__mockData__/nucleotideMutations_2024_01.json';
10
+ import nucleotideMutation_02 from '../../preact/mutationsOverTime/__mockData__/nucleotideMutations_2024_02.json';
11
+ import nucleotideMutation_03 from '../../preact/mutationsOverTime/__mockData__/nucleotideMutations_2024_03.json';
12
+ import nucleotideMutation_04 from '../../preact/mutationsOverTime/__mockData__/nucleotideMutations_2024_04.json';
13
+ import nucleotideMutation_05 from '../../preact/mutationsOverTime/__mockData__/nucleotideMutations_2024_05.json';
14
+ import nucleotideMutation_06 from '../../preact/mutationsOverTime/__mockData__/nucleotideMutations_2024_06.json';
15
+ import nucleotideMutation_07 from '../../preact/mutationsOverTime/__mockData__/nucleotideMutations_2024_07.json';
16
+ import { type MutationsOverTimeProps } from '../../preact/mutationsOverTime/mutations-over-time';
17
+
18
+ const codeExample = String.raw`
19
+ <gs-mutations-over-time
20
+ lapisFilter='{ "pangoLineage": "JN.1*", "dateFrom": "2024-01-15", "dateTo": "2024-07-10" }'
21
+ sequenceType="nucleotide"
22
+ views='["grid"]'
23
+ headline="Mutations over time"
24
+ width='100%'
25
+ height='700px'
26
+ granularity="month"
27
+ lapisDateField="date"
28
+ ></gs-mutations-over-time>`;
29
+
30
+ const meta: Meta<Required<MutationsOverTimeProps>> = {
31
+ title: 'Visualization/Mutations over time',
32
+ component: 'gs-mutations-over-time',
33
+ argTypes: {
34
+ lapisFilter: { control: 'object' },
35
+ sequenceType: {
36
+ options: ['nucleotide', 'amino acid'],
37
+ control: { type: 'radio' },
38
+ },
39
+ views: {
40
+ options: ['grid'],
41
+ control: { type: 'check' },
42
+ },
43
+ width: { control: 'text' },
44
+ height: { control: 'text' },
45
+ granularity: {
46
+ options: ['day', 'week', 'month', 'year'],
47
+ control: { type: 'radio' },
48
+ },
49
+ lapisDateField: { control: 'text' },
50
+ },
51
+ args: {
52
+ lapisFilter: { pangoLineage: 'JN.1*', dateFrom: '2024-01-15', dateTo: '2024-07-10' },
53
+ sequenceType: 'nucleotide',
54
+ views: ['grid'],
55
+ width: '100%',
56
+ height: '700px',
57
+ granularity: 'month',
58
+ lapisDateField: 'date',
59
+ },
60
+ parameters: withComponentDocs({
61
+ componentDocs: {
62
+ opensShadowDom: true,
63
+ expectsChildren: false,
64
+ codeExample,
65
+ },
66
+ }),
67
+ tags: ['autodocs'],
68
+ };
69
+
70
+ export default meta;
71
+
72
+ const Template: StoryObj<Required<MutationsOverTimeProps>> = {
73
+ render: (args) => html`
74
+ <gs-app lapis="${LAPIS_URL}">
75
+ <gs-mutations-over-time
76
+ .lapisFilter=${args.lapisFilter}
77
+ .sequenceType=${args.sequenceType}
78
+ .views=${args.views}
79
+ .width=${args.width}
80
+ .height=${args.height}
81
+ .granularity=${args.granularity}
82
+ .lapisDateField=${args.lapisDateField}
83
+ ></gs-mutations-over-time>
84
+ </gs-app>
85
+ `,
86
+ };
87
+
88
+ export const Default: StoryObj<Required<MutationsOverTimeProps>> = {
89
+ ...Template,
90
+ parameters: {
91
+ fetchMock: {
92
+ mocks: [
93
+ {
94
+ matcher: {
95
+ name: 'aggregated_dates',
96
+ url: AGGREGATED_ENDPOINT,
97
+ body: {
98
+ dateFrom: '2024-01-15',
99
+ dateTo: '2024-07-10',
100
+ fields: ['date'],
101
+ pangoLineage: 'JN.1*',
102
+ },
103
+ },
104
+ response: {
105
+ status: 200,
106
+ body: aggregated_date,
107
+ },
108
+ },
109
+ {
110
+ matcher: {
111
+ name: 'nucleotideMutations_01',
112
+ url: NUCLEOTIDE_MUTATIONS_ENDPOINT,
113
+ body: {
114
+ pangoLineage: 'JN.1*',
115
+ dateFrom: '2024-01-01',
116
+ dateTo: '2024-01-31',
117
+ minProportion: 0.001,
118
+ },
119
+ },
120
+ response: {
121
+ status: 200,
122
+ body: nucleotideMutation_01,
123
+ },
124
+ },
125
+ {
126
+ matcher: {
127
+ name: 'nucleotideMutations_02',
128
+ url: NUCLEOTIDE_MUTATIONS_ENDPOINT,
129
+ body: {
130
+ pangoLineage: 'JN.1*',
131
+ dateFrom: '2024-02-01',
132
+ dateTo: '2024-02-29',
133
+ minProportion: 0.001,
134
+ },
135
+ },
136
+ response: {
137
+ status: 200,
138
+ body: nucleotideMutation_02,
139
+ },
140
+ },
141
+ {
142
+ matcher: {
143
+ name: 'nucleotideMutations_03',
144
+ url: NUCLEOTIDE_MUTATIONS_ENDPOINT,
145
+ body: {
146
+ pangoLineage: 'JN.1*',
147
+ dateFrom: '2024-03-01',
148
+ dateTo: '2024-03-31',
149
+ minProportion: 0.001,
150
+ },
151
+ response: {
152
+ status: 200,
153
+ body: nucleotideMutation_03,
154
+ },
155
+ },
156
+ },
157
+ {
158
+ matcher: {
159
+ name: 'nucleotideMutations_04',
160
+ url: NUCLEOTIDE_MUTATIONS_ENDPOINT,
161
+ body: {
162
+ pangoLineage: 'JN.1*',
163
+ dateFrom: '2024-04-01',
164
+ dateTo: '2024-04-30',
165
+ minProportion: 0.001,
166
+ },
167
+ response: {
168
+ status: 200,
169
+ body: nucleotideMutation_04,
170
+ },
171
+ },
172
+ },
173
+ {
174
+ matcher: {
175
+ name: 'nucleotideMutations_05',
176
+ url: NUCLEOTIDE_MUTATIONS_ENDPOINT,
177
+ body: {
178
+ pangoLineage: 'JN.1*',
179
+ dateFrom: '2024-05-01',
180
+ dateTo: '2024-05-31',
181
+ minProportion: 0.001,
182
+ },
183
+ response: {
184
+ status: 200,
185
+ body: nucleotideMutation_05,
186
+ },
187
+ },
188
+ },
189
+ {
190
+ matcher: {
191
+ name: 'nucleotideMutations_06',
192
+ url: NUCLEOTIDE_MUTATIONS_ENDPOINT,
193
+ body: {
194
+ pangoLineage: 'JN.1*',
195
+ dateFrom: '2024-06-01',
196
+ dateTo: '2024-06-30',
197
+ minProportion: 0.001,
198
+ },
199
+ response: {
200
+ status: 200,
201
+ body: nucleotideMutation_06,
202
+ },
203
+ },
204
+ },
205
+
206
+ {
207
+ matcher: {
208
+ name: 'nucleotideMutations_07',
209
+ url: NUCLEOTIDE_MUTATIONS_ENDPOINT,
210
+ body: {
211
+ pangoLineage: 'JN.1*',
212
+ dateFrom: '2024-07-01',
213
+ dateTo: '2024-07-31',
214
+ minProportion: 0.001,
215
+ },
216
+ response: {
217
+ status: 200,
218
+ body: nucleotideMutation_07,
219
+ },
220
+ },
221
+ },
222
+ ],
223
+ },
224
+ },
225
+ };