@genspectrum/dashboard-components 0.19.7 → 0.19.8
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 +2 -2
- package/dist/assets/mutationOverTimeWorker-CBXsEsiT.js.map +1 -0
- package/dist/components.d.ts +29 -29
- package/dist/components.js +118 -16
- package/dist/components.js.map +1 -1
- package/dist/util.d.ts +29 -29
- package/package.json +1 -1
- package/src/lapisApi/LineageDefinition.ts +9 -0
- package/src/lapisApi/lapisApi.ts +27 -0
- package/src/preact/lineageFilter/__mockData__/lineageDefinition.json +38118 -0
- package/src/preact/lineageFilter/fetchLineageAutocompleteList.spec.ts +264 -6
- package/src/preact/lineageFilter/fetchLineageAutocompleteList.ts +104 -7
- package/src/preact/lineageFilter/lineage-filter.stories.tsx +13 -1
- package/src/preact/lineageFilter/lineage-filter.tsx +21 -11
- package/src/web-components/input/gs-lineage-filter.stories.ts +13 -1
- package/standalone-bundle/assets/mutationOverTimeWorker-CN4SJC7C.js.map +1 -0
- package/standalone-bundle/dashboard-components.js +2548 -2463
- package/standalone-bundle/dashboard-components.js.map +1 -1
- package/dist/assets/mutationOverTimeWorker-DQGh08AS.js.map +0 -1
- package/standalone-bundle/assets/mutationOverTimeWorker-DAf2_NiP.js.map +0 -1
|
@@ -4,25 +4,283 @@ import { fetchLineageAutocompleteList } from './fetchLineageAutocompleteList';
|
|
|
4
4
|
import { DUMMY_LAPIS_URL, lapisRequestMocks } from '../../../vitest.setup';
|
|
5
5
|
|
|
6
6
|
describe('fetchLineageAutocompleteList', () => {
|
|
7
|
+
const lapisFilter = { country: 'Germany' };
|
|
8
|
+
const lineageField = 'lineageField';
|
|
9
|
+
|
|
10
|
+
test('should return single lineage', async () => {
|
|
11
|
+
lapisRequestMocks.aggregated(
|
|
12
|
+
{ fields: [lineageField], ...lapisFilter },
|
|
13
|
+
{
|
|
14
|
+
data: [
|
|
15
|
+
{
|
|
16
|
+
[lineageField]: 'A',
|
|
17
|
+
count: 1,
|
|
18
|
+
},
|
|
19
|
+
],
|
|
20
|
+
},
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
lapisRequestMocks.lineageDefinition(
|
|
24
|
+
{
|
|
25
|
+
A: {
|
|
26
|
+
aliases: ['a'],
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
lineageField,
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
const result = await fetchLineageAutocompleteList({
|
|
33
|
+
lapisUrl: DUMMY_LAPIS_URL,
|
|
34
|
+
lapisField: lineageField,
|
|
35
|
+
lapisFilter,
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
expect(result).to.deep.equal([
|
|
39
|
+
{
|
|
40
|
+
lineage: 'A',
|
|
41
|
+
count: 1,
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
lineage: 'A*',
|
|
45
|
+
count: 1,
|
|
46
|
+
},
|
|
47
|
+
]);
|
|
48
|
+
});
|
|
49
|
+
|
|
7
50
|
test('should add sublineage values', async () => {
|
|
8
51
|
lapisRequestMocks.aggregated(
|
|
9
|
-
{ fields: [
|
|
52
|
+
{ fields: [lineageField], ...lapisFilter },
|
|
10
53
|
{
|
|
11
54
|
data: [
|
|
12
55
|
{
|
|
13
|
-
lineageField: '
|
|
56
|
+
[lineageField]: 'A',
|
|
14
57
|
count: 1,
|
|
15
58
|
},
|
|
59
|
+
{
|
|
60
|
+
[lineageField]: 'A.1',
|
|
61
|
+
count: 2,
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
[lineageField]: 'A.2',
|
|
65
|
+
count: 3,
|
|
66
|
+
},
|
|
16
67
|
],
|
|
17
68
|
},
|
|
18
69
|
);
|
|
19
70
|
|
|
71
|
+
lapisRequestMocks.lineageDefinition(
|
|
72
|
+
{
|
|
73
|
+
A: {
|
|
74
|
+
aliases: ['a'],
|
|
75
|
+
},
|
|
76
|
+
'A.1': {
|
|
77
|
+
parents: ['A'],
|
|
78
|
+
aliases: ['a.1'],
|
|
79
|
+
},
|
|
80
|
+
'A.2': {
|
|
81
|
+
parents: ['A'],
|
|
82
|
+
aliases: ['a.1'],
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
lineageField,
|
|
86
|
+
);
|
|
87
|
+
|
|
20
88
|
const result = await fetchLineageAutocompleteList({
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
lapisFilter
|
|
89
|
+
lapisUrl: DUMMY_LAPIS_URL,
|
|
90
|
+
lapisField: lineageField,
|
|
91
|
+
lapisFilter,
|
|
24
92
|
});
|
|
25
93
|
|
|
26
|
-
expect(result).to.deep.equal([
|
|
94
|
+
expect(result).to.deep.equal([
|
|
95
|
+
{
|
|
96
|
+
lineage: 'A',
|
|
97
|
+
count: 1,
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
lineage: 'A*',
|
|
101
|
+
count: 6,
|
|
102
|
+
},
|
|
103
|
+
|
|
104
|
+
{
|
|
105
|
+
lineage: 'A.1',
|
|
106
|
+
count: 2,
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
lineage: 'A.1*',
|
|
110
|
+
count: 2,
|
|
111
|
+
},
|
|
112
|
+
|
|
113
|
+
{
|
|
114
|
+
lineage: 'A.2',
|
|
115
|
+
count: 3,
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
lineage: 'A.2*',
|
|
119
|
+
count: 3,
|
|
120
|
+
},
|
|
121
|
+
]);
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
test('should work with recombinations', async () => {
|
|
125
|
+
lapisRequestMocks.aggregated(
|
|
126
|
+
{ fields: [lineageField], ...lapisFilter },
|
|
127
|
+
{
|
|
128
|
+
data: [
|
|
129
|
+
{
|
|
130
|
+
[lineageField]: 'A',
|
|
131
|
+
count: 1,
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
[lineageField]: 'A.1',
|
|
135
|
+
count: 2,
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
[lineageField]: 'A.2',
|
|
139
|
+
count: 3,
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
[lineageField]: 'XA',
|
|
143
|
+
count: 4,
|
|
144
|
+
},
|
|
145
|
+
],
|
|
146
|
+
},
|
|
147
|
+
);
|
|
148
|
+
|
|
149
|
+
lapisRequestMocks.lineageDefinition(
|
|
150
|
+
{
|
|
151
|
+
A: {
|
|
152
|
+
aliases: ['a'],
|
|
153
|
+
},
|
|
154
|
+
'A.1': {
|
|
155
|
+
parents: ['A'],
|
|
156
|
+
aliases: ['a.1'],
|
|
157
|
+
},
|
|
158
|
+
'A.2': {
|
|
159
|
+
parents: ['A'],
|
|
160
|
+
aliases: ['a.1'],
|
|
161
|
+
},
|
|
162
|
+
XA: {
|
|
163
|
+
aliases: ['xa'],
|
|
164
|
+
parents: ['A.1', 'A.2'],
|
|
165
|
+
},
|
|
166
|
+
},
|
|
167
|
+
lineageField,
|
|
168
|
+
);
|
|
169
|
+
|
|
170
|
+
const result = await fetchLineageAutocompleteList({
|
|
171
|
+
lapisUrl: DUMMY_LAPIS_URL,
|
|
172
|
+
lapisField: lineageField,
|
|
173
|
+
lapisFilter,
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
expect(result).to.deep.equal([
|
|
177
|
+
{
|
|
178
|
+
lineage: 'A',
|
|
179
|
+
count: 1,
|
|
180
|
+
},
|
|
181
|
+
{
|
|
182
|
+
lineage: 'A*',
|
|
183
|
+
count: 10,
|
|
184
|
+
},
|
|
185
|
+
|
|
186
|
+
{
|
|
187
|
+
lineage: 'A.1',
|
|
188
|
+
count: 2,
|
|
189
|
+
},
|
|
190
|
+
{
|
|
191
|
+
lineage: 'A.1*',
|
|
192
|
+
count: 6,
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
lineage: 'A.2',
|
|
196
|
+
count: 3,
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
lineage: 'A.2*',
|
|
200
|
+
count: 7,
|
|
201
|
+
},
|
|
202
|
+
{
|
|
203
|
+
lineage: 'XA',
|
|
204
|
+
count: 4,
|
|
205
|
+
},
|
|
206
|
+
{
|
|
207
|
+
lineage: 'XA*',
|
|
208
|
+
count: 4,
|
|
209
|
+
},
|
|
210
|
+
]);
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
test('should work with grandchildren', async () => {
|
|
214
|
+
lapisRequestMocks.aggregated(
|
|
215
|
+
{ fields: [lineageField], ...lapisFilter },
|
|
216
|
+
{
|
|
217
|
+
data: [
|
|
218
|
+
{
|
|
219
|
+
[lineageField]: 'A',
|
|
220
|
+
count: 1,
|
|
221
|
+
},
|
|
222
|
+
{
|
|
223
|
+
[lineageField]: 'A.1',
|
|
224
|
+
count: 2,
|
|
225
|
+
},
|
|
226
|
+
{
|
|
227
|
+
[lineageField]: 'A.1.1',
|
|
228
|
+
count: 3,
|
|
229
|
+
},
|
|
230
|
+
],
|
|
231
|
+
},
|
|
232
|
+
);
|
|
233
|
+
|
|
234
|
+
lapisRequestMocks.lineageDefinition(
|
|
235
|
+
{
|
|
236
|
+
'A.1': {
|
|
237
|
+
parents: ['A'],
|
|
238
|
+
aliases: ['a.1'],
|
|
239
|
+
},
|
|
240
|
+
A: {
|
|
241
|
+
aliases: ['a'],
|
|
242
|
+
},
|
|
243
|
+
'A.1.1': {
|
|
244
|
+
parents: ['A.1'],
|
|
245
|
+
aliases: ['a.1.1'],
|
|
246
|
+
},
|
|
247
|
+
},
|
|
248
|
+
lineageField,
|
|
249
|
+
);
|
|
250
|
+
|
|
251
|
+
const result = await fetchLineageAutocompleteList({
|
|
252
|
+
lapisUrl: DUMMY_LAPIS_URL,
|
|
253
|
+
lapisField: lineageField,
|
|
254
|
+
lapisFilter,
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
expect(result).to.deep.equal([
|
|
258
|
+
{
|
|
259
|
+
lineage: 'A',
|
|
260
|
+
count: 1,
|
|
261
|
+
},
|
|
262
|
+
{
|
|
263
|
+
lineage: 'A*',
|
|
264
|
+
count: 6,
|
|
265
|
+
},
|
|
266
|
+
|
|
267
|
+
{
|
|
268
|
+
lineage: 'A.1',
|
|
269
|
+
count: 2,
|
|
270
|
+
},
|
|
271
|
+
{
|
|
272
|
+
lineage: 'A.1*',
|
|
273
|
+
count: 5,
|
|
274
|
+
},
|
|
275
|
+
|
|
276
|
+
{
|
|
277
|
+
lineage: 'A.1.1',
|
|
278
|
+
count: 3,
|
|
279
|
+
},
|
|
280
|
+
{
|
|
281
|
+
lineage: 'A.1.1*',
|
|
282
|
+
count: 3,
|
|
283
|
+
},
|
|
284
|
+
]);
|
|
27
285
|
});
|
|
28
286
|
});
|
|
@@ -1,20 +1,117 @@
|
|
|
1
|
+
import { fetchLineageDefinition } from '../../lapisApi/lapisApi';
|
|
1
2
|
import { FetchAggregatedOperator } from '../../operator/FetchAggregatedOperator';
|
|
2
3
|
import type { LapisFilter } from '../../types';
|
|
3
4
|
|
|
4
5
|
export async function fetchLineageAutocompleteList({
|
|
5
|
-
|
|
6
|
-
|
|
6
|
+
lapisUrl,
|
|
7
|
+
lapisField,
|
|
8
|
+
lapisFilter,
|
|
7
9
|
signal,
|
|
10
|
+
}: {
|
|
11
|
+
lapisUrl: string;
|
|
12
|
+
lapisField: string;
|
|
13
|
+
lapisFilter?: LapisFilter;
|
|
14
|
+
signal?: AbortSignal;
|
|
15
|
+
}): Promise<LineageItem[]> {
|
|
16
|
+
const [countsByLineage, lineageTree] = await Promise.all([
|
|
17
|
+
getCountsByLineage({
|
|
18
|
+
lapisUrl,
|
|
19
|
+
lapisField,
|
|
20
|
+
lapisFilter,
|
|
21
|
+
signal,
|
|
22
|
+
}),
|
|
23
|
+
getLineageTree({ lapisUrl, lapisField, signal }),
|
|
24
|
+
]);
|
|
25
|
+
|
|
26
|
+
return Array.from(lineageTree.keys())
|
|
27
|
+
.sort((a, b) => a.localeCompare(b))
|
|
28
|
+
.map((lineage) => {
|
|
29
|
+
return [
|
|
30
|
+
{
|
|
31
|
+
lineage,
|
|
32
|
+
count: countsByLineage.get(lineage) ?? 0,
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
lineage: `${lineage}*`,
|
|
36
|
+
count: getCountsIncludingSublineages(lineage, lineageTree, countsByLineage),
|
|
37
|
+
},
|
|
38
|
+
];
|
|
39
|
+
})
|
|
40
|
+
.flat();
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export type LineageItem = { lineage: string; count: number };
|
|
44
|
+
|
|
45
|
+
async function getCountsByLineage({
|
|
46
|
+
lapisUrl,
|
|
47
|
+
lapisField,
|
|
8
48
|
lapisFilter,
|
|
49
|
+
signal,
|
|
9
50
|
}: {
|
|
10
|
-
|
|
11
|
-
|
|
51
|
+
lapisUrl: string;
|
|
52
|
+
lapisField: string;
|
|
12
53
|
lapisFilter?: LapisFilter;
|
|
13
54
|
signal?: AbortSignal;
|
|
14
55
|
}) {
|
|
15
|
-
const fetchAggregatedOperator = new FetchAggregatedOperator<Record<string, string>>(lapisFilter ?? {}, [
|
|
56
|
+
const fetchAggregatedOperator = new FetchAggregatedOperator<Record<string, string>>(lapisFilter ?? {}, [
|
|
57
|
+
lapisField,
|
|
58
|
+
]);
|
|
59
|
+
|
|
60
|
+
const countsByLineageArray = (await fetchAggregatedOperator.evaluate(lapisUrl, signal)).content;
|
|
61
|
+
return new Map<string, number>(countsByLineageArray.map((value) => [value[lapisField], value.count]));
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
async function getLineageTree({
|
|
65
|
+
lapisUrl,
|
|
66
|
+
lapisField,
|
|
67
|
+
signal,
|
|
68
|
+
}: {
|
|
69
|
+
lapisUrl: string;
|
|
70
|
+
lapisField: string;
|
|
71
|
+
signal?: AbortSignal;
|
|
72
|
+
}) {
|
|
73
|
+
const lineageDefinitions = await fetchLineageDefinition({ lapisUrl, lapisField, signal });
|
|
74
|
+
|
|
75
|
+
const lineageTree = new Map<string, { children: string[] }>();
|
|
76
|
+
|
|
77
|
+
Object.entries(lineageDefinitions).forEach(([lineage, definition]) => {
|
|
78
|
+
if (!lineageTree.has(lineage)) {
|
|
79
|
+
lineageTree.set(lineage, { children: [] });
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
definition.parents?.forEach((parent) => {
|
|
83
|
+
const parentChildren = lineageTree.get(parent)?.children;
|
|
84
|
+
|
|
85
|
+
const newParentChildren = parentChildren ? [...parentChildren, lineage] : [lineage];
|
|
86
|
+
|
|
87
|
+
lineageTree.set(parent, { children: newParentChildren });
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
return lineageTree;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function getCountsIncludingSublineages(
|
|
95
|
+
lineage: string,
|
|
96
|
+
lineageTree: Map<string, { children: string[] }>,
|
|
97
|
+
countsByLineage: Map<string, number>,
|
|
98
|
+
): number {
|
|
99
|
+
const descendants = getAllDescendants(lineage, lineageTree);
|
|
100
|
+
|
|
101
|
+
const countOfChildren = [...descendants].reduce((sum, child) => {
|
|
102
|
+
return sum + (countsByLineage.get(child) ?? 0);
|
|
103
|
+
}, 0);
|
|
104
|
+
const countLineage = countsByLineage.get(lineage) ?? 0;
|
|
105
|
+
|
|
106
|
+
return countOfChildren + countLineage;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function getAllDescendants(lineage: string, lineageTree: Map<string, { children: string[] }>): Set<string> {
|
|
110
|
+
const children = lineageTree.get(lineage)?.children ?? [];
|
|
16
111
|
|
|
17
|
-
const
|
|
112
|
+
const childrenOfChildren = children.flatMap((child) => {
|
|
113
|
+
return getAllDescendants(child, lineageTree);
|
|
114
|
+
});
|
|
18
115
|
|
|
19
|
-
return
|
|
116
|
+
return new Set([...children, ...childrenOfChildren.flatMap((child) => Array.from(child))]);
|
|
20
117
|
}
|
|
@@ -5,6 +5,8 @@ import type { StepFunction } from '@storybook/types';
|
|
|
5
5
|
import { LineageFilter, type LineageFilterProps } from './lineage-filter';
|
|
6
6
|
import { previewHandles } from '../../../.storybook/preview';
|
|
7
7
|
import { AGGREGATED_ENDPOINT, LAPIS_URL } from '../../constants';
|
|
8
|
+
import lineageDefinition from './__mockData__/lineageDefinition.json';
|
|
9
|
+
import { lineageDefinitionEndpoint } from '../../lapisApi/lapisApi';
|
|
8
10
|
import aggregatedData from '../../preact/lineageFilter/__mockData__/aggregated.json';
|
|
9
11
|
import { gsEventNames } from '../../utils/gsEventNames';
|
|
10
12
|
import { LapisUrlContextProvider } from '../LapisUrlContext';
|
|
@@ -33,6 +35,16 @@ const meta: Meta = {
|
|
|
33
35
|
body: aggregatedData,
|
|
34
36
|
},
|
|
35
37
|
},
|
|
38
|
+
{
|
|
39
|
+
matcher: {
|
|
40
|
+
name: 'lineageDefinition',
|
|
41
|
+
url: lineageDefinitionEndpoint(LAPIS_URL, 'pangoLineage'),
|
|
42
|
+
},
|
|
43
|
+
response: {
|
|
44
|
+
status: 200,
|
|
45
|
+
body: lineageDefinition,
|
|
46
|
+
},
|
|
47
|
+
},
|
|
36
48
|
],
|
|
37
49
|
},
|
|
38
50
|
},
|
|
@@ -90,7 +102,7 @@ export const Default: StoryObj<LineageFilterProps> = {
|
|
|
90
102
|
const input = await inputField(canvas);
|
|
91
103
|
await userEvent.clear(input);
|
|
92
104
|
await userEvent.type(input, 'B.1');
|
|
93
|
-
await userEvent.click(canvas.getByRole('option', { name: 'B.1' }));
|
|
105
|
+
await userEvent.click(canvas.getByRole('option', { name: 'B.1(53802)' }));
|
|
94
106
|
|
|
95
107
|
await waitFor(() => {
|
|
96
108
|
return expect(lineageChangedListenerMock.mock.calls.at(-1)![0].detail).toStrictEqual({
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { type FunctionComponent } from 'preact';
|
|
2
|
+
import { useMemo } from 'preact/hooks';
|
|
2
3
|
import z from 'zod';
|
|
3
4
|
|
|
4
5
|
import { useLapisUrl } from '../LapisUrlContext';
|
|
5
6
|
import { LineageFilterChangedEvent } from './LineageFilterChangedEvent';
|
|
6
|
-
import { fetchLineageAutocompleteList } from './fetchLineageAutocompleteList';
|
|
7
|
+
import { fetchLineageAutocompleteList, type LineageItem } from './fetchLineageAutocompleteList';
|
|
7
8
|
import { lapisFilterSchema } from '../../types';
|
|
8
9
|
import { DownshiftCombobox } from '../components/downshift-combobox';
|
|
9
10
|
import { ErrorBoundary } from '../components/error-boundary';
|
|
@@ -46,11 +47,11 @@ const LineageFilterInner: FunctionComponent<LineageFilterInnerProps> = ({
|
|
|
46
47
|
value,
|
|
47
48
|
lapisFilter,
|
|
48
49
|
}) => {
|
|
49
|
-
const
|
|
50
|
+
const lapisUrl = useLapisUrl();
|
|
50
51
|
|
|
51
52
|
const { data, error, isLoading } = useQuery(
|
|
52
|
-
() => fetchLineageAutocompleteList({
|
|
53
|
-
[lapisField,
|
|
53
|
+
() => fetchLineageAutocompleteList({ lapisUrl, lapisField, lapisFilter }),
|
|
54
|
+
[lapisField, lapisUrl, lapisFilter],
|
|
54
55
|
);
|
|
55
56
|
|
|
56
57
|
if (isLoading) {
|
|
@@ -70,24 +71,33 @@ const LineageSelector = ({
|
|
|
70
71
|
placeholderText,
|
|
71
72
|
data,
|
|
72
73
|
}: LineageSelectorProps & {
|
|
73
|
-
data:
|
|
74
|
+
data: LineageItem[];
|
|
74
75
|
}) => {
|
|
76
|
+
const selectedItem = useMemo(() => {
|
|
77
|
+
return data.find((item) => item.lineage === value) ?? null;
|
|
78
|
+
}, [data, value]);
|
|
79
|
+
|
|
75
80
|
return (
|
|
76
81
|
<DownshiftCombobox
|
|
77
82
|
allItems={data}
|
|
78
|
-
value={
|
|
83
|
+
value={selectedItem}
|
|
79
84
|
filterItemsByInputValue={filterByInputValue}
|
|
80
|
-
createEvent={(item) => new LineageFilterChangedEvent({ [lapisField]: item ?? undefined })}
|
|
81
|
-
itemToString={(item) => item ?? ''}
|
|
85
|
+
createEvent={(item) => new LineageFilterChangedEvent({ [lapisField]: item?.lineage ?? undefined })}
|
|
86
|
+
itemToString={(item) => item?.lineage ?? ''}
|
|
82
87
|
placeholderText={placeholderText}
|
|
83
|
-
formatItemInList={(item:
|
|
88
|
+
formatItemInList={(item: LineageItem) => (
|
|
89
|
+
<p>
|
|
90
|
+
<span>{item.lineage}</span>
|
|
91
|
+
<span className='ml-2 text-gray-500'>({item.count})</span>
|
|
92
|
+
</p>
|
|
93
|
+
)}
|
|
84
94
|
/>
|
|
85
95
|
);
|
|
86
96
|
};
|
|
87
97
|
|
|
88
|
-
function filterByInputValue(item:
|
|
98
|
+
function filterByInputValue(item: LineageItem, inputValue: string | null) {
|
|
89
99
|
if (inputValue === null || inputValue === '') {
|
|
90
100
|
return true;
|
|
91
101
|
}
|
|
92
|
-
return item?.toLowerCase().includes(inputValue?.toLowerCase() || '');
|
|
102
|
+
return item.lineage?.toLowerCase().includes(inputValue?.toLowerCase() || '');
|
|
93
103
|
}
|
|
@@ -7,7 +7,9 @@ import { previewHandles } from '../../../.storybook/preview';
|
|
|
7
7
|
import { AGGREGATED_ENDPOINT, LAPIS_URL } from '../../constants';
|
|
8
8
|
import '../gs-app';
|
|
9
9
|
import './gs-lineage-filter';
|
|
10
|
+
import { lineageDefinitionEndpoint } from '../../lapisApi/lapisApi';
|
|
10
11
|
import aggregatedData from '../../preact/lineageFilter/__mockData__/aggregated.json';
|
|
12
|
+
import lineageDefinition from '../../preact/lineageFilter/__mockData__/lineageDefinition.json';
|
|
11
13
|
import { type LineageFilterProps } from '../../preact/lineageFilter/lineage-filter';
|
|
12
14
|
import { gsEventNames } from '../../utils/gsEventNames';
|
|
13
15
|
import { withinShadowRoot } from '../withinShadowRoot.story';
|
|
@@ -44,6 +46,16 @@ const meta: Meta<Required<LineageFilterProps>> = {
|
|
|
44
46
|
body: aggregatedData,
|
|
45
47
|
},
|
|
46
48
|
},
|
|
49
|
+
{
|
|
50
|
+
matcher: {
|
|
51
|
+
name: 'lineageDefinition',
|
|
52
|
+
url: lineageDefinitionEndpoint(LAPIS_URL, 'pangoLineage'),
|
|
53
|
+
},
|
|
54
|
+
response: {
|
|
55
|
+
status: 200,
|
|
56
|
+
body: lineageDefinition,
|
|
57
|
+
},
|
|
58
|
+
},
|
|
47
59
|
],
|
|
48
60
|
},
|
|
49
61
|
componentDocs: {
|
|
@@ -210,7 +222,7 @@ export const FiresEvent: StoryObj<Required<LineageFilterProps>> = {
|
|
|
210
222
|
|
|
211
223
|
await step('Enter a valid lineage value', async () => {
|
|
212
224
|
await userEvent.type(inputField(), 'B.1.1.7*');
|
|
213
|
-
await userEvent.click(canvas.getByRole('option', { name: 'B.1.1.7*' }));
|
|
225
|
+
await userEvent.click(canvas.getByRole('option', { name: 'B.1.1.7*(677146)' }));
|
|
214
226
|
|
|
215
227
|
await waitFor(() => {
|
|
216
228
|
return expect(listenerMock.mock.calls.at(-1)![0].detail).toStrictEqual({
|