@genspectrum/dashboard-components 1.0.1 → 1.1.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/dist/components.d.ts +31 -31
- package/dist/components.js +112 -25
- package/dist/components.js.map +1 -1
- package/dist/util.d.ts +31 -31
- package/package.json +1 -1
- package/src/preact/MutationAnnotationsContext.tsx +15 -7
- package/src/preact/components/mutations-over-time-mutations-filter.stories.tsx +109 -0
- package/src/preact/components/mutations-over-time-mutations-filter.tsx +139 -0
- package/src/preact/mutationsOverTime/getFilteredMutationsOverTime.spec.ts +27 -16
- package/src/preact/mutationsOverTime/getFilteredMutationsOverTimeData.ts +45 -11
- package/src/preact/mutationsOverTime/mutations-over-time.tsx +13 -6
- package/src/preact/wastewater/mutationsOverTime/wastewater-mutations-over-time.tsx +13 -7
- package/standalone-bundle/dashboard-components.js +2862 -2784
- package/standalone-bundle/dashboard-components.js.map +1 -1
- package/src/preact/components/mutations-over-time-text-filter.stories.tsx +0 -57
- package/src/preact/components/mutations-over-time-text-filter.tsx +0 -63
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { describe, expect, it } from 'vitest';
|
|
2
2
|
|
|
3
3
|
import { BaseMutationOverTimeDataMap } from './MutationOverTimeData';
|
|
4
|
-
import { getFilteredMutationOverTimeData } from './getFilteredMutationsOverTimeData';
|
|
4
|
+
import { getFilteredMutationOverTimeData, type MutationFilter } from './getFilteredMutationsOverTimeData';
|
|
5
5
|
import { type MutationOverTimeMutationValue } from '../../query/queryMutationsOverTime';
|
|
6
6
|
import { type DeletionEntry, type SubstitutionEntry } from '../../types';
|
|
7
7
|
import { type Deletion, type Substitution } from '../../utils/mutations';
|
|
@@ -28,7 +28,7 @@ describe('getFilteredMutationOverTimeData', () => {
|
|
|
28
28
|
displayedMutationTypes: [],
|
|
29
29
|
proportionInterval,
|
|
30
30
|
displayMutations: undefined,
|
|
31
|
-
mutationFilterValue: '',
|
|
31
|
+
mutationFilterValue: { textFilter: '', annotationNameFilter: new Set() },
|
|
32
32
|
sequenceType: 'nucleotide',
|
|
33
33
|
annotationProvider: () => {
|
|
34
34
|
return [];
|
|
@@ -62,7 +62,7 @@ describe('getFilteredMutationOverTimeData', () => {
|
|
|
62
62
|
},
|
|
63
63
|
],
|
|
64
64
|
proportionInterval,
|
|
65
|
-
mutationFilterValue: '',
|
|
65
|
+
mutationFilterValue: { textFilter: '', annotationNameFilter: new Set() },
|
|
66
66
|
sequenceType: 'nucleotide',
|
|
67
67
|
annotationProvider: () => {
|
|
68
68
|
return [];
|
|
@@ -85,7 +85,7 @@ describe('getFilteredMutationOverTimeData', () => {
|
|
|
85
85
|
displayedSegments: [],
|
|
86
86
|
displayedMutationTypes: [],
|
|
87
87
|
proportionInterval,
|
|
88
|
-
mutationFilterValue: '',
|
|
88
|
+
mutationFilterValue: { textFilter: '', annotationNameFilter: new Set() },
|
|
89
89
|
sequenceType: 'nucleotide',
|
|
90
90
|
annotationProvider: () => {
|
|
91
91
|
return [];
|
|
@@ -108,7 +108,7 @@ describe('getFilteredMutationOverTimeData', () => {
|
|
|
108
108
|
displayedSegments: [],
|
|
109
109
|
displayedMutationTypes: [],
|
|
110
110
|
proportionInterval,
|
|
111
|
-
mutationFilterValue: '',
|
|
111
|
+
mutationFilterValue: { textFilter: '', annotationNameFilter: new Set() },
|
|
112
112
|
sequenceType: 'nucleotide',
|
|
113
113
|
annotationProvider: () => {
|
|
114
114
|
return [];
|
|
@@ -132,7 +132,7 @@ describe('getFilteredMutationOverTimeData', () => {
|
|
|
132
132
|
displayedSegments: [],
|
|
133
133
|
displayedMutationTypes: [],
|
|
134
134
|
proportionInterval,
|
|
135
|
-
mutationFilterValue: '',
|
|
135
|
+
mutationFilterValue: { textFilter: '', annotationNameFilter: new Set() },
|
|
136
136
|
sequenceType: 'nucleotide',
|
|
137
137
|
annotationProvider: () => {
|
|
138
138
|
return [];
|
|
@@ -156,7 +156,7 @@ describe('getFilteredMutationOverTimeData', () => {
|
|
|
156
156
|
displayedSegments: [],
|
|
157
157
|
displayedMutationTypes: [],
|
|
158
158
|
proportionInterval,
|
|
159
|
-
mutationFilterValue: '',
|
|
159
|
+
mutationFilterValue: { textFilter: '', annotationNameFilter: new Set() },
|
|
160
160
|
sequenceType: 'nucleotide',
|
|
161
161
|
annotationProvider: () => {
|
|
162
162
|
return [];
|
|
@@ -178,7 +178,7 @@ describe('getFilteredMutationOverTimeData', () => {
|
|
|
178
178
|
displayedSegments: [],
|
|
179
179
|
displayedMutationTypes: [],
|
|
180
180
|
proportionInterval,
|
|
181
|
-
mutationFilterValue: '',
|
|
181
|
+
mutationFilterValue: { textFilter: '', annotationNameFilter: new Set() },
|
|
182
182
|
sequenceType: 'nucleotide',
|
|
183
183
|
annotationProvider: () => {
|
|
184
184
|
return [];
|
|
@@ -201,7 +201,7 @@ describe('getFilteredMutationOverTimeData', () => {
|
|
|
201
201
|
displayedSegments: [],
|
|
202
202
|
displayedMutationTypes: [],
|
|
203
203
|
proportionInterval,
|
|
204
|
-
mutationFilterValue: '',
|
|
204
|
+
mutationFilterValue: { textFilter: '', annotationNameFilter: new Set() },
|
|
205
205
|
sequenceType: 'nucleotide',
|
|
206
206
|
annotationProvider: () => {
|
|
207
207
|
return [];
|
|
@@ -225,7 +225,7 @@ describe('getFilteredMutationOverTimeData', () => {
|
|
|
225
225
|
displayedMutationTypes: [],
|
|
226
226
|
proportionInterval,
|
|
227
227
|
displayMutations: [anotherSubstitution.code, someDeletion.code],
|
|
228
|
-
mutationFilterValue: '',
|
|
228
|
+
mutationFilterValue: { textFilter: '', annotationNameFilter: new Set() },
|
|
229
229
|
sequenceType: 'nucleotide',
|
|
230
230
|
annotationProvider: () => {
|
|
231
231
|
return [];
|
|
@@ -235,7 +235,7 @@ describe('getFilteredMutationOverTimeData', () => {
|
|
|
235
235
|
expect(result.getFirstAxisKeys()).to.deep.equal([anotherSubstitution, someDeletion]);
|
|
236
236
|
});
|
|
237
237
|
|
|
238
|
-
it('should filter by mutation filter value', () => {
|
|
238
|
+
it('should filter by mutation filter text value', () => {
|
|
239
239
|
const { data, overallMutationData } = prepareMutationOverTimeData([
|
|
240
240
|
someSubstitutionEntry,
|
|
241
241
|
anotherSubstitutionEntry,
|
|
@@ -248,7 +248,7 @@ describe('getFilteredMutationOverTimeData', () => {
|
|
|
248
248
|
displayedSegments: [],
|
|
249
249
|
displayedMutationTypes: [],
|
|
250
250
|
proportionInterval,
|
|
251
|
-
mutationFilterValue: '23T',
|
|
251
|
+
mutationFilterValue: { textFilter: '23T', annotationNameFilter: new Set() },
|
|
252
252
|
sequenceType: 'nucleotide',
|
|
253
253
|
annotationProvider: () => {
|
|
254
254
|
return [];
|
|
@@ -265,7 +265,7 @@ describe('getFilteredMutationOverTimeData', () => {
|
|
|
265
265
|
someDeletionEntry,
|
|
266
266
|
]);
|
|
267
267
|
|
|
268
|
-
const expectFilteredValue = (filterValue:
|
|
268
|
+
const expectFilteredValue = (filterValue: MutationFilter, annotations: MutationAnnotations) => {
|
|
269
269
|
const annotationProvider = getMutationAnnotationsProvider(getMutationAnnotationsContext(annotations));
|
|
270
270
|
|
|
271
271
|
const result = getFilteredMutationOverTimeData({
|
|
@@ -283,7 +283,7 @@ describe('getFilteredMutationOverTimeData', () => {
|
|
|
283
283
|
};
|
|
284
284
|
|
|
285
285
|
it('with filter value in symbol', () => {
|
|
286
|
-
expectFilteredValue('#', [
|
|
286
|
+
expectFilteredValue({ textFilter: '#', annotationNameFilter: new Set() }, [
|
|
287
287
|
{
|
|
288
288
|
name: 'Annotation 1',
|
|
289
289
|
description: 'Description 1',
|
|
@@ -294,7 +294,7 @@ describe('getFilteredMutationOverTimeData', () => {
|
|
|
294
294
|
});
|
|
295
295
|
|
|
296
296
|
it('with filter value in name', () => {
|
|
297
|
-
expectFilteredValue('Annota', [
|
|
297
|
+
expectFilteredValue({ textFilter: 'Annota', annotationNameFilter: new Set() }, [
|
|
298
298
|
{
|
|
299
299
|
name: 'Annotation 1 #',
|
|
300
300
|
description: 'Description 1',
|
|
@@ -305,7 +305,18 @@ describe('getFilteredMutationOverTimeData', () => {
|
|
|
305
305
|
});
|
|
306
306
|
|
|
307
307
|
it('with filter value in name', () => {
|
|
308
|
-
expectFilteredValue('Descr', [
|
|
308
|
+
expectFilteredValue({ textFilter: 'Descr', annotationNameFilter: new Set() }, [
|
|
309
|
+
{
|
|
310
|
+
name: 'Annotation 1',
|
|
311
|
+
description: 'Description 1',
|
|
312
|
+
symbol: '#',
|
|
313
|
+
nucleotideMutations: ['A123T'],
|
|
314
|
+
},
|
|
315
|
+
]);
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
it('with annotation name filter', () => {
|
|
319
|
+
expectFilteredValue({ textFilter: '', annotationNameFilter: new Set(['Annotation 1']) }, [
|
|
309
320
|
{
|
|
310
321
|
name: 'Annotation 1',
|
|
311
322
|
description: 'Description 1',
|
|
@@ -11,6 +11,11 @@ import type { DisplayedSegment } from '../components/segment-selector';
|
|
|
11
11
|
export const displayMutationsSchema = z.array(z.string()).min(1);
|
|
12
12
|
export type DisplayMutations = z.infer<typeof displayMutationsSchema>;
|
|
13
13
|
|
|
14
|
+
export type MutationFilter = {
|
|
15
|
+
textFilter: string;
|
|
16
|
+
annotationNameFilter: Set<string>;
|
|
17
|
+
};
|
|
18
|
+
|
|
14
19
|
export type GetFilteredMutationOverTimeDataArgs = {
|
|
15
20
|
data: MutationOverTimeDataMap;
|
|
16
21
|
overallMutationData: SubstitutionOrDeletionEntry<Substitution, Deletion>[];
|
|
@@ -18,7 +23,7 @@ export type GetFilteredMutationOverTimeDataArgs = {
|
|
|
18
23
|
displayedMutationTypes: DisplayedMutationType[];
|
|
19
24
|
proportionInterval: { min: number; max: number };
|
|
20
25
|
displayMutations?: DisplayMutations;
|
|
21
|
-
mutationFilterValue:
|
|
26
|
+
mutationFilterValue: MutationFilter;
|
|
22
27
|
sequenceType: SequenceType;
|
|
23
28
|
annotationProvider: ReturnType<typeof useMutationAnnotationsProvider>;
|
|
24
29
|
};
|
|
@@ -72,25 +77,54 @@ export function getFilteredMutationOverTimeData({
|
|
|
72
77
|
export function mutationOrAnnotationDoNotMatchFilter(
|
|
73
78
|
mutation: Mutation,
|
|
74
79
|
sequenceType: SequenceType,
|
|
75
|
-
|
|
80
|
+
mutationFilter: MutationFilter,
|
|
76
81
|
annotationProvider: ReturnType<typeof useMutationAnnotationsProvider>,
|
|
77
82
|
) {
|
|
78
|
-
|
|
79
|
-
|
|
83
|
+
return !(
|
|
84
|
+
mutationOrAnnotationMatchesTextFilter(mutation, sequenceType, mutationFilter.textFilter, annotationProvider) &&
|
|
85
|
+
mutationMatchesAnnotationFilter(mutation, sequenceType, mutationFilter.annotationNameFilter, annotationProvider)
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function mutationOrAnnotationMatchesTextFilter(
|
|
90
|
+
mutation: Mutation,
|
|
91
|
+
sequenceType: SequenceType,
|
|
92
|
+
textFilter: string,
|
|
93
|
+
annotationProvider: ReturnType<typeof useMutationAnnotationsProvider>,
|
|
94
|
+
) {
|
|
95
|
+
if (textFilter === '') {
|
|
96
|
+
return true;
|
|
80
97
|
}
|
|
81
98
|
|
|
82
|
-
if (mutation.code.includes(
|
|
83
|
-
return
|
|
99
|
+
if (mutation.code.includes(textFilter)) {
|
|
100
|
+
return true;
|
|
84
101
|
}
|
|
85
102
|
|
|
86
103
|
const mutationAnnotations = annotationProvider(mutation, sequenceType);
|
|
87
104
|
if (mutationAnnotations === undefined || mutationAnnotations.length === 0) {
|
|
88
|
-
return
|
|
105
|
+
return false;
|
|
89
106
|
}
|
|
90
|
-
return
|
|
107
|
+
return mutationAnnotations.some(
|
|
91
108
|
(annotation) =>
|
|
92
|
-
annotation.description.includes(
|
|
93
|
-
annotation.name.includes(
|
|
94
|
-
annotation.symbol.includes(
|
|
109
|
+
annotation.description.includes(textFilter) ||
|
|
110
|
+
annotation.name.includes(textFilter) ||
|
|
111
|
+
annotation.symbol.includes(textFilter),
|
|
95
112
|
);
|
|
96
113
|
}
|
|
114
|
+
|
|
115
|
+
function mutationMatchesAnnotationFilter(
|
|
116
|
+
mutation: Mutation,
|
|
117
|
+
sequenceType: SequenceType,
|
|
118
|
+
annotationNameFilter: Set<string>,
|
|
119
|
+
annotationProvider: ReturnType<typeof useMutationAnnotationsProvider>,
|
|
120
|
+
) {
|
|
121
|
+
if (annotationNameFilter.size === 0) {
|
|
122
|
+
return true;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const mutationAnnotations = annotationProvider(mutation, sequenceType);
|
|
126
|
+
if (mutationAnnotations === undefined || mutationAnnotations.length === 0) {
|
|
127
|
+
return false;
|
|
128
|
+
}
|
|
129
|
+
return mutationAnnotations.some((annotation) => annotationNameFilter.has(annotation.name));
|
|
130
|
+
}
|
|
@@ -5,7 +5,11 @@ import z from 'zod';
|
|
|
5
5
|
// @ts-expect-error -- uses subpath imports and vite worker import
|
|
6
6
|
import MutationOverTimeWorker from '#mutationOverTime?worker&inline';
|
|
7
7
|
import { BaseMutationOverTimeDataMap, type MutationOverTimeDataMap } from './MutationOverTimeData';
|
|
8
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
displayMutationsSchema,
|
|
10
|
+
getFilteredMutationOverTimeData,
|
|
11
|
+
type MutationFilter,
|
|
12
|
+
} from './getFilteredMutationsOverTimeData';
|
|
9
13
|
import { type MutationOverTimeWorkerResponse } from './mutationOverTimeWorker';
|
|
10
14
|
import MutationsOverTimeGrid from './mutations-over-time-grid';
|
|
11
15
|
import { type MutationOverTimeQuery } from '../../query/queryMutationsOverTime';
|
|
@@ -29,7 +33,7 @@ import { Fullscreen } from '../components/fullscreen';
|
|
|
29
33
|
import Info, { InfoComponentCode, InfoHeadline1, InfoParagraph } from '../components/info';
|
|
30
34
|
import { LoadingDisplay } from '../components/loading-display';
|
|
31
35
|
import { type DisplayedMutationType, MutationTypeSelector } from '../components/mutation-type-selector';
|
|
32
|
-
import {
|
|
36
|
+
import { MutationsOverTimeMutationsFilter } from '../components/mutations-over-time-mutations-filter';
|
|
33
37
|
import { NoDataDisplay } from '../components/no-data-display';
|
|
34
38
|
import type { ProportionInterval } from '../components/proportion-selector';
|
|
35
39
|
import { ProportionSelectorDropdown } from '../components/proportion-selector-dropdown';
|
|
@@ -129,7 +133,10 @@ const MutationsOverTimeTabs: FunctionComponent<MutationOverTimeTabsProps> = ({
|
|
|
129
133
|
}) => {
|
|
130
134
|
const tabsRef = useDispatchFinishedLoadingEvent();
|
|
131
135
|
|
|
132
|
-
const [mutationFilterValue, setMutationFilterValue] = useState(
|
|
136
|
+
const [mutationFilterValue, setMutationFilterValue] = useState<MutationFilter>({
|
|
137
|
+
textFilter: '',
|
|
138
|
+
annotationNameFilter: new Set(),
|
|
139
|
+
});
|
|
133
140
|
const annotationProvider = useMutationAnnotationsProvider();
|
|
134
141
|
|
|
135
142
|
const [proportionInterval, setProportionInterval] = useState(originalComponentProps.initialMeanProportionInterval);
|
|
@@ -227,8 +234,8 @@ type ToolbarProps = {
|
|
|
227
234
|
colorScale: ColorScale;
|
|
228
235
|
setColorScale: Dispatch<StateUpdater<ColorScale>>;
|
|
229
236
|
originalComponentProps: MutationsOverTimeProps;
|
|
230
|
-
mutationFilterValue:
|
|
231
|
-
setFilterValue:
|
|
237
|
+
mutationFilterValue: MutationFilter;
|
|
238
|
+
setFilterValue: Dispatch<StateUpdater<MutationFilter>>;
|
|
232
239
|
};
|
|
233
240
|
|
|
234
241
|
const Toolbar: FunctionComponent<ToolbarProps> = ({
|
|
@@ -248,7 +255,7 @@ const Toolbar: FunctionComponent<ToolbarProps> = ({
|
|
|
248
255
|
}) => {
|
|
249
256
|
return (
|
|
250
257
|
<>
|
|
251
|
-
<
|
|
258
|
+
<MutationsOverTimeMutationsFilter setFilterValue={setFilterValue} value={mutationFilterValue} />
|
|
252
259
|
{activeTab === 'Grid' && (
|
|
253
260
|
<ColorScaleSelectorDropdown colorScale={colorScale} setColorScale={setColorScale} />
|
|
254
261
|
)}
|
|
@@ -14,13 +14,16 @@ import { ErrorBoundary } from '../../components/error-boundary';
|
|
|
14
14
|
import { Fullscreen } from '../../components/fullscreen';
|
|
15
15
|
import Info, { InfoComponentCode, InfoHeadline1, InfoParagraph } from '../../components/info';
|
|
16
16
|
import { LoadingDisplay } from '../../components/loading-display';
|
|
17
|
-
import {
|
|
17
|
+
import { MutationsOverTimeMutationsFilter } from '../../components/mutations-over-time-mutations-filter';
|
|
18
18
|
import { NoDataDisplay } from '../../components/no-data-display';
|
|
19
19
|
import { ResizeContainer } from '../../components/resize-container';
|
|
20
20
|
import { type DisplayedSegment, SegmentSelector } from '../../components/segment-selector';
|
|
21
21
|
import Tabs from '../../components/tabs';
|
|
22
22
|
import { type MutationOverTimeDataMap } from '../../mutationsOverTime/MutationOverTimeData';
|
|
23
|
-
import {
|
|
23
|
+
import {
|
|
24
|
+
type MutationFilter,
|
|
25
|
+
mutationOrAnnotationDoNotMatchFilter,
|
|
26
|
+
} from '../../mutationsOverTime/getFilteredMutationsOverTimeData';
|
|
24
27
|
import MutationsOverTimeGrid from '../../mutationsOverTime/mutations-over-time-grid';
|
|
25
28
|
import { pageSizesSchema } from '../../shared/tanstackTable/pagination';
|
|
26
29
|
import { PageSizeContextProvider } from '../../shared/tanstackTable/pagination-context';
|
|
@@ -121,7 +124,7 @@ function getFilteredMutationOverTimeData({
|
|
|
121
124
|
}: {
|
|
122
125
|
data: MutationOverTimeDataMap;
|
|
123
126
|
displayedSegments: DisplayedSegment[];
|
|
124
|
-
mutationFilterValue:
|
|
127
|
+
mutationFilterValue: MutationFilter;
|
|
125
128
|
sequenceType: SequenceType;
|
|
126
129
|
annotationProvider: ReturnType<typeof useMutationAnnotationsProvider>;
|
|
127
130
|
}): MutationOverTimeDataMap {
|
|
@@ -148,7 +151,10 @@ const MutationsOverTimeTabs: FunctionComponent<MutationOverTimeTabsProps> = ({
|
|
|
148
151
|
}) => {
|
|
149
152
|
const tabsRef = useDispatchFinishedLoadingEvent();
|
|
150
153
|
|
|
151
|
-
const [mutationFilterValue, setMutationFilterValue] = useState(
|
|
154
|
+
const [mutationFilterValue, setMutationFilterValue] = useState<MutationFilter>({
|
|
155
|
+
textFilter: '',
|
|
156
|
+
annotationNameFilter: new Set(),
|
|
157
|
+
});
|
|
152
158
|
const annotationProvider = useMutationAnnotationsProvider();
|
|
153
159
|
|
|
154
160
|
const [colorScale, setColorScale] = useState<ColorScale>({ min: 0, max: 1, color: 'indigo' });
|
|
@@ -211,8 +217,8 @@ type ToolbarProps = {
|
|
|
211
217
|
data: MutationOverTimeDataPerLocation;
|
|
212
218
|
displayedSegments: DisplayedSegment[];
|
|
213
219
|
setDisplayedSegments: (segments: DisplayedSegment[]) => void;
|
|
214
|
-
mutationFilterValue:
|
|
215
|
-
setFilterValue:
|
|
220
|
+
mutationFilterValue: MutationFilter;
|
|
221
|
+
setFilterValue: Dispatch<StateUpdater<MutationFilter>>;
|
|
216
222
|
};
|
|
217
223
|
|
|
218
224
|
const Toolbar: FunctionComponent<ToolbarProps> = ({
|
|
@@ -226,7 +232,7 @@ const Toolbar: FunctionComponent<ToolbarProps> = ({
|
|
|
226
232
|
}) => {
|
|
227
233
|
return (
|
|
228
234
|
<>
|
|
229
|
-
<
|
|
235
|
+
<MutationsOverTimeMutationsFilter setFilterValue={setFilterValue} value={mutationFilterValue} />
|
|
230
236
|
<ColorScaleSelectorDropdown colorScale={colorScale} setColorScale={setColorScale} />
|
|
231
237
|
<SegmentSelector
|
|
232
238
|
displayedSegments={displayedSegments}
|