@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.
Files changed (28) hide show
  1. package/README.md +4 -0
  2. package/custom-elements.json +1 -1
  3. package/dist/{NumberRangeFilterChangedEvent-CQ32Qy8D.js → NumberRangeFilterChangedEvent-BnPI-Asz.js} +17 -3
  4. package/dist/NumberRangeFilterChangedEvent-BnPI-Asz.js.map +1 -0
  5. package/dist/assets/{mutationOverTimeWorker-BmB6BvVM.js.map → mutationOverTimeWorker-DPS3tmOd.js.map} +1 -1
  6. package/dist/components.d.ts +23 -23
  7. package/dist/components.js +97 -50
  8. package/dist/components.js.map +1 -1
  9. package/dist/util.d.ts +46 -22
  10. package/dist/util.js +2 -1
  11. package/package.json +1 -1
  12. package/src/preact/genomeViewer/CDSPlot.tsx +13 -2
  13. package/src/preact/genomeViewer/loadGff3.ts +6 -0
  14. package/src/preact/mutationFilter/mutation-filter.stories.tsx +2 -1
  15. package/src/preact/mutationFilter/mutation-filter.tsx +24 -27
  16. package/src/preact/mutationFilter/parseAndValidateMutation.ts +11 -11
  17. package/src/preact/mutationFilter/parseMutation.spec.ts +32 -22
  18. package/src/preact/mutationsOverTime/mutations-over-time.tsx +7 -4
  19. package/src/types.ts +17 -1
  20. package/src/utilEntrypoint.ts +4 -0
  21. package/src/utils/mutations.spec.ts +19 -0
  22. package/src/utils/mutations.ts +57 -10
  23. package/src/web-components/input/gs-mutation-filter.stories.ts +2 -1
  24. package/src/web-components/input/gs-mutation-filter.tsx +2 -6
  25. package/standalone-bundle/assets/{mutationOverTimeWorker-B_xP8pIC.js.map → mutationOverTimeWorker-Dp-A14AP.js.map} +1 -1
  26. package/standalone-bundle/dashboard-components.js +7103 -7062
  27. package/standalone-bundle/dashboard-components.js.map +1 -1
  28. 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
  });
@@ -1,9 +1,9 @@
1
- import { type MutationType, type SequenceType } from '../types';
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: MutationType;
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
- export const substitutionRegex =
17
- /^((?<segment>[A-Z0-9_-]+)(?=:):)?(?<valueAtReference>[A-Z*])?(?<position>\d+)(?<substitutionValue>[A-Z.*])?$/i;
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 match = substitutionRegex.exec(mutationStr);
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
- export const deletionRegex = /^((?<segment>[A-Z0-9_-]+)(?=:):)?(?<valueAtReference>[A-Z*])?(?<position>\d+)(-)$/i;
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 match = deletionRegex.exec(mutationStr);
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
- export const insertionRegexp =
122
- /^ins_((?<segment>[A-Z0-9_-]+)(?=:):)?(?<position>\d+):(?<insertedSymbols>(([A-Z?*]|(\.\*))+))$/i;
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 match = insertionRegexp.exec(mutationStr);
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: ['nucleotideMutations', 'aminoAcidMutations'],
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
- MutationFilter,
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';