@genspectrum/dashboard-components 1.4.0 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/custom-elements.json +97 -5
- package/dist/assets/{mutationOverTimeWorker-CQxrFo53.js.map → mutationOverTimeWorker-BJ_P2T8Y.js.map} +1 -1
- package/dist/components.d.ts +45 -25
- package/dist/components.js +117 -20
- package/dist/components.js.map +1 -1
- package/dist/util.d.ts +25 -25
- package/package.json +1 -1
- package/src/preact/lineageFilter/lineage-filter.stories.tsx +24 -0
- package/src/preact/lineageFilter/lineage-filter.tsx +13 -2
- package/src/preact/locationFilter/location-filter.stories.tsx +24 -0
- package/src/preact/locationFilter/location-filter.tsx +19 -3
- package/src/preact/mutationsOverTime/__mockData__/withGaps.ts +352 -0
- package/src/preact/mutationsOverTime/getFilteredMutationsOverTime.spec.ts +38 -0
- package/src/preact/mutationsOverTime/getFilteredMutationsOverTimeData.ts +10 -0
- package/src/preact/mutationsOverTime/mutationOverTimeWorker.mock.ts +2 -0
- package/src/preact/mutationsOverTime/mutations-over-time.stories.tsx +35 -0
- package/src/preact/mutationsOverTime/mutations-over-time.tsx +28 -4
- package/src/preact/textFilter/text-filter.stories.tsx +29 -4
- package/src/preact/textFilter/text-filter.tsx +13 -2
- package/src/query/queryMutationsOverTime.ts +37 -4
- package/src/query/queryMutationsOverTimeNewEndpoint.spec.ts +122 -0
- package/src/utils/map2d.spec.ts +30 -0
- package/src/utils/map2d.ts +14 -1
- package/src/web-components/input/gs-lineage-filter.stories.ts +7 -0
- package/src/web-components/input/gs-lineage-filter.tsx +8 -0
- package/src/web-components/input/gs-location-filter.stories.ts +7 -0
- package/src/web-components/input/gs-location-filter.tsx +9 -1
- package/src/web-components/input/gs-text-filter.stories.ts +7 -0
- package/src/web-components/input/gs-text-filter.tsx +8 -0
- package/src/web-components/visualization/gs-mutations-over-time.stories.ts +14 -1
- package/src/web-components/visualization/gs-mutations-over-time.tsx +8 -0
- package/standalone-bundle/assets/{mutationOverTimeWorker-CDACUs6w.js.map → mutationOverTimeWorker-CkeGpKWp.js.map} +1 -1
- package/standalone-bundle/dashboard-components.js +1412 -1329
- package/standalone-bundle/dashboard-components.js.map +1 -1
|
@@ -0,0 +1,352 @@
|
|
|
1
|
+
import { type MutationOverTimeMockData } from './mockConversion';
|
|
2
|
+
|
|
3
|
+
export const withGaps: MutationOverTimeMockData = {
|
|
4
|
+
query: {
|
|
5
|
+
lapisFilter: {
|
|
6
|
+
pangoLineage: 'JN.1*',
|
|
7
|
+
dateFrom: '2024-01-15',
|
|
8
|
+
dateTo: '2024-07-10',
|
|
9
|
+
},
|
|
10
|
+
sequenceType: 'nucleotide',
|
|
11
|
+
granularity: 'month',
|
|
12
|
+
lapisDateField: 'date',
|
|
13
|
+
lapis: 'https://lapis.cov-spectrum.org/open/v2',
|
|
14
|
+
displayMutations: ['A19722G', 'G21641T', 'T21652-'],
|
|
15
|
+
useNewEndpoint: false,
|
|
16
|
+
},
|
|
17
|
+
response: {
|
|
18
|
+
overallMutationData: [
|
|
19
|
+
{
|
|
20
|
+
type: 'substitution',
|
|
21
|
+
mutation: {
|
|
22
|
+
valueAtReference: 'A',
|
|
23
|
+
substitutionValue: 'G',
|
|
24
|
+
position: 19722,
|
|
25
|
+
type: 'substitution',
|
|
26
|
+
code: 'A19722G',
|
|
27
|
+
},
|
|
28
|
+
count: 10234,
|
|
29
|
+
proportion: 0.09900453714363107,
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
type: 'substitution',
|
|
33
|
+
mutation: {
|
|
34
|
+
valueAtReference: 'G',
|
|
35
|
+
substitutionValue: 'T',
|
|
36
|
+
position: 21641,
|
|
37
|
+
type: 'substitution',
|
|
38
|
+
code: 'G21641T',
|
|
39
|
+
},
|
|
40
|
+
count: 4485,
|
|
41
|
+
proportion: 0.05001951709139575,
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
type: 'deletion',
|
|
45
|
+
mutation: {
|
|
46
|
+
valueAtReference: 'T',
|
|
47
|
+
position: 21652,
|
|
48
|
+
type: 'deletion',
|
|
49
|
+
code: 'T21652-',
|
|
50
|
+
},
|
|
51
|
+
count: 17169,
|
|
52
|
+
proportion: 0.16814713976514833,
|
|
53
|
+
},
|
|
54
|
+
],
|
|
55
|
+
mutationOverTimeSerializedAsArray: {
|
|
56
|
+
data: [
|
|
57
|
+
[
|
|
58
|
+
'A19722G',
|
|
59
|
+
[
|
|
60
|
+
[
|
|
61
|
+
'2024-01',
|
|
62
|
+
{
|
|
63
|
+
type: 'belowThreshold',
|
|
64
|
+
totalCount: 26387,
|
|
65
|
+
},
|
|
66
|
+
],
|
|
67
|
+
[
|
|
68
|
+
'2024-02',
|
|
69
|
+
{
|
|
70
|
+
type: 'belowThreshold',
|
|
71
|
+
totalCount: 17340,
|
|
72
|
+
},
|
|
73
|
+
],
|
|
74
|
+
[
|
|
75
|
+
'2024-03',
|
|
76
|
+
{
|
|
77
|
+
type: 'value',
|
|
78
|
+
count: 36,
|
|
79
|
+
proportion: 0.0043859649122807015,
|
|
80
|
+
totalCount: 8236,
|
|
81
|
+
},
|
|
82
|
+
],
|
|
83
|
+
[
|
|
84
|
+
'2024-04',
|
|
85
|
+
{
|
|
86
|
+
type: 'value',
|
|
87
|
+
count: 0,
|
|
88
|
+
proportion: NaN,
|
|
89
|
+
totalCount: 0,
|
|
90
|
+
},
|
|
91
|
+
],
|
|
92
|
+
[
|
|
93
|
+
'2024-05',
|
|
94
|
+
{
|
|
95
|
+
type: 'value',
|
|
96
|
+
count: 759,
|
|
97
|
+
proportion: 0.08675277174534232,
|
|
98
|
+
totalCount: 8799,
|
|
99
|
+
},
|
|
100
|
+
],
|
|
101
|
+
[
|
|
102
|
+
'2024-06',
|
|
103
|
+
{
|
|
104
|
+
type: 'value',
|
|
105
|
+
count: 0,
|
|
106
|
+
proportion: NaN,
|
|
107
|
+
totalCount: 0,
|
|
108
|
+
},
|
|
109
|
+
],
|
|
110
|
+
[
|
|
111
|
+
'2024-07',
|
|
112
|
+
{
|
|
113
|
+
type: 'value',
|
|
114
|
+
count: 6318,
|
|
115
|
+
proportion: 0.2995448511283899,
|
|
116
|
+
totalCount: 21234,
|
|
117
|
+
},
|
|
118
|
+
],
|
|
119
|
+
],
|
|
120
|
+
],
|
|
121
|
+
[
|
|
122
|
+
'G21641T',
|
|
123
|
+
[
|
|
124
|
+
[
|
|
125
|
+
'2024-01',
|
|
126
|
+
{
|
|
127
|
+
type: 'value',
|
|
128
|
+
count: 1625,
|
|
129
|
+
proportion: 0.07085858806087297,
|
|
130
|
+
totalCount: 26387,
|
|
131
|
+
},
|
|
132
|
+
],
|
|
133
|
+
[
|
|
134
|
+
'2024-02',
|
|
135
|
+
{
|
|
136
|
+
type: 'value',
|
|
137
|
+
count: 419,
|
|
138
|
+
proportion: 0.027931471235250985,
|
|
139
|
+
totalCount: 17340,
|
|
140
|
+
},
|
|
141
|
+
],
|
|
142
|
+
[
|
|
143
|
+
'2024-03',
|
|
144
|
+
{
|
|
145
|
+
type: 'value',
|
|
146
|
+
count: 228,
|
|
147
|
+
proportion: 0.03451930355791068,
|
|
148
|
+
totalCount: 8236,
|
|
149
|
+
},
|
|
150
|
+
],
|
|
151
|
+
[
|
|
152
|
+
'2024-04',
|
|
153
|
+
{
|
|
154
|
+
type: 'value',
|
|
155
|
+
count: 0,
|
|
156
|
+
proportion: NaN,
|
|
157
|
+
totalCount: 0,
|
|
158
|
+
},
|
|
159
|
+
],
|
|
160
|
+
[
|
|
161
|
+
'2024-05',
|
|
162
|
+
{
|
|
163
|
+
type: 'value',
|
|
164
|
+
count: 666,
|
|
165
|
+
proportion: 0.09498003422703936,
|
|
166
|
+
totalCount: 8799,
|
|
167
|
+
},
|
|
168
|
+
],
|
|
169
|
+
[
|
|
170
|
+
'2024-06',
|
|
171
|
+
{
|
|
172
|
+
type: 'value',
|
|
173
|
+
count: 0,
|
|
174
|
+
proportion: NaN,
|
|
175
|
+
totalCount: 0,
|
|
176
|
+
},
|
|
177
|
+
],
|
|
178
|
+
[
|
|
179
|
+
'2024-07',
|
|
180
|
+
{
|
|
181
|
+
type: 'value',
|
|
182
|
+
count: 270,
|
|
183
|
+
proportion: 0.013585589212035825,
|
|
184
|
+
totalCount: 21234,
|
|
185
|
+
},
|
|
186
|
+
],
|
|
187
|
+
],
|
|
188
|
+
],
|
|
189
|
+
[
|
|
190
|
+
'T21652-',
|
|
191
|
+
[
|
|
192
|
+
[
|
|
193
|
+
'2024-01',
|
|
194
|
+
{
|
|
195
|
+
type: 'belowThreshold',
|
|
196
|
+
totalCount: 26387,
|
|
197
|
+
},
|
|
198
|
+
],
|
|
199
|
+
[
|
|
200
|
+
'2024-02',
|
|
201
|
+
{
|
|
202
|
+
type: 'belowThreshold',
|
|
203
|
+
totalCount: 17340,
|
|
204
|
+
},
|
|
205
|
+
],
|
|
206
|
+
[
|
|
207
|
+
'2024-03',
|
|
208
|
+
{
|
|
209
|
+
type: 'value',
|
|
210
|
+
count: 40,
|
|
211
|
+
proportion: 0.004921259842519685,
|
|
212
|
+
totalCount: 8236,
|
|
213
|
+
},
|
|
214
|
+
],
|
|
215
|
+
[
|
|
216
|
+
'2024-04',
|
|
217
|
+
{
|
|
218
|
+
type: 'value',
|
|
219
|
+
count: 0,
|
|
220
|
+
proportion: NaN,
|
|
221
|
+
totalCount: 0,
|
|
222
|
+
},
|
|
223
|
+
],
|
|
224
|
+
[
|
|
225
|
+
'2024-05',
|
|
226
|
+
{
|
|
227
|
+
type: 'value',
|
|
228
|
+
count: 1304,
|
|
229
|
+
proportion: 0.151504589287789,
|
|
230
|
+
totalCount: 8799,
|
|
231
|
+
},
|
|
232
|
+
],
|
|
233
|
+
[
|
|
234
|
+
'2024-06',
|
|
235
|
+
{
|
|
236
|
+
type: 'value',
|
|
237
|
+
count: 0,
|
|
238
|
+
proportion: NaN,
|
|
239
|
+
totalCount: 0,
|
|
240
|
+
},
|
|
241
|
+
],
|
|
242
|
+
[
|
|
243
|
+
'2024-07',
|
|
244
|
+
{
|
|
245
|
+
type: 'value',
|
|
246
|
+
count: 10815,
|
|
247
|
+
proportion: 0.5221610660486674,
|
|
248
|
+
totalCount: 21234,
|
|
249
|
+
},
|
|
250
|
+
],
|
|
251
|
+
],
|
|
252
|
+
],
|
|
253
|
+
],
|
|
254
|
+
keysFirstAxis: [
|
|
255
|
+
[
|
|
256
|
+
'A19722G',
|
|
257
|
+
{
|
|
258
|
+
type: 'substitution',
|
|
259
|
+
code: 'A19722G',
|
|
260
|
+
position: 19722,
|
|
261
|
+
valueAtReference: 'A',
|
|
262
|
+
substitutionValue: 'G',
|
|
263
|
+
},
|
|
264
|
+
],
|
|
265
|
+
[
|
|
266
|
+
'G21641T',
|
|
267
|
+
{
|
|
268
|
+
type: 'substitution',
|
|
269
|
+
code: 'G21641T',
|
|
270
|
+
position: 21641,
|
|
271
|
+
valueAtReference: 'G',
|
|
272
|
+
substitutionValue: 'T',
|
|
273
|
+
},
|
|
274
|
+
],
|
|
275
|
+
[
|
|
276
|
+
'T21652-',
|
|
277
|
+
{
|
|
278
|
+
type: 'deletion',
|
|
279
|
+
code: 'T21652-',
|
|
280
|
+
position: 21652,
|
|
281
|
+
valueAtReference: 'T',
|
|
282
|
+
},
|
|
283
|
+
],
|
|
284
|
+
],
|
|
285
|
+
keysSecondAxis: [
|
|
286
|
+
[
|
|
287
|
+
'2024-01',
|
|
288
|
+
{
|
|
289
|
+
type: 'YearMonth',
|
|
290
|
+
yearNumber: 2024,
|
|
291
|
+
monthNumber: 1,
|
|
292
|
+
dateString: '2024-01',
|
|
293
|
+
},
|
|
294
|
+
],
|
|
295
|
+
[
|
|
296
|
+
'2024-02',
|
|
297
|
+
{
|
|
298
|
+
type: 'YearMonth',
|
|
299
|
+
yearNumber: 2024,
|
|
300
|
+
monthNumber: 2,
|
|
301
|
+
dateString: '2024-02',
|
|
302
|
+
},
|
|
303
|
+
],
|
|
304
|
+
[
|
|
305
|
+
'2024-03',
|
|
306
|
+
{
|
|
307
|
+
type: 'YearMonth',
|
|
308
|
+
yearNumber: 2024,
|
|
309
|
+
monthNumber: 3,
|
|
310
|
+
dateString: '2024-03',
|
|
311
|
+
},
|
|
312
|
+
],
|
|
313
|
+
[
|
|
314
|
+
'2024-04',
|
|
315
|
+
{
|
|
316
|
+
type: 'YearMonth',
|
|
317
|
+
yearNumber: 2024,
|
|
318
|
+
monthNumber: 4,
|
|
319
|
+
dateString: '2024-04',
|
|
320
|
+
},
|
|
321
|
+
],
|
|
322
|
+
[
|
|
323
|
+
'2024-05',
|
|
324
|
+
{
|
|
325
|
+
type: 'YearMonth',
|
|
326
|
+
yearNumber: 2024,
|
|
327
|
+
monthNumber: 5,
|
|
328
|
+
dateString: '2024-05',
|
|
329
|
+
},
|
|
330
|
+
],
|
|
331
|
+
[
|
|
332
|
+
'2024-06',
|
|
333
|
+
{
|
|
334
|
+
type: 'YearMonth',
|
|
335
|
+
yearNumber: 2024,
|
|
336
|
+
monthNumber: 6,
|
|
337
|
+
dateString: '2024-06',
|
|
338
|
+
},
|
|
339
|
+
],
|
|
340
|
+
[
|
|
341
|
+
'2024-07',
|
|
342
|
+
{
|
|
343
|
+
type: 'YearMonth',
|
|
344
|
+
yearNumber: 2024,
|
|
345
|
+
monthNumber: 7,
|
|
346
|
+
dateString: '2024-07',
|
|
347
|
+
},
|
|
348
|
+
],
|
|
349
|
+
],
|
|
350
|
+
},
|
|
351
|
+
},
|
|
352
|
+
};
|
|
@@ -27,6 +27,7 @@ describe('getFilteredMutationOverTimeData', () => {
|
|
|
27
27
|
],
|
|
28
28
|
displayedMutationTypes: [],
|
|
29
29
|
proportionInterval,
|
|
30
|
+
hideGaps: false,
|
|
30
31
|
displayMutations: undefined,
|
|
31
32
|
mutationFilterValue: { textFilter: '', annotationNameFilter: new Set() },
|
|
32
33
|
sequenceType: 'nucleotide',
|
|
@@ -62,6 +63,7 @@ describe('getFilteredMutationOverTimeData', () => {
|
|
|
62
63
|
},
|
|
63
64
|
],
|
|
64
65
|
proportionInterval,
|
|
66
|
+
hideGaps: false,
|
|
65
67
|
mutationFilterValue: { textFilter: '', annotationNameFilter: new Set() },
|
|
66
68
|
sequenceType: 'nucleotide',
|
|
67
69
|
annotationProvider: () => {
|
|
@@ -85,6 +87,7 @@ describe('getFilteredMutationOverTimeData', () => {
|
|
|
85
87
|
displayedSegments: [],
|
|
86
88
|
displayedMutationTypes: [],
|
|
87
89
|
proportionInterval,
|
|
90
|
+
hideGaps: false,
|
|
88
91
|
mutationFilterValue: { textFilter: '', annotationNameFilter: new Set() },
|
|
89
92
|
sequenceType: 'nucleotide',
|
|
90
93
|
annotationProvider: () => {
|
|
@@ -108,6 +111,7 @@ describe('getFilteredMutationOverTimeData', () => {
|
|
|
108
111
|
displayedSegments: [],
|
|
109
112
|
displayedMutationTypes: [],
|
|
110
113
|
proportionInterval,
|
|
114
|
+
hideGaps: false,
|
|
111
115
|
mutationFilterValue: { textFilter: '', annotationNameFilter: new Set() },
|
|
112
116
|
sequenceType: 'nucleotide',
|
|
113
117
|
annotationProvider: () => {
|
|
@@ -132,6 +136,7 @@ describe('getFilteredMutationOverTimeData', () => {
|
|
|
132
136
|
displayedSegments: [],
|
|
133
137
|
displayedMutationTypes: [],
|
|
134
138
|
proportionInterval,
|
|
139
|
+
hideGaps: false,
|
|
135
140
|
mutationFilterValue: { textFilter: '', annotationNameFilter: new Set() },
|
|
136
141
|
sequenceType: 'nucleotide',
|
|
137
142
|
annotationProvider: () => {
|
|
@@ -156,6 +161,7 @@ describe('getFilteredMutationOverTimeData', () => {
|
|
|
156
161
|
displayedSegments: [],
|
|
157
162
|
displayedMutationTypes: [],
|
|
158
163
|
proportionInterval,
|
|
164
|
+
hideGaps: false,
|
|
159
165
|
mutationFilterValue: { textFilter: '', annotationNameFilter: new Set() },
|
|
160
166
|
sequenceType: 'nucleotide',
|
|
161
167
|
annotationProvider: () => {
|
|
@@ -178,6 +184,7 @@ describe('getFilteredMutationOverTimeData', () => {
|
|
|
178
184
|
displayedSegments: [],
|
|
179
185
|
displayedMutationTypes: [],
|
|
180
186
|
proportionInterval,
|
|
187
|
+
hideGaps: false,
|
|
181
188
|
mutationFilterValue: { textFilter: '', annotationNameFilter: new Set() },
|
|
182
189
|
sequenceType: 'nucleotide',
|
|
183
190
|
annotationProvider: () => {
|
|
@@ -201,6 +208,7 @@ describe('getFilteredMutationOverTimeData', () => {
|
|
|
201
208
|
displayedSegments: [],
|
|
202
209
|
displayedMutationTypes: [],
|
|
203
210
|
proportionInterval,
|
|
211
|
+
hideGaps: false,
|
|
204
212
|
mutationFilterValue: { textFilter: '', annotationNameFilter: new Set() },
|
|
205
213
|
sequenceType: 'nucleotide',
|
|
206
214
|
annotationProvider: () => {
|
|
@@ -224,6 +232,7 @@ describe('getFilteredMutationOverTimeData', () => {
|
|
|
224
232
|
displayedSegments: [],
|
|
225
233
|
displayedMutationTypes: [],
|
|
226
234
|
proportionInterval,
|
|
235
|
+
hideGaps: false,
|
|
227
236
|
mutationFilterValue: { textFilter: '23T', annotationNameFilter: new Set() },
|
|
228
237
|
sequenceType: 'nucleotide',
|
|
229
238
|
annotationProvider: () => {
|
|
@@ -250,6 +259,7 @@ describe('getFilteredMutationOverTimeData', () => {
|
|
|
250
259
|
displayedSegments: [],
|
|
251
260
|
displayedMutationTypes: [],
|
|
252
261
|
proportionInterval,
|
|
262
|
+
hideGaps: false,
|
|
253
263
|
mutationFilterValue: filterValue,
|
|
254
264
|
sequenceType: 'nucleotide',
|
|
255
265
|
annotationProvider,
|
|
@@ -303,6 +313,28 @@ describe('getFilteredMutationOverTimeData', () => {
|
|
|
303
313
|
});
|
|
304
314
|
});
|
|
305
315
|
|
|
316
|
+
it('should remove date ranges that have no samples', () => {
|
|
317
|
+
const { data, overallMutationData } = prepareMutationOverTimeData([someSubstitutionEntry, someDeletionEntry]);
|
|
318
|
+
data.set(someSubstitution, anotherTemporal, emptyMutationOverTimeValue);
|
|
319
|
+
data.set(someDeletion, anotherTemporal, emptyMutationOverTimeValue);
|
|
320
|
+
|
|
321
|
+
const result = getFilteredMutationOverTimeData({
|
|
322
|
+
data,
|
|
323
|
+
overallMutationData,
|
|
324
|
+
displayedSegments: [],
|
|
325
|
+
displayedMutationTypes: [],
|
|
326
|
+
proportionInterval,
|
|
327
|
+
hideGaps: true,
|
|
328
|
+
mutationFilterValue: { textFilter: '', annotationNameFilter: new Set() },
|
|
329
|
+
sequenceType: 'nucleotide',
|
|
330
|
+
annotationProvider: () => {
|
|
331
|
+
return [];
|
|
332
|
+
},
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
expect(result.getSecondAxisKeys()).to.deep.equal([someTemporal]);
|
|
336
|
+
});
|
|
337
|
+
|
|
306
338
|
const belowFilter = 0.1;
|
|
307
339
|
const atFilterMin = 0.2;
|
|
308
340
|
const inFilter = 0.5;
|
|
@@ -361,6 +393,12 @@ describe('getFilteredMutationOverTimeData', () => {
|
|
|
361
393
|
proportion: inFilter,
|
|
362
394
|
totalCount: 10,
|
|
363
395
|
} satisfies MutationOverTimeMutationValue;
|
|
396
|
+
const emptyMutationOverTimeValue = {
|
|
397
|
+
type: 'value',
|
|
398
|
+
count: 0,
|
|
399
|
+
proportion: NaN,
|
|
400
|
+
totalCount: 0,
|
|
401
|
+
} satisfies MutationOverTimeMutationValue;
|
|
364
402
|
|
|
365
403
|
function prepareMutationOverTimeData(
|
|
366
404
|
mutationEntries: (SubstitutionEntry<Substitution> | DeletionEntry<Deletion>)[],
|
|
@@ -22,6 +22,7 @@ export type GetFilteredMutationOverTimeDataArgs = {
|
|
|
22
22
|
displayedSegments: DisplayedSegment[];
|
|
23
23
|
displayedMutationTypes: DisplayedMutationType[];
|
|
24
24
|
proportionInterval: { min: number; max: number };
|
|
25
|
+
hideGaps: boolean;
|
|
25
26
|
displayMutations?: DisplayMutations;
|
|
26
27
|
mutationFilterValue: MutationFilter;
|
|
27
28
|
sequenceType: SequenceType;
|
|
@@ -34,6 +35,7 @@ export function getFilteredMutationOverTimeData({
|
|
|
34
35
|
displayedSegments,
|
|
35
36
|
displayedMutationTypes,
|
|
36
37
|
proportionInterval,
|
|
38
|
+
hideGaps,
|
|
37
39
|
mutationFilterValue,
|
|
38
40
|
sequenceType,
|
|
39
41
|
annotationProvider,
|
|
@@ -63,6 +65,14 @@ export function getFilteredMutationOverTimeData({
|
|
|
63
65
|
filteredData.deleteRow(entry.mutation);
|
|
64
66
|
});
|
|
65
67
|
|
|
68
|
+
if (hideGaps) {
|
|
69
|
+
const dateRangesToFilterOut = filteredData.getSecondAxisKeys().filter((dateRange) => {
|
|
70
|
+
const vals = filteredData.getColumn(dateRange);
|
|
71
|
+
return !vals.some((v) => v?.type === 'value' && v.totalCount > 0);
|
|
72
|
+
});
|
|
73
|
+
dateRangesToFilterOut.forEach((dateRange) => filteredData.deleteColumn(dateRange));
|
|
74
|
+
}
|
|
75
|
+
|
|
66
76
|
return filteredData;
|
|
67
77
|
}
|
|
68
78
|
|
|
@@ -8,6 +8,7 @@ import { getMutationOverTimeMock } from './__mockData__/mockConversion';
|
|
|
8
8
|
import { noDataWhenNoMutationsAreInFilter } from './__mockData__/noDataWhenNoMutationsAreInFilter';
|
|
9
9
|
import { showsMessageWhenTooManyMutations } from './__mockData__/showsMessageWhenTooManyMutations';
|
|
10
10
|
import { withDisplayMutations } from './__mockData__/withDisplayMutations';
|
|
11
|
+
import { withGaps } from './__mockData__/withGaps';
|
|
11
12
|
|
|
12
13
|
const mockQueries: { query: MutationOverTimeQuery; response: MutationOverTimeWorkerResponse }[] = [
|
|
13
14
|
getMutationOverTimeMock(defaultMockData),
|
|
@@ -16,6 +17,7 @@ const mockQueries: { query: MutationOverTimeQuery; response: MutationOverTimeWor
|
|
|
16
17
|
getMutationOverTimeMock(withDisplayMutations),
|
|
17
18
|
getMutationOverTimeMock(aminoAcidMutationsByDay),
|
|
18
19
|
getMutationOverTimeMock(noDataWhenNoMutationsAreInFilter),
|
|
20
|
+
getMutationOverTimeMock(withGaps),
|
|
19
21
|
];
|
|
20
22
|
|
|
21
23
|
self.onmessage = async function (event: MessageEvent<MutationOverTimeQuery>) {
|
|
@@ -35,6 +35,7 @@ const meta: Meta<MutationsOverTimeProps> = {
|
|
|
35
35
|
lapisDateField: { control: 'text' },
|
|
36
36
|
displayMutations: { control: 'object' },
|
|
37
37
|
initialMeanProportionInterval: { control: 'object' },
|
|
38
|
+
hideGaps: { control: 'boolean' },
|
|
38
39
|
pageSizes: { control: 'object' },
|
|
39
40
|
useNewEndpoint: { control: 'boolean' },
|
|
40
41
|
},
|
|
@@ -81,6 +82,7 @@ export const Default: StoryObj<MutationsOverTimeProps> = {
|
|
|
81
82
|
granularity: 'month',
|
|
82
83
|
lapisDateField: 'date',
|
|
83
84
|
initialMeanProportionInterval: { min: 0.05, max: 0.9 },
|
|
85
|
+
hideGaps: false,
|
|
84
86
|
useNewEndpoint: false,
|
|
85
87
|
pageSizes: [10, 20, 30, 40, 50],
|
|
86
88
|
},
|
|
@@ -113,6 +115,32 @@ export const ShowsNoDataWhenNoMutationsAreInFilter: StoryObj<MutationsOverTimePr
|
|
|
113
115
|
},
|
|
114
116
|
};
|
|
115
117
|
|
|
118
|
+
export const UsesHideGaps: StoryObj<MutationsOverTimeProps> = {
|
|
119
|
+
...Default,
|
|
120
|
+
args: {
|
|
121
|
+
...Default.args,
|
|
122
|
+
displayMutations: ['A19722G', 'G21641T', 'T21652-'],
|
|
123
|
+
},
|
|
124
|
+
play: async ({ canvas, step }) => {
|
|
125
|
+
await expectDateRangeOnPage(canvas, '2024-04');
|
|
126
|
+
|
|
127
|
+
await step('hide gaps', async () => {
|
|
128
|
+
const hideGapsButton = canvas.getByRole('button', { name: 'Hide gaps' });
|
|
129
|
+
await userEvent.click(hideGapsButton);
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
const filteredDateRange = canvas.queryByText('2024-04');
|
|
133
|
+
await expect(filteredDateRange).not.toBeInTheDocument();
|
|
134
|
+
|
|
135
|
+
await step('un-hide gaps', async () => {
|
|
136
|
+
const hideGapsButton = canvas.getByRole('button', { name: 'Gaps hidden' });
|
|
137
|
+
await userEvent.click(hideGapsButton);
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
await expectDateRangeOnPage(canvas, '2024-04');
|
|
141
|
+
},
|
|
142
|
+
};
|
|
143
|
+
|
|
116
144
|
export const UsesPagination: StoryObj<MutationsOverTimeProps> = {
|
|
117
145
|
...Default,
|
|
118
146
|
play: async ({ canvas, step }) => {
|
|
@@ -175,6 +203,13 @@ async function expectMutationOnPage(canvas: Canvas, mutation: string) {
|
|
|
175
203
|
});
|
|
176
204
|
}
|
|
177
205
|
|
|
206
|
+
async function expectDateRangeOnPage(canvas: Canvas, dateRange: string) {
|
|
207
|
+
await waitFor(async () => {
|
|
208
|
+
const dateRangeOnFirstPage = canvas.getAllByText(dateRange)[0];
|
|
209
|
+
await expect(dateRangeOnFirstPage).toBeVisible();
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
|
|
178
213
|
export const ShowsNoDataMessageWhenThereAreNoDatesInFilter: StoryObj<MutationsOverTimeProps> = {
|
|
179
214
|
...Default,
|
|
180
215
|
args: {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type FunctionComponent } from 'preact';
|
|
2
|
-
import { type Dispatch, type StateUpdater, useMemo, useState } from 'preact/hooks';
|
|
2
|
+
import { type Dispatch, type StateUpdater, useMemo, useState, useEffect } from 'preact/hooks';
|
|
3
3
|
import z from 'zod';
|
|
4
4
|
|
|
5
5
|
// @ts-expect-error -- uses subpath imports and vite worker import
|
|
@@ -59,6 +59,7 @@ const mutationOverTimeSchema = z.object({
|
|
|
59
59
|
min: z.number().min(0).max(1),
|
|
60
60
|
max: z.number().min(0).max(1),
|
|
61
61
|
}),
|
|
62
|
+
hideGaps: z.boolean().optional(),
|
|
62
63
|
width: z.string(),
|
|
63
64
|
height: z.string().optional(),
|
|
64
65
|
pageSizes: pageSizesSchema,
|
|
@@ -154,6 +155,10 @@ const MutationsOverTimeTabs: FunctionComponent<MutationOverTimeTabsProps> = ({
|
|
|
154
155
|
{ label: 'Deletions', checked: true, type: 'deletion' },
|
|
155
156
|
]);
|
|
156
157
|
|
|
158
|
+
const [hideGaps, setHideGaps] = useState<boolean>(originalComponentProps.hideGaps ?? false);
|
|
159
|
+
|
|
160
|
+
useEffect(() => setHideGaps(originalComponentProps.hideGaps ?? false), [originalComponentProps.hideGaps]);
|
|
161
|
+
|
|
157
162
|
const filteredData = useMemo(() => {
|
|
158
163
|
return getFilteredMutationOverTimeData({
|
|
159
164
|
data: mutationOverTimeData,
|
|
@@ -161,6 +166,7 @@ const MutationsOverTimeTabs: FunctionComponent<MutationOverTimeTabsProps> = ({
|
|
|
161
166
|
displayedSegments,
|
|
162
167
|
displayedMutationTypes,
|
|
163
168
|
proportionInterval,
|
|
169
|
+
hideGaps,
|
|
164
170
|
mutationFilterValue,
|
|
165
171
|
sequenceType: originalComponentProps.sequenceType,
|
|
166
172
|
annotationProvider,
|
|
@@ -171,6 +177,7 @@ const MutationsOverTimeTabs: FunctionComponent<MutationOverTimeTabsProps> = ({
|
|
|
171
177
|
displayedSegments,
|
|
172
178
|
displayedMutationTypes,
|
|
173
179
|
proportionInterval,
|
|
180
|
+
hideGaps,
|
|
174
181
|
originalComponentProps.sequenceType,
|
|
175
182
|
mutationFilterValue,
|
|
176
183
|
annotationProvider,
|
|
@@ -205,6 +212,8 @@ const MutationsOverTimeTabs: FunctionComponent<MutationOverTimeTabsProps> = ({
|
|
|
205
212
|
setDisplayedMutationTypes={setDisplayedMutationTypes}
|
|
206
213
|
proportionInterval={proportionInterval}
|
|
207
214
|
setProportionInterval={setProportionInterval}
|
|
215
|
+
hideGaps={hideGaps}
|
|
216
|
+
setHideGaps={setHideGaps}
|
|
208
217
|
filteredData={filteredData}
|
|
209
218
|
colorScale={colorScale}
|
|
210
219
|
setColorScale={setColorScale}
|
|
@@ -229,6 +238,8 @@ type ToolbarProps = {
|
|
|
229
238
|
setDisplayedMutationTypes: (types: DisplayedMutationType[]) => void;
|
|
230
239
|
proportionInterval: ProportionInterval;
|
|
231
240
|
setProportionInterval: Dispatch<StateUpdater<ProportionInterval>>;
|
|
241
|
+
hideGaps: boolean;
|
|
242
|
+
setHideGaps: Dispatch<StateUpdater<boolean>>;
|
|
232
243
|
filteredData: MutationOverTimeDataMap;
|
|
233
244
|
colorScale: ColorScale;
|
|
234
245
|
setColorScale: Dispatch<StateUpdater<ColorScale>>;
|
|
@@ -245,6 +256,8 @@ const Toolbar: FunctionComponent<ToolbarProps> = ({
|
|
|
245
256
|
setDisplayedMutationTypes,
|
|
246
257
|
proportionInterval,
|
|
247
258
|
setProportionInterval,
|
|
259
|
+
hideGaps,
|
|
260
|
+
setHideGaps,
|
|
248
261
|
filteredData,
|
|
249
262
|
colorScale,
|
|
250
263
|
setColorScale,
|
|
@@ -255,9 +268,6 @@ const Toolbar: FunctionComponent<ToolbarProps> = ({
|
|
|
255
268
|
return (
|
|
256
269
|
<>
|
|
257
270
|
<MutationsOverTimeMutationsFilter setFilterValue={setFilterValue} value={mutationFilterValue} />
|
|
258
|
-
{activeTab === 'Grid' && (
|
|
259
|
-
<ColorScaleSelectorDropdown colorScale={colorScale} setColorScale={setColorScale} />
|
|
260
|
-
)}
|
|
261
271
|
<SegmentSelector
|
|
262
272
|
displayedSegments={displayedSegments}
|
|
263
273
|
setDisplayedSegments={setDisplayedSegments}
|
|
@@ -273,6 +283,20 @@ const Toolbar: FunctionComponent<ToolbarProps> = ({
|
|
|
273
283
|
setMaxProportion={(max) => setProportionInterval((prev) => ({ ...prev, max }))}
|
|
274
284
|
labelPrefix='Mean proportion'
|
|
275
285
|
/>
|
|
286
|
+
<button
|
|
287
|
+
className='btn btn-xs w-24'
|
|
288
|
+
onClick={() => setHideGaps((s) => !s)}
|
|
289
|
+
title={
|
|
290
|
+
hideGaps
|
|
291
|
+
? 'Date ranges that do not contain data are excluded from the table'
|
|
292
|
+
: 'Exclude date ranges without data from the table'
|
|
293
|
+
}
|
|
294
|
+
>
|
|
295
|
+
{hideGaps ? 'Gaps hidden' : 'Hide gaps'}
|
|
296
|
+
</button>
|
|
297
|
+
{activeTab === 'Grid' && (
|
|
298
|
+
<ColorScaleSelectorDropdown colorScale={colorScale} setColorScale={setColorScale} />
|
|
299
|
+
)}
|
|
276
300
|
<CsvDownloadButton
|
|
277
301
|
className='btn btn-xs'
|
|
278
302
|
getData={() => getDownloadData(filteredData)}
|