@genspectrum/dashboard-components 1.10.3 → 1.11.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/custom-elements.json +38 -3
- package/dist/assets/{mutationOverTimeWorker-dhufsWQ2.js.map → mutationOverTimeWorker-CQQFRoK4.js.map} +1 -1
- package/dist/components.d.ts +40 -32
- package/dist/components.js +144 -66
- package/dist/components.js.map +1 -1
- package/dist/util.d.ts +45 -32
- package/package.json +1 -1
- package/src/preact/mutationsOverTime/getFilteredMutationsOverTimeData.ts +1 -1
- package/src/preact/mutationsOverTime/mutations-over-time-grid-tooltip.stories.tsx +62 -10
- package/src/preact/mutationsOverTime/mutations-over-time-grid-tooltip.tsx +93 -51
- package/src/preact/mutationsOverTime/mutations-over-time-grid.tsx +36 -8
- package/src/preact/mutationsOverTime/mutations-over-time.stories.tsx +27 -0
- package/src/preact/mutationsOverTime/mutations-over-time.tsx +5 -3
- package/src/query/queryMutationsOverTime.ts +21 -2
- package/src/query/queryMutationsOverTimeNewEndpoint.spec.ts +23 -23
- package/src/utilEntrypoint.ts +1 -0
- package/src/web-components/input/gs-lineage-filter.stories.ts +9 -1
- package/src/web-components/visualization/gs-mutations-over-time.spec-d.ts +3 -0
- package/src/web-components/visualization/gs-mutations-over-time.stories.ts +21 -0
- package/src/web-components/visualization/gs-mutations-over-time.tsx +8 -0
- package/standalone-bundle/assets/{mutationOverTimeWorker-CGqPKySO.js.map → mutationOverTimeWorker-DIpJukJC.js.map} +1 -1
- package/standalone-bundle/dashboard-components.js +2953 -2880
- package/standalone-bundle/dashboard-components.js.map +1 -1
|
@@ -38,6 +38,7 @@ const meta: Meta<MutationsOverTimeProps> = {
|
|
|
38
38
|
hideGaps: { control: 'boolean' },
|
|
39
39
|
pageSizes: { control: 'object' },
|
|
40
40
|
useNewEndpoint: { control: 'boolean' },
|
|
41
|
+
customColumns: { control: 'object' },
|
|
41
42
|
},
|
|
42
43
|
parameters: {
|
|
43
44
|
fetchMock: {},
|
|
@@ -196,6 +197,32 @@ export const UsesMutationFilter: StoryObj<MutationsOverTimeProps> = {
|
|
|
196
197
|
},
|
|
197
198
|
};
|
|
198
199
|
|
|
200
|
+
export const WithCustomColumns: StoryObj<MutationsOverTimeProps> = {
|
|
201
|
+
...Default,
|
|
202
|
+
args: {
|
|
203
|
+
...Default.args,
|
|
204
|
+
displayMutations: ['A19722G', 'G21641T', 'T21653-'],
|
|
205
|
+
customColumns: [
|
|
206
|
+
{
|
|
207
|
+
header: 'Jaccard Index',
|
|
208
|
+
values: {
|
|
209
|
+
A19722G: 0.75,
|
|
210
|
+
G21641T: 'Foobar',
|
|
211
|
+
},
|
|
212
|
+
},
|
|
213
|
+
],
|
|
214
|
+
},
|
|
215
|
+
play: async ({ canvas }) => {
|
|
216
|
+
await waitFor(() => expect(canvas.getByText('Jaccard Index')).toBeVisible(), {
|
|
217
|
+
timeout: 5000,
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
await waitFor(() => expect(canvas.getByText('0.75')).toBeVisible());
|
|
221
|
+
|
|
222
|
+
await waitFor(() => expect(canvas.getByText('Foobar')).toBeVisible());
|
|
223
|
+
},
|
|
224
|
+
};
|
|
225
|
+
|
|
199
226
|
async function expectMutationOnPage(canvas: Canvas, mutation: string) {
|
|
200
227
|
await waitFor(async () => {
|
|
201
228
|
const mutationOnFirstPage = canvas.getAllByText(mutation)[0];
|
|
@@ -11,8 +11,8 @@ import {
|
|
|
11
11
|
type MutationFilter,
|
|
12
12
|
} from './getFilteredMutationsOverTimeData';
|
|
13
13
|
import { type MutationOverTimeWorkerResponse } from './mutationOverTimeWorker';
|
|
14
|
-
import MutationsOverTimeGrid from './mutations-over-time-grid';
|
|
15
|
-
import { type MutationOverTimeQuery } from '../../query/queryMutationsOverTime';
|
|
14
|
+
import MutationsOverTimeGrid, { customColumnSchema } from './mutations-over-time-grid';
|
|
15
|
+
import { getProportion, type MutationOverTimeQuery } from '../../query/queryMutationsOverTime';
|
|
16
16
|
import {
|
|
17
17
|
lapisFilterSchema,
|
|
18
18
|
sequenceTypeSchema,
|
|
@@ -66,6 +66,7 @@ const mutationOverTimeSchema = z.object({
|
|
|
66
66
|
width: z.string(),
|
|
67
67
|
height: z.string().optional(),
|
|
68
68
|
pageSizes: pageSizesSchema,
|
|
69
|
+
customColumns: z.array(customColumnSchema).optional(),
|
|
69
70
|
});
|
|
70
71
|
export type MutationsOverTimeProps = z.infer<typeof mutationOverTimeSchema>;
|
|
71
72
|
|
|
@@ -204,6 +205,7 @@ const MutationsOverTimeTabs: FunctionComponent<MutationOverTimeTabsProps> = ({
|
|
|
204
205
|
colorScale={colorScale}
|
|
205
206
|
sequenceType={originalComponentProps.sequenceType}
|
|
206
207
|
pageSizes={originalComponentProps.pageSizes}
|
|
208
|
+
customColumns={originalComponentProps.customColumns}
|
|
207
209
|
tooltipPortalTarget={tooltipPortalTarget}
|
|
208
210
|
/>
|
|
209
211
|
),
|
|
@@ -354,7 +356,7 @@ function getDownloadData(filteredData: MutationOverTimeDataMap) {
|
|
|
354
356
|
return dates.reduce(
|
|
355
357
|
(accumulated, date) => {
|
|
356
358
|
const value = filteredData.get(mutation, date);
|
|
357
|
-
const proportion = value
|
|
359
|
+
const proportion = getProportion(value ?? null) ?? '';
|
|
358
360
|
return {
|
|
359
361
|
...accumulated,
|
|
360
362
|
[date.dateString]: proportion,
|
|
@@ -49,6 +49,12 @@ export type MutationOverTimeMutationValue =
|
|
|
49
49
|
count: number;
|
|
50
50
|
totalCount: number;
|
|
51
51
|
}
|
|
52
|
+
| {
|
|
53
|
+
type: 'valueWithCoverage';
|
|
54
|
+
count: number;
|
|
55
|
+
coverage: number;
|
|
56
|
+
totalCount: number;
|
|
57
|
+
}
|
|
52
58
|
| {
|
|
53
59
|
type: 'wastewaterValue';
|
|
54
60
|
proportion: number;
|
|
@@ -59,6 +65,19 @@ export type MutationOverTimeMutationValue =
|
|
|
59
65
|
}
|
|
60
66
|
| null;
|
|
61
67
|
|
|
68
|
+
export function getProportion(value: MutationOverTimeMutationValue) {
|
|
69
|
+
switch (value?.type) {
|
|
70
|
+
case 'value':
|
|
71
|
+
case 'wastewaterValue':
|
|
72
|
+
return value.proportion;
|
|
73
|
+
case 'valueWithCoverage':
|
|
74
|
+
return value.count / value.coverage;
|
|
75
|
+
case 'belowThreshold':
|
|
76
|
+
return undefined;
|
|
77
|
+
}
|
|
78
|
+
return undefined;
|
|
79
|
+
}
|
|
80
|
+
|
|
62
81
|
const MAX_NUMBER_OF_GRID_COLUMNS = 200;
|
|
63
82
|
export const MUTATIONS_OVER_TIME_MIN_PROPORTION = 0.001;
|
|
64
83
|
|
|
@@ -316,9 +335,9 @@ async function queryMutationsOverTimeDataDirectEndpoint(
|
|
|
316
335
|
return [
|
|
317
336
|
date.dateString,
|
|
318
337
|
{
|
|
319
|
-
type: '
|
|
320
|
-
proportion: count / coverage,
|
|
338
|
+
type: 'valueWithCoverage',
|
|
321
339
|
count,
|
|
340
|
+
coverage,
|
|
322
341
|
totalCount,
|
|
323
342
|
},
|
|
324
343
|
];
|
|
@@ -120,14 +120,14 @@ describe('queryMutationsOverTimeNewEndpoint', () => {
|
|
|
120
120
|
|
|
121
121
|
const expectedData = [
|
|
122
122
|
[
|
|
123
|
-
{ type: '
|
|
124
|
-
{ type: '
|
|
125
|
-
{ type: '
|
|
123
|
+
{ type: 'valueWithCoverage', count: 4, coverage: 10, totalCount: 11 },
|
|
124
|
+
{ type: 'valueWithCoverage', count: 0, coverage: 10, totalCount: 12 },
|
|
125
|
+
{ type: 'valueWithCoverage', count: 0, coverage: 10, totalCount: 13 },
|
|
126
126
|
],
|
|
127
127
|
[
|
|
128
|
-
{ type: '
|
|
129
|
-
{ type: '
|
|
130
|
-
{ type: '
|
|
128
|
+
{ type: 'valueWithCoverage', count: 1, coverage: 10, totalCount: 11 },
|
|
129
|
+
{ type: 'valueWithCoverage', count: 2, coverage: 10, totalCount: 12 },
|
|
130
|
+
{ type: 'valueWithCoverage', count: 3, coverage: 10, totalCount: 13 },
|
|
131
131
|
],
|
|
132
132
|
];
|
|
133
133
|
expect(mutationOverTimeData.getAsArray()).to.deep.equal(expectedData);
|
|
@@ -289,14 +289,14 @@ describe('queryMutationsOverTimeNewEndpoint', () => {
|
|
|
289
289
|
|
|
290
290
|
expect(mutationOverTimeData.getAsArray()).to.deep.equal([
|
|
291
291
|
[
|
|
292
|
-
{ type: '
|
|
292
|
+
{ type: 'valueWithCoverage', count: 4, coverage: 10, totalCount: 11 },
|
|
293
293
|
null,
|
|
294
|
-
{ type: '
|
|
294
|
+
{ type: 'valueWithCoverage', count: 0, coverage: 10, totalCount: 13 },
|
|
295
295
|
],
|
|
296
296
|
[
|
|
297
|
-
{ type: '
|
|
297
|
+
{ type: 'valueWithCoverage', count: 1, coverage: 10, totalCount: 11 },
|
|
298
298
|
null,
|
|
299
|
-
{ type: '
|
|
299
|
+
{ type: 'valueWithCoverage', count: 3, coverage: 10, totalCount: 13 },
|
|
300
300
|
],
|
|
301
301
|
]);
|
|
302
302
|
|
|
@@ -523,8 +523,8 @@ describe('queryMutationsOverTimeNewEndpoint', () => {
|
|
|
523
523
|
|
|
524
524
|
expect(mutationOverTimeData.getAsArray()).to.deep.equal([
|
|
525
525
|
[
|
|
526
|
-
{ type: '
|
|
527
|
-
{ type: '
|
|
526
|
+
{ type: 'valueWithCoverage', count: 2, coverage: 10, totalCount: 11 },
|
|
527
|
+
{ type: 'valueWithCoverage', count: 3, coverage: 10, totalCount: 12 },
|
|
528
528
|
],
|
|
529
529
|
]);
|
|
530
530
|
|
|
@@ -635,8 +635,8 @@ describe('queryMutationsOverTimeNewEndpoint', () => {
|
|
|
635
635
|
|
|
636
636
|
expect(mutationOverTimeData.getAsArray()).to.deep.equal([
|
|
637
637
|
[
|
|
638
|
-
{ type: '
|
|
639
|
-
{ type: '
|
|
638
|
+
{ type: 'valueWithCoverage', count: 1, coverage: 10, totalCount: 11 },
|
|
639
|
+
{ type: 'valueWithCoverage', count: 2, coverage: 10, totalCount: 12 },
|
|
640
640
|
],
|
|
641
641
|
]);
|
|
642
642
|
|
|
@@ -726,7 +726,7 @@ describe('queryMutationsOverTimeNewEndpoint', () => {
|
|
|
726
726
|
});
|
|
727
727
|
|
|
728
728
|
expect(mutationOverTimeData.getAsArray()).to.deep.equal([
|
|
729
|
-
[{ type: '
|
|
729
|
+
[{ type: 'valueWithCoverage', count: 2, coverage: 10, totalCount: 11 }],
|
|
730
730
|
]);
|
|
731
731
|
|
|
732
732
|
const sequences = mutationOverTimeData.getFirstAxisKeys();
|
|
@@ -839,12 +839,12 @@ describe('queryMutationsOverTimeNewEndpoint', () => {
|
|
|
839
839
|
|
|
840
840
|
expect(mutationOverTimeData.getAsArray()).to.deep.equal([
|
|
841
841
|
[
|
|
842
|
-
{ type: '
|
|
843
|
-
{ type: '
|
|
842
|
+
{ type: 'valueWithCoverage', count: 2, coverage: 10, totalCount: 11 },
|
|
843
|
+
{ type: 'valueWithCoverage', count: 3, coverage: 10, totalCount: 12 },
|
|
844
844
|
],
|
|
845
845
|
[
|
|
846
|
-
{ type: '
|
|
847
|
-
{ type: '
|
|
846
|
+
{ type: 'valueWithCoverage', count: 4, coverage: 10, totalCount: 11 },
|
|
847
|
+
{ type: 'valueWithCoverage', count: 5, coverage: 10, totalCount: 12 },
|
|
848
848
|
],
|
|
849
849
|
]);
|
|
850
850
|
|
|
@@ -1017,8 +1017,8 @@ describe('queryMutationsOverTimeNewEndpoint', () => {
|
|
|
1017
1017
|
{ type: 'belowThreshold', totalCount: 12 },
|
|
1018
1018
|
],
|
|
1019
1019
|
[
|
|
1020
|
-
{ type: '
|
|
1021
|
-
{ type: '
|
|
1020
|
+
{ type: 'valueWithCoverage', count: 2, coverage: 10, totalCount: 11 },
|
|
1021
|
+
{ type: 'valueWithCoverage', count: 3, coverage: 10, totalCount: 12 },
|
|
1022
1022
|
],
|
|
1023
1023
|
]);
|
|
1024
1024
|
|
|
@@ -1139,8 +1139,8 @@ describe('queryMutationsOverTimeNewEndpoint', () => {
|
|
|
1139
1139
|
{ type: 'belowThreshold', totalCount: 12 },
|
|
1140
1140
|
],
|
|
1141
1141
|
[
|
|
1142
|
-
{ type: '
|
|
1143
|
-
{ type: '
|
|
1142
|
+
{ type: 'valueWithCoverage', count: 2, coverage: 10, totalCount: 11 },
|
|
1143
|
+
{ type: 'valueWithCoverage', count: 3, coverage: 10, totalCount: 12 },
|
|
1144
1144
|
],
|
|
1145
1145
|
]);
|
|
1146
1146
|
|
package/src/utilEntrypoint.ts
CHANGED
|
@@ -17,7 +17,7 @@ import { withinShadowRoot } from '../withinShadowRoot.story';
|
|
|
17
17
|
const codeExample = String.raw`
|
|
18
18
|
<gs-lineage-filter
|
|
19
19
|
lapisField="pangoLineage"
|
|
20
|
-
lapisFilter='{"
|
|
20
|
+
lapisFilter='{"country": "Germany"}'
|
|
21
21
|
placeholderText="Enter lineage"
|
|
22
22
|
value="B.1.1.7"
|
|
23
23
|
width="50%">
|
|
@@ -163,6 +163,7 @@ export const LineageFilterStringValue: StoryObj<Required<LineageFilterProps>> =
|
|
|
163
163
|
lapisField="pangoLineage"
|
|
164
164
|
placeholderText="Enter a lineage"
|
|
165
165
|
value="B.1.1.7"
|
|
166
|
+
.lapisFilter=${args.lapisFilter}
|
|
166
167
|
.multiSelect=${args.multiSelect}
|
|
167
168
|
></gs-lineage-filter>
|
|
168
169
|
</div>
|
|
@@ -170,6 +171,9 @@ export const LineageFilterStringValue: StoryObj<Required<LineageFilterProps>> =
|
|
|
170
171
|
},
|
|
171
172
|
args: {
|
|
172
173
|
multiSelect: false,
|
|
174
|
+
lapisFilter: {
|
|
175
|
+
country: 'Germany',
|
|
176
|
+
},
|
|
173
177
|
},
|
|
174
178
|
play: async ({ canvasElement }) => {
|
|
175
179
|
const canvas = await withinShadowRoot(canvasElement, 'gs-lineage-filter');
|
|
@@ -187,6 +191,7 @@ export const LineageFilterArrayValue: StoryObj<Required<LineageFilterProps>> = {
|
|
|
187
191
|
lapisField="pangoLineage"
|
|
188
192
|
placeholderText="Enter a lineage"
|
|
189
193
|
value='["B.1.1.7", "B.1.1.10"]'
|
|
194
|
+
.lapisFilter=${args.lapisFilter}
|
|
190
195
|
.multiSelect=${args.multiSelect}
|
|
191
196
|
></gs-lineage-filter>
|
|
192
197
|
</div>
|
|
@@ -194,6 +199,9 @@ export const LineageFilterArrayValue: StoryObj<Required<LineageFilterProps>> = {
|
|
|
194
199
|
},
|
|
195
200
|
args: {
|
|
196
201
|
multiSelect: true,
|
|
202
|
+
lapisFilter: {
|
|
203
|
+
country: 'Germany',
|
|
204
|
+
},
|
|
197
205
|
},
|
|
198
206
|
play: async ({ canvasElement }) => {
|
|
199
207
|
const canvas = await withinShadowRoot(canvasElement, 'gs-lineage-filter');
|
|
@@ -38,5 +38,8 @@ describe('gs-mutations-over-time', () => {
|
|
|
38
38
|
expectTypeOf<typeof MutationsOverTimeComponent.prototype.pageSizes>().toEqualTypeOf<
|
|
39
39
|
MutationsOverTimeProps['pageSizes']
|
|
40
40
|
>();
|
|
41
|
+
expectTypeOf<typeof MutationsOverTimeComponent.prototype.customColumns>().toEqualTypeOf<
|
|
42
|
+
MutationsOverTimeProps['customColumns']
|
|
43
|
+
>();
|
|
41
44
|
});
|
|
42
45
|
});
|
|
@@ -45,6 +45,7 @@ const meta: Meta<Required<MutationsOverTimeProps>> = {
|
|
|
45
45
|
hideGaps: { control: 'boolean' },
|
|
46
46
|
useNewEndpoint: { control: 'boolean' },
|
|
47
47
|
pageSizes: { control: 'object' },
|
|
48
|
+
customColumns: { control: 'object' },
|
|
48
49
|
},
|
|
49
50
|
args: {
|
|
50
51
|
lapisFilter: { pangoLineage: 'JN.1*', dateFrom: '2024-01-15', dateTo: '2024-07-10' },
|
|
@@ -116,6 +117,7 @@ const Template: StoryObj<Required<MutationsOverTimeProps>> = {
|
|
|
116
117
|
.hideGaps=${args.hideGaps}
|
|
117
118
|
.pageSizes=${args.pageSizes}
|
|
118
119
|
.useNewEndpoint=${args.useNewEndpoint}
|
|
120
|
+
.customColumns=${args.customColumns}
|
|
119
121
|
></gs-mutations-over-time>
|
|
120
122
|
</gs-app>
|
|
121
123
|
`,
|
|
@@ -183,3 +185,22 @@ export const WithFixedHeight: StoryObj<Required<MutationsOverTimeProps>> = {
|
|
|
183
185
|
height: '700px',
|
|
184
186
|
},
|
|
185
187
|
};
|
|
188
|
+
|
|
189
|
+
// This test uses mock data: withDisplayMutations.ts (through mutationOverTimeWorker.mock.ts)
|
|
190
|
+
export const WithCustomColumns: StoryObj<Required<MutationsOverTimeProps>> = {
|
|
191
|
+
...Template,
|
|
192
|
+
args: {
|
|
193
|
+
...Template.args,
|
|
194
|
+
displayMutations: ['A19722G', 'G21641T', 'T21653-'],
|
|
195
|
+
customColumns: [
|
|
196
|
+
{
|
|
197
|
+
header: 'Jaccard Index',
|
|
198
|
+
values: {
|
|
199
|
+
A19722G: 0.75,
|
|
200
|
+
G21641T: 0.92,
|
|
201
|
+
'T21653-': 0.58,
|
|
202
|
+
},
|
|
203
|
+
},
|
|
204
|
+
],
|
|
205
|
+
},
|
|
206
|
+
};
|
|
@@ -136,6 +136,13 @@ export class MutationsOverTimeComponent extends PreactLitAdapterWithGridJsStyles
|
|
|
136
136
|
@property({ type: Array })
|
|
137
137
|
pageSizes: number[] | number = [10, 20, 30, 40, 50];
|
|
138
138
|
|
|
139
|
+
/**
|
|
140
|
+
* Custom columns to add to the grid.
|
|
141
|
+
* Each column has a header and a map of mutation codes to values.
|
|
142
|
+
*/
|
|
143
|
+
@property({ type: Array })
|
|
144
|
+
customColumns?: { header: string; values: Record<string, string | number> }[] = undefined;
|
|
145
|
+
|
|
139
146
|
/**
|
|
140
147
|
* @internal
|
|
141
148
|
*/
|
|
@@ -165,6 +172,7 @@ export class MutationsOverTimeComponent extends PreactLitAdapterWithGridJsStyles
|
|
|
165
172
|
hideGaps={this.hideGaps}
|
|
166
173
|
useNewEndpoint={this.useNewEndpoint}
|
|
167
174
|
pageSizes={this.pageSizes}
|
|
175
|
+
customColumns={this.customColumns}
|
|
168
176
|
/>
|
|
169
177
|
</MutationLinkTemplateContextProvider>
|
|
170
178
|
</MutationAnnotationsContextProvider>
|