@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
package/dist/util.d.ts
CHANGED
|
@@ -917,7 +917,7 @@ declare global {
|
|
|
917
917
|
|
|
918
918
|
declare global {
|
|
919
919
|
interface HTMLElementTagNameMap {
|
|
920
|
-
'gs-
|
|
920
|
+
'gs-genome-data-viewer': GenomeDataViewerComponent;
|
|
921
921
|
}
|
|
922
922
|
}
|
|
923
923
|
|
|
@@ -925,7 +925,7 @@ declare global {
|
|
|
925
925
|
declare global {
|
|
926
926
|
namespace JSX {
|
|
927
927
|
interface IntrinsicElements {
|
|
928
|
-
'gs-
|
|
928
|
+
'gs-genome-data-viewer': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
|
|
929
929
|
}
|
|
930
930
|
}
|
|
931
931
|
}
|
|
@@ -933,7 +933,7 @@ declare global {
|
|
|
933
933
|
|
|
934
934
|
declare global {
|
|
935
935
|
interface HTMLElementTagNameMap {
|
|
936
|
-
'gs-
|
|
936
|
+
'gs-mutation-comparison-component': MutationComparisonComponent;
|
|
937
937
|
}
|
|
938
938
|
}
|
|
939
939
|
|
|
@@ -941,7 +941,7 @@ declare global {
|
|
|
941
941
|
declare global {
|
|
942
942
|
namespace JSX {
|
|
943
943
|
interface IntrinsicElements {
|
|
944
|
-
'gs-
|
|
944
|
+
'gs-mutation-comparison-component': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
|
|
945
945
|
}
|
|
946
946
|
}
|
|
947
947
|
}
|
|
@@ -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
|
}
|
|
@@ -981,7 +981,7 @@ declare global {
|
|
|
981
981
|
|
|
982
982
|
declare global {
|
|
983
983
|
interface HTMLElementTagNameMap {
|
|
984
|
-
'gs-
|
|
984
|
+
'gs-relative-growth-advantage': RelativeGrowthAdvantageComponent;
|
|
985
985
|
}
|
|
986
986
|
}
|
|
987
987
|
|
|
@@ -989,7 +989,7 @@ declare global {
|
|
|
989
989
|
declare global {
|
|
990
990
|
namespace JSX {
|
|
991
991
|
interface IntrinsicElements {
|
|
992
|
-
'gs-
|
|
992
|
+
'gs-relative-growth-advantage': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
|
|
993
993
|
}
|
|
994
994
|
}
|
|
995
995
|
}
|
|
@@ -997,7 +997,7 @@ declare global {
|
|
|
997
997
|
|
|
998
998
|
declare global {
|
|
999
999
|
interface HTMLElementTagNameMap {
|
|
1000
|
-
'gs-
|
|
1000
|
+
'gs-aggregate': AggregateComponent;
|
|
1001
1001
|
}
|
|
1002
1002
|
}
|
|
1003
1003
|
|
|
@@ -1005,7 +1005,7 @@ declare global {
|
|
|
1005
1005
|
declare global {
|
|
1006
1006
|
namespace JSX {
|
|
1007
1007
|
interface IntrinsicElements {
|
|
1008
|
-
'gs-
|
|
1008
|
+
'gs-aggregate': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
|
|
1009
1009
|
}
|
|
1010
1010
|
}
|
|
1011
1011
|
}
|
|
@@ -1029,7 +1029,7 @@ declare global {
|
|
|
1029
1029
|
|
|
1030
1030
|
declare global {
|
|
1031
1031
|
interface HTMLElementTagNameMap {
|
|
1032
|
-
'gs-
|
|
1032
|
+
'gs-mutations-over-time': MutationsOverTimeComponent;
|
|
1033
1033
|
}
|
|
1034
1034
|
}
|
|
1035
1035
|
|
|
@@ -1037,7 +1037,7 @@ declare global {
|
|
|
1037
1037
|
declare global {
|
|
1038
1038
|
namespace JSX {
|
|
1039
1039
|
interface IntrinsicElements {
|
|
1040
|
-
'gs-
|
|
1040
|
+
'gs-mutations-over-time': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
|
|
1041
1041
|
}
|
|
1042
1042
|
}
|
|
1043
1043
|
}
|
|
@@ -1045,7 +1045,7 @@ declare global {
|
|
|
1045
1045
|
|
|
1046
1046
|
declare global {
|
|
1047
1047
|
interface HTMLElementTagNameMap {
|
|
1048
|
-
'gs-
|
|
1048
|
+
'gs-sequences-by-location': SequencesByLocationComponent;
|
|
1049
1049
|
}
|
|
1050
1050
|
}
|
|
1051
1051
|
|
|
@@ -1053,7 +1053,7 @@ declare global {
|
|
|
1053
1053
|
declare global {
|
|
1054
1054
|
namespace JSX {
|
|
1055
1055
|
interface IntrinsicElements {
|
|
1056
|
-
'gs-
|
|
1056
|
+
'gs-sequences-by-location': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
|
|
1057
1057
|
}
|
|
1058
1058
|
}
|
|
1059
1059
|
}
|
|
@@ -1077,11 +1077,7 @@ declare global {
|
|
|
1077
1077
|
|
|
1078
1078
|
declare global {
|
|
1079
1079
|
interface HTMLElementTagNameMap {
|
|
1080
|
-
'gs-
|
|
1081
|
-
}
|
|
1082
|
-
interface HTMLElementEventMap {
|
|
1083
|
-
[gsEventNames.dateRangeFilterChanged]: CustomEvent<Record<string, string>>;
|
|
1084
|
-
[gsEventNames.dateRangeOptionChanged]: DateRangeOptionChangedEvent;
|
|
1080
|
+
'gs-wastewater-mutations-over-time': WastewaterMutationsOverTimeComponent;
|
|
1085
1081
|
}
|
|
1086
1082
|
}
|
|
1087
1083
|
|
|
@@ -1089,7 +1085,7 @@ declare global {
|
|
|
1089
1085
|
declare global {
|
|
1090
1086
|
namespace JSX {
|
|
1091
1087
|
interface IntrinsicElements {
|
|
1092
|
-
'gs-
|
|
1088
|
+
'gs-wastewater-mutations-over-time': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
|
|
1093
1089
|
}
|
|
1094
1090
|
}
|
|
1095
1091
|
}
|
|
@@ -1135,10 +1131,11 @@ declare global {
|
|
|
1135
1131
|
|
|
1136
1132
|
declare global {
|
|
1137
1133
|
interface HTMLElementTagNameMap {
|
|
1138
|
-
'gs-
|
|
1134
|
+
'gs-date-range-filter': DateRangeFilterComponent;
|
|
1139
1135
|
}
|
|
1140
1136
|
interface HTMLElementEventMap {
|
|
1141
|
-
[gsEventNames.
|
|
1137
|
+
[gsEventNames.dateRangeFilterChanged]: CustomEvent<Record<string, string>>;
|
|
1138
|
+
[gsEventNames.dateRangeOptionChanged]: DateRangeOptionChangedEvent;
|
|
1142
1139
|
}
|
|
1143
1140
|
}
|
|
1144
1141
|
|
|
@@ -1146,7 +1143,7 @@ declare global {
|
|
|
1146
1143
|
declare global {
|
|
1147
1144
|
namespace JSX {
|
|
1148
1145
|
interface IntrinsicElements {
|
|
1149
|
-
'gs-
|
|
1146
|
+
'gs-date-range-filter': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
|
|
1150
1147
|
}
|
|
1151
1148
|
}
|
|
1152
1149
|
}
|
|
@@ -1173,11 +1170,10 @@ declare global {
|
|
|
1173
1170
|
|
|
1174
1171
|
declare global {
|
|
1175
1172
|
interface HTMLElementTagNameMap {
|
|
1176
|
-
'gs-
|
|
1173
|
+
'gs-lineage-filter': LineageFilterComponent;
|
|
1177
1174
|
}
|
|
1178
1175
|
interface HTMLElementEventMap {
|
|
1179
|
-
[gsEventNames.
|
|
1180
|
-
[gsEventNames.numberRangeValueChanged]: NumberRangeValueChangedEvent;
|
|
1176
|
+
[gsEventNames.lineageFilterChanged]: LineageFilterChangedEvent;
|
|
1181
1177
|
}
|
|
1182
1178
|
}
|
|
1183
1179
|
|
|
@@ -1185,7 +1181,7 @@ declare global {
|
|
|
1185
1181
|
declare global {
|
|
1186
1182
|
namespace JSX {
|
|
1187
1183
|
interface IntrinsicElements {
|
|
1188
|
-
'gs-
|
|
1184
|
+
'gs-lineage-filter': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
|
|
1189
1185
|
}
|
|
1190
1186
|
}
|
|
1191
1187
|
}
|
|
@@ -1193,7 +1189,11 @@ declare global {
|
|
|
1193
1189
|
|
|
1194
1190
|
declare global {
|
|
1195
1191
|
interface HTMLElementTagNameMap {
|
|
1196
|
-
'gs-
|
|
1192
|
+
'gs-number-range-filter': NumberRangeFilterComponent;
|
|
1193
|
+
}
|
|
1194
|
+
interface HTMLElementEventMap {
|
|
1195
|
+
[gsEventNames.numberRangeFilterChanged]: NumberRangeFilterChangedEvent;
|
|
1196
|
+
[gsEventNames.numberRangeValueChanged]: NumberRangeValueChangedEvent;
|
|
1197
1197
|
}
|
|
1198
1198
|
}
|
|
1199
1199
|
|
|
@@ -1201,7 +1201,7 @@ declare global {
|
|
|
1201
1201
|
declare global {
|
|
1202
1202
|
namespace JSX {
|
|
1203
1203
|
interface IntrinsicElements {
|
|
1204
|
-
'gs-
|
|
1204
|
+
'gs-number-range-filter': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
|
|
1205
1205
|
}
|
|
1206
1206
|
}
|
|
1207
1207
|
}
|
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
|
+
};
|