@genspectrum/dashboard-components 1.9.0 → 1.9.1
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/dist/components.d.ts +29 -29
- package/dist/components.js +51 -16
- package/dist/components.js.map +1 -1
- package/dist/util.d.ts +29 -29
- package/package.json +1 -1
- package/src/preact/lineageFilter/fetchLineageAutocompleteList.spec.ts +72 -0
- package/src/preact/lineageFilter/fetchLineageAutocompleteList.ts +81 -19
- package/standalone-bundle/dashboard-components.js +2579 -2557
- package/standalone-bundle/dashboard-components.js.map +1 -1
package/dist/util.d.ts
CHANGED
|
@@ -941,7 +941,7 @@ declare global {
|
|
|
941
941
|
|
|
942
942
|
declare global {
|
|
943
943
|
interface HTMLElementTagNameMap {
|
|
944
|
-
'gs-
|
|
944
|
+
'gs-wastewater-mutations-over-time': WastewaterMutationsOverTimeComponent;
|
|
945
945
|
}
|
|
946
946
|
}
|
|
947
947
|
|
|
@@ -949,7 +949,7 @@ declare global {
|
|
|
949
949
|
declare global {
|
|
950
950
|
namespace JSX {
|
|
951
951
|
interface IntrinsicElements {
|
|
952
|
-
'gs-
|
|
952
|
+
'gs-wastewater-mutations-over-time': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
|
|
953
953
|
}
|
|
954
954
|
}
|
|
955
955
|
}
|
|
@@ -957,7 +957,7 @@ declare global {
|
|
|
957
957
|
|
|
958
958
|
declare global {
|
|
959
959
|
interface HTMLElementTagNameMap {
|
|
960
|
-
'gs-
|
|
960
|
+
'gs-genome-data-viewer': GenomeDataViewerComponent;
|
|
961
961
|
}
|
|
962
962
|
}
|
|
963
963
|
|
|
@@ -965,7 +965,7 @@ declare global {
|
|
|
965
965
|
declare global {
|
|
966
966
|
namespace JSX {
|
|
967
967
|
interface IntrinsicElements {
|
|
968
|
-
'gs-
|
|
968
|
+
'gs-genome-data-viewer': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
|
|
969
969
|
}
|
|
970
970
|
}
|
|
971
971
|
}
|
|
@@ -973,7 +973,7 @@ declare global {
|
|
|
973
973
|
|
|
974
974
|
declare global {
|
|
975
975
|
interface HTMLElementTagNameMap {
|
|
976
|
-
'gs-
|
|
976
|
+
'gs-mutation-comparison-component': MutationComparisonComponent;
|
|
977
977
|
}
|
|
978
978
|
}
|
|
979
979
|
|
|
@@ -981,7 +981,7 @@ declare global {
|
|
|
981
981
|
declare global {
|
|
982
982
|
namespace JSX {
|
|
983
983
|
interface IntrinsicElements {
|
|
984
|
-
'gs-
|
|
984
|
+
'gs-mutation-comparison-component': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
|
|
985
985
|
}
|
|
986
986
|
}
|
|
987
987
|
}
|
|
@@ -989,7 +989,7 @@ declare global {
|
|
|
989
989
|
|
|
990
990
|
declare global {
|
|
991
991
|
interface HTMLElementTagNameMap {
|
|
992
|
-
'gs-
|
|
992
|
+
'gs-mutations': MutationsComponent;
|
|
993
993
|
}
|
|
994
994
|
}
|
|
995
995
|
|
|
@@ -997,7 +997,7 @@ declare global {
|
|
|
997
997
|
declare global {
|
|
998
998
|
namespace JSX {
|
|
999
999
|
interface IntrinsicElements {
|
|
1000
|
-
'gs-
|
|
1000
|
+
'gs-mutations': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
|
|
1001
1001
|
}
|
|
1002
1002
|
}
|
|
1003
1003
|
}
|
|
@@ -1005,7 +1005,7 @@ declare global {
|
|
|
1005
1005
|
|
|
1006
1006
|
declare global {
|
|
1007
1007
|
interface HTMLElementTagNameMap {
|
|
1008
|
-
'gs-
|
|
1008
|
+
'gs-prevalence-over-time': PrevalenceOverTimeComponent;
|
|
1009
1009
|
}
|
|
1010
1010
|
}
|
|
1011
1011
|
|
|
@@ -1013,7 +1013,7 @@ declare global {
|
|
|
1013
1013
|
declare global {
|
|
1014
1014
|
namespace JSX {
|
|
1015
1015
|
interface IntrinsicElements {
|
|
1016
|
-
'gs-
|
|
1016
|
+
'gs-prevalence-over-time': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
|
|
1017
1017
|
}
|
|
1018
1018
|
}
|
|
1019
1019
|
}
|
|
@@ -1021,7 +1021,7 @@ declare global {
|
|
|
1021
1021
|
|
|
1022
1022
|
declare global {
|
|
1023
1023
|
interface HTMLElementTagNameMap {
|
|
1024
|
-
'gs-
|
|
1024
|
+
'gs-relative-growth-advantage': RelativeGrowthAdvantageComponent;
|
|
1025
1025
|
}
|
|
1026
1026
|
}
|
|
1027
1027
|
|
|
@@ -1029,7 +1029,7 @@ declare global {
|
|
|
1029
1029
|
declare global {
|
|
1030
1030
|
namespace JSX {
|
|
1031
1031
|
interface IntrinsicElements {
|
|
1032
|
-
'gs-
|
|
1032
|
+
'gs-relative-growth-advantage': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
|
|
1033
1033
|
}
|
|
1034
1034
|
}
|
|
1035
1035
|
}
|
|
@@ -1037,7 +1037,7 @@ declare global {
|
|
|
1037
1037
|
|
|
1038
1038
|
declare global {
|
|
1039
1039
|
interface HTMLElementTagNameMap {
|
|
1040
|
-
'gs-
|
|
1040
|
+
'gs-aggregate': AggregateComponent;
|
|
1041
1041
|
}
|
|
1042
1042
|
}
|
|
1043
1043
|
|
|
@@ -1045,7 +1045,7 @@ declare global {
|
|
|
1045
1045
|
declare global {
|
|
1046
1046
|
namespace JSX {
|
|
1047
1047
|
interface IntrinsicElements {
|
|
1048
|
-
'gs-
|
|
1048
|
+
'gs-aggregate': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
|
|
1049
1049
|
}
|
|
1050
1050
|
}
|
|
1051
1051
|
}
|
|
@@ -1053,7 +1053,7 @@ declare global {
|
|
|
1053
1053
|
|
|
1054
1054
|
declare global {
|
|
1055
1055
|
interface HTMLElementTagNameMap {
|
|
1056
|
-
'gs-sequences-
|
|
1056
|
+
'gs-number-sequences-over-time': NumberSequencesOverTimeComponent;
|
|
1057
1057
|
}
|
|
1058
1058
|
}
|
|
1059
1059
|
|
|
@@ -1061,7 +1061,7 @@ declare global {
|
|
|
1061
1061
|
declare global {
|
|
1062
1062
|
namespace JSX {
|
|
1063
1063
|
interface IntrinsicElements {
|
|
1064
|
-
'gs-sequences-
|
|
1064
|
+
'gs-number-sequences-over-time': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
|
|
1065
1065
|
}
|
|
1066
1066
|
}
|
|
1067
1067
|
}
|
|
@@ -1085,7 +1085,7 @@ declare global {
|
|
|
1085
1085
|
|
|
1086
1086
|
declare global {
|
|
1087
1087
|
interface HTMLElementTagNameMap {
|
|
1088
|
-
'gs-
|
|
1088
|
+
'gs-sequences-by-location': SequencesByLocationComponent;
|
|
1089
1089
|
}
|
|
1090
1090
|
}
|
|
1091
1091
|
|
|
@@ -1093,7 +1093,7 @@ declare global {
|
|
|
1093
1093
|
declare global {
|
|
1094
1094
|
namespace JSX {
|
|
1095
1095
|
interface IntrinsicElements {
|
|
1096
|
-
'gs-
|
|
1096
|
+
'gs-sequences-by-location': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
|
|
1097
1097
|
}
|
|
1098
1098
|
}
|
|
1099
1099
|
}
|
|
@@ -1101,11 +1101,7 @@ declare global {
|
|
|
1101
1101
|
|
|
1102
1102
|
declare global {
|
|
1103
1103
|
interface HTMLElementTagNameMap {
|
|
1104
|
-
'gs-
|
|
1105
|
-
}
|
|
1106
|
-
interface HTMLElementEventMap {
|
|
1107
|
-
[gsEventNames.dateRangeFilterChanged]: CustomEvent<Record<string, string>>;
|
|
1108
|
-
[gsEventNames.dateRangeOptionChanged]: DateRangeOptionChangedEvent;
|
|
1104
|
+
'gs-statistics': StatisticsComponent;
|
|
1109
1105
|
}
|
|
1110
1106
|
}
|
|
1111
1107
|
|
|
@@ -1113,7 +1109,7 @@ declare global {
|
|
|
1113
1109
|
declare global {
|
|
1114
1110
|
namespace JSX {
|
|
1115
1111
|
interface IntrinsicElements {
|
|
1116
|
-
'gs-
|
|
1112
|
+
'gs-statistics': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
|
|
1117
1113
|
}
|
|
1118
1114
|
}
|
|
1119
1115
|
}
|
|
@@ -1121,7 +1117,10 @@ declare global {
|
|
|
1121
1117
|
|
|
1122
1118
|
declare global {
|
|
1123
1119
|
interface HTMLElementTagNameMap {
|
|
1124
|
-
'gs-
|
|
1120
|
+
'gs-location-filter': LocationFilterComponent;
|
|
1121
|
+
}
|
|
1122
|
+
interface HTMLElementEventMap {
|
|
1123
|
+
[gsEventNames.locationChanged]: LocationChangedEvent;
|
|
1125
1124
|
}
|
|
1126
1125
|
}
|
|
1127
1126
|
|
|
@@ -1129,7 +1128,7 @@ declare global {
|
|
|
1129
1128
|
declare global {
|
|
1130
1129
|
namespace JSX {
|
|
1131
1130
|
interface IntrinsicElements {
|
|
1132
|
-
'gs-
|
|
1131
|
+
'gs-location-filter': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
|
|
1133
1132
|
}
|
|
1134
1133
|
}
|
|
1135
1134
|
}
|
|
@@ -1137,10 +1136,11 @@ declare global {
|
|
|
1137
1136
|
|
|
1138
1137
|
declare global {
|
|
1139
1138
|
interface HTMLElementTagNameMap {
|
|
1140
|
-
'gs-
|
|
1139
|
+
'gs-date-range-filter': DateRangeFilterComponent;
|
|
1141
1140
|
}
|
|
1142
1141
|
interface HTMLElementEventMap {
|
|
1143
|
-
[gsEventNames.
|
|
1142
|
+
[gsEventNames.dateRangeFilterChanged]: CustomEvent<Record<string, string>>;
|
|
1143
|
+
[gsEventNames.dateRangeOptionChanged]: DateRangeOptionChangedEvent;
|
|
1144
1144
|
}
|
|
1145
1145
|
}
|
|
1146
1146
|
|
|
@@ -1148,7 +1148,7 @@ declare global {
|
|
|
1148
1148
|
declare global {
|
|
1149
1149
|
namespace JSX {
|
|
1150
1150
|
interface IntrinsicElements {
|
|
1151
|
-
'gs-
|
|
1151
|
+
'gs-date-range-filter': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
|
|
1152
1152
|
}
|
|
1153
1153
|
}
|
|
1154
1154
|
}
|
package/package.json
CHANGED
|
@@ -321,4 +321,76 @@ describe('fetchLineageAutocompleteList', () => {
|
|
|
321
321
|
},
|
|
322
322
|
]);
|
|
323
323
|
});
|
|
324
|
+
|
|
325
|
+
test('should include prefix aliases that are missing from lineage tree', async () => {
|
|
326
|
+
lapisRequestMocks.aggregated(
|
|
327
|
+
{ fields: [lineageField], ...lapisFilter },
|
|
328
|
+
{
|
|
329
|
+
data: [
|
|
330
|
+
{
|
|
331
|
+
[lineageField]: 'BA.3.2.1',
|
|
332
|
+
count: 1,
|
|
333
|
+
},
|
|
334
|
+
{
|
|
335
|
+
[lineageField]: 'BA.3.2.2',
|
|
336
|
+
count: 2,
|
|
337
|
+
},
|
|
338
|
+
],
|
|
339
|
+
},
|
|
340
|
+
);
|
|
341
|
+
|
|
342
|
+
lapisRequestMocks.lineageDefinition(
|
|
343
|
+
{
|
|
344
|
+
'B.1.1.529.3.2': {
|
|
345
|
+
aliases: ['BA.3.2'],
|
|
346
|
+
},
|
|
347
|
+
'BA.3.2.1': {
|
|
348
|
+
parents: ['B.1.1.529.3.2'],
|
|
349
|
+
aliases: ['B.1.1.529.3.2.1'],
|
|
350
|
+
},
|
|
351
|
+
'BA.3.2.2': {
|
|
352
|
+
parents: ['B.1.1.529.3.2'],
|
|
353
|
+
aliases: ['B.1.1.529.3.2.2'],
|
|
354
|
+
},
|
|
355
|
+
},
|
|
356
|
+
lineageField,
|
|
357
|
+
);
|
|
358
|
+
|
|
359
|
+
const result = await fetchLineageAutocompleteList({
|
|
360
|
+
lapisUrl: DUMMY_LAPIS_URL,
|
|
361
|
+
lapisField: lineageField,
|
|
362
|
+
lapisFilter,
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
expect(result).to.deep.equal([
|
|
366
|
+
{
|
|
367
|
+
lineage: 'B.1.1.529.3.2',
|
|
368
|
+
count: 0,
|
|
369
|
+
},
|
|
370
|
+
{
|
|
371
|
+
lineage: 'B.1.1.529.3.2*',
|
|
372
|
+
count: 3,
|
|
373
|
+
},
|
|
374
|
+
{
|
|
375
|
+
lineage: 'BA.3.2*',
|
|
376
|
+
count: 3, // Same as B.1.1.529.3.2* (includes .3.2 and .3.2.1)
|
|
377
|
+
},
|
|
378
|
+
{
|
|
379
|
+
lineage: 'BA.3.2.1',
|
|
380
|
+
count: 1,
|
|
381
|
+
},
|
|
382
|
+
{
|
|
383
|
+
lineage: 'BA.3.2.1*',
|
|
384
|
+
count: 1,
|
|
385
|
+
},
|
|
386
|
+
{
|
|
387
|
+
lineage: 'BA.3.2.2',
|
|
388
|
+
count: 2,
|
|
389
|
+
},
|
|
390
|
+
{
|
|
391
|
+
lineage: 'BA.3.2.2*',
|
|
392
|
+
count: 2,
|
|
393
|
+
},
|
|
394
|
+
]);
|
|
395
|
+
});
|
|
324
396
|
});
|
|
@@ -2,6 +2,10 @@ import { fetchLineageDefinition } from '../../lapisApi/lapisApi';
|
|
|
2
2
|
import { FetchAggregatedOperator } from '../../operator/FetchAggregatedOperator';
|
|
3
3
|
import type { LapisFilter } from '../../types';
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* Generates the autocomplete list for lineage search. It includes lineages with wild cards
|
|
7
|
+
* (i.e. "BA.3.2.1" and "BA.3.2.1*") as well as all prefixes of lineages with an asterisk ("BA.3.2*").
|
|
8
|
+
*/
|
|
5
9
|
export async function fetchLineageAutocompleteList({
|
|
6
10
|
lapisUrl,
|
|
7
11
|
lapisField,
|
|
@@ -13,31 +17,43 @@ export async function fetchLineageAutocompleteList({
|
|
|
13
17
|
lapisFilter?: LapisFilter;
|
|
14
18
|
signal?: AbortSignal;
|
|
15
19
|
}): Promise<LineageItem[]> {
|
|
16
|
-
const [countsByLineage, lineageTree] = await Promise.all([
|
|
20
|
+
const [countsByLineage, { lineageTree, aliasMapping }] = await Promise.all([
|
|
17
21
|
getCountsByLineage({
|
|
18
22
|
lapisUrl,
|
|
19
23
|
lapisField,
|
|
20
24
|
lapisFilter,
|
|
21
25
|
signal,
|
|
22
26
|
}),
|
|
23
|
-
|
|
27
|
+
getLineageTreeAndAliases({ lapisUrl, lapisField, signal }),
|
|
24
28
|
]);
|
|
25
29
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
30
|
+
const prefixToLineage = findMissingPrefixMappings(lineageTree, aliasMapping);
|
|
31
|
+
|
|
32
|
+
// Combine actual lineages with their wildcard versions
|
|
33
|
+
const actualLineageItems = Array.from(lineageTree.keys()).flatMap((lineage) => [
|
|
34
|
+
{
|
|
35
|
+
lineage,
|
|
36
|
+
count: countsByLineage.get(lineage) ?? 0,
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
lineage: `${lineage}*`,
|
|
40
|
+
count: getCountsIncludingSublineages(lineage, lineageTree, countsByLineage),
|
|
41
|
+
},
|
|
42
|
+
]);
|
|
43
|
+
|
|
44
|
+
// Add prefix alias items with wildcard and their counts
|
|
45
|
+
const prefixAliasItems = Array.from(prefixToLineage.entries()).map(([prefix, actualLineage]) => ({
|
|
46
|
+
lineage: `${prefix}*`,
|
|
47
|
+
count: getCountsIncludingSublineages(actualLineage, lineageTree, countsByLineage),
|
|
48
|
+
}));
|
|
49
|
+
|
|
50
|
+
// Combine and sort all items (asterisk before period for same prefix)
|
|
51
|
+
return [...actualLineageItems, ...prefixAliasItems].sort((a, b) => {
|
|
52
|
+
// Replace * with a character that sorts before . in ASCII
|
|
53
|
+
const aKey = a.lineage.replace(/\*/g, ' ');
|
|
54
|
+
const bKey = b.lineage.replace(/\*/g, ' ');
|
|
55
|
+
return aKey.localeCompare(bKey);
|
|
56
|
+
});
|
|
41
57
|
}
|
|
42
58
|
|
|
43
59
|
export type LineageItem = { lineage: string; count: number };
|
|
@@ -61,7 +77,7 @@ async function getCountsByLineage({
|
|
|
61
77
|
return new Map<string, number>(countsByLineageArray.map((value) => [value[lapisField], value.count]));
|
|
62
78
|
}
|
|
63
79
|
|
|
64
|
-
async function
|
|
80
|
+
async function getLineageTreeAndAliases({
|
|
65
81
|
lapisUrl,
|
|
66
82
|
lapisField,
|
|
67
83
|
signal,
|
|
@@ -73,12 +89,17 @@ async function getLineageTree({
|
|
|
73
89
|
const lineageDefinitions = await fetchLineageDefinition({ lapisUrl, lapisField, signal });
|
|
74
90
|
|
|
75
91
|
const lineageTree = new Map<string, { children: string[] }>();
|
|
92
|
+
const aliasMapping = new Map<string, string[]>();
|
|
76
93
|
|
|
77
94
|
Object.entries(lineageDefinitions).forEach(([lineage, definition]) => {
|
|
78
95
|
if (!lineageTree.has(lineage)) {
|
|
79
96
|
lineageTree.set(lineage, { children: [] });
|
|
80
97
|
}
|
|
81
98
|
|
|
99
|
+
if (definition.aliases && definition.aliases.length > 0) {
|
|
100
|
+
aliasMapping.set(lineage, definition.aliases);
|
|
101
|
+
}
|
|
102
|
+
|
|
82
103
|
definition.parents?.forEach((parent) => {
|
|
83
104
|
const parentChildren = lineageTree.get(parent)?.children;
|
|
84
105
|
|
|
@@ -88,7 +109,7 @@ async function getLineageTree({
|
|
|
88
109
|
});
|
|
89
110
|
});
|
|
90
111
|
|
|
91
|
-
return lineageTree;
|
|
112
|
+
return { lineageTree, aliasMapping };
|
|
92
113
|
}
|
|
93
114
|
|
|
94
115
|
function getCountsIncludingSublineages(
|
|
@@ -115,3 +136,44 @@ function getAllDescendants(lineage: string, lineageTree: Map<string, { children:
|
|
|
115
136
|
|
|
116
137
|
return new Set([...children, ...childrenOfChildren.flatMap((child) => Array.from(child))]);
|
|
117
138
|
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* This function finds prefixes (i.e. "BA.3.2" for "BA.3.2.1") that are not in the lineageTree,
|
|
142
|
+
* but do appear as an alias. It returns a reverse mapping for those prefixes, back to a lineage
|
|
143
|
+
* that can be found in the lineageTree (i.e. "BA.3.2" -> "B.1.1.529.3.2").
|
|
144
|
+
*/
|
|
145
|
+
function findMissingPrefixMappings(
|
|
146
|
+
lineageTree: Map<string, { children: string[] }>,
|
|
147
|
+
aliasMapping: Map<string, string[]>,
|
|
148
|
+
): Map<string, string> {
|
|
149
|
+
const lineages = Array.from(lineageTree.keys());
|
|
150
|
+
const lineagesSet = new Set(lineages);
|
|
151
|
+
|
|
152
|
+
// Generate all prefixes for each lineage (e.g., "A.B.1" -> ["A", "A.B", "A.B.1"])
|
|
153
|
+
const allPrefixes = lineages.flatMap((lineage) => {
|
|
154
|
+
const parts = lineage.split('.');
|
|
155
|
+
return parts.map((_, i) => parts.slice(0, i + 1).join('.'));
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
// Find prefixes that are NOT in the actual lineages list
|
|
159
|
+
const missingPrefixes = new Set(allPrefixes.filter((prefix) => !lineagesSet.has(prefix)));
|
|
160
|
+
|
|
161
|
+
// Create reverse alias mapping: alias -> original lineage
|
|
162
|
+
const reverseAliasMapping = new Map<string, string>();
|
|
163
|
+
aliasMapping.forEach((aliases, lineage) => {
|
|
164
|
+
aliases.forEach((alias) => {
|
|
165
|
+
reverseAliasMapping.set(alias, lineage);
|
|
166
|
+
});
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
// Map missing prefixes to their actual lineage names via reverse alias lookup
|
|
170
|
+
const prefixToLineage = new Map<string, string>();
|
|
171
|
+
missingPrefixes.forEach((prefix) => {
|
|
172
|
+
const actualLineage = reverseAliasMapping.get(prefix);
|
|
173
|
+
if (actualLineage) {
|
|
174
|
+
prefixToLineage.set(prefix, actualLineage);
|
|
175
|
+
}
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
return prefixToLineage;
|
|
179
|
+
}
|