@genspectrum/dashboard-components 0.18.2 → 0.18.3

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 (26) hide show
  1. package/custom-elements.json +2 -2
  2. package/dist/assets/{mutationOverTimeWorker-ChQTFL68.js.map → mutationOverTimeWorker--b8ZHlji.js.map} +1 -1
  3. package/dist/components.d.ts +16 -287
  4. package/dist/components.js +114 -39
  5. package/dist/components.js.map +1 -1
  6. package/dist/style.css +2 -2
  7. package/dist/util.d.ts +24 -291
  8. package/package.json +3 -3
  9. package/src/preact/MutationAnnotationsContext.spec.tsx +103 -34
  10. package/src/preact/MutationAnnotationsContext.tsx +49 -7
  11. package/src/preact/components/annotated-mutation.stories.tsx +0 -5
  12. package/src/preact/components/annotated-mutation.tsx +6 -2
  13. package/src/preact/mutationComparison/mutation-comparison.stories.tsx +3 -1
  14. package/src/preact/mutations/mutations.stories.tsx +4 -1
  15. package/src/preact/mutationsOverTime/mutations-over-time.stories.tsx +3 -1
  16. package/src/preact/sequencesByLocation/leafletStyleModifications.css +5 -0
  17. package/src/preact/wastewater/mutationsOverTime/wastewater-mutations-over-time.stories.tsx +25 -0
  18. package/src/preact/wastewater/mutationsOverTime/wastewater-mutations-over-time.tsx +65 -13
  19. package/src/web-components/MutationAnnotations.mdx +8 -0
  20. package/src/web-components/gs-app.stories.ts +2 -0
  21. package/src/web-components/gs-app.ts +4 -2
  22. package/src/web-components/mutation-annotations-context.ts +6 -2
  23. package/standalone-bundle/assets/mutationOverTimeWorker-jChgWnwp.js.map +1 -1
  24. package/standalone-bundle/dashboard-components.js +5699 -5643
  25. package/standalone-bundle/dashboard-components.js.map +1 -1
  26. package/standalone-bundle/style.css +1 -1
package/dist/util.d.ts CHANGED
@@ -208,40 +208,52 @@ declare const mutationAnnotationSchema: default_2.ZodObject<{
208
208
  name: default_2.ZodString;
209
209
  description: default_2.ZodString;
210
210
  symbol: default_2.ZodString;
211
- nucleotideMutations: default_2.ZodArray<default_2.ZodString, "many">;
212
- aminoAcidMutations: default_2.ZodArray<default_2.ZodString, "many">;
211
+ nucleotideMutations: default_2.ZodOptional<default_2.ZodArray<default_2.ZodString, "many">>;
212
+ nucleotidePositions: default_2.ZodOptional<default_2.ZodArray<default_2.ZodString, "many">>;
213
+ aminoAcidMutations: default_2.ZodOptional<default_2.ZodArray<default_2.ZodString, "many">>;
214
+ aminoAcidPositions: default_2.ZodOptional<default_2.ZodArray<default_2.ZodString, "many">>;
213
215
  }, "strip", default_2.ZodTypeAny, {
214
216
  symbol: string;
215
217
  name: string;
216
218
  description: string;
217
- nucleotideMutations: string[];
218
- aminoAcidMutations: string[];
219
+ nucleotideMutations?: string[] | undefined;
220
+ nucleotidePositions?: string[] | undefined;
221
+ aminoAcidMutations?: string[] | undefined;
222
+ aminoAcidPositions?: string[] | undefined;
219
223
  }, {
220
224
  symbol: string;
221
225
  name: string;
222
226
  description: string;
223
- nucleotideMutations: string[];
224
- aminoAcidMutations: string[];
227
+ nucleotideMutations?: string[] | undefined;
228
+ nucleotidePositions?: string[] | undefined;
229
+ aminoAcidMutations?: string[] | undefined;
230
+ aminoAcidPositions?: string[] | undefined;
225
231
  }>;
226
232
 
227
233
  declare const mutationAnnotationsSchema: default_2.ZodArray<default_2.ZodObject<{
228
234
  name: default_2.ZodString;
229
235
  description: default_2.ZodString;
230
236
  symbol: default_2.ZodString;
231
- nucleotideMutations: default_2.ZodArray<default_2.ZodString, "many">;
232
- aminoAcidMutations: default_2.ZodArray<default_2.ZodString, "many">;
237
+ nucleotideMutations: default_2.ZodOptional<default_2.ZodArray<default_2.ZodString, "many">>;
238
+ nucleotidePositions: default_2.ZodOptional<default_2.ZodArray<default_2.ZodString, "many">>;
239
+ aminoAcidMutations: default_2.ZodOptional<default_2.ZodArray<default_2.ZodString, "many">>;
240
+ aminoAcidPositions: default_2.ZodOptional<default_2.ZodArray<default_2.ZodString, "many">>;
233
241
  }, "strip", default_2.ZodTypeAny, {
234
242
  symbol: string;
235
243
  name: string;
236
244
  description: string;
237
- nucleotideMutations: string[];
238
- aminoAcidMutations: string[];
245
+ nucleotideMutations?: string[] | undefined;
246
+ nucleotidePositions?: string[] | undefined;
247
+ aminoAcidMutations?: string[] | undefined;
248
+ aminoAcidPositions?: string[] | undefined;
239
249
  }, {
240
250
  symbol: string;
241
251
  name: string;
242
252
  description: string;
243
- nucleotideMutations: string[];
244
- aminoAcidMutations: string[];
253
+ nucleotideMutations?: string[] | undefined;
254
+ nucleotidePositions?: string[] | undefined;
255
+ aminoAcidMutations?: string[] | undefined;
256
+ aminoAcidPositions?: string[] | undefined;
245
257
  }>, "many">;
246
258
 
247
259
  export declare type MutationComparisonProps = default_2.infer<typeof mutationComparisonPropsSchema>;
@@ -865,285 +877,6 @@ declare const yAxisMaxConfigSchema: default_2.ZodObject<{
865
877
  export { }
866
878
 
867
879
 
868
- declare global {
869
- interface HTMLElementEventMap {
870
- 'gs-error': ErrorEvent;
871
- }
872
- }
873
-
874
-
875
- declare global {
876
- interface HTMLElementTagNameMap {
877
- 'gs-app': AppComponent;
878
- }
879
- }
880
-
881
-
882
- declare global {
883
- namespace JSX {
884
- interface IntrinsicElements {
885
- 'gs-app': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
886
- }
887
- }
888
- }
889
-
890
-
891
- declare global {
892
- interface HTMLElementTagNameMap {
893
- 'gs-mutation-comparison-component': MutationComparisonComponent;
894
- }
895
- }
896
-
897
-
898
- declare global {
899
- namespace JSX {
900
- interface IntrinsicElements {
901
- 'gs-mutation-comparison-component': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
902
- }
903
- }
904
- }
905
-
906
-
907
- declare global {
908
- interface HTMLElementTagNameMap {
909
- 'gs-mutations-component': MutationsComponent;
910
- }
911
- }
912
-
913
-
914
- declare global {
915
- namespace JSX {
916
- interface IntrinsicElements {
917
- 'gs-mutations-component': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
918
- }
919
- }
920
- }
921
-
922
-
923
- declare global {
924
- interface HTMLElementTagNameMap {
925
- 'gs-relative-growth-advantage': RelativeGrowthAdvantageComponent;
926
- }
927
- }
928
-
929
-
930
- declare global {
931
- namespace JSX {
932
- interface IntrinsicElements {
933
- 'gs-relative-growth-advantage': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
934
- }
935
- }
936
- }
937
-
938
-
939
- declare global {
940
- interface HTMLElementTagNameMap {
941
- 'gs-prevalence-over-time': PrevalenceOverTimeComponent;
942
- }
943
- }
944
-
945
-
946
- declare global {
947
- namespace JSX {
948
- interface IntrinsicElements {
949
- 'gs-prevalence-over-time': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
950
- }
951
- }
952
- }
953
-
954
-
955
- declare global {
956
- interface HTMLElementTagNameMap {
957
- 'gs-aggregate': AggregateComponent;
958
- }
959
- }
960
-
961
-
962
- declare global {
963
- namespace JSX {
964
- interface IntrinsicElements {
965
- 'gs-aggregate': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
966
- }
967
- }
968
- }
969
-
970
-
971
- declare global {
972
- interface HTMLElementTagNameMap {
973
- 'gs-number-sequences-over-time': NumberSequencesOverTimeComponent;
974
- }
975
- }
976
-
977
-
978
- declare global {
979
- namespace JSX {
980
- interface IntrinsicElements {
981
- 'gs-number-sequences-over-time': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
982
- }
983
- }
984
- }
985
-
986
-
987
- declare global {
988
- interface HTMLElementTagNameMap {
989
- 'gs-mutations-over-time': MutationsOverTimeComponent;
990
- }
991
- }
992
-
993
-
994
- declare global {
995
- namespace JSX {
996
- interface IntrinsicElements {
997
- 'gs-mutations-over-time': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
998
- }
999
- }
1000
- }
1001
-
1002
-
1003
- declare global {
1004
- interface HTMLElementTagNameMap {
1005
- 'gs-statistics': StatisticsComponent;
1006
- }
1007
- }
1008
-
1009
-
1010
- declare global {
1011
- namespace JSX {
1012
- interface IntrinsicElements {
1013
- 'gs-statistics': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1014
- }
1015
- }
1016
- }
1017
-
1018
-
1019
- declare global {
1020
- interface HTMLElementTagNameMap {
1021
- 'gs-sequences-by-location': SequencesByLocationComponent;
1022
- }
1023
- }
1024
-
1025
-
1026
- declare global {
1027
- namespace JSX {
1028
- interface IntrinsicElements {
1029
- 'gs-sequences-by-location': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1030
- }
1031
- }
1032
- }
1033
-
1034
-
1035
- declare global {
1036
- interface HTMLElementTagNameMap {
1037
- 'gs-date-range-filter': DateRangeFilterComponent;
1038
- }
1039
- interface HTMLElementEventMap {
1040
- 'gs-date-range-filter-changed': CustomEvent<Record<string, string>>;
1041
- 'gs-date-range-option-changed': DateRangeOptionChangedEvent;
1042
- }
1043
- }
1044
-
1045
-
1046
- declare global {
1047
- namespace JSX {
1048
- interface IntrinsicElements {
1049
- 'gs-date-range-filter': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1050
- }
1051
- }
1052
- }
1053
-
1054
-
1055
- declare global {
1056
- interface HTMLElementTagNameMap {
1057
- 'gs-location-filter': LocationFilterComponent;
1058
- }
1059
- interface HTMLElementEventMap {
1060
- 'gs-location-changed': LocationChangedEvent;
1061
- }
1062
- }
1063
-
1064
-
1065
- declare global {
1066
- namespace JSX {
1067
- interface IntrinsicElements {
1068
- 'gs-location-filter': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1069
- }
1070
- }
1071
- }
1072
-
1073
-
1074
- declare global {
1075
- interface HTMLElementTagNameMap {
1076
- 'gs-text-filter': TextFilterComponent;
1077
- }
1078
- interface HTMLElementEventMap {
1079
- 'gs-text-filter-changed': TextFilterChangedEvent;
1080
- }
1081
- }
1082
-
1083
-
1084
- declare global {
1085
- namespace JSX {
1086
- interface IntrinsicElements {
1087
- 'gs-text-filter': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1088
- }
1089
- }
1090
- }
1091
-
1092
-
1093
- declare global {
1094
- interface HTMLElementTagNameMap {
1095
- 'gs-mutation-filter': MutationFilterComponent;
1096
- }
1097
- interface HTMLElementEventMap {
1098
- 'gs-mutation-filter-changed': CustomEvent<MutationsFilter>;
1099
- }
1100
- }
1101
-
1102
-
1103
- declare global {
1104
- namespace JSX {
1105
- interface IntrinsicElements {
1106
- 'gs-mutation-filter': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1107
- }
1108
- }
1109
- }
1110
-
1111
-
1112
- declare global {
1113
- interface HTMLElementTagNameMap {
1114
- 'gs-lineage-filter': LineageFilterComponent;
1115
- }
1116
- interface HTMLElementEventMap {
1117
- 'gs-lineage-filter-changed': LineageFilterChangedEvent;
1118
- }
1119
- }
1120
-
1121
-
1122
- declare global {
1123
- namespace JSX {
1124
- interface IntrinsicElements {
1125
- 'gs-lineage-filter': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1126
- }
1127
- }
1128
- }
1129
-
1130
-
1131
- declare global {
1132
- interface HTMLElementTagNameMap {
1133
- 'gs-wastewater-mutations-over-time': WastewaterMutationsOverTimeComponent;
1134
- }
1135
- }
1136
-
1137
-
1138
- declare global {
1139
- namespace JSX {
1140
- interface IntrinsicElements {
1141
- 'gs-wastewater-mutations-over-time': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1142
- }
1143
- }
1144
- }
1145
-
1146
-
1147
880
  declare module 'chart.js' {
1148
881
  interface CartesianScaleTypeRegistry {
1149
882
  logit: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@genspectrum/dashboard-components",
3
- "version": "0.18.2",
3
+ "version": "0.18.3",
4
4
  "description": "GenSpectrum web components for building dashboards",
5
5
  "type": "module",
6
6
  "license": "AGPL-3.0-only",
@@ -112,7 +112,7 @@
112
112
  "@storybook/preact": "^8.0.9",
113
113
  "@storybook/preact-vite": "^8.0.9",
114
114
  "@storybook/test": "^8.0.0",
115
- "@storybook/test-runner": "^0.21.0",
115
+ "@storybook/test-runner": "^0.22.0",
116
116
  "@storybook/types": "^8.0.9",
117
117
  "@storybook/web-components": "^8.0.9",
118
118
  "@storybook/web-components-vite": "^8.0.9",
@@ -143,7 +143,7 @@
143
143
  "storybook": "^8.0.9",
144
144
  "storybook-addon-fetch-mock": "^2.0.0",
145
145
  "tailwindcss": "^4.0.9",
146
- "typescript": "~5.7.2",
146
+ "typescript": "^5.8.2",
147
147
  "vite": "^6.0.3",
148
148
  "vite-plugin-dts": "^4.0.3",
149
149
  "vitest": "^3.0.2"
@@ -3,56 +3,125 @@ import { type FunctionalComponent } from 'preact';
3
3
  import { describe, expect, it } from 'vitest';
4
4
 
5
5
  import { MutationAnnotationsContextProvider, useMutationAnnotationsProvider } from './MutationAnnotationsContext';
6
+ import { SubstitutionClass } from '../utils/mutations';
6
7
  import { type MutationAnnotations } from '../web-components/mutation-annotations-context';
7
8
 
8
9
  describe('useMutationAnnotation', () => {
9
- const mockAnnotations: MutationAnnotations = [
10
- {
11
- name: 'Annotation 1',
12
- description: 'Description 1',
13
- symbol: 'A1',
14
- nucleotideMutations: ['A123', 'A456'],
15
- aminoAcidMutations: ['B123'],
16
- },
17
- {
18
- name: 'Annotation 2',
19
- description: 'Description 2',
20
- symbol: 'A2',
21
- nucleotideMutations: ['A456', 'A789'],
22
- aminoAcidMutations: ['B456', 'B789'],
23
- },
24
- ];
25
-
26
- const wrapper: FunctionalComponent = ({ children }) => (
27
- <MutationAnnotationsContextProvider value={mockAnnotations}>{children}</MutationAnnotationsContextProvider>
28
- );
29
-
30
- function renderAnnotationsHook() {
10
+ function renderAnnotationsHook(mockAnnotations: MutationAnnotations) {
11
+ const wrapper: FunctionalComponent = ({ children }) => (
12
+ <MutationAnnotationsContextProvider value={mockAnnotations}>{children}</MutationAnnotationsContextProvider>
13
+ );
14
+
31
15
  const { result } = renderHook(() => useMutationAnnotationsProvider(), { wrapper });
32
16
  return result.current;
33
17
  }
34
18
 
35
- it('should return the correct annotation for a given nucleotide mutation', () => {
36
- const result = renderAnnotationsHook()('A123', 'nucleotide');
19
+ describe('annotations for nucleotide mutations', () => {
20
+ const mockAnnotations: MutationAnnotations = [
21
+ {
22
+ name: 'Annotation 1',
23
+ description: 'Description 1',
24
+ symbol: 'A1',
25
+ nucleotideMutations: ['A123', 'A456'],
26
+ },
27
+ {
28
+ name: 'Annotation 2',
29
+ description: 'Description 2',
30
+ symbol: 'A2',
31
+ nucleotideMutations: ['A456', 'A789'],
32
+ },
33
+ ];
34
+
35
+ it('should return the correct annotation for a given mutation', () => {
36
+ const result = renderAnnotationsHook(mockAnnotations)(SubstitutionClass.parse('A123')!, 'nucleotide');
37
+
38
+ expect(result).toEqual([mockAnnotations[0]]);
39
+ });
37
40
 
38
- expect(result).toEqual([mockAnnotations[0]]);
41
+ it('should return the correct annotations if multiple contain a mutation', () => {
42
+ const result = renderAnnotationsHook(mockAnnotations)(SubstitutionClass.parse('A456')!, 'nucleotide');
43
+
44
+ expect(result).toEqual([mockAnnotations[0], mockAnnotations[1]]);
45
+ });
39
46
  });
40
47
 
41
- it('should return the correct annotations if multiple contain a mutation', () => {
42
- const result = renderAnnotationsHook()('A456', 'nucleotide');
48
+ describe('annotations for amino acid mutations', () => {
49
+ const mockAnnotations: MutationAnnotations = [
50
+ {
51
+ name: 'Annotation 1',
52
+ description: 'Description 1',
53
+ symbol: 'A1',
54
+ aminoAcidMutations: ['B456', 'B789'],
55
+ },
56
+ ];
57
+
58
+ it('should return the correct mutation annotation for a given mutations', () => {
59
+ const result = renderAnnotationsHook(mockAnnotations)(SubstitutionClass.parse('B456')!, 'amino acid');
43
60
 
44
- expect(result).toEqual([mockAnnotations[0], mockAnnotations[1]]);
61
+ expect(result).toEqual([mockAnnotations[0]]);
62
+ });
45
63
  });
46
64
 
47
- it('should return undefined for a non-existent mutation code', () => {
48
- const result = renderAnnotationsHook()('NON_EXISTENT', 'nucleotide');
65
+ describe('annotations for nucleotide positions', () => {
66
+ const mockAnnotations: MutationAnnotations = [
67
+ {
68
+ name: 'Annotation 1',
69
+ description: 'Description 1',
70
+ symbol: 'A1',
71
+ nucleotideMutations: ['A321T', 'A432T'],
72
+ nucleotidePositions: ['321', '543'],
73
+ },
74
+ {
75
+ name: 'Annotation 2',
76
+ description: 'Description 2',
77
+ symbol: 'A2',
78
+ nucleotidePositions: ['432'],
79
+ },
80
+ ];
49
81
 
50
- expect(result).toBeUndefined();
82
+ it('should return the correct mutation annotation covered by position only', () => {
83
+ const result = renderAnnotationsHook(mockAnnotations)(SubstitutionClass.parse('A543T')!, 'nucleotide');
84
+ expect(result).toEqual([mockAnnotations[0]]);
85
+ });
86
+
87
+ it('should return the correct mutation annotation covered both by position and mutation', () => {
88
+ const result = renderAnnotationsHook(mockAnnotations)(SubstitutionClass.parse('A321T')!, 'nucleotide');
89
+ expect(result).toEqual([mockAnnotations[0]]);
90
+ });
91
+
92
+ it('should return both annotations if one matches the mutations and the other the position', () => {
93
+ const result = renderAnnotationsHook(mockAnnotations)(SubstitutionClass.parse('A432T')!, 'nucleotide');
94
+ expect(result).toEqual([mockAnnotations[1], mockAnnotations[0]]);
95
+ });
51
96
  });
52
97
 
53
- it('should return the correct mutation annotation for amino acid mutations', () => {
54
- const result = renderAnnotationsHook()('B456', 'amino acid');
98
+ describe('annotations for amino acid positions', () => {
99
+ const mockAnnotations: MutationAnnotations = [
100
+ {
101
+ name: 'Annotation 1',
102
+ description: 'Description 1',
103
+ symbol: 'A1',
104
+ aminoAcidMutations: ['Gene:B321C', 'Gene:B432G'],
105
+ aminoAcidPositions: ['Gene:432', 'Gene:543'],
106
+ },
107
+ ];
108
+
109
+ it('should return the correct mutation annotation covered both by position and mutation', () => {
110
+ const result = renderAnnotationsHook(mockAnnotations)(SubstitutionClass.parse('Gene:B432G')!, 'amino acid');
111
+ expect(result).toEqual([mockAnnotations[0]]);
112
+ });
113
+
114
+ it('should return the correct mutation annotation covered both by position only', () => {
115
+ const result = renderAnnotationsHook(mockAnnotations)(SubstitutionClass.parse('Gene:B543G')!, 'amino acid');
116
+ expect(result).toEqual([mockAnnotations[0]]);
117
+ });
55
118
 
56
- expect(result).toEqual([mockAnnotations[1]]);
119
+ it('should return no mutation annotation for an amino acid position of wrong gene', () => {
120
+ const result = renderAnnotationsHook(mockAnnotations)(
121
+ SubstitutionClass.parse('NotTheGene:B543G')!,
122
+ 'amino acid',
123
+ );
124
+ expect(result).toBeUndefined();
125
+ });
57
126
  });
58
127
  });
@@ -9,10 +9,22 @@ import {
9
9
  } from '../web-components/mutation-annotations-context';
10
10
  import { ErrorDisplay } from './components/error-display';
11
11
  import { ResizeContainer } from './components/resize-container';
12
+ import { type Mutation } from '../utils/mutations';
12
13
 
13
- const MutationAnnotationsContext = createContext<Record<SequenceType, Map<string, MutationAnnotations>>>({
14
- nucleotide: new Map(),
15
- 'amino acid': new Map(),
14
+ type MutationAnnotationPerSequenceType = {
15
+ mutation: Map<string, MutationAnnotations>;
16
+ position: Map<string, MutationAnnotations>;
17
+ };
18
+
19
+ const MutationAnnotationsContext = createContext<Record<SequenceType, MutationAnnotationPerSequenceType>>({
20
+ nucleotide: {
21
+ mutation: new Map(),
22
+ position: new Map(),
23
+ },
24
+ 'amino acid': {
25
+ mutation: new Map(),
26
+ position: new Map(),
27
+ },
16
28
  });
17
29
 
18
30
  export const MutationAnnotationsContextProvider: FunctionalComponent<
@@ -26,7 +38,9 @@ export const MutationAnnotationsContextProvider: FunctionalComponent<
26
38
  }
27
39
 
28
40
  const nucleotideMap = new Map<string, MutationAnnotations>();
41
+ const nucleotidePositions = new Map<string, MutationAnnotations>();
29
42
  const aminoAcidMap = new Map<string, MutationAnnotations>();
43
+ const aminoAcidPositions = new Map<string, MutationAnnotations>();
30
44
 
31
45
  value.forEach((annotation) => {
32
46
  new Set(annotation.nucleotideMutations).forEach((code) => {
@@ -35,13 +49,19 @@ export const MutationAnnotationsContextProvider: FunctionalComponent<
35
49
  new Set(annotation.aminoAcidMutations).forEach((code) => {
36
50
  addAnnotationToMap(aminoAcidMap, code, annotation);
37
51
  });
52
+ new Set(annotation.nucleotidePositions).forEach((position) => {
53
+ addAnnotationToMap(nucleotidePositions, position, annotation);
54
+ });
55
+ new Set(annotation.aminoAcidPositions).forEach((position) => {
56
+ addAnnotationToMap(aminoAcidPositions, position, annotation);
57
+ });
38
58
  });
39
59
 
40
60
  return {
41
61
  success: true as const,
42
62
  value: {
43
- nucleotide: nucleotideMap,
44
- 'amino acid': aminoAcidMap,
63
+ nucleotide: { mutation: nucleotideMap, position: nucleotidePositions },
64
+ 'amino acid': { mutation: aminoAcidMap, position: aminoAcidPositions },
45
65
  },
46
66
  };
47
67
  }, [value]);
@@ -67,6 +87,28 @@ function addAnnotationToMap(map: Map<string, MutationAnnotations>, code: string,
67
87
  export function useMutationAnnotationsProvider() {
68
88
  const mutationAnnotations = useContext(MutationAnnotationsContext);
69
89
 
70
- return (mutationCode: string, sequenceType: SequenceType) =>
71
- mutationAnnotations[sequenceType].get(mutationCode.toUpperCase());
90
+ return (mutation: Mutation, sequenceType: SequenceType) => {
91
+ const position =
92
+ mutation.segment === undefined
93
+ ? `${mutation.position}`
94
+ : `${mutation.segment.toUpperCase()}:${mutation.position}`;
95
+
96
+ const possiblePositionAnnotations = mutationAnnotations[sequenceType].position.get(position);
97
+ const possibleExactAnnotations = mutationAnnotations[sequenceType].mutation.get(mutation.code.toUpperCase());
98
+
99
+ const annotations =
100
+ possiblePositionAnnotations && possibleExactAnnotations
101
+ ? [...possiblePositionAnnotations, ...possibleExactAnnotations]
102
+ : (possiblePositionAnnotations ?? possibleExactAnnotations);
103
+
104
+ const uniqueNames = new Set<string>();
105
+
106
+ return annotations?.filter((item) => {
107
+ if (uniqueNames.has(item.name)) {
108
+ return false;
109
+ }
110
+ uniqueNames.add(item.name);
111
+ return true;
112
+ });
113
+ };
72
114
  }
@@ -46,7 +46,6 @@ export const MutationWithoutAnnotationEntry: StoryObj<AnnotatedMutationProps & {
46
46
  description: 'This is a test annotation',
47
47
  symbol: '*',
48
48
  nucleotideMutations: ['123T'],
49
- aminoAcidMutations: [],
50
49
  },
51
50
  ],
52
51
  },
@@ -69,7 +68,6 @@ export const MutationWithAnnotationEntry: StoryObj<AnnotatedMutationProps & { an
69
68
  description: 'This is a test annotation <a class="link" href="/">with a link.</a>',
70
69
  symbol: '*',
71
70
  nucleotideMutations: ['A23403G'],
72
- aminoAcidMutations: [],
73
71
  },
74
72
  ],
75
73
  },
@@ -97,14 +95,12 @@ export const MutationWithMultipleAnnotationEntries: StoryObj<
97
95
  description: 'This is a test annotation',
98
96
  symbol: '*',
99
97
  nucleotideMutations: ['A23403G'],
100
- aminoAcidMutations: [],
101
98
  },
102
99
  {
103
100
  name: 'Another test annotation',
104
101
  description: 'This is a test annotation',
105
102
  symbol: '+',
106
103
  nucleotideMutations: ['A23403G'],
107
- aminoAcidMutations: [],
108
104
  },
109
105
  ],
110
106
  },
@@ -139,7 +135,6 @@ export const AminoAcidMutationWithAnnotationEntry: StoryObj<
139
135
  name: 'Test annotation',
140
136
  description: 'This is a test annotation',
141
137
  symbol: '*',
142
- nucleotideMutations: [],
143
138
  aminoAcidMutations: ['S:A501G'],
144
139
  },
145
140
  ],
@@ -45,7 +45,7 @@ const AnnotatedMutationWithoutContext: FunctionComponent<AnnotatedMutationWithou
45
45
  annotationsProvider,
46
46
  modalRef,
47
47
  }) => {
48
- const mutationAnnotations = annotationsProvider(mutation.code, sequenceType);
48
+ const mutationAnnotations = annotationsProvider(mutation, sequenceType);
49
49
 
50
50
  if (mutationAnnotations === undefined || mutationAnnotations.length === 0) {
51
51
  return mutation.code;
@@ -67,7 +67,11 @@ const AnnotatedMutationWithoutContext: FunctionComponent<AnnotatedMutationWithou
67
67
  );
68
68
 
69
69
  return (
70
- <ButtonWithModalDialog buttonClassName={'select-text'} modalContent={modalContent} modalRef={modalRef}>
70
+ <ButtonWithModalDialog
71
+ buttonClassName={'select-text cursor-pointer'}
72
+ modalContent={modalContent}
73
+ modalRef={modalRef}
74
+ >
71
75
  {mutation.code}
72
76
  <sup>
73
77
  {mutationAnnotations