@genspectrum/dashboard-components 0.10.1 → 0.10.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/README.md +19 -19
- package/custom-elements.json +72 -56
- package/dist/assets/{mutationOverTimeWorker-CvZg52rf.js.map → mutationOverTimeWorker-CNg_ztNp.js.map} +1 -1
- package/dist/components.d.ts +19 -28
- package/dist/components.js +343 -96
- package/dist/components.js.map +1 -1
- package/dist/{utilEntrypoint-g4DsyhU7.js → dateRangeOption-DjtcAEWq.js} +46 -2
- package/dist/dateRangeOption-DjtcAEWq.js.map +1 -0
- package/dist/style.css +11 -5
- package/dist/util.d.ts +112 -21
- package/dist/util.js +3 -2
- package/package.json +2 -2
- package/src/preact/aggregatedData/aggregate.stories.tsx +14 -0
- package/src/preact/aggregatedData/aggregate.tsx +17 -15
- package/src/preact/components/color-scale-selector.tsx +7 -3
- package/src/preact/components/error-boundary.stories.tsx +24 -3
- package/src/preact/components/error-boundary.tsx +38 -5
- package/src/preact/components/error-display.tsx +62 -6
- package/src/preact/components/tabs.tsx +2 -2
- package/src/preact/dateRangeSelector/computeInitialValues.spec.ts +8 -2
- package/src/preact/dateRangeSelector/computeInitialValues.ts +6 -0
- package/src/preact/dateRangeSelector/date-range-selector.stories.tsx +16 -2
- package/src/preact/dateRangeSelector/date-range-selector.tsx +20 -15
- package/src/preact/dateRangeSelector/dateRangeOption.ts +10 -5
- package/src/preact/lineageFilter/lineage-filter.stories.tsx +18 -4
- package/src/preact/lineageFilter/lineage-filter.tsx +15 -10
- package/src/preact/locationFilter/location-filter.stories.tsx +14 -0
- package/src/preact/locationFilter/location-filter.tsx +15 -10
- package/src/preact/mutationComparison/mutation-comparison-venn.tsx +17 -18
- package/src/preact/mutationComparison/mutation-comparison.tsx +18 -12
- package/src/preact/mutationFilter/mutation-filter.tsx +26 -13
- package/src/preact/mutations/mutations.tsx +16 -12
- package/src/preact/mutationsOverTime/__mockData__/aminoAcidMutationsByDay.ts +1326 -9341
- package/src/preact/mutationsOverTime/__mockData__/byWeek.ts +615 -4920
- package/src/preact/mutationsOverTime/__mockData__/defaultMockData.ts +2203 -17624
- package/src/preact/mutationsOverTime/mutations-over-time-grid.tsx +16 -8
- package/src/preact/mutationsOverTime/mutations-over-time.stories.tsx +14 -0
- package/src/preact/mutationsOverTime/mutations-over-time.tsx +19 -17
- package/src/preact/numberSequencesOverTime/number-sequences-over-time.stories.tsx +14 -0
- package/src/preact/numberSequencesOverTime/number-sequences-over-time.tsx +22 -14
- package/src/preact/prevalenceOverTime/prevalence-over-time.stories.tsx +14 -0
- package/src/preact/prevalenceOverTime/prevalence-over-time.tsx +28 -19
- package/src/preact/relativeGrowthAdvantage/relative-growth-advantage.stories.tsx +14 -0
- package/src/preact/relativeGrowthAdvantage/relative-growth-advantage.tsx +18 -15
- package/src/preact/shared/charts/confideceInterval.ts +10 -8
- package/src/preact/shared/charts/getYAxisMax.ts +10 -5
- package/src/preact/shared/stories/expectInvalidAttributesErrorMessage.ts +13 -0
- package/src/preact/statistic/statistics.tsx +10 -8
- package/src/preact/textInput/text-input.stories.tsx +14 -0
- package/src/preact/textInput/text-input.tsx +16 -11
- package/src/preact/webWorkers/useWebWorker.ts +8 -4
- package/src/query/queryAggregateData.ts +2 -1
- package/src/query/queryMutationsOverTime.spec.ts +12 -27
- package/src/query/queryMutationsOverTime.ts +2 -6
- package/src/types.ts +31 -7
- package/src/utilEntrypoint.ts +15 -0
- package/src/utils/map2d.spec.ts +10 -10
- package/src/utils/map2d.ts +10 -10
- package/src/web-components/app.stories.ts +17 -2
- package/src/web-components/app.ts +17 -5
- package/src/web-components/input/gs-date-range-selector.stories.ts +2 -2
- package/src/web-components/input/gs-date-range-selector.tsx +3 -3
- package/src/web-components/input/gs-lineage-filter.tsx +1 -1
- package/src/web-components/input/gs-location-filter.tsx +2 -2
- package/src/web-components/input/gs-mutation-filter.stories.ts +2 -0
- package/src/web-components/input/gs-text-input.tsx +2 -2
- package/src/web-components/introduction.mdx +4 -4
- package/src/web-components/visualization/data_visualization_statistical_analysis.mdx +3 -3
- package/src/web-components/visualization/gs-aggregate.tsx +2 -2
- package/src/web-components/visualization/gs-mutations-over-time.tsx +1 -3
- package/src/web-components/visualization/gs-mutations.tsx +1 -3
- package/src/web-components/visualization/gs-number-sequences-over-time.tsx +1 -3
- package/src/web-components/visualization/gs-prevalence-over-time.tsx +3 -6
- package/src/web-components/visualization/gs-relative-growth-advantage.tsx +1 -5
- package/standalone-bundle/assets/{mutationOverTimeWorker-CypX_PYM.js.map → mutationOverTimeWorker-cIyshfj_.js.map} +1 -1
- package/standalone-bundle/dashboard-components.js +8800 -8577
- package/standalone-bundle/dashboard-components.js.map +1 -1
- package/standalone-bundle/style.css +1 -1
- package/dist/utilEntrypoint-g4DsyhU7.js.map +0 -1
|
@@ -99,12 +99,8 @@ describe('queryMutationsOverTime', () => {
|
|
|
99
99
|
granularity: 'day',
|
|
100
100
|
});
|
|
101
101
|
|
|
102
|
-
expect(mutationOverTimeData.getAsArray(
|
|
103
|
-
[
|
|
104
|
-
{ proportion: 0.4, count: 4, totalCount: 11 },
|
|
105
|
-
{ proportion: 0, count: 0, totalCount: 0 },
|
|
106
|
-
{ proportion: 0, count: 0, totalCount: 0 },
|
|
107
|
-
],
|
|
102
|
+
expect(mutationOverTimeData.getAsArray()).to.deep.equal([
|
|
103
|
+
[{ proportion: 0.4, count: 4, totalCount: 11 }, null, null],
|
|
108
104
|
[
|
|
109
105
|
{ proportion: 0.1, count: 1, totalCount: 11 },
|
|
110
106
|
{ proportion: 0.2, count: 2, totalCount: 12 },
|
|
@@ -250,17 +246,9 @@ describe('queryMutationsOverTime', () => {
|
|
|
250
246
|
granularity: 'day',
|
|
251
247
|
});
|
|
252
248
|
|
|
253
|
-
expect(mutationOverTimeData.getAsArray(
|
|
254
|
-
[
|
|
255
|
-
|
|
256
|
-
{ proportion: 0, count: 0, totalCount: 0 },
|
|
257
|
-
{ proportion: 0, count: 0, totalCount: 0 },
|
|
258
|
-
],
|
|
259
|
-
[
|
|
260
|
-
{ proportion: 0.1, count: 1, totalCount: 11 },
|
|
261
|
-
{ proportion: 0, count: 0, totalCount: 0 },
|
|
262
|
-
{ proportion: 0.3, count: 3, totalCount: 13 },
|
|
263
|
-
],
|
|
249
|
+
expect(mutationOverTimeData.getAsArray()).to.deep.equal([
|
|
250
|
+
[{ proportion: 0.4, count: 4, totalCount: 11 }, null, null],
|
|
251
|
+
[{ proportion: 0.1, count: 1, totalCount: 11 }, null, { proportion: 0.3, count: 3, totalCount: 13 }],
|
|
264
252
|
]);
|
|
265
253
|
|
|
266
254
|
const sequences = mutationOverTimeData.getFirstAxisKeys();
|
|
@@ -368,7 +356,7 @@ describe('queryMutationsOverTime', () => {
|
|
|
368
356
|
granularity: 'day',
|
|
369
357
|
});
|
|
370
358
|
|
|
371
|
-
expect(mutationOverTimeData.getAsArray(
|
|
359
|
+
expect(mutationOverTimeData.getAsArray()).to.deep.equal([]);
|
|
372
360
|
expect(mutationOverTimeData.getFirstAxisKeys()).to.deep.equal([]);
|
|
373
361
|
expect(mutationOverTimeData.getSecondAxisKeys()).to.deep.equal([]);
|
|
374
362
|
});
|
|
@@ -450,7 +438,7 @@ describe('queryMutationsOverTime', () => {
|
|
|
450
438
|
granularity: 'day',
|
|
451
439
|
});
|
|
452
440
|
|
|
453
|
-
expect(mutationOverTimeData.getAsArray(
|
|
441
|
+
expect(mutationOverTimeData.getAsArray()).to.deep.equal([
|
|
454
442
|
[
|
|
455
443
|
{ proportion: 0.2, count: 2, totalCount: 11 },
|
|
456
444
|
{ proportion: 0.3, count: 3, totalCount: 12 },
|
|
@@ -542,7 +530,7 @@ describe('queryMutationsOverTime', () => {
|
|
|
542
530
|
granularity: 'day',
|
|
543
531
|
});
|
|
544
532
|
|
|
545
|
-
expect(mutationOverTimeData.getAsArray(
|
|
533
|
+
expect(mutationOverTimeData.getAsArray()).to.deep.equal([
|
|
546
534
|
[
|
|
547
535
|
{ proportion: 0.1, count: 1, totalCount: 11 },
|
|
548
536
|
{ proportion: 0.2, count: 2, totalCount: 12 },
|
|
@@ -605,7 +593,7 @@ describe('queryMutationsOverTime', () => {
|
|
|
605
593
|
granularity: 'day',
|
|
606
594
|
});
|
|
607
595
|
|
|
608
|
-
expect(mutationOverTimeData.getAsArray(
|
|
596
|
+
expect(mutationOverTimeData.getAsArray()).to.deep.equal([
|
|
609
597
|
[
|
|
610
598
|
{
|
|
611
599
|
proportion: 0.2,
|
|
@@ -699,11 +687,8 @@ describe('queryMutationsOverTime', () => {
|
|
|
699
687
|
granularity: 'month',
|
|
700
688
|
});
|
|
701
689
|
|
|
702
|
-
expect(mutationOverTimeData.getAsArray(
|
|
703
|
-
[
|
|
704
|
-
{ proportion: 0.4, count: 4, totalCount: 11 },
|
|
705
|
-
{ proportion: 0, count: 0, totalCount: 0 },
|
|
706
|
-
],
|
|
690
|
+
expect(mutationOverTimeData.getAsArray()).to.deep.equal([
|
|
691
|
+
[{ proportion: 0.4, count: 4, totalCount: 11 }, null],
|
|
707
692
|
[
|
|
708
693
|
{ proportion: 0.1, count: 1, totalCount: 11 },
|
|
709
694
|
{ proportion: 0.2, count: 2, totalCount: 12 },
|
|
@@ -740,7 +725,7 @@ describe('queryMutationsOverTime', () => {
|
|
|
740
725
|
granularity: 'month',
|
|
741
726
|
});
|
|
742
727
|
|
|
743
|
-
expect(mutationOverTimeData.getAsArray(
|
|
728
|
+
expect(mutationOverTimeData.getAsArray()).to.deep.equal([]);
|
|
744
729
|
|
|
745
730
|
const sequences = mutationOverTimeData.getFirstAxisKeys();
|
|
746
731
|
expect(sequences.length).toBe(0);
|
|
@@ -41,7 +41,7 @@ export type MutationOverTimeData = {
|
|
|
41
41
|
totalCount: number;
|
|
42
42
|
};
|
|
43
43
|
|
|
44
|
-
export type MutationOverTimeMutationValue = { proportion: number; count: number; totalCount: number };
|
|
44
|
+
export type MutationOverTimeMutationValue = { proportion: number; count: number; totalCount: number } | null;
|
|
45
45
|
export type MutationOverTimeDataGroupedByMutation = Map2d<
|
|
46
46
|
SubstitutionClass | DeletionClass,
|
|
47
47
|
TemporalClass,
|
|
@@ -239,11 +239,7 @@ export function groupByMutation(
|
|
|
239
239
|
|
|
240
240
|
sortedOverallMutationData.forEach((mutationData) => {
|
|
241
241
|
sortedDates.forEach((date) => {
|
|
242
|
-
dataArray.set(mutationData, date,
|
|
243
|
-
count: 0,
|
|
244
|
-
proportion: 0,
|
|
245
|
-
totalCount: 0,
|
|
246
|
-
});
|
|
242
|
+
dataArray.set(mutationData, date, null);
|
|
247
243
|
});
|
|
248
244
|
});
|
|
249
245
|
|
package/src/types.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import z from 'zod';
|
|
2
|
+
|
|
1
3
|
import {
|
|
2
4
|
type Deletion,
|
|
3
5
|
type DeletionClass,
|
|
@@ -7,16 +9,25 @@ import {
|
|
|
7
9
|
type SubstitutionClass,
|
|
8
10
|
} from './utils/mutations';
|
|
9
11
|
|
|
10
|
-
export
|
|
12
|
+
export const lapisFilterSchema = z.record(z.union([z.string(), z.number(), z.null(), z.boolean()]));
|
|
13
|
+
export type LapisFilter = z.infer<typeof lapisFilterSchema>;
|
|
11
14
|
|
|
12
|
-
export
|
|
13
|
-
lapisFilter:
|
|
14
|
-
displayName: string
|
|
15
|
-
};
|
|
15
|
+
export const namedLapisFilterSchema = z.object({
|
|
16
|
+
lapisFilter: lapisFilterSchema,
|
|
17
|
+
displayName: z.string(),
|
|
18
|
+
});
|
|
19
|
+
export type NamedLapisFilter = z.infer<typeof namedLapisFilterSchema>;
|
|
16
20
|
|
|
17
|
-
export
|
|
21
|
+
export const temporalGranularitySchema = z.union([
|
|
22
|
+
z.literal('day'),
|
|
23
|
+
z.literal('week'),
|
|
24
|
+
z.literal('month'),
|
|
25
|
+
z.literal('year'),
|
|
26
|
+
]);
|
|
27
|
+
export type TemporalGranularity = z.infer<typeof temporalGranularitySchema>;
|
|
18
28
|
|
|
19
|
-
export
|
|
29
|
+
export const sequenceTypeSchema = z.union([z.literal('nucleotide'), z.literal('amino acid')]);
|
|
30
|
+
export type SequenceType = z.infer<typeof sequenceTypeSchema>;
|
|
20
31
|
|
|
21
32
|
export type SubstitutionOrDeletion = 'substitution' | 'deletion';
|
|
22
33
|
|
|
@@ -44,3 +55,16 @@ export type SubstitutionOrDeletionEntry<
|
|
|
44
55
|
> = SubstitutionEntry<S> | DeletionEntry<D>;
|
|
45
56
|
|
|
46
57
|
export type MutationEntry = SubstitutionEntry | DeletionEntry | InsertionEntry;
|
|
58
|
+
|
|
59
|
+
export const views = {
|
|
60
|
+
table: 'table',
|
|
61
|
+
venn: 'venn',
|
|
62
|
+
grid: 'grid',
|
|
63
|
+
insertions: 'insertions',
|
|
64
|
+
bar: 'bar',
|
|
65
|
+
line: 'line',
|
|
66
|
+
bubble: 'bubble',
|
|
67
|
+
} as const;
|
|
68
|
+
|
|
69
|
+
export const mutationComparisonViewSchema = z.union([z.literal(views.table), z.literal(views.venn)]);
|
|
70
|
+
export type MutationComparisonView = z.infer<typeof mutationComparisonViewSchema>;
|
package/src/utilEntrypoint.ts
CHANGED
|
@@ -4,3 +4,18 @@ export {
|
|
|
4
4
|
dateRangeOptionPresets,
|
|
5
5
|
DateRangeOptionChangedEvent,
|
|
6
6
|
} from './preact/dateRangeSelector/dateRangeOption';
|
|
7
|
+
|
|
8
|
+
export {
|
|
9
|
+
type NamedLapisFilter,
|
|
10
|
+
type LapisFilter,
|
|
11
|
+
type SequenceType,
|
|
12
|
+
views,
|
|
13
|
+
type MutationComparisonView,
|
|
14
|
+
type TemporalGranularity,
|
|
15
|
+
} from './types';
|
|
16
|
+
|
|
17
|
+
export { type SelectedMutationFilterStrings } from './preact/mutationFilter/mutation-filter';
|
|
18
|
+
|
|
19
|
+
export { type ConfidenceIntervalMethod } from './preact/shared/charts/confideceInterval';
|
|
20
|
+
|
|
21
|
+
export { type AxisMax, type YAxisMaxConfig } from './preact/shared/charts/getYAxisMax';
|
package/src/utils/map2d.spec.ts
CHANGED
|
@@ -32,7 +32,7 @@ describe('Map2dBase', () => {
|
|
|
32
32
|
map2d.set('c', 'b', 3);
|
|
33
33
|
map2d.set('c', 'd', 4);
|
|
34
34
|
|
|
35
|
-
expect(map2d.getAsArray(
|
|
35
|
+
expect(map2d.getAsArray()).toEqual([
|
|
36
36
|
[1, 2],
|
|
37
37
|
[3, 4],
|
|
38
38
|
]);
|
|
@@ -45,9 +45,9 @@ describe('Map2dBase', () => {
|
|
|
45
45
|
);
|
|
46
46
|
map2d.set('a', 'b', 2);
|
|
47
47
|
map2d.set('c', 'd', 4);
|
|
48
|
-
expect(map2d.getAsArray(
|
|
49
|
-
[2,
|
|
50
|
-
[
|
|
48
|
+
expect(map2d.getAsArray()).toEqual([
|
|
49
|
+
[2, undefined],
|
|
50
|
+
[undefined, 4],
|
|
51
51
|
]);
|
|
52
52
|
});
|
|
53
53
|
|
|
@@ -103,8 +103,8 @@ describe('Map2dBase', () => {
|
|
|
103
103
|
map2d.set('a', 'b', 2);
|
|
104
104
|
map2d.set('c', 'd', 4);
|
|
105
105
|
|
|
106
|
-
expect(map2d.getRow('a'
|
|
107
|
-
expect(map2d.getRow('c'
|
|
106
|
+
expect(map2d.getRow('a')).toEqual([2, undefined]);
|
|
107
|
+
expect(map2d.getRow('c')).toEqual([undefined, 4]);
|
|
108
108
|
});
|
|
109
109
|
|
|
110
110
|
it('should return an empty array when the row does not exist', () => {
|
|
@@ -114,7 +114,7 @@ describe('Map2dBase', () => {
|
|
|
114
114
|
);
|
|
115
115
|
map2d.set('a', 'b', 2);
|
|
116
116
|
|
|
117
|
-
expect(map2d.getRow('c'
|
|
117
|
+
expect(map2d.getRow('c')).toEqual([]);
|
|
118
118
|
});
|
|
119
119
|
|
|
120
120
|
it('should return content as object', () => {
|
|
@@ -185,7 +185,7 @@ describe('Map2dView', () => {
|
|
|
185
185
|
const view = new Map2dView<string, string, number>(container);
|
|
186
186
|
view.deleteRow('c');
|
|
187
187
|
|
|
188
|
-
expect(view.getAsArray(
|
|
188
|
+
expect(view.getAsArray()).toEqual([[1, undefined]]);
|
|
189
189
|
});
|
|
190
190
|
|
|
191
191
|
it('should throw an error when trying to set a value', () => {
|
|
@@ -198,7 +198,7 @@ describe('Map2dView', () => {
|
|
|
198
198
|
const container = createBaseContainer();
|
|
199
199
|
const view = new Map2dView<string, string, number>(container);
|
|
200
200
|
|
|
201
|
-
expect(view.getRow('a'
|
|
201
|
+
expect(view.getRow('a')).toEqual([1, undefined]);
|
|
202
202
|
});
|
|
203
203
|
|
|
204
204
|
it('should return an empty array when the row does not exist', () => {
|
|
@@ -206,7 +206,7 @@ describe('Map2dView', () => {
|
|
|
206
206
|
const view = new Map2dView<string, string, number>(container);
|
|
207
207
|
view.deleteRow('c');
|
|
208
208
|
|
|
209
|
-
expect(view.getRow('c'
|
|
209
|
+
expect(view.getRow('c')).toEqual([]);
|
|
210
210
|
});
|
|
211
211
|
|
|
212
212
|
function createBaseContainer() {
|
package/src/utils/map2d.ts
CHANGED
|
@@ -3,7 +3,7 @@ export interface Map2d<Key1, Key2, Value> {
|
|
|
3
3
|
|
|
4
4
|
set(keyFirstAxis: Key1, keySecondAxis: Key2, value: Value): void;
|
|
5
5
|
|
|
6
|
-
getRow(key: Key1
|
|
6
|
+
getRow(key: Key1): (Value | undefined)[];
|
|
7
7
|
|
|
8
8
|
deleteRow(key: Key1): void;
|
|
9
9
|
|
|
@@ -11,7 +11,7 @@ export interface Map2d<Key1, Key2, Value> {
|
|
|
11
11
|
|
|
12
12
|
getSecondAxisKeys(): Key2[];
|
|
13
13
|
|
|
14
|
-
getAsArray(
|
|
14
|
+
getAsArray(): (Value | undefined)[][];
|
|
15
15
|
|
|
16
16
|
serializeFirstAxis(key: Key1): string;
|
|
17
17
|
|
|
@@ -52,13 +52,13 @@ export class Map2dBase<Key1 extends object | string, Key2 extends object | strin
|
|
|
52
52
|
return this.data.get(serializedKeyFirstAxis)?.get(serializedKeySecondAxis);
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
-
getRow(key: Key1
|
|
55
|
+
getRow(key: Key1) {
|
|
56
56
|
const serializedKeyFirstAxis = this.serializeFirstAxis(key);
|
|
57
57
|
const row = this.data.get(serializedKeyFirstAxis);
|
|
58
58
|
if (row === undefined) {
|
|
59
59
|
return [];
|
|
60
60
|
}
|
|
61
|
-
return Array.from(this.keysSecondAxis.keys()).map((key) => row.get(key)
|
|
61
|
+
return Array.from(this.keysSecondAxis.keys()).map((key) => row.get(key));
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
set(keyFirstAxis: Key1, keySecondAxis: Key2, value: Value) {
|
|
@@ -89,10 +89,10 @@ export class Map2dBase<Key1 extends object | string, Key2 extends object | strin
|
|
|
89
89
|
return Array.from(this.keysSecondAxis.values());
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
-
getAsArray(
|
|
92
|
+
getAsArray() {
|
|
93
93
|
return this.getFirstAxisKeys().map((firstAxisKey) => {
|
|
94
94
|
return this.getSecondAxisKeys().map((secondAxisKey) => {
|
|
95
|
-
return this.get(firstAxisKey, secondAxisKey)
|
|
95
|
+
return this.get(firstAxisKey, secondAxisKey);
|
|
96
96
|
});
|
|
97
97
|
});
|
|
98
98
|
}
|
|
@@ -159,20 +159,20 @@ export class Map2dView<Key1 extends object | string, Key2 extends object | strin
|
|
|
159
159
|
return Array.from(this.keysSecondAxis.values());
|
|
160
160
|
}
|
|
161
161
|
|
|
162
|
-
getAsArray(
|
|
162
|
+
getAsArray() {
|
|
163
163
|
return this.getFirstAxisKeys().map((firstAxisKey) => {
|
|
164
164
|
return this.getSecondAxisKeys().map((secondAxisKey) => {
|
|
165
|
-
return this.baseMap.get(firstAxisKey, secondAxisKey)
|
|
165
|
+
return this.baseMap.get(firstAxisKey, secondAxisKey);
|
|
166
166
|
});
|
|
167
167
|
});
|
|
168
168
|
}
|
|
169
169
|
|
|
170
|
-
getRow(key: Key1
|
|
170
|
+
getRow(key: Key1) {
|
|
171
171
|
const serializedKeyFirstAxis = this.serializeFirstAxis(key);
|
|
172
172
|
if (!this.keysFirstAxis.has(serializedKeyFirstAxis)) {
|
|
173
173
|
return [];
|
|
174
174
|
}
|
|
175
175
|
|
|
176
|
-
return this.baseMap.getRow(key
|
|
176
|
+
return this.baseMap.getRow(key);
|
|
177
177
|
}
|
|
178
178
|
}
|
|
@@ -57,6 +57,21 @@ export const Default: StoryObj<{ lapis: string }> = {
|
|
|
57
57
|
},
|
|
58
58
|
};
|
|
59
59
|
|
|
60
|
+
export const WithNoLapisUrl: StoryObj<{ lapis: string }> = {
|
|
61
|
+
...Default,
|
|
62
|
+
args: {
|
|
63
|
+
...Default.args,
|
|
64
|
+
lapis: 'notAValidUrl',
|
|
65
|
+
},
|
|
66
|
+
play: async ({ canvasElement }) => {
|
|
67
|
+
const canvas = within(canvasElement);
|
|
68
|
+
|
|
69
|
+
await waitFor(() => {
|
|
70
|
+
expect(canvas.getByText("Error: Invalid LAPIS URL: 'notAValidUrl'", { exact: false })).toBeVisible();
|
|
71
|
+
});
|
|
72
|
+
},
|
|
73
|
+
};
|
|
74
|
+
|
|
60
75
|
export const DelayFetchingReferenceGenome: StoryObj<{ lapis: string }> = {
|
|
61
76
|
...Template,
|
|
62
77
|
parameters: {
|
|
@@ -83,13 +98,13 @@ export const DelayFetchingReferenceGenome: StoryObj<{ lapis: string }> = {
|
|
|
83
98
|
export const FailsToFetchReferenceGenome: StoryObj<{ lapis: string }> = {
|
|
84
99
|
...Template,
|
|
85
100
|
args: {
|
|
86
|
-
lapis: 'definitely-not-a-valid-url',
|
|
101
|
+
lapis: 'https://url.to.lapis-definitely-not-a-valid-url',
|
|
87
102
|
},
|
|
88
103
|
play: async ({ canvasElement }) => {
|
|
89
104
|
const canvas = within(canvasElement);
|
|
90
105
|
|
|
91
106
|
await waitFor(() => {
|
|
92
|
-
expect(canvas.getByText('Error', { exact: false })).toBeVisible();
|
|
107
|
+
expect(canvas.getByText('Error: Cannot fetch reference genome.', { exact: false })).toBeVisible();
|
|
93
108
|
});
|
|
94
109
|
},
|
|
95
110
|
};
|
|
@@ -3,12 +3,15 @@ import { Task } from '@lit/task';
|
|
|
3
3
|
import { html, LitElement } from 'lit';
|
|
4
4
|
import { customElement, property } from 'lit/decorators.js';
|
|
5
5
|
import { type DetailedHTMLProps, type HTMLAttributes } from 'react';
|
|
6
|
+
import z from 'zod';
|
|
6
7
|
|
|
7
8
|
import { lapisContext } from './lapis-context';
|
|
8
9
|
import { referenceGenomeContext } from './reference-genome-context';
|
|
9
10
|
import { type ReferenceGenome } from '../lapisApi/ReferenceGenome';
|
|
10
11
|
import { fetchReferenceGenome } from '../lapisApi/lapisApi';
|
|
11
12
|
|
|
13
|
+
const lapisUrlSchema = z.string().url();
|
|
14
|
+
|
|
12
15
|
/**
|
|
13
16
|
* ## Context
|
|
14
17
|
*
|
|
@@ -50,7 +53,9 @@ export class App extends LitElement {
|
|
|
50
53
|
*/
|
|
51
54
|
private updateReferenceGenome = new Task(this, {
|
|
52
55
|
task: async () => {
|
|
53
|
-
|
|
56
|
+
const lapisUrl = lapisUrlSchema.parse(this.lapis);
|
|
57
|
+
|
|
58
|
+
this.referenceGenome = await fetchReferenceGenome(lapisUrl);
|
|
54
59
|
},
|
|
55
60
|
args: () => [this.lapis],
|
|
56
61
|
});
|
|
@@ -58,10 +63,13 @@ export class App extends LitElement {
|
|
|
58
63
|
override render() {
|
|
59
64
|
return this.updateReferenceGenome.render({
|
|
60
65
|
complete: () => html``, // Children will be rendered in the light DOM anyway. We can't use slots without a shadow DOM.
|
|
61
|
-
error: () =>
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
66
|
+
error: (error) => {
|
|
67
|
+
if (error instanceof z.ZodError) {
|
|
68
|
+
return GsAppError(`Invalid LAPIS URL: '${this.lapis}'`);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return GsAppError('Cannot fetch reference genome. Is LAPIS available?');
|
|
72
|
+
},
|
|
65
73
|
});
|
|
66
74
|
}
|
|
67
75
|
|
|
@@ -70,6 +78,10 @@ export class App extends LitElement {
|
|
|
70
78
|
}
|
|
71
79
|
}
|
|
72
80
|
|
|
81
|
+
function GsAppError(error: string) {
|
|
82
|
+
return html` <div class="m-2 w-full alert alert-error">Error: ${error}</div>`;
|
|
83
|
+
}
|
|
84
|
+
|
|
73
85
|
declare global {
|
|
74
86
|
interface HTMLElementTagNameMap {
|
|
75
87
|
'gs-app': App;
|
|
@@ -74,8 +74,8 @@ const meta: Meta<Required<DateRangeSelectorProps>> = {
|
|
|
74
74
|
initialValue: dateRangeOptionPresets.lastMonth.label,
|
|
75
75
|
dateColumn: 'aDateColumn',
|
|
76
76
|
width: '100%',
|
|
77
|
-
initialDateFrom:
|
|
78
|
-
initialDateTo:
|
|
77
|
+
initialDateFrom: undefined,
|
|
78
|
+
initialDateTo: undefined,
|
|
79
79
|
},
|
|
80
80
|
tags: ['autodocs'],
|
|
81
81
|
};
|
|
@@ -84,7 +84,7 @@ export class DateRangeSelectorComponent extends PreactLitAdapter {
|
|
|
84
84
|
* If `initialDateTo` is set, but this is unset, it will default to `earliestDate`.
|
|
85
85
|
*/
|
|
86
86
|
@property()
|
|
87
|
-
initialDateFrom: string =
|
|
87
|
+
initialDateFrom: string | undefined = undefined;
|
|
88
88
|
|
|
89
89
|
/**
|
|
90
90
|
* A date string in the format `YYYY-MM-DD`.
|
|
@@ -92,7 +92,7 @@ export class DateRangeSelectorComponent extends PreactLitAdapter {
|
|
|
92
92
|
* If `initialDateFrom` is set, but this is unset, it will default to the current date.
|
|
93
93
|
*/
|
|
94
94
|
@property()
|
|
95
|
-
initialDateTo: string =
|
|
95
|
+
initialDateTo: string | undefined = undefined;
|
|
96
96
|
|
|
97
97
|
/**
|
|
98
98
|
* The width of the component.
|
|
@@ -106,7 +106,7 @@ export class DateRangeSelectorComponent extends PreactLitAdapter {
|
|
|
106
106
|
* The name of the column in LAPIS that contains the date information.
|
|
107
107
|
*/
|
|
108
108
|
@property({ type: String })
|
|
109
|
-
dateColumn: string = '
|
|
109
|
+
dateColumn: string = '';
|
|
110
110
|
|
|
111
111
|
override render() {
|
|
112
112
|
return (
|
|
@@ -48,7 +48,7 @@ export class LineageFilterComponent extends PreactLitAdapter {
|
|
|
48
48
|
* The placeholder text to display in the input field.
|
|
49
49
|
*/
|
|
50
50
|
@property()
|
|
51
|
-
placeholderText: string =
|
|
51
|
+
placeholderText: string | undefined = undefined;
|
|
52
52
|
|
|
53
53
|
/**
|
|
54
54
|
* The width of the component.
|
|
@@ -40,7 +40,7 @@ export class LocationFilterComponent extends PreactLitAdapter {
|
|
|
40
40
|
* Must be of the form `valueForField1 / valueForField2 / ... / valueForFieldN`.
|
|
41
41
|
*/
|
|
42
42
|
@property()
|
|
43
|
-
initialValue =
|
|
43
|
+
initialValue: string | undefined = undefined;
|
|
44
44
|
|
|
45
45
|
/**
|
|
46
46
|
* Required.
|
|
@@ -65,7 +65,7 @@ export class LocationFilterComponent extends PreactLitAdapter {
|
|
|
65
65
|
* The placeholder text to display in the input field, if it is empty.
|
|
66
66
|
*/
|
|
67
67
|
@property()
|
|
68
|
-
placeholderText: string =
|
|
68
|
+
placeholderText: string | undefined = undefined;
|
|
69
69
|
|
|
70
70
|
override render() {
|
|
71
71
|
return (
|
|
@@ -60,6 +60,7 @@ const Template: StoryObj<MutationFilterProps> = {
|
|
|
60
60
|
export const Default: StoryObj<MutationFilterProps> = {
|
|
61
61
|
...Template,
|
|
62
62
|
args: {
|
|
63
|
+
...Template.args,
|
|
63
64
|
initialValue: ['A123T'],
|
|
64
65
|
},
|
|
65
66
|
};
|
|
@@ -105,6 +106,7 @@ export const FiresFilterChangedEvent: StoryObj<MutationFilterProps> = {
|
|
|
105
106
|
export const MultiSegmentedReferenceGenomes: StoryObj<MutationFilterProps> = {
|
|
106
107
|
...Template,
|
|
107
108
|
args: {
|
|
109
|
+
...Template.args,
|
|
108
110
|
initialValue: ['seg1:123T', 'gene2:56', 'ins_seg2:78:AAA'],
|
|
109
111
|
},
|
|
110
112
|
parameters: {
|
|
@@ -27,7 +27,7 @@ export class TextInputComponent extends PreactLitAdapter {
|
|
|
27
27
|
* The initial value to use for this text input.
|
|
28
28
|
*/
|
|
29
29
|
@property()
|
|
30
|
-
initialValue: string =
|
|
30
|
+
initialValue: string | undefined = undefined;
|
|
31
31
|
|
|
32
32
|
/**
|
|
33
33
|
* Required.
|
|
@@ -42,7 +42,7 @@ export class TextInputComponent extends PreactLitAdapter {
|
|
|
42
42
|
* The placeholder text to display in the input field.
|
|
43
43
|
*/
|
|
44
44
|
@property()
|
|
45
|
-
placeholderText: string =
|
|
45
|
+
placeholderText: string | undefined = undefined;
|
|
46
46
|
|
|
47
47
|
/**
|
|
48
48
|
* The width of the component.
|
|
@@ -24,10 +24,10 @@ data of a specific instance of [LAPIS](https://github.com/GenSpectrum/LAPIS).
|
|
|
24
24
|
|
|
25
25
|
We primarily provide two kinds of components:
|
|
26
26
|
|
|
27
|
-
-
|
|
28
|
-
|
|
29
|
-
-
|
|
30
|
-
|
|
27
|
+
- Visualization components (charts, tables, etc.).
|
|
28
|
+
Those components fetch data from the LAPIS instance and visualize it.
|
|
29
|
+
- Input components that let you specify sequence filters for the LAPIS requests.
|
|
30
|
+
Input changes will fire events that can be listened to by the visualization components. It is the responsibility of the dashbaord maintainer to listen to those events and to wire the data correctly into the visualization components.
|
|
31
31
|
|
|
32
32
|
## Installation
|
|
33
33
|
|
|
@@ -12,9 +12,9 @@ By default, it is set to a linear scale.
|
|
|
12
12
|
|
|
13
13
|
In general, for each scale the displayed height of a value is calculated by applying the corresponding scale function.
|
|
14
14
|
|
|
15
|
-
-
|
|
16
|
-
-
|
|
17
|
-
-
|
|
15
|
+
- Linear: `value`
|
|
16
|
+
- Logarithmic: `ln(value)`
|
|
17
|
+
- Logistic: `ln(value / (1 - value))`
|
|
18
18
|
|
|
19
19
|
## Confidence Intervals
|
|
20
20
|
|
|
@@ -101,7 +101,7 @@ export class AggregateComponent extends PreactLitAdapterWithGridJsStyles {
|
|
|
101
101
|
|
|
102
102
|
declare global {
|
|
103
103
|
interface HTMLElementTagNameMap {
|
|
104
|
-
'gs-aggregate
|
|
104
|
+
'gs-aggregate': AggregateComponent;
|
|
105
105
|
}
|
|
106
106
|
}
|
|
107
107
|
|
|
@@ -109,7 +109,7 @@ declare global {
|
|
|
109
109
|
// eslint-disable-next-line @typescript-eslint/no-namespace
|
|
110
110
|
namespace JSX {
|
|
111
111
|
interface IntrinsicElements {
|
|
112
|
-
'gs-aggregate
|
|
112
|
+
'gs-aggregate': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
|
|
113
113
|
}
|
|
114
114
|
}
|
|
115
115
|
}
|
|
@@ -33,9 +33,7 @@ import { PreactLitAdapterWithGridJsStyles } from '../PreactLitAdapterWithGridJsS
|
|
|
33
33
|
@customElement('gs-mutations-over-time')
|
|
34
34
|
export class MutationsOverTimeComponent extends PreactLitAdapterWithGridJsStyles {
|
|
35
35
|
/**
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
* LAPIS filter to select the displayed data.
|
|
36
|
+
* LAPIS filter to select the displayed data. If not provided, all data is displayed.
|
|
39
37
|
*/
|
|
40
38
|
@property({ type: Object })
|
|
41
39
|
lapisFilter: Record<string, string | number | null | boolean> = {};
|
|
@@ -35,9 +35,7 @@ import { PreactLitAdapterWithGridJsStyles } from '../PreactLitAdapterWithGridJsS
|
|
|
35
35
|
@customElement('gs-mutations')
|
|
36
36
|
export class MutationsComponent extends PreactLitAdapterWithGridJsStyles {
|
|
37
37
|
/**
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
* LAPIS filter to select the displayed data.
|
|
38
|
+
* LAPIS filter to select the displayed data. If not provided, all data is displayed.
|
|
41
39
|
*/
|
|
42
40
|
@property({ type: Object })
|
|
43
41
|
lapisFilter: Record<string, string | number | null | boolean> = {};
|
|
@@ -25,8 +25,6 @@ export class NumberSequencesOverTimeComponent extends PreactLitAdapterWithGridJs
|
|
|
25
25
|
// prettier-ignore
|
|
26
26
|
// The multiline union type must not start with `|` because it looks weird in the Storybook docs
|
|
27
27
|
/**
|
|
28
|
-
* Required.
|
|
29
|
-
*
|
|
30
28
|
* Either a LAPIS filter or an array of LAPIS filters to fetch the number of sequences for.
|
|
31
29
|
*
|
|
32
30
|
* The `lapisFilter` will be sent as is to LAPIS to select the data.
|
|
@@ -54,7 +52,7 @@ export class NumberSequencesOverTimeComponent extends PreactLitAdapterWithGridJs
|
|
|
54
52
|
* Must be a field of type `date` in LAPIS.
|
|
55
53
|
*/
|
|
56
54
|
@property({ type: String })
|
|
57
|
-
lapisDateField: string = '
|
|
55
|
+
lapisDateField: string = '';
|
|
58
56
|
|
|
59
57
|
/**
|
|
60
58
|
* A list of tabs with views that this component should provide.
|
|
@@ -47,7 +47,6 @@ export class PrevalenceOverTimeComponent extends PreactLitAdapterWithGridJsStyle
|
|
|
47
47
|
// prettier-ignore
|
|
48
48
|
// The multiline union type must not start with `|` because it looks weird in the Storybook docs
|
|
49
49
|
/**
|
|
50
|
-
* Required.
|
|
51
50
|
* Either a LAPIS filter or an array of LAPIS filters to calculate the prevalence for.
|
|
52
51
|
*
|
|
53
52
|
* The `lapisFilter` will be sent as is to LAPIS to select the data.
|
|
@@ -57,7 +56,7 @@ export class PrevalenceOverTimeComponent extends PreactLitAdapterWithGridJsStyle
|
|
|
57
56
|
* It should be human-readable.
|
|
58
57
|
*
|
|
59
58
|
*/
|
|
60
|
-
@property({type: Object})
|
|
59
|
+
@property({ type: Object })
|
|
61
60
|
numeratorFilter:
|
|
62
61
|
{
|
|
63
62
|
lapisFilter: Record<string, string | number | null | boolean>;
|
|
@@ -66,11 +65,9 @@ export class PrevalenceOverTimeComponent extends PreactLitAdapterWithGridJsStyle
|
|
|
66
65
|
| {
|
|
67
66
|
lapisFilter: Record<string, string | number | null | boolean>;
|
|
68
67
|
displayName: string;
|
|
69
|
-
}[] = {displayName: '', lapisFilter: {}};
|
|
68
|
+
}[] = { displayName: '', lapisFilter: {} };
|
|
70
69
|
|
|
71
70
|
/**
|
|
72
|
-
* Required.
|
|
73
|
-
*
|
|
74
71
|
* The LAPIS filter, to select the data of the reference.
|
|
75
72
|
* It must be a valid LAPIS filter object.
|
|
76
73
|
*/
|
|
@@ -133,7 +130,7 @@ export class PrevalenceOverTimeComponent extends PreactLitAdapterWithGridJsStyle
|
|
|
133
130
|
* Must be a field of type `date` in LAPIS.
|
|
134
131
|
*/
|
|
135
132
|
@property({ type: String })
|
|
136
|
-
lapisDateField: string = '
|
|
133
|
+
lapisDateField: string = '';
|
|
137
134
|
|
|
138
135
|
/**
|
|
139
136
|
* The maximum number of rows to display in the table view.
|