@genspectrum/dashboard-components 0.6.1 → 0.6.3
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 +228 -0
- package/dist/dashboard-components.js +778 -244
- package/dist/dashboard-components.js.map +1 -1
- package/dist/genspectrum-components.d.ts +119 -9
- package/dist/style.css +7 -4
- package/package.json +3 -1
- package/src/constants.ts +1 -1
- package/src/lapisApi/lapisTypes.ts +1 -0
- package/src/operator/FillMissingOperator.spec.ts +3 -1
- package/src/operator/FillMissingOperator.ts +4 -2
- package/src/preact/mutationComparison/queryMutationData.ts +12 -4
- package/src/preact/mutationsOverTime/__mockData__/aggregated_date.json +642 -0
- package/src/preact/mutationsOverTime/__mockData__/nucleotideMutations_2024_01.json +1747 -0
- package/src/preact/mutationsOverTime/__mockData__/nucleotideMutations_2024_02.json +1774 -0
- package/src/preact/mutationsOverTime/__mockData__/nucleotideMutations_2024_03.json +1819 -0
- package/src/preact/mutationsOverTime/__mockData__/nucleotideMutations_2024_04.json +1864 -0
- package/src/preact/mutationsOverTime/__mockData__/nucleotideMutations_2024_05.json +1927 -0
- package/src/preact/mutationsOverTime/__mockData__/nucleotideMutations_2024_06.json +1864 -0
- package/src/preact/mutationsOverTime/__mockData__/nucleotideMutations_2024_07.json +9 -0
- package/src/preact/mutationsOverTime/getFilteredMutationsOverTime.spec.ts +86 -0
- package/src/preact/mutationsOverTime/getFilteredMutationsOverTimeData.ts +62 -0
- package/src/preact/mutationsOverTime/mutations-over-time-grid.tsx +92 -0
- package/src/preact/mutationsOverTime/mutations-over-time.stories.tsx +206 -0
- package/src/preact/mutationsOverTime/mutations-over-time.tsx +170 -0
- package/src/preact/numberSequencesOverTime/getNumberOfSequencesOverTimeTableData.ts +1 -1
- package/src/preact/prevalenceOverTime/prevalence-over-time.stories.tsx +1 -0
- package/src/preact/shared/table/formatProportion.ts +2 -2
- package/src/query/queryAggregatedDataOverTime.ts +8 -33
- package/src/query/queryMutationsOverTime.spec.ts +352 -0
- package/src/query/queryMutationsOverTime.ts +164 -0
- package/src/query/queryNumberOfSequencesOverTime.ts +0 -1
- package/src/query/queryRelativeGrowthAdvantage.ts +3 -3
- package/src/utils/Map2d.ts +75 -0
- package/src/utils/map2d.spec.ts +94 -0
- package/src/utils/mutations.ts +5 -1
- package/src/utils/temporal.ts +64 -5
- package/src/web-components/input/index.ts +1 -0
- package/src/web-components/visualization/gs-mutations-over-time.stories.ts +225 -0
- package/src/web-components/visualization/gs-mutations-over-time.tsx +107 -0
- package/src/web-components/visualization/index.ts +1 -0
|
@@ -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
|
+
});
|
package/src/utils/mutations.ts
CHANGED
|
@@ -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,
|
package/src/utils/temporal.ts
CHANGED
|
@@ -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,14 @@ export class YearMonthDay {
|
|
|
70
72
|
return this.text;
|
|
71
73
|
}
|
|
72
74
|
|
|
75
|
+
get firstDay(): YearMonthDay {
|
|
76
|
+
return this;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
get lastDay(): YearMonthDay {
|
|
80
|
+
return this;
|
|
81
|
+
}
|
|
82
|
+
|
|
73
83
|
get year(): Year {
|
|
74
84
|
return this.cache.getYear(`${this.yearNumber}`);
|
|
75
85
|
}
|
|
@@ -124,6 +134,16 @@ export class YearWeek {
|
|
|
124
134
|
return this.cache.getYearMonthDay(firstDay.format('YYYY-MM-DD'));
|
|
125
135
|
}
|
|
126
136
|
|
|
137
|
+
get lastDay(): YearMonthDay {
|
|
138
|
+
const lastDay = dayjs()
|
|
139
|
+
.year(this.isoYearNumber)
|
|
140
|
+
.month(12)
|
|
141
|
+
.date(31)
|
|
142
|
+
.isoWeek(this.isoWeekNumber)
|
|
143
|
+
.endOf('isoWeek');
|
|
144
|
+
return this.cache.getYearMonthDay(lastDay.format('YYYY-MM-DD'));
|
|
145
|
+
}
|
|
146
|
+
|
|
127
147
|
get year(): Year {
|
|
128
148
|
return this.cache.getYear(`${this.isoYearNumber}`);
|
|
129
149
|
}
|
|
@@ -163,6 +183,12 @@ export class YearMonth {
|
|
|
163
183
|
return this.cache.getYearMonthDay(dayjs(`${this.yearNumber}-${this.monthNumber}-01`).format('YYYY-MM-DD'));
|
|
164
184
|
}
|
|
165
185
|
|
|
186
|
+
get lastDay(): YearMonthDay {
|
|
187
|
+
return this.cache.getYearMonthDay(
|
|
188
|
+
dayjs(`${this.yearNumber}-${this.monthNumber}-01`).endOf('month').format('YYYY-MM-DD'),
|
|
189
|
+
);
|
|
190
|
+
}
|
|
191
|
+
|
|
166
192
|
get year(): Year {
|
|
167
193
|
return this.cache.getYear(`${this.yearNumber}`);
|
|
168
194
|
}
|
|
@@ -201,10 +227,18 @@ export class Year {
|
|
|
201
227
|
return this.cache.getYearMonth(`${this.year}-01`);
|
|
202
228
|
}
|
|
203
229
|
|
|
230
|
+
get lastMonth(): YearMonth {
|
|
231
|
+
return this.cache.getYearMonth(`${this.year}-12`);
|
|
232
|
+
}
|
|
233
|
+
|
|
204
234
|
get firstDay(): YearMonthDay {
|
|
205
235
|
return this.firstMonth.firstDay;
|
|
206
236
|
}
|
|
207
237
|
|
|
238
|
+
get lastDay(): YearMonthDay {
|
|
239
|
+
return this.lastMonth.lastDay;
|
|
240
|
+
}
|
|
241
|
+
|
|
208
242
|
addYears(years: number): Year {
|
|
209
243
|
const date = this.firstDay.dayjs.add(years, 'year');
|
|
210
244
|
const s = date.format('YYYY');
|
|
@@ -311,9 +345,9 @@ export function compareTemporal(a: Temporal | null, b: Temporal | null): number
|
|
|
311
345
|
return 0;
|
|
312
346
|
}
|
|
313
347
|
|
|
314
|
-
export function getMinMaxTemporal(values: Iterable<
|
|
315
|
-
let min = null;
|
|
316
|
-
let max = null;
|
|
348
|
+
export function getMinMaxTemporal<T extends Temporal>(values: Iterable<T | null>) {
|
|
349
|
+
let min: T | null = null;
|
|
350
|
+
let max: T | null = null;
|
|
317
351
|
for (const value of values) {
|
|
318
352
|
if (value === null) {
|
|
319
353
|
continue;
|
|
@@ -326,9 +360,9 @@ export function getMinMaxTemporal(values: Iterable<Temporal | null>): [Temporal,
|
|
|
326
360
|
}
|
|
327
361
|
}
|
|
328
362
|
if (min === null || max === null) {
|
|
329
|
-
return null;
|
|
363
|
+
return { min: null, max: null };
|
|
330
364
|
}
|
|
331
|
-
return
|
|
365
|
+
return { min, max };
|
|
332
366
|
}
|
|
333
367
|
|
|
334
368
|
export function addUnit(temporal: Temporal, amount: number): Temporal {
|
|
@@ -346,3 +380,28 @@ export function addUnit(temporal: Temporal, amount: number): Temporal {
|
|
|
346
380
|
}
|
|
347
381
|
throw new Error(`Invalid argument: ${temporal}`);
|
|
348
382
|
}
|
|
383
|
+
|
|
384
|
+
export function parseDateStringToTemporal(date: string, granularity: TemporalGranularity) {
|
|
385
|
+
const cache = TemporalCache.getInstance();
|
|
386
|
+
const day = cache.getYearMonthDay(date);
|
|
387
|
+
switch (granularity) {
|
|
388
|
+
case 'day':
|
|
389
|
+
return day;
|
|
390
|
+
case 'week':
|
|
391
|
+
return day.week;
|
|
392
|
+
case 'month':
|
|
393
|
+
return day.month;
|
|
394
|
+
case 'year':
|
|
395
|
+
return day.year;
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
export function dateRangeCompare(a: { dateRange: Temporal | null }, b: { dateRange: Temporal | null }) {
|
|
400
|
+
if (a.dateRange === null) {
|
|
401
|
+
return 1;
|
|
402
|
+
}
|
|
403
|
+
if (b.dateRange === null) {
|
|
404
|
+
return -1;
|
|
405
|
+
}
|
|
406
|
+
return compareTemporal(a.dateRange, b.dateRange);
|
|
407
|
+
}
|
|
@@ -2,3 +2,4 @@ export { DateRangeSelectorComponent } from './gs-date-range-selector';
|
|
|
2
2
|
export { LocationFilterComponent } from './gs-location-filter';
|
|
3
3
|
export { TextInputComponent } from './gs-text-input';
|
|
4
4
|
export { MutationFilterComponent } from './gs-mutation-filter';
|
|
5
|
+
export { LineageFilterComponent } from './gs-lineage-filter';
|
|
@@ -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
|
+
};
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { customElement, property } from 'lit/decorators.js';
|
|
2
|
+
|
|
3
|
+
import { MutationsOverTime, type MutationsOverTimeProps } from '../../preact/mutationsOverTime/mutations-over-time';
|
|
4
|
+
import type { Equals, Expect } from '../../utils/typeAssertions';
|
|
5
|
+
import { PreactLitAdapterWithGridJsStyles } from '../PreactLitAdapterWithGridJsStyles';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* ## Context
|
|
9
|
+
*
|
|
10
|
+
* This component displays mutations (substitutions and deletions) over time for a dataset selected by a LAPIS filter.
|
|
11
|
+
* The shown date range is determined by the date field in the LAPIS filter.
|
|
12
|
+
* If the date field is not set, the date range is determined by all available dates in the dataset.
|
|
13
|
+
*
|
|
14
|
+
* ## Views
|
|
15
|
+
*
|
|
16
|
+
* ### Grid View
|
|
17
|
+
*
|
|
18
|
+
* The grid view shows the proportion for each mutation over date ranges.
|
|
19
|
+
*
|
|
20
|
+
*/
|
|
21
|
+
@customElement('gs-mutations-over-time')
|
|
22
|
+
export class MutationsOverTimeComponent extends PreactLitAdapterWithGridJsStyles {
|
|
23
|
+
/**
|
|
24
|
+
* Required.
|
|
25
|
+
*
|
|
26
|
+
* LAPIS filter to select the displayed data.
|
|
27
|
+
*/
|
|
28
|
+
@property({ type: Object })
|
|
29
|
+
lapisFilter: Record<string, string | number | null | boolean> = {};
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* The type of the sequence for which the mutations should be shown.
|
|
33
|
+
*/
|
|
34
|
+
@property({ type: String })
|
|
35
|
+
sequenceType: 'nucleotide' | 'amino acid' = 'nucleotide';
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* A list of tabs with views that this component should provide.
|
|
39
|
+
*/
|
|
40
|
+
@property({ type: Array })
|
|
41
|
+
views: 'grid'[] = ['grid'];
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* The width of the component.
|
|
45
|
+
*
|
|
46
|
+
* Visit https://genspectrum.github.io/dashboard-components/?path=/docs/components-size-of-components--docs for more information.
|
|
47
|
+
*/
|
|
48
|
+
@property({ type: String })
|
|
49
|
+
width: string = '100%';
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* The height of the component.
|
|
53
|
+
*
|
|
54
|
+
* Visit https://genspectrum.github.io/dashboard-components/?path=/docs/components-size-of-components--docs for more information.
|
|
55
|
+
*/
|
|
56
|
+
@property({ type: String })
|
|
57
|
+
height: string = '700px';
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* The granularity of the time axis.
|
|
61
|
+
*/
|
|
62
|
+
@property({ type: String })
|
|
63
|
+
granularity: 'day' | 'week' | 'month' | 'year' = 'week';
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Required.
|
|
67
|
+
*
|
|
68
|
+
* The LAPIS field that the data should be aggregated by.
|
|
69
|
+
* The values will be used for the columns of the grid.
|
|
70
|
+
* Must be a field of type `date` in LAPIS.
|
|
71
|
+
*/
|
|
72
|
+
@property({ type: String })
|
|
73
|
+
lapisDateField: string = 'date';
|
|
74
|
+
|
|
75
|
+
override render() {
|
|
76
|
+
return (
|
|
77
|
+
<MutationsOverTime
|
|
78
|
+
lapisFilter={this.lapisFilter}
|
|
79
|
+
sequenceType={this.sequenceType}
|
|
80
|
+
views={this.views}
|
|
81
|
+
width={this.width}
|
|
82
|
+
height={this.height}
|
|
83
|
+
granularity={this.granularity}
|
|
84
|
+
lapisDateField={this.lapisDateField}
|
|
85
|
+
/>
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
declare global {
|
|
91
|
+
interface HTMLElementTagNameMap {
|
|
92
|
+
'gs-mutations-over-time-component': MutationsOverTimeComponent;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/* eslint-disable @typescript-eslint/no-unused-vars, no-unused-vars */
|
|
97
|
+
type LapisFilterMatches = Expect<
|
|
98
|
+
Equals<typeof MutationsOverTimeComponent.prototype.lapisFilter, MutationsOverTimeProps['lapisFilter']>
|
|
99
|
+
>;
|
|
100
|
+
type SequenceTypeMatches = Expect<
|
|
101
|
+
Equals<typeof MutationsOverTimeComponent.prototype.sequenceType, MutationsOverTimeProps['sequenceType']>
|
|
102
|
+
>;
|
|
103
|
+
type ViewsMatches = Expect<Equals<typeof MutationsOverTimeComponent.prototype.views, MutationsOverTimeProps['views']>>;
|
|
104
|
+
type GranularityMatches = Expect<
|
|
105
|
+
Equals<typeof MutationsOverTimeComponent.prototype.granularity, MutationsOverTimeProps['granularity']>
|
|
106
|
+
>;
|
|
107
|
+
/* eslint-enable @typescript-eslint/no-unused-vars, no-unused-vars */
|
|
@@ -4,3 +4,4 @@ export { PrevalenceOverTimeComponent } from './gs-prevalence-over-time';
|
|
|
4
4
|
export { RelativeGrowthAdvantageComponent } from './gs-relative-growth-advantage';
|
|
5
5
|
export { AggregateComponent } from './gs-aggregate';
|
|
6
6
|
export { NumberSequencesOverTimeComponent } from './gs-number-sequences-over-time';
|
|
7
|
+
export { MutationsOverTimeComponent } from './gs-mutations-over-time';
|