@genspectrum/dashboard-components 1.6.0 → 1.7.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/README.md +4 -0
- package/custom-elements.json +1 -1
- package/dist/{NumberRangeFilterChangedEvent-CQ32Qy8D.js → NumberRangeFilterChangedEvent-BnPI-Asz.js} +17 -3
- package/dist/NumberRangeFilterChangedEvent-BnPI-Asz.js.map +1 -0
- package/dist/assets/{mutationOverTimeWorker-BmB6BvVM.js.map → mutationOverTimeWorker-DPS3tmOd.js.map} +1 -1
- package/dist/components.d.ts +23 -23
- package/dist/components.js +97 -50
- package/dist/components.js.map +1 -1
- package/dist/util.d.ts +46 -22
- package/dist/util.js +2 -1
- package/package.json +1 -1
- package/src/preact/genomeViewer/CDSPlot.tsx +13 -2
- package/src/preact/genomeViewer/loadGff3.ts +6 -0
- package/src/preact/mutationFilter/mutation-filter.stories.tsx +2 -1
- package/src/preact/mutationFilter/mutation-filter.tsx +24 -27
- package/src/preact/mutationFilter/parseAndValidateMutation.ts +11 -11
- package/src/preact/mutationFilter/parseMutation.spec.ts +32 -22
- package/src/preact/mutationsOverTime/mutations-over-time.tsx +7 -4
- package/src/types.ts +17 -1
- package/src/utilEntrypoint.ts +4 -0
- package/src/utils/mutations.spec.ts +19 -0
- package/src/utils/mutations.ts +57 -10
- package/src/web-components/input/gs-mutation-filter.stories.ts +2 -1
- package/src/web-components/input/gs-mutation-filter.tsx +2 -6
- package/standalone-bundle/assets/{mutationOverTimeWorker-B_xP8pIC.js.map → mutationOverTimeWorker-Dp-A14AP.js.map} +1 -1
- package/standalone-bundle/dashboard-components.js +7103 -7062
- package/standalone-bundle/dashboard-components.js.map +1 -1
- package/dist/NumberRangeFilterChangedEvent-CQ32Qy8D.js.map +0 -1
|
@@ -6,6 +6,7 @@ describe('SubstitutionClass', () => {
|
|
|
6
6
|
it('should be parsed from string', () => {
|
|
7
7
|
expect(SubstitutionClass.parse('A1T')).deep.equal(new SubstitutionClass(undefined, 'A', 'T', 1));
|
|
8
8
|
expect(SubstitutionClass.parse('seg1:A1T')).deep.equal(new SubstitutionClass('seg1', 'A', 'T', 1));
|
|
9
|
+
expect(SubstitutionClass.parse('1')).deep.equal(new SubstitutionClass(undefined, undefined, undefined, 1));
|
|
9
10
|
});
|
|
10
11
|
|
|
11
12
|
it('should be parsed with stop codons', () => {
|
|
@@ -13,6 +14,12 @@ describe('SubstitutionClass', () => {
|
|
|
13
14
|
expect(SubstitutionClass.parse('S:T1247*')).deep.equal(new SubstitutionClass('S', 'T', '*', 1247));
|
|
14
15
|
});
|
|
15
16
|
|
|
17
|
+
it('invalid substitution strings should return null', () => {
|
|
18
|
+
expect(SubstitutionClass.parse('A1-')).to.equal(null);
|
|
19
|
+
expect(SubstitutionClass.parse('ins_1:A')).to.equal(null);
|
|
20
|
+
expect(SubstitutionClass.parse('E34Q')).to.equal(null);
|
|
21
|
+
});
|
|
22
|
+
|
|
16
23
|
it('should render to string correctly', () => {
|
|
17
24
|
const substitutions = [
|
|
18
25
|
{
|
|
@@ -39,6 +46,12 @@ describe('DeletionClass', () => {
|
|
|
39
46
|
expect(DeletionClass.parse('seg1:*1-')).deep.equal(new DeletionClass('seg1', '*', 1));
|
|
40
47
|
});
|
|
41
48
|
|
|
49
|
+
it('invalid deletion strings should return null', () => {
|
|
50
|
+
expect(DeletionClass.parse('seg1:A1T')).to.equal(null);
|
|
51
|
+
expect(DeletionClass.parse('ins_1:A')).to.equal(null);
|
|
52
|
+
expect(DeletionClass.parse('E34-')).to.equal(null);
|
|
53
|
+
});
|
|
54
|
+
|
|
42
55
|
it('should render to string correctly', () => {
|
|
43
56
|
const substitutions = [
|
|
44
57
|
{
|
|
@@ -74,4 +87,10 @@ describe('InsertionClass', () => {
|
|
|
74
87
|
it('should be parsed with stop codon insertion', () => {
|
|
75
88
|
expect(InsertionClass.parse('ins_134:*')).deep.equal(new InsertionClass(undefined, 134, '*'));
|
|
76
89
|
});
|
|
90
|
+
|
|
91
|
+
it('invalid insertion strings should return null', () => {
|
|
92
|
+
expect(InsertionClass.parse('A1-')).to.equal(null);
|
|
93
|
+
expect(InsertionClass.parse('seg1:A1T')).to.equal(null);
|
|
94
|
+
expect(InsertionClass.parse('ins_34:Q')).to.equal(null);
|
|
95
|
+
});
|
|
77
96
|
});
|
package/src/utils/mutations.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type SubstitutionOrDeletionOrInsertion, type SequenceType } from '../types';
|
|
2
2
|
|
|
3
3
|
export interface Mutation {
|
|
4
4
|
readonly position: number;
|
|
5
5
|
readonly code: string;
|
|
6
|
-
readonly type:
|
|
6
|
+
readonly type: SubstitutionOrDeletionOrInsertion;
|
|
7
7
|
readonly segment?: string;
|
|
8
8
|
}
|
|
9
9
|
|
|
@@ -13,8 +13,28 @@ export interface MutationClass extends Mutation {
|
|
|
13
13
|
toString(): string;
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
// Allowed IUPAC characters: https://www.bioinformatics.org/sms/iupac.html
|
|
17
|
+
const nucleotideChars = 'ACGTRYKMSWBDHVN';
|
|
18
|
+
const aminoAcidChars = 'ACDEFGHIKLMNPQRSTVWY';
|
|
19
|
+
|
|
20
|
+
function segmentPart(type: 'nucleotide' | 'aminoAcid') {
|
|
21
|
+
return type === 'aminoAcid' ? `(?<segment>[A-Z0-9_-]+):` : `((?<segment>[A-Z0-9_-]+)(?=:):)?`;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function buildSubstitutionRegex(type: 'nucleotide' | 'aminoAcid') {
|
|
25
|
+
const chars = type === 'nucleotide' ? nucleotideChars : aminoAcidChars;
|
|
26
|
+
|
|
27
|
+
return new RegExp(
|
|
28
|
+
`^${segmentPart(type)}` +
|
|
29
|
+
`(?<valueAtReference>[${chars}*])?` +
|
|
30
|
+
`(?<position>\\d+)` +
|
|
31
|
+
`(?<substitutionValue>[${chars}.*])?$`,
|
|
32
|
+
'i',
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const nucleotideSubstitutionRegex = buildSubstitutionRegex('nucleotide');
|
|
37
|
+
const aminoAcidSubstitutionRegex = buildSubstitutionRegex('aminoAcid');
|
|
18
38
|
|
|
19
39
|
export interface Substitution extends Mutation {
|
|
20
40
|
type: 'substitution';
|
|
@@ -55,7 +75,9 @@ export class SubstitutionClass implements MutationClass, Substitution {
|
|
|
55
75
|
}
|
|
56
76
|
|
|
57
77
|
static parse(mutationStr: string): SubstitutionClass | null {
|
|
58
|
-
const
|
|
78
|
+
const matchNucleotide = nucleotideSubstitutionRegex.exec(mutationStr);
|
|
79
|
+
const matchAminoAcid = aminoAcidSubstitutionRegex.exec(mutationStr);
|
|
80
|
+
const match = matchNucleotide ?? matchAminoAcid;
|
|
59
81
|
if (match?.groups === undefined) {
|
|
60
82
|
return null;
|
|
61
83
|
}
|
|
@@ -68,7 +90,17 @@ export class SubstitutionClass implements MutationClass, Substitution {
|
|
|
68
90
|
}
|
|
69
91
|
}
|
|
70
92
|
|
|
71
|
-
|
|
93
|
+
function buildDeletionRegex(type: 'nucleotide' | 'aminoAcid') {
|
|
94
|
+
const chars = type === 'nucleotide' ? nucleotideChars : aminoAcidChars;
|
|
95
|
+
|
|
96
|
+
return new RegExp(
|
|
97
|
+
`^${segmentPart(type)}` + `(?<valueAtReference>[${chars}*])?` + `(?<position>\\d+)` + `(-)$`,
|
|
98
|
+
'i',
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const nucleotideDeletionRegex = buildDeletionRegex('nucleotide');
|
|
103
|
+
const aminoAcidDeletionRegex = buildDeletionRegex('aminoAcid');
|
|
72
104
|
|
|
73
105
|
export interface Deletion extends Mutation {
|
|
74
106
|
type: 'deletion';
|
|
@@ -105,7 +137,9 @@ export class DeletionClass implements MutationClass, Deletion {
|
|
|
105
137
|
}
|
|
106
138
|
|
|
107
139
|
static parse(mutationStr: string): DeletionClass | null {
|
|
108
|
-
const
|
|
140
|
+
const matchNucleotide = nucleotideDeletionRegex.exec(mutationStr);
|
|
141
|
+
const matchAminoAcid = aminoAcidDeletionRegex.exec(mutationStr);
|
|
142
|
+
const match = matchNucleotide ?? matchAminoAcid;
|
|
109
143
|
if (match?.groups === undefined) {
|
|
110
144
|
return null;
|
|
111
145
|
}
|
|
@@ -118,8 +152,19 @@ export class DeletionClass implements MutationClass, Deletion {
|
|
|
118
152
|
}
|
|
119
153
|
}
|
|
120
154
|
|
|
121
|
-
|
|
122
|
-
|
|
155
|
+
function buildInsertionRegex(type: 'nucleotide' | 'aminoAcid') {
|
|
156
|
+
const chars = type === 'nucleotide' ? nucleotideChars : aminoAcidChars;
|
|
157
|
+
|
|
158
|
+
const wildcardToken = `(?:\\.\\*)`;
|
|
159
|
+
|
|
160
|
+
return new RegExp(
|
|
161
|
+
`^ins_${segmentPart(type)}(?<position>\\d+):(?<insertedSymbols>(?:[${chars}?*]|${wildcardToken})+)$`,
|
|
162
|
+
'i',
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const nucleotideInsertionRegex = buildInsertionRegex('nucleotide');
|
|
167
|
+
const aminoAcidInsertionRegex = buildInsertionRegex('aminoAcid');
|
|
123
168
|
|
|
124
169
|
export interface Insertion extends Mutation {
|
|
125
170
|
type: 'insertion';
|
|
@@ -154,7 +199,9 @@ export class InsertionClass implements MutationClass {
|
|
|
154
199
|
}
|
|
155
200
|
|
|
156
201
|
static parse(mutationStr: string): InsertionClass | null {
|
|
157
|
-
const
|
|
202
|
+
const matchNucleotide = nucleotideInsertionRegex.exec(mutationStr);
|
|
203
|
+
const matchAminoAcid = aminoAcidInsertionRegex.exec(mutationStr);
|
|
204
|
+
const match = matchNucleotide ?? matchAminoAcid;
|
|
158
205
|
if (match?.groups === undefined) {
|
|
159
206
|
return null;
|
|
160
207
|
}
|
|
@@ -7,6 +7,7 @@ import { previewHandles } from '../../../.storybook/preview';
|
|
|
7
7
|
import { LAPIS_URL, REFERENCE_GENOME_ENDPOINT } from '../../constants';
|
|
8
8
|
import '../gs-app';
|
|
9
9
|
import { type MutationFilterProps } from '../../preact/mutationFilter/mutation-filter';
|
|
10
|
+
import { mutationType } from '../../types';
|
|
10
11
|
import { gsEventNames } from '../../utils/gsEventNames';
|
|
11
12
|
import { withinShadowRoot } from '../withinShadowRoot.story';
|
|
12
13
|
import './gs-mutation-filter';
|
|
@@ -117,7 +118,7 @@ export const RestrictEnabledMutationTypes: StoryObj<MutationFilterProps> = {
|
|
|
117
118
|
...Template,
|
|
118
119
|
args: {
|
|
119
120
|
...Template.args,
|
|
120
|
-
enabledMutationTypes: [
|
|
121
|
+
enabledMutationTypes: [mutationType.nucleotideMutations, mutationType.aminoAcidMutations],
|
|
121
122
|
},
|
|
122
123
|
play: async ({ canvasElement }) => {
|
|
123
124
|
const canvas = await withinShadowRoot(canvasElement, 'gs-mutation-filter');
|
|
@@ -2,12 +2,8 @@ import { customElement, property } from 'lit/decorators.js';
|
|
|
2
2
|
import type { DetailedHTMLProps, HTMLAttributes } from 'react';
|
|
3
3
|
|
|
4
4
|
import { ReferenceGenomesAwaiter } from '../../preact/components/ReferenceGenomesAwaiter';
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
type MutationType,
|
|
8
|
-
type MutationFilterProps,
|
|
9
|
-
} from '../../preact/mutationFilter/mutation-filter';
|
|
10
|
-
import type { MutationsFilter } from '../../types';
|
|
5
|
+
import { MutationFilter, type MutationFilterProps } from '../../preact/mutationFilter/mutation-filter';
|
|
6
|
+
import type { MutationType, MutationsFilter } from '../../types';
|
|
11
7
|
import { type gsEventNames } from '../../utils/gsEventNames';
|
|
12
8
|
import type { Equals, Expect } from '../../utils/typeAssertions';
|
|
13
9
|
import { PreactLitAdapter } from '../PreactLitAdapter';
|