@genspectrum/dashboard-components 0.13.6 → 0.14.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 +41 -79
- package/dist/{LineageFilterChangedEvent-GedKNGFI.js → LineageFilterChangedEvent-C9dXOxt6.js} +11 -3
- package/dist/LineageFilterChangedEvent-C9dXOxt6.js.map +1 -0
- package/dist/components.d.ts +63 -71
- package/dist/components.js +440 -312
- package/dist/components.js.map +1 -1
- package/dist/style.css +20 -5
- package/dist/util.d.ts +61 -51
- package/dist/util.js +1 -1
- package/package.json +1 -1
- package/src/preact/LapisUrlContext.ts +14 -1
- package/src/preact/aggregatedData/aggregate.stories.tsx +3 -3
- package/src/preact/aggregatedData/aggregate.tsx +3 -4
- package/src/preact/components/tabs.tsx +3 -5
- package/src/preact/dateRangeSelector/computeInitialValues.spec.ts +34 -20
- package/src/preact/dateRangeSelector/computeInitialValues.ts +25 -21
- package/src/preact/dateRangeSelector/date-range-selector.stories.tsx +104 -40
- package/src/preact/dateRangeSelector/date-range-selector.tsx +29 -20
- package/src/preact/dateRangeSelector/dateRangeOption.ts +11 -1
- package/src/preact/lineageFilter/lineage-filter.stories.tsx +3 -3
- package/src/preact/lineageFilter/lineage-filter.tsx +3 -4
- package/src/preact/locationFilter/location-filter.stories.tsx +3 -3
- package/src/preact/locationFilter/location-filter.tsx +4 -4
- package/src/preact/map/sequences-by-location.stories.tsx +3 -3
- package/src/preact/map/sequences-by-location.tsx +3 -4
- package/src/preact/mutationComparison/mutation-comparison.stories.tsx +3 -3
- package/src/preact/mutationComparison/mutation-comparison.tsx +4 -4
- package/src/preact/mutationFilter/ExampleMutation.tsx +68 -0
- package/src/preact/mutationFilter/mutation-filter-info.tsx +179 -112
- package/src/preact/mutationFilter/mutation-filter.stories.tsx +5 -5
- package/src/preact/mutationFilter/mutation-filter.tsx +10 -5
- package/src/preact/mutations/mutations.stories.tsx +3 -3
- package/src/preact/mutations/mutations.tsx +4 -4
- package/src/preact/mutationsOverTime/mutations-over-time-grid.tsx +26 -4
- package/src/preact/mutationsOverTime/mutations-over-time.stories.tsx +3 -3
- package/src/preact/mutationsOverTime/mutations-over-time.tsx +4 -4
- package/src/preact/numberSequencesOverTime/number-sequences-over-time.stories.tsx +3 -3
- package/src/preact/numberSequencesOverTime/number-sequences-over-time.tsx +4 -4
- package/src/preact/prevalenceOverTime/prevalence-over-time.stories.tsx +3 -3
- package/src/preact/prevalenceOverTime/prevalence-over-time.tsx +4 -4
- package/src/preact/relativeGrowthAdvantage/relative-growth-advantage.stories.tsx +3 -3
- package/src/preact/relativeGrowthAdvantage/relative-growth-advantage.tsx +4 -4
- package/src/preact/statistic/statistics.stories.tsx +3 -3
- package/src/preact/statistic/statistics.tsx +2 -3
- package/src/preact/textInput/text-input.stories.tsx +3 -3
- package/src/preact/textInput/text-input.tsx +3 -4
- package/src/preact/wastewater/mutationsOverTime/wastewater-mutations-over-time.stories.tsx +5 -9
- package/src/preact/wastewater/mutationsOverTime/wastewater-mutations-over-time.tsx +5 -5
- package/src/web-components/PreactLitAdapter.tsx +3 -3
- package/src/web-components/{app.stories.ts → gs-app.stories.ts} +1 -1
- package/src/web-components/{app.ts → gs-app.ts} +5 -3
- package/src/web-components/index.ts +1 -1
- package/src/web-components/input/gs-date-range-selector.stories.ts +6 -13
- package/src/web-components/input/gs-date-range-selector.tsx +15 -38
- package/src/web-components/input/gs-lineage-filter.stories.ts +1 -1
- package/src/web-components/input/gs-location-filter.stories.ts +1 -1
- package/src/web-components/input/gs-mutation-filter.stories.ts +2 -2
- package/src/web-components/input/gs-text-input.stories.ts +1 -1
- package/src/web-components/visualization/gs-aggregate.stories.ts +1 -1
- package/src/web-components/visualization/gs-mutation-comparison.stories.ts +1 -1
- package/src/web-components/visualization/gs-mutations-over-time.stories.ts +1 -1
- package/src/web-components/visualization/gs-mutations.stories.ts +1 -1
- package/src/web-components/visualization/gs-number-sequences-over-time.stories.ts +1 -1
- package/src/web-components/visualization/gs-prevalence-over-time.stories.ts +1 -1
- package/src/web-components/visualization/gs-relative-growth-advantage.stories.ts +1 -1
- package/src/web-components/visualization/gs-sequences-by-location.stories.ts +1 -1
- package/src/web-components/visualization/gs-statistics.stories.ts +1 -1
- package/src/web-components/wastewaterVisualization/gs-wastewater-mutations-over-time.stories.ts +4 -1
- package/src/web-components/wastewaterVisualization/gs-wastewater-mutations-over-time.tsx +6 -2
- package/standalone-bundle/dashboard-components.js +7016 -6951
- package/standalone-bundle/dashboard-components.js.map +1 -1
- package/standalone-bundle/style.css +1 -1
- package/dist/LineageFilterChangedEvent-GedKNGFI.js.map +0 -1
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { useContext } from 'preact/hooks';
|
|
2
|
+
import type { FC } from 'react';
|
|
3
|
+
|
|
4
|
+
import { type ReferenceGenome } from '../../lapisApi/ReferenceGenome';
|
|
5
|
+
import type { SequenceType } from '../../types';
|
|
6
|
+
import { ReferenceGenomeContext } from '../ReferenceGenomeContext';
|
|
7
|
+
|
|
8
|
+
type ExampleMutationProps = {
|
|
9
|
+
sequenceType: SequenceType;
|
|
10
|
+
mutationType: 'substitution' | 'insertion';
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export const ExampleMutation: FC<ExampleMutationProps> = ({ sequenceType, mutationType }) => {
|
|
14
|
+
const referenceGenome = useContext(ReferenceGenomeContext);
|
|
15
|
+
|
|
16
|
+
return <b>{getExampleMutation(referenceGenome, sequenceType, mutationType)}</b>;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export function getExampleMutation(
|
|
20
|
+
referenceGenome: ReferenceGenome,
|
|
21
|
+
sequenceType: SequenceType,
|
|
22
|
+
mutationType: 'substitution' | 'insertion',
|
|
23
|
+
) {
|
|
24
|
+
switch (sequenceType) {
|
|
25
|
+
case 'amino acid': {
|
|
26
|
+
if (referenceGenome.genes.length === 0) {
|
|
27
|
+
return '';
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const firstGene = referenceGenome.genes[0].name;
|
|
31
|
+
|
|
32
|
+
switch (mutationType) {
|
|
33
|
+
case 'substitution':
|
|
34
|
+
return `${firstGene}:57Q`;
|
|
35
|
+
case 'insertion':
|
|
36
|
+
return `ins_${firstGene}:31:N`;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
// Issue of linter https://github.com/typescript-eslint/typescript-eslint/issues/3455
|
|
40
|
+
// eslint-disable-next-line no-fallthrough
|
|
41
|
+
case 'nucleotide': {
|
|
42
|
+
switch (referenceGenome.nucleotideSequences.length) {
|
|
43
|
+
case 0: {
|
|
44
|
+
return '';
|
|
45
|
+
}
|
|
46
|
+
case 1: {
|
|
47
|
+
switch (mutationType) {
|
|
48
|
+
case 'substitution':
|
|
49
|
+
return '23T';
|
|
50
|
+
case 'insertion':
|
|
51
|
+
return 'ins_1046:A';
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
// Issue of linter https://github.com/typescript-eslint/typescript-eslint/issues/3455
|
|
55
|
+
// eslint-disable-next-line no-fallthrough
|
|
56
|
+
default: {
|
|
57
|
+
const firstSegment = referenceGenome.nucleotideSequences[0].name;
|
|
58
|
+
switch (mutationType) {
|
|
59
|
+
case 'substitution':
|
|
60
|
+
return `${firstSegment}:23T`;
|
|
61
|
+
case 'insertion':
|
|
62
|
+
return `ins_${firstSegment}:10462:A`;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
@@ -1,58 +1,140 @@
|
|
|
1
1
|
import { useContext } from 'preact/hooks';
|
|
2
|
-
import { type FC } from 'react';
|
|
3
2
|
|
|
4
|
-
import { isSingleSegmented } from '../../lapisApi/ReferenceGenome';
|
|
5
|
-
import { type SequenceType } from '../../types';
|
|
3
|
+
import { isSingleSegmented, type ReferenceGenome } from '../../lapisApi/ReferenceGenome';
|
|
6
4
|
import { ReferenceGenomeContext } from '../ReferenceGenomeContext';
|
|
5
|
+
import { ExampleMutation } from './ExampleMutation';
|
|
7
6
|
import Info, { InfoHeadline1, InfoHeadline2, InfoParagraph } from '../components/info';
|
|
8
7
|
|
|
9
8
|
export const MutationFilterInfo = () => {
|
|
10
|
-
const referenceGenome = useContext(ReferenceGenomeContext);
|
|
11
|
-
|
|
12
|
-
const firstGene = referenceGenome.genes[0].name;
|
|
13
9
|
return (
|
|
14
10
|
<Info>
|
|
15
11
|
<InfoHeadline1> Mutation Filter</InfoHeadline1>
|
|
16
12
|
<InfoParagraph>This component allows you to filter for mutations at specific positions.</InfoParagraph>
|
|
17
13
|
|
|
14
|
+
<QuickStart />
|
|
15
|
+
<NucleotideMutationsInfo />
|
|
16
|
+
<AminoAcidMutationsInfo />
|
|
17
|
+
<InsertionWildcards />
|
|
18
|
+
<MultipleMutations />
|
|
19
|
+
<AnyMutation />
|
|
20
|
+
<NoMutation />
|
|
21
|
+
</Info>
|
|
22
|
+
);
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const QuickStart = () => {
|
|
26
|
+
const referenceGenome = useContext(ReferenceGenomeContext);
|
|
27
|
+
return (
|
|
28
|
+
<>
|
|
18
29
|
<InfoHeadline2>Quickstart</InfoHeadline2>
|
|
19
30
|
<InfoParagraph>
|
|
20
31
|
<ul className='list-disc list-inside'>
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
32
|
+
{referenceGenome.nucleotideSequences.length > 0 && (
|
|
33
|
+
<li>
|
|
34
|
+
Filter for nucleotide mutations:{' '}
|
|
35
|
+
<ExampleMutation mutationType='substitution' sequenceType='nucleotide' />
|
|
36
|
+
</li>
|
|
37
|
+
)}
|
|
38
|
+
{referenceGenome.genes.length > 0 && (
|
|
39
|
+
<li>
|
|
40
|
+
Filter for amino acid mutations:{' '}
|
|
41
|
+
<ExampleMutation mutationType='substitution' sequenceType='amino acid' />
|
|
42
|
+
</li>
|
|
43
|
+
)}
|
|
44
|
+
{referenceGenome.nucleotideSequences.length > 0 && (
|
|
45
|
+
<li>
|
|
46
|
+
Filter for nucleotide insertions:{' '}
|
|
47
|
+
<ExampleMutation mutationType='insertion' sequenceType='nucleotide' />
|
|
48
|
+
</li>
|
|
49
|
+
)}
|
|
50
|
+
{referenceGenome.genes.length > 0 && (
|
|
51
|
+
<li>
|
|
52
|
+
Filter for amino acid insertions:{' '}
|
|
53
|
+
<ExampleMutation mutationType='insertion' sequenceType='amino acid' />
|
|
54
|
+
</li>
|
|
55
|
+
)}
|
|
37
56
|
</ul>
|
|
38
57
|
</InfoParagraph>
|
|
39
|
-
{
|
|
58
|
+
{referenceGenome.nucleotideSequences.length > 1 && (
|
|
40
59
|
<InfoParagraph>
|
|
41
60
|
This organism has the following segments:{' '}
|
|
42
61
|
{referenceGenome.nucleotideSequences.map((gene) => gene.name).join(', ')}.
|
|
43
62
|
</InfoParagraph>
|
|
44
63
|
)}
|
|
64
|
+
{referenceGenome.nucleotideSequences.length === 0 && (
|
|
65
|
+
<InfoParagraph>This organism doesn't support nucleotide sequences.</InfoParagraph>
|
|
66
|
+
)}
|
|
67
|
+
{referenceGenome.genes.length !== 0 ? (
|
|
68
|
+
<InfoParagraph>
|
|
69
|
+
This organism has the following genes: {referenceGenome.genes.map((gene) => gene.name).join(', ')}.
|
|
70
|
+
</InfoParagraph>
|
|
71
|
+
) : (
|
|
72
|
+
<InfoParagraph>This organism doesn't support amino acid sequences.</InfoParagraph>
|
|
73
|
+
)}
|
|
74
|
+
</>
|
|
75
|
+
);
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
const NucleotideMutationsInfo = () => {
|
|
79
|
+
const referenceGenome = useContext(ReferenceGenomeContext);
|
|
80
|
+
|
|
81
|
+
if (referenceGenome.nucleotideSequences.length === 0) {
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (isSingleSegmented(referenceGenome)) {
|
|
86
|
+
return (
|
|
87
|
+
<>
|
|
88
|
+
<InfoHeadline2>Nucleotide Mutations and Insertions</InfoHeadline2>
|
|
89
|
+
<InfoParagraph>
|
|
90
|
+
This organism is single-segmented. Thus, nucleotide mutations have the format{' '}
|
|
91
|
+
<b><position><base></b> or <b><base_ref><position><base></b>. The{' '}
|
|
92
|
+
<b><base_ref></b> is the reference base at the position. It is optional. A <b><base></b>{' '}
|
|
93
|
+
can be one of the four nucleotides <b>A</b>, <b>T</b>, <b>C</b>, and <b>G</b>. It can also be{' '}
|
|
94
|
+
<b>-</b> for deletion and <b>N</b> for unknown. For example if the reference sequence is <b>A</b> at
|
|
95
|
+
position <b>23</b> both: <b>23T</b> and <b>A23T</b> will yield the same results.
|
|
96
|
+
</InfoParagraph>
|
|
97
|
+
<InfoParagraph>
|
|
98
|
+
Insertions can be searched for in the same manner, they just need to have <b>ins_</b> appended to
|
|
99
|
+
the start of the mutation. Example: <b>ins_1046:A</b> would filter for sequences with an insertion
|
|
100
|
+
of A between the positions 1046 and 1047 in the nucleotide sequence.
|
|
101
|
+
</InfoParagraph>
|
|
102
|
+
</>
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const firstSegment = referenceGenome.nucleotideSequences[0].name;
|
|
107
|
+
return (
|
|
108
|
+
<>
|
|
109
|
+
<InfoHeadline2>Nucleotide Mutations and Insertions</InfoHeadline2>
|
|
45
110
|
<InfoParagraph>
|
|
46
|
-
This organism
|
|
111
|
+
This organism is multi-segmented. Thus, nucleotide mutations have the format{' '}
|
|
112
|
+
<b><segment>:<position><base></b> or{' '}
|
|
113
|
+
<b><segment>:<base_ref><position><base></b>. <b><base_ref></b> is the
|
|
114
|
+
reference base at the position. It is optional. A <b><base></b> can be one of the four nucleotides{' '}
|
|
115
|
+
<b>A</b>, <b>T</b>, <b>C</b>, and <b>G</b>. It can also be <b>-</b> for deletion and <b>N</b> for
|
|
116
|
+
unknown. For example if the reference sequence is <b>A</b> at position <b>23</b> both:{' '}
|
|
117
|
+
<b>{firstSegment}:23T</b> and <b>{firstSegment}:A23T</b> will yield the same results.
|
|
47
118
|
</InfoParagraph>
|
|
119
|
+
<InfoParagraph>
|
|
120
|
+
Insertions can be searched for in the same manner, they just need to have <b>ins_</b> appended to the
|
|
121
|
+
start of the mutation. Example: <ExampleMutation mutationType='insertion' sequenceType='nucleotide' />.
|
|
122
|
+
</InfoParagraph>
|
|
123
|
+
</>
|
|
124
|
+
);
|
|
125
|
+
};
|
|
48
126
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
127
|
+
const AminoAcidMutationsInfo = () => {
|
|
128
|
+
const referenceGenome = useContext(ReferenceGenomeContext);
|
|
129
|
+
|
|
130
|
+
if (referenceGenome.genes.length === 0) {
|
|
131
|
+
return null;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const firstGene = referenceGenome.genes[0].name;
|
|
55
135
|
|
|
136
|
+
return (
|
|
137
|
+
<>
|
|
56
138
|
<InfoHeadline2>Amino Acid Mutations and Insertions</InfoHeadline2>
|
|
57
139
|
<InfoParagraph>
|
|
58
140
|
An amino acid mutation has the format <b><gene>:<position><base></b> or
|
|
@@ -65,116 +147,101 @@ export const MutationFilterInfo = () => {
|
|
|
65
147
|
start of the mutation. Example: <b>ins_{firstGene}:31:N</b> would filter for sequences with an insertion
|
|
66
148
|
of N between positions 31 and 32 in the gene {firstGene}.
|
|
67
149
|
</InfoParagraph>
|
|
150
|
+
</>
|
|
151
|
+
);
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
const InsertionWildcards = () => {
|
|
155
|
+
const referenceGenome = useContext(ReferenceGenomeContext);
|
|
68
156
|
|
|
157
|
+
if (referenceGenome.nucleotideSequences.length === 0 && referenceGenome.genes.length === 0) {
|
|
158
|
+
return null;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return (
|
|
162
|
+
<>
|
|
69
163
|
<InfoHeadline2>Insertion Wildcards</InfoHeadline2>
|
|
70
164
|
<InfoParagraph>
|
|
71
|
-
This component supports insertion queries that contain wildcards <b>?</b>. For
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
165
|
+
This component supports nucleotide and amino acid insertion queries that contain wildcards <b>?</b>. For
|
|
166
|
+
example{' '}
|
|
167
|
+
<b>
|
|
168
|
+
ins_{exampleSegmentString(referenceGenome)}214:?{exampleWildcardInsertion(referenceGenome)}?
|
|
169
|
+
</b>{' '}
|
|
170
|
+
will match all cases where segment <b>{exampleSegment(referenceGenome)}</b> has an insertion of{' '}
|
|
171
|
+
<b>{exampleWildcardInsertion(referenceGenome)}</b> between the positions <b>214</b> and <b>215</b> but
|
|
172
|
+
also an insertion of other amino acids which include the <b>EP</b>, e.g. the insertion{' '}
|
|
173
|
+
<b>{exampleWildcardInsertion(referenceGenome)}T</b> will be matched.
|
|
75
174
|
</InfoParagraph>
|
|
76
175
|
<InfoParagraph>
|
|
77
176
|
You can also use wildcards to match any insertion at a given position. For example{' '}
|
|
78
|
-
<b>ins_{
|
|
177
|
+
<b>ins_{exampleSegmentString(referenceGenome)}214:?</b> match any (but at least one) insertion between
|
|
178
|
+
the positions 214 and 215.
|
|
79
179
|
</InfoParagraph>
|
|
180
|
+
</>
|
|
181
|
+
);
|
|
182
|
+
};
|
|
80
183
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
184
|
+
const exampleSegmentString = (referenceGenome: ReferenceGenome) => {
|
|
185
|
+
const segment = exampleSegment(referenceGenome);
|
|
186
|
+
if (segment === '') {
|
|
187
|
+
return '';
|
|
188
|
+
}
|
|
189
|
+
return `${segment}:`;
|
|
190
|
+
};
|
|
85
191
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
192
|
+
const exampleSegment = (referenceGenome: ReferenceGenome) => {
|
|
193
|
+
if (referenceGenome.genes.length > 0) {
|
|
194
|
+
return `${referenceGenome.genes[0].name}`;
|
|
195
|
+
}
|
|
196
|
+
if (referenceGenome.nucleotideSequences.length > 1) {
|
|
197
|
+
return `${referenceGenome.nucleotideSequences[0].name}`;
|
|
198
|
+
}
|
|
199
|
+
return '';
|
|
200
|
+
};
|
|
91
201
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
202
|
+
const exampleWildcardInsertion = (referenceGenome: ReferenceGenome) => {
|
|
203
|
+
if (referenceGenome.genes.length > 0) {
|
|
204
|
+
return 'EP';
|
|
205
|
+
}
|
|
206
|
+
if (referenceGenome.nucleotideSequences.length > 0) {
|
|
207
|
+
return 'CG';
|
|
208
|
+
}
|
|
209
|
+
return '';
|
|
99
210
|
};
|
|
100
211
|
|
|
101
|
-
const
|
|
212
|
+
const MultipleMutations = () => {
|
|
102
213
|
return (
|
|
103
214
|
<>
|
|
215
|
+
<InfoHeadline2>Multiple Mutations</InfoHeadline2>
|
|
104
216
|
<InfoParagraph>
|
|
105
|
-
|
|
106
|
-
<b><position><base></b> or <b><base_ref><position><base></b>. The{' '}
|
|
107
|
-
<b><base_ref></b> is the reference base at the position. It is optional. A <b><base></b> can
|
|
108
|
-
be one of the four nucleotides <b>A</b>, <b>T</b>, <b>C</b>, and <b>G</b>. It can also be <b>-</b> for
|
|
109
|
-
deletion and <b>N</b> for unknown. For example if the reference sequence is <b>A</b> at position{' '}
|
|
110
|
-
<b>23</b> both: <b>23T</b> and <b>A23T</b> will yield the same results.
|
|
111
|
-
</InfoParagraph>
|
|
112
|
-
<InfoParagraph>
|
|
113
|
-
Insertions can be searched for in the same manner, they just need to have <b>ins_</b> appended to the
|
|
114
|
-
start of the mutation. Example: <b>ins_1046:A</b> would filter for sequences with an insertion of A
|
|
115
|
-
between the positions 1046 and 1047 in the nucleotide sequence.
|
|
217
|
+
Multiple mutation filters can be provided by adding one mutation after the other.
|
|
116
218
|
</InfoParagraph>
|
|
117
219
|
</>
|
|
118
220
|
);
|
|
119
221
|
};
|
|
120
222
|
|
|
121
|
-
const
|
|
223
|
+
const AnyMutation = () => {
|
|
122
224
|
const referenceGenome = useContext(ReferenceGenomeContext);
|
|
123
225
|
|
|
124
|
-
const firstSegment = referenceGenome.nucleotideSequences[0].name;
|
|
125
|
-
|
|
126
226
|
return (
|
|
127
227
|
<>
|
|
228
|
+
<InfoHeadline2>Any Mutation</InfoHeadline2>
|
|
128
229
|
<InfoParagraph>
|
|
129
|
-
|
|
130
|
-
<b
|
|
131
|
-
<b><segment>:<base_ref><position><base></b>. <b><base_ref></b> is the
|
|
132
|
-
reference base at the position. It is optional. A <b><base></b> can be one of the four nucleotides{' '}
|
|
133
|
-
<b>A</b>, <b>T</b>, <b>C</b>, and <b>G</b>. It can also be <b>-</b> for deletion and <b>N</b> for
|
|
134
|
-
unknown. For example if the reference sequence is <b>A</b> at position <b>23</b> both:{' '}
|
|
135
|
-
<b>{firstSegment}:23T</b> and <b>{firstSegment}:A23T</b> will yield the same results.
|
|
136
|
-
</InfoParagraph>
|
|
137
|
-
<InfoParagraph>
|
|
138
|
-
Insertions can be searched for in the same manner, they just need to have <b>ins_</b> appended to the
|
|
139
|
-
start of the mutation. Example: <ExampleMutation mutationType='insertion' sequenceType='nucleotide' />.
|
|
230
|
+
To filter for any mutation at a given position you can omit the <b><base></b>. Example:{' '}
|
|
231
|
+
<b>{exampleSegmentString(referenceGenome)}20</b>.
|
|
140
232
|
</InfoParagraph>
|
|
141
233
|
</>
|
|
142
234
|
);
|
|
143
235
|
};
|
|
144
236
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
if (sequenceType === 'amino acid') {
|
|
157
|
-
switch (mutationType) {
|
|
158
|
-
case 'substitution':
|
|
159
|
-
return <b>{firstGene}:57Q</b>;
|
|
160
|
-
case 'insertion':
|
|
161
|
-
return <b>ins_{firstGene}:31:N</b>;
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
if (isSingleSegmented(referenceGenome)) {
|
|
166
|
-
switch (mutationType) {
|
|
167
|
-
case 'substitution':
|
|
168
|
-
return <b>23T</b>;
|
|
169
|
-
case 'insertion':
|
|
170
|
-
return <b>ins_1046:A</b>;
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
switch (mutationType) {
|
|
175
|
-
case 'substitution':
|
|
176
|
-
return <b>{firstSegment}:23T</b>;
|
|
177
|
-
case 'insertion':
|
|
178
|
-
return <b>ins_{firstSegment}:10462:A</b>;
|
|
179
|
-
}
|
|
237
|
+
const NoMutation = () => {
|
|
238
|
+
return (
|
|
239
|
+
<>
|
|
240
|
+
<InfoHeadline2>No Mutation</InfoHeadline2>
|
|
241
|
+
<InfoParagraph>
|
|
242
|
+
You can write a <b>.</b> for the <b><base></b> to filter for sequences for which it is confirmed
|
|
243
|
+
that no mutation occurred, i.e. has the same base as the reference genome at the specified position.
|
|
244
|
+
</InfoParagraph>
|
|
245
|
+
</>
|
|
246
|
+
);
|
|
180
247
|
};
|
|
@@ -6,7 +6,7 @@ import { MutationFilter, type MutationFilterProps } from './mutation-filter';
|
|
|
6
6
|
import { previewHandles } from '../../../.storybook/preview';
|
|
7
7
|
import { LAPIS_URL } from '../../constants';
|
|
8
8
|
import referenceGenome from '../../lapisApi/__mockData__/referenceGenome.json';
|
|
9
|
-
import {
|
|
9
|
+
import { LapisUrlContextProvider } from '../LapisUrlContext';
|
|
10
10
|
import { ReferenceGenomeContext } from '../ReferenceGenomeContext';
|
|
11
11
|
|
|
12
12
|
const meta: Meta<MutationFilterProps> = {
|
|
@@ -32,11 +32,11 @@ export default meta;
|
|
|
32
32
|
|
|
33
33
|
export const Default: StoryObj<MutationFilterProps> = {
|
|
34
34
|
render: (args) => (
|
|
35
|
-
<
|
|
35
|
+
<LapisUrlContextProvider value={LAPIS_URL}>
|
|
36
36
|
<ReferenceGenomeContext.Provider value={referenceGenome}>
|
|
37
37
|
<MutationFilter width={args.width} initialValue={args.initialValue} />
|
|
38
38
|
</ReferenceGenomeContext.Provider>
|
|
39
|
-
</
|
|
39
|
+
</LapisUrlContextProvider>
|
|
40
40
|
),
|
|
41
41
|
args: {
|
|
42
42
|
width: '100%',
|
|
@@ -184,11 +184,11 @@ export const FiresFilterChangedEvents: StoryObj<MutationFilterProps> = {
|
|
|
184
184
|
|
|
185
185
|
export const WithInitialValue: StoryObj<MutationFilterProps> = {
|
|
186
186
|
render: (args) => (
|
|
187
|
-
<
|
|
187
|
+
<LapisUrlContextProvider value={LAPIS_URL}>
|
|
188
188
|
<ReferenceGenomeContext.Provider value={referenceGenome}>
|
|
189
189
|
<MutationFilter initialValue={args.initialValue} width={args.width} />
|
|
190
190
|
</ReferenceGenomeContext.Provider>
|
|
191
|
-
</
|
|
191
|
+
</LapisUrlContextProvider>
|
|
192
192
|
),
|
|
193
193
|
args: {
|
|
194
194
|
initialValue: {
|
|
@@ -2,6 +2,7 @@ import { type FunctionComponent } from 'preact';
|
|
|
2
2
|
import { useContext, useRef, useState } from 'preact/hooks';
|
|
3
3
|
import z from 'zod';
|
|
4
4
|
|
|
5
|
+
import { getExampleMutation } from './ExampleMutation';
|
|
5
6
|
import { MutationFilterInfo } from './mutation-filter-info';
|
|
6
7
|
import { parseAndValidateMutation, type ParsedMutationFilter } from './parseAndValidateMutation';
|
|
7
8
|
import { type ReferenceGenome } from '../../lapisApi/ReferenceGenome';
|
|
@@ -208,7 +209,6 @@ const MutationFilterSelector: FunctionComponent<{
|
|
|
208
209
|
};
|
|
209
210
|
|
|
210
211
|
const handleBlur = (event: FocusEvent) => {
|
|
211
|
-
// Check if click was inside the selector
|
|
212
212
|
if (!selectorRef.current?.contains(event.relatedTarget as Node)) {
|
|
213
213
|
setOption(null);
|
|
214
214
|
}
|
|
@@ -242,11 +242,16 @@ const MutationFilterSelector: FunctionComponent<{
|
|
|
242
242
|
};
|
|
243
243
|
|
|
244
244
|
function getPlaceholder(referenceGenome: ReferenceGenome) {
|
|
245
|
-
const
|
|
246
|
-
|
|
247
|
-
const
|
|
245
|
+
const nucleotideSubstitution = getExampleMutation(referenceGenome, 'nucleotide', 'substitution');
|
|
246
|
+
const nucleotideInsertion = getExampleMutation(referenceGenome, 'nucleotide', 'insertion');
|
|
247
|
+
const aminoAcidSubstitution = getExampleMutation(referenceGenome, 'amino acid', 'substitution');
|
|
248
|
+
const aminoAcidInsertion = getExampleMutation(referenceGenome, 'amino acid', 'insertion');
|
|
248
249
|
|
|
249
|
-
|
|
250
|
+
const exampleMutations = [nucleotideSubstitution, nucleotideInsertion, aminoAcidSubstitution, aminoAcidInsertion]
|
|
251
|
+
.filter((example) => example !== '')
|
|
252
|
+
.join(', ');
|
|
253
|
+
|
|
254
|
+
return `Enter a mutation (e.g. ${exampleMutations})`;
|
|
250
255
|
}
|
|
251
256
|
|
|
252
257
|
const backgroundColor: { [key in keyof SelectedFilters]: string } = {
|
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
import referenceGenome from '../../lapisApi/__mockData__/referenceGenome.json';
|
|
14
14
|
import baselineNucleotideMutations from '../../preact/mutations/__mockData__/baselineNucleotideMutations.json';
|
|
15
15
|
import overallVariantCount from '../../preact/mutations/__mockData__/overallVariantCount.json';
|
|
16
|
-
import {
|
|
16
|
+
import { LapisUrlContextProvider } from '../LapisUrlContext';
|
|
17
17
|
import { ReferenceGenomeContext } from '../ReferenceGenomeContext';
|
|
18
18
|
|
|
19
19
|
const meta: Meta<MutationsProps> = {
|
|
@@ -39,11 +39,11 @@ export default meta;
|
|
|
39
39
|
|
|
40
40
|
const Template = {
|
|
41
41
|
render: (args: MutationsProps) => (
|
|
42
|
-
<
|
|
42
|
+
<LapisUrlContextProvider value={LAPIS_URL}>
|
|
43
43
|
<ReferenceGenomeContext.Provider value={referenceGenome}>
|
|
44
44
|
<Mutations {...args} />
|
|
45
45
|
</ReferenceGenomeContext.Provider>
|
|
46
|
-
</
|
|
46
|
+
</LapisUrlContextProvider>
|
|
47
47
|
),
|
|
48
48
|
};
|
|
49
49
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type FunctionComponent } from 'preact';
|
|
2
|
-
import { type Dispatch, type StateUpdater,
|
|
2
|
+
import { type Dispatch, type StateUpdater, useState } from 'preact/hooks';
|
|
3
3
|
import z from 'zod';
|
|
4
4
|
|
|
5
5
|
import { getInsertionsTableData } from './getInsertionsTableData';
|
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
type SubstitutionOrDeletionEntry,
|
|
16
16
|
views,
|
|
17
17
|
} from '../../types';
|
|
18
|
-
import {
|
|
18
|
+
import { useLapisUrl } from '../LapisUrlContext';
|
|
19
19
|
import { CsvDownloadButton } from '../components/csv-download-button';
|
|
20
20
|
import { ErrorBoundary } from '../components/error-boundary';
|
|
21
21
|
import { Fullscreen } from '../components/fullscreen';
|
|
@@ -59,7 +59,7 @@ export const Mutations: FunctionComponent<MutationsProps> = (componentProps) =>
|
|
|
59
59
|
};
|
|
60
60
|
|
|
61
61
|
export const MutationsInner: FunctionComponent<MutationsProps> = (componentProps) => {
|
|
62
|
-
const lapis =
|
|
62
|
+
const lapis = useLapisUrl();
|
|
63
63
|
const { lapisFilter, baselineLapisFilter, sequenceType } = componentProps;
|
|
64
64
|
|
|
65
65
|
const { data, error, isLoading } = useQuery(async () => {
|
|
@@ -237,7 +237,7 @@ type MutationsInfoProps = {
|
|
|
237
237
|
};
|
|
238
238
|
|
|
239
239
|
const MutationsInfo: FunctionComponent<MutationsInfoProps> = ({ originalComponentProps }) => {
|
|
240
|
-
const lapis =
|
|
240
|
+
const lapis = useLapisUrl();
|
|
241
241
|
|
|
242
242
|
return (
|
|
243
243
|
<Info>
|
|
@@ -49,13 +49,23 @@ const MutationsOverTimeGrid: FunctionComponent<MutationsOverTimeGridProps> = ({
|
|
|
49
49
|
gridTemplateRows: `repeat(${shownMutations.length}, 24px)`,
|
|
50
50
|
gridTemplateColumns: `${MUTATION_CELL_WIDTH_REM}rem repeat(${dates.length}, minmax(0.05rem, 1fr))`,
|
|
51
51
|
}}
|
|
52
|
+
className='text-center'
|
|
52
53
|
>
|
|
54
|
+
{dates.map((date, columnIndex) => (
|
|
55
|
+
<div
|
|
56
|
+
className='@container font-semibold'
|
|
57
|
+
style={{ gridRowStart: 1, gridColumnStart: columnIndex + 2 }}
|
|
58
|
+
key={date.dateString}
|
|
59
|
+
>
|
|
60
|
+
<p {...styleGridHeader(columnIndex, dates)}>{date.dateString}</p>
|
|
61
|
+
</div>
|
|
62
|
+
))}
|
|
53
63
|
{shownMutations.map((mutation, rowIndex) => {
|
|
54
64
|
return (
|
|
55
65
|
<Fragment key={`fragment-${mutation.toString()}`}>
|
|
56
66
|
<div
|
|
57
67
|
key={`mutation-${mutation.toString()}`}
|
|
58
|
-
style={{ gridRowStart: rowIndex +
|
|
68
|
+
style={{ gridRowStart: rowIndex + 2, gridColumnStart: 1 }}
|
|
59
69
|
>
|
|
60
70
|
<MutationCell mutation={mutation} />
|
|
61
71
|
</div>
|
|
@@ -69,7 +79,7 @@ const MutationsOverTimeGrid: FunctionComponent<MutationsOverTimeGridProps> = ({
|
|
|
69
79
|
);
|
|
70
80
|
return (
|
|
71
81
|
<div
|
|
72
|
-
style={{ gridRowStart: rowIndex +
|
|
82
|
+
style={{ gridRowStart: rowIndex + 2, gridColumnStart: columnIndex + 2 }}
|
|
73
83
|
key={`${mutation.toString()}-${date.toString()}`}
|
|
74
84
|
>
|
|
75
85
|
<ProportionCell
|
|
@@ -90,6 +100,18 @@ const MutationsOverTimeGrid: FunctionComponent<MutationsOverTimeGridProps> = ({
|
|
|
90
100
|
);
|
|
91
101
|
};
|
|
92
102
|
|
|
103
|
+
function styleGridHeader(columnIndex: number, dates: Temporal[]) {
|
|
104
|
+
if (columnIndex === 0) {
|
|
105
|
+
return { className: 'overflow-visible text-nowrap' };
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (columnIndex === dates.length - 1) {
|
|
109
|
+
return { className: 'overflow-visible text-nowrap', style: { direction: 'rtl' } };
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return { className: 'invisible @[6rem]:visible' };
|
|
113
|
+
}
|
|
114
|
+
|
|
93
115
|
function getTooltipPosition(rowIndex: number, rows: number, columnIndex: number, columns: number) {
|
|
94
116
|
const tooltipX = rowIndex < rows / 2 || rowIndex < 6 ? 'bottom' : 'top';
|
|
95
117
|
const tooltipY = columnIndex < columns / 2 ? 'start' : 'end';
|
|
@@ -135,7 +157,7 @@ const ProportionCell: FunctionComponent<{
|
|
|
135
157
|
backgroundColor: getColorWithingScale(value?.proportion, colorScale),
|
|
136
158
|
color: getTextColorForScale(value?.proportion, colorScale),
|
|
137
159
|
}}
|
|
138
|
-
className={`w-full h-full
|
|
160
|
+
className={`w-full h-full hover:font-bold text-xs group @container`}
|
|
139
161
|
>
|
|
140
162
|
<span className='invisible @[2rem]:visible'>
|
|
141
163
|
{value === null ? '' : formatProportion(value.proportion, 0)}
|
|
@@ -155,7 +177,7 @@ const timeIntervalDisplay = (date: TemporalClass) => {
|
|
|
155
177
|
};
|
|
156
178
|
|
|
157
179
|
const MutationCell: FunctionComponent<{ mutation: Substitution | Deletion }> = ({ mutation }) => {
|
|
158
|
-
return <div
|
|
180
|
+
return <div>{mutation.code}</div>;
|
|
159
181
|
};
|
|
160
182
|
|
|
161
183
|
export default MutationsOverTimeGrid;
|