@genspectrum/dashboard-components 1.0.0 → 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/assets/mutationOverTimeWorker-DjH04AQB.js.map +1 -0
- package/dist/components.d.ts +14 -14
- package/dist/components.js +114 -27
- package/dist/components.js.map +1 -1
- package/dist/util.d.ts +14 -14
- package/package.json +1 -1
- package/src/lapisApi/LineageDefinition.ts +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/lineageFilter/fetchLineageAutocompleteList.spec.ts +38 -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/assets/mutationOverTimeWorker-B6bf3R3j.js.map +1 -0
- package/standalone-bundle/dashboard-components.js +2873 -2788
- package/standalone-bundle/dashboard-components.js.map +1 -1
- package/dist/assets/mutationOverTimeWorker-BzmkceEA.js.map +0 -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
- package/standalone-bundle/assets/mutationOverTimeWorker-jUeItsGM.js.map +0 -1
package/dist/util.d.ts
CHANGED
|
@@ -949,7 +949,7 @@ declare global {
|
|
|
949
949
|
|
|
950
950
|
declare global {
|
|
951
951
|
interface HTMLElementTagNameMap {
|
|
952
|
-
'gs-
|
|
952
|
+
'gs-prevalence-over-time': PrevalenceOverTimeComponent;
|
|
953
953
|
}
|
|
954
954
|
}
|
|
955
955
|
|
|
@@ -957,7 +957,7 @@ declare global {
|
|
|
957
957
|
declare global {
|
|
958
958
|
namespace JSX {
|
|
959
959
|
interface IntrinsicElements {
|
|
960
|
-
'gs-
|
|
960
|
+
'gs-prevalence-over-time': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
|
|
961
961
|
}
|
|
962
962
|
}
|
|
963
963
|
}
|
|
@@ -965,7 +965,7 @@ declare global {
|
|
|
965
965
|
|
|
966
966
|
declare global {
|
|
967
967
|
interface HTMLElementTagNameMap {
|
|
968
|
-
'gs-
|
|
968
|
+
'gs-mutations': MutationsComponent;
|
|
969
969
|
}
|
|
970
970
|
}
|
|
971
971
|
|
|
@@ -973,7 +973,7 @@ declare global {
|
|
|
973
973
|
declare global {
|
|
974
974
|
namespace JSX {
|
|
975
975
|
interface IntrinsicElements {
|
|
976
|
-
'gs-
|
|
976
|
+
'gs-mutations': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
|
|
977
977
|
}
|
|
978
978
|
}
|
|
979
979
|
}
|
|
@@ -1093,11 +1093,10 @@ declare global {
|
|
|
1093
1093
|
|
|
1094
1094
|
declare global {
|
|
1095
1095
|
interface HTMLElementTagNameMap {
|
|
1096
|
-
'gs-
|
|
1096
|
+
'gs-location-filter': LocationFilterComponent;
|
|
1097
1097
|
}
|
|
1098
1098
|
interface HTMLElementEventMap {
|
|
1099
|
-
[gsEventNames.
|
|
1100
|
-
[gsEventNames.dateRangeOptionChanged]: DateRangeOptionChangedEvent;
|
|
1099
|
+
[gsEventNames.locationChanged]: LocationChangedEvent;
|
|
1101
1100
|
}
|
|
1102
1101
|
}
|
|
1103
1102
|
|
|
@@ -1105,7 +1104,7 @@ declare global {
|
|
|
1105
1104
|
declare global {
|
|
1106
1105
|
namespace JSX {
|
|
1107
1106
|
interface IntrinsicElements {
|
|
1108
|
-
'gs-
|
|
1107
|
+
'gs-location-filter': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
|
|
1109
1108
|
}
|
|
1110
1109
|
}
|
|
1111
1110
|
}
|
|
@@ -1113,10 +1112,10 @@ declare global {
|
|
|
1113
1112
|
|
|
1114
1113
|
declare global {
|
|
1115
1114
|
interface HTMLElementTagNameMap {
|
|
1116
|
-
'gs-
|
|
1115
|
+
'gs-text-filter': TextFilterComponent;
|
|
1117
1116
|
}
|
|
1118
1117
|
interface HTMLElementEventMap {
|
|
1119
|
-
[gsEventNames.
|
|
1118
|
+
[gsEventNames.textFilterChanged]: TextFilterChangedEvent;
|
|
1120
1119
|
}
|
|
1121
1120
|
}
|
|
1122
1121
|
|
|
@@ -1124,7 +1123,7 @@ declare global {
|
|
|
1124
1123
|
declare global {
|
|
1125
1124
|
namespace JSX {
|
|
1126
1125
|
interface IntrinsicElements {
|
|
1127
|
-
'gs-
|
|
1126
|
+
'gs-text-filter': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
|
|
1128
1127
|
}
|
|
1129
1128
|
}
|
|
1130
1129
|
}
|
|
@@ -1132,10 +1131,11 @@ declare global {
|
|
|
1132
1131
|
|
|
1133
1132
|
declare global {
|
|
1134
1133
|
interface HTMLElementTagNameMap {
|
|
1135
|
-
'gs-
|
|
1134
|
+
'gs-date-range-filter': DateRangeFilterComponent;
|
|
1136
1135
|
}
|
|
1137
1136
|
interface HTMLElementEventMap {
|
|
1138
|
-
[gsEventNames.
|
|
1137
|
+
[gsEventNames.dateRangeFilterChanged]: CustomEvent<Record<string, string>>;
|
|
1138
|
+
[gsEventNames.dateRangeOptionChanged]: DateRangeOptionChangedEvent;
|
|
1139
1139
|
}
|
|
1140
1140
|
}
|
|
1141
1141
|
|
|
@@ -1143,7 +1143,7 @@ declare global {
|
|
|
1143
1143
|
declare global {
|
|
1144
1144
|
namespace JSX {
|
|
1145
1145
|
interface IntrinsicElements {
|
|
1146
|
-
'gs-
|
|
1146
|
+
'gs-date-range-filter': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
|
|
1147
1147
|
}
|
|
1148
1148
|
}
|
|
1149
1149
|
}
|
package/package.json
CHANGED
|
@@ -16,7 +16,12 @@ type MutationAnnotationPerSequenceType = {
|
|
|
16
16
|
position: Map<string, MutationAnnotations>;
|
|
17
17
|
};
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
type MutationAnnotationsContextValue = Record<SequenceType, MutationAnnotationPerSequenceType> & {
|
|
20
|
+
rawAnnotations: MutationAnnotations;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const MutationAnnotationsContext = createContext<MutationAnnotationsContextValue>({
|
|
24
|
+
rawAnnotations: [],
|
|
20
25
|
nucleotide: {
|
|
21
26
|
mutation: new Map(),
|
|
22
27
|
position: new Map(),
|
|
@@ -75,6 +80,7 @@ export function getMutationAnnotationsContext(value: MutationAnnotations) {
|
|
|
75
80
|
});
|
|
76
81
|
|
|
77
82
|
return {
|
|
83
|
+
rawAnnotations: value,
|
|
78
84
|
nucleotide: { mutation: nucleotideMap, position: nucleotidePositions },
|
|
79
85
|
'amino acid': { mutation: aminoAcidMap, position: aminoAcidPositions },
|
|
80
86
|
};
|
|
@@ -85,15 +91,17 @@ function addAnnotationToMap(map: Map<string, MutationAnnotations>, code: string,
|
|
|
85
91
|
map.set(code.toUpperCase(), [...oldAnnotations, annotation]);
|
|
86
92
|
}
|
|
87
93
|
|
|
94
|
+
export function useRawMutationAnnotations() {
|
|
95
|
+
return useContext(MutationAnnotationsContext).rawAnnotations;
|
|
96
|
+
}
|
|
97
|
+
|
|
88
98
|
export function useMutationAnnotationsProvider() {
|
|
89
99
|
const mutationAnnotations = useContext(MutationAnnotationsContext);
|
|
90
100
|
|
|
91
101
|
return getMutationAnnotationsProvider(mutationAnnotations);
|
|
92
102
|
}
|
|
93
103
|
|
|
94
|
-
export function getMutationAnnotationsProvider(
|
|
95
|
-
mutationAnnotations: Record<SequenceType, MutationAnnotationPerSequenceType>,
|
|
96
|
-
) {
|
|
104
|
+
export function getMutationAnnotationsProvider(mutationAnnotations: MutationAnnotationsContextValue) {
|
|
97
105
|
return (mutation: Mutation, sequenceType: SequenceType) => {
|
|
98
106
|
const position =
|
|
99
107
|
mutation.segment === undefined
|
|
@@ -110,11 +118,11 @@ export function getMutationAnnotationsProvider(
|
|
|
110
118
|
|
|
111
119
|
const uniqueNames = new Set<string>();
|
|
112
120
|
|
|
113
|
-
return annotations?.filter((
|
|
114
|
-
if (uniqueNames.has(
|
|
121
|
+
return annotations?.filter((annotation) => {
|
|
122
|
+
if (uniqueNames.has(annotation.name)) {
|
|
115
123
|
return false;
|
|
116
124
|
}
|
|
117
|
-
uniqueNames.add(
|
|
125
|
+
uniqueNames.add(annotation.name);
|
|
118
126
|
return true;
|
|
119
127
|
});
|
|
120
128
|
};
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { type StoryObj } from '@storybook/preact';
|
|
2
|
+
import { expect, fn, userEvent, waitFor, within } from '@storybook/test';
|
|
3
|
+
import { type Meta } from '@storybook/web-components';
|
|
4
|
+
import { useState, type Dispatch, type StateUpdater } from 'preact/hooks';
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
MutationsOverTimeMutationsFilter,
|
|
8
|
+
type MutationsOverTimeMutationsFilterProps,
|
|
9
|
+
} from './mutations-over-time-mutations-filter';
|
|
10
|
+
import { MutationAnnotationsContextProvider } from '../MutationAnnotationsContext';
|
|
11
|
+
import { type MutationFilter } from '../mutationsOverTime/getFilteredMutationsOverTimeData';
|
|
12
|
+
|
|
13
|
+
const meta: Meta = {
|
|
14
|
+
title: 'Component/Mutations over time mutations filter',
|
|
15
|
+
component: 'MutationsOverTimeTextFilter',
|
|
16
|
+
parameters: { fetchMock: {} },
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export default meta;
|
|
20
|
+
|
|
21
|
+
const WrapperWithState = ({
|
|
22
|
+
setFilterValue,
|
|
23
|
+
value,
|
|
24
|
+
}: {
|
|
25
|
+
setFilterValue: Dispatch<StateUpdater<MutationFilter>>;
|
|
26
|
+
value: MutationFilter;
|
|
27
|
+
}) => {
|
|
28
|
+
const [state, setState] = useState(value);
|
|
29
|
+
|
|
30
|
+
return (
|
|
31
|
+
<MutationAnnotationsContextProvider
|
|
32
|
+
value={[
|
|
33
|
+
{
|
|
34
|
+
name: 'Test Annotation 1',
|
|
35
|
+
description: 'Test Annotation 1',
|
|
36
|
+
symbol: '#',
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
name: 'Test Annotation 2',
|
|
40
|
+
description: 'Test Annotation 2',
|
|
41
|
+
symbol: '+',
|
|
42
|
+
},
|
|
43
|
+
]}
|
|
44
|
+
>
|
|
45
|
+
<MutationsOverTimeMutationsFilter
|
|
46
|
+
setFilterValue={(value) => {
|
|
47
|
+
setFilterValue(value);
|
|
48
|
+
setState(value);
|
|
49
|
+
}}
|
|
50
|
+
value={state}
|
|
51
|
+
/>
|
|
52
|
+
</MutationAnnotationsContextProvider>
|
|
53
|
+
);
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
export const FilterByText: StoryObj<MutationsOverTimeMutationsFilterProps> = {
|
|
57
|
+
render: (args) => {
|
|
58
|
+
return <WrapperWithState setFilterValue={args.setFilterValue} value={args.value} />;
|
|
59
|
+
},
|
|
60
|
+
args: {
|
|
61
|
+
setFilterValue: fn(),
|
|
62
|
+
value: { textFilter: 'Test', annotationNameFilter: new Set([]) },
|
|
63
|
+
},
|
|
64
|
+
play: async ({ canvasElement, step }) => {
|
|
65
|
+
const canvas = within(canvasElement);
|
|
66
|
+
|
|
67
|
+
await step('Expect initial value to show on the button', async () => {
|
|
68
|
+
const button = canvas.getByRole('button');
|
|
69
|
+
await expect(button).toHaveTextContent('Test');
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
await step('Change filter and expect it to show on the button', async () => {
|
|
73
|
+
const button = canvas.getByRole('button');
|
|
74
|
+
await userEvent.click(button);
|
|
75
|
+
|
|
76
|
+
const inputField = canvas.getByRole('textbox');
|
|
77
|
+
await userEvent.clear(inputField);
|
|
78
|
+
await userEvent.type(inputField, 'OtherText');
|
|
79
|
+
|
|
80
|
+
await waitFor(() => expect(button).toHaveTextContent('OtherText'));
|
|
81
|
+
});
|
|
82
|
+
},
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
export const FilterByAnnotation: StoryObj<MutationsOverTimeMutationsFilterProps> = {
|
|
86
|
+
...FilterByText,
|
|
87
|
+
args: {
|
|
88
|
+
setFilterValue: fn(),
|
|
89
|
+
value: { textFilter: '', annotationNameFilter: new Set() },
|
|
90
|
+
},
|
|
91
|
+
play: async ({ canvasElement, step }) => {
|
|
92
|
+
const canvas = within(canvasElement);
|
|
93
|
+
|
|
94
|
+
await step('Expect default text to show on the button', async () => {
|
|
95
|
+
const button = canvas.getByRole('button');
|
|
96
|
+
await expect(button).toHaveTextContent('Filter mutations');
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
await step('Change filter and expect it to show on the button', async () => {
|
|
100
|
+
const button = canvas.getByRole('button');
|
|
101
|
+
await userEvent.click(button);
|
|
102
|
+
|
|
103
|
+
const inputField = canvas.getByRole('checkbox', { name: /Test Annotation 1/ });
|
|
104
|
+
await userEvent.click(inputField);
|
|
105
|
+
|
|
106
|
+
await waitFor(() => expect(button).toHaveTextContent('Test Annotation 1'));
|
|
107
|
+
});
|
|
108
|
+
},
|
|
109
|
+
};
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { type FunctionComponent, type h } from 'preact';
|
|
2
|
+
import { type Dispatch, type StateUpdater, useCallback, useEffect, useState } from 'preact/hooks';
|
|
3
|
+
|
|
4
|
+
import { Dropdown } from './dropdown';
|
|
5
|
+
import { useRawMutationAnnotations } from '../MutationAnnotationsContext';
|
|
6
|
+
import { type MutationFilter } from '../mutationsOverTime/getFilteredMutationsOverTimeData';
|
|
7
|
+
import { DeleteIcon } from '../shared/icons/DeleteIcon';
|
|
8
|
+
|
|
9
|
+
export type MutationsOverTimeMutationsFilterProps = {
|
|
10
|
+
setFilterValue: Dispatch<StateUpdater<MutationFilter>>;
|
|
11
|
+
value: MutationFilter;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export function MutationsOverTimeMutationsFilter({ setFilterValue, value }: MutationsOverTimeMutationsFilterProps) {
|
|
15
|
+
return (
|
|
16
|
+
<div className={'w-28 inline-flex'}>
|
|
17
|
+
<Dropdown buttonTitle={getButtonTitle(value)} placement={'bottom-start'}>
|
|
18
|
+
<TextInput value={value} setFilterValue={setFilterValue} />
|
|
19
|
+
<AnnotationCheckboxes value={value} setFilterValue={setFilterValue} />
|
|
20
|
+
</Dropdown>
|
|
21
|
+
</div>
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function getButtonTitle(value: MutationFilter) {
|
|
26
|
+
if (value.textFilter === '' && value.annotationNameFilter.size === 0) {
|
|
27
|
+
return `Filter mutations`;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return [value.textFilter, ...value.annotationNameFilter].filter((it) => it !== '').join(', ');
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const TextInput: FunctionComponent<MutationsOverTimeMutationsFilterProps> = ({ setFilterValue, value }) => {
|
|
34
|
+
const onInput = useCallback(
|
|
35
|
+
(newValue: string) => {
|
|
36
|
+
setFilterValue((previousFilter) => ({
|
|
37
|
+
...previousFilter,
|
|
38
|
+
textFilter: newValue,
|
|
39
|
+
}));
|
|
40
|
+
},
|
|
41
|
+
[setFilterValue],
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
const onDeleteClick = () => {
|
|
45
|
+
setFilterValue((previousFilter) => ({
|
|
46
|
+
...previousFilter,
|
|
47
|
+
textFilter: '',
|
|
48
|
+
}));
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
return (
|
|
52
|
+
<div>
|
|
53
|
+
<label className='flex gap-1 input input-xs'>
|
|
54
|
+
<DebouncedInput placeholder={'Filter'} onInput={onInput} value={value.textFilter} type='text' />
|
|
55
|
+
{value.textFilter !== '' && (
|
|
56
|
+
<button className={'cursor-pointer'} onClick={onDeleteClick}>
|
|
57
|
+
<DeleteIcon />
|
|
58
|
+
</button>
|
|
59
|
+
)}
|
|
60
|
+
</label>
|
|
61
|
+
</div>
|
|
62
|
+
);
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
function DebouncedInput({
|
|
66
|
+
value: initialValue,
|
|
67
|
+
onInput,
|
|
68
|
+
debounce = 500,
|
|
69
|
+
...props
|
|
70
|
+
}: {
|
|
71
|
+
onInput: (value: string) => void;
|
|
72
|
+
debounce?: number;
|
|
73
|
+
value?: string;
|
|
74
|
+
} & Omit<h.JSX.IntrinsicElements['input'], 'onInput'>) {
|
|
75
|
+
const [value, setValue] = useState<string | undefined>(initialValue);
|
|
76
|
+
|
|
77
|
+
useEffect(() => {
|
|
78
|
+
setValue(initialValue);
|
|
79
|
+
}, [initialValue]);
|
|
80
|
+
|
|
81
|
+
useEffect(() => {
|
|
82
|
+
const timeout = setTimeout(() => {
|
|
83
|
+
onInput(value ?? '');
|
|
84
|
+
}, debounce);
|
|
85
|
+
|
|
86
|
+
return () => clearTimeout(timeout);
|
|
87
|
+
}, [value, debounce, onInput]);
|
|
88
|
+
|
|
89
|
+
const onChangeInput = useCallback((event: h.JSX.TargetedEvent<HTMLInputElement>) => {
|
|
90
|
+
setValue(event.currentTarget.value);
|
|
91
|
+
}, []);
|
|
92
|
+
|
|
93
|
+
return <input {...props} value={value} onInput={onChangeInput} />;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const AnnotationCheckboxes: FunctionComponent<MutationsOverTimeMutationsFilterProps> = ({ value, setFilterValue }) => {
|
|
97
|
+
const mutationAnnotations = useRawMutationAnnotations();
|
|
98
|
+
|
|
99
|
+
if (mutationAnnotations.length === 0) {
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return (
|
|
104
|
+
<>
|
|
105
|
+
<div className='divider mt-0.5 mb-0' />
|
|
106
|
+
<div className='text-sm'>
|
|
107
|
+
<div className='font-bold mb-1'>Filter by annotations</div>
|
|
108
|
+
{mutationAnnotations.map((annotation, index) => (
|
|
109
|
+
<li className='flex flex-row items-center' key={annotation.name}>
|
|
110
|
+
<label>
|
|
111
|
+
<input
|
|
112
|
+
className={'mr-2'}
|
|
113
|
+
type='checkbox'
|
|
114
|
+
id={`item-${index}`}
|
|
115
|
+
checked={value.annotationNameFilter.has(annotation.name)}
|
|
116
|
+
onChange={() => {
|
|
117
|
+
setFilterValue((previousFilter) => {
|
|
118
|
+
const newAnnotationFilter = previousFilter.annotationNameFilter.has(
|
|
119
|
+
annotation.name,
|
|
120
|
+
)
|
|
121
|
+
? [...previousFilter.annotationNameFilter].filter(
|
|
122
|
+
(name) => name !== annotation.name,
|
|
123
|
+
)
|
|
124
|
+
: [...previousFilter.annotationNameFilter, annotation.name];
|
|
125
|
+
return {
|
|
126
|
+
...previousFilter,
|
|
127
|
+
annotationNameFilter: new Set(newAnnotationFilter),
|
|
128
|
+
};
|
|
129
|
+
});
|
|
130
|
+
}}
|
|
131
|
+
/>
|
|
132
|
+
{annotation.name} (<span className='text-red-600'>{annotation.symbol}</span>)
|
|
133
|
+
</label>
|
|
134
|
+
</li>
|
|
135
|
+
))}
|
|
136
|
+
</div>
|
|
137
|
+
</>
|
|
138
|
+
);
|
|
139
|
+
};
|
|
@@ -47,6 +47,44 @@ describe('fetchLineageAutocompleteList', () => {
|
|
|
47
47
|
]);
|
|
48
48
|
});
|
|
49
49
|
|
|
50
|
+
test('should work without aliases', async () => {
|
|
51
|
+
lapisRequestMocks.aggregated(
|
|
52
|
+
{ fields: [lineageField], ...lapisFilter },
|
|
53
|
+
{
|
|
54
|
+
data: [
|
|
55
|
+
{
|
|
56
|
+
[lineageField]: 'A',
|
|
57
|
+
count: 1,
|
|
58
|
+
},
|
|
59
|
+
],
|
|
60
|
+
},
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
lapisRequestMocks.lineageDefinition(
|
|
64
|
+
{
|
|
65
|
+
A: {},
|
|
66
|
+
},
|
|
67
|
+
lineageField,
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
const result = await fetchLineageAutocompleteList({
|
|
71
|
+
lapisUrl: DUMMY_LAPIS_URL,
|
|
72
|
+
lapisField: lineageField,
|
|
73
|
+
lapisFilter,
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
expect(result).to.deep.equal([
|
|
77
|
+
{
|
|
78
|
+
lineage: 'A',
|
|
79
|
+
count: 1,
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
lineage: 'A*',
|
|
83
|
+
count: 1,
|
|
84
|
+
},
|
|
85
|
+
]);
|
|
86
|
+
});
|
|
87
|
+
|
|
50
88
|
test('should add sublineage values', async () => {
|
|
51
89
|
lapisRequestMocks.aggregated(
|
|
52
90
|
{ fields: [lineageField], ...lapisFilter },
|
|
@@ -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',
|