@genspectrum/dashboard-components 0.18.2 → 0.18.4

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 +62 -54
  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 +70 -58
  8. package/package.json +4 -4
  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
@@ -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
@@ -6,6 +6,7 @@ import nucleotideMutationsSomeDataset from './__mockData__/nucleotideMutationsSo
6
6
  import { MutationComparison, type MutationComparisonProps } from './mutation-comparison';
7
7
  import { LAPIS_URL, NUCLEOTIDE_MUTATIONS_ENDPOINT } from '../../constants';
8
8
  import referenceGenome from '../../lapisApi/__mockData__/referenceGenome.json';
9
+ import { type MutationAnnotations } from '../../web-components/mutation-annotations-context';
9
10
  import { LapisUrlContextProvider } from '../LapisUrlContext';
10
11
  import { MutationAnnotationsContextProvider } from '../MutationAnnotationsContext';
11
12
  import { ReferenceGenomeContext } from '../ReferenceGenomeContext';
@@ -90,8 +91,9 @@ const mutationAnnotations = [
90
91
  symbol: '+',
91
92
  nucleotideMutations: ['C3037T', 'A23403G'],
92
93
  aminoAcidMutations: ['ORF1a:I2230T'],
94
+ aminoAcidPositions: ['ORF1a:3675'],
93
95
  },
94
- ];
96
+ ] satisfies MutationAnnotations;
95
97
 
96
98
  const Template: StoryObj<MutationComparisonProps> = {
97
99
  render: (args) => (
@@ -13,6 +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 { type MutationAnnotations } from '../../web-components/mutation-annotations-context';
16
17
  import { LapisUrlContextProvider } from '../LapisUrlContext';
17
18
  import { MutationAnnotationsContextProvider } from '../MutationAnnotationsContext';
18
19
  import { ReferenceGenomeContext } from '../ReferenceGenomeContext';
@@ -45,6 +46,7 @@ const mutationAnnotations = [
45
46
  description: 'This describes what is special about these mutations.',
46
47
  symbol: '#',
47
48
  nucleotideMutations: ['C241T', 'C3037T'],
49
+ nucleotidePositions: [],
48
50
  aminoAcidMutations: ['N:G204R', 'N:S235F'],
49
51
  },
50
52
  {
@@ -52,9 +54,10 @@ const mutationAnnotations = [
52
54
  description: 'This describes what is special about these other mutations.',
53
55
  symbol: '+',
54
56
  nucleotideMutations: ['C3037T', 'C11750T'],
57
+ nucleotidePositions: ['14408'],
55
58
  aminoAcidMutations: ['ORF1a:S2255F'],
56
59
  },
57
- ];
60
+ ] satisfies MutationAnnotations;
58
61
 
59
62
  const Template = {
60
63
  render: (args: MutationsProps) => (
@@ -5,6 +5,7 @@ import { type Canvas } from '@storybook/types';
5
5
  import { MutationsOverTime, type MutationsOverTimeProps } from './mutations-over-time';
6
6
  import { LAPIS_URL } from '../../constants';
7
7
  import referenceGenome from '../../lapisApi/__mockData__/referenceGenome.json';
8
+ import { type MutationAnnotations } from '../../web-components/mutation-annotations-context';
8
9
  import { LapisUrlContextProvider } from '../LapisUrlContext';
9
10
  import { MutationAnnotationsContextProvider } from '../MutationAnnotationsContext';
10
11
  import { ReferenceGenomeContext } from '../ReferenceGenomeContext';
@@ -49,6 +50,7 @@ const mutationAnnotations = [
49
50
  symbol: '#',
50
51
  nucleotideMutations: ['C44T', 'C774T', 'G24872T', 'T23011-'],
51
52
  aminoAcidMutations: ['S:501Y', 'S:S31-', 'ORF1a:S4286C'],
53
+ nucleotidePositions: ['17334'],
52
54
  },
53
55
  {
54
56
  name: 'I am another mutation annotation!',
@@ -57,7 +59,7 @@ const mutationAnnotations = [
57
59
  nucleotideMutations: ['C44T', 'A13121T'],
58
60
  aminoAcidMutations: ['S:501Y', 'S:S31-', 'ORF1a:S4286C'],
59
61
  },
60
- ];
62
+ ] satisfies MutationAnnotations;
61
63
 
62
64
  export const Default: StoryObj<MutationsOverTimeProps> = {
63
65
  render: (args: MutationsOverTimeProps) => (
@@ -1,3 +1,8 @@
1
1
  .leaflet-container {
2
2
  background: transparent;
3
3
  }
4
+
5
+ .leaflet-interactive {
6
+ outline: none;
7
+ cursor: pointer;
8
+ }
@@ -1,4 +1,5 @@
1
1
  import { type Meta, type StoryObj } from '@storybook/preact';
2
+ import { expect } from '@storybook/test';
2
3
 
3
4
  import { WastewaterMutationsOverTime, type WastewaterMutationsOverTimeProps } from './wastewater-mutations-over-time';
4
5
  import { WISE_DETAILS_ENDPOINT, WISE_LAPIS_URL } from '../../../constants';
@@ -65,3 +66,27 @@ export const Default: StoryObj<WastewaterMutationsOverTimeProps> = {
65
66
  },
66
67
  },
67
68
  };
69
+
70
+ export const AminoAcids: StoryObj<WastewaterMutationsOverTimeProps> = {
71
+ ...Default,
72
+ args: {
73
+ ...Default.args,
74
+ sequenceType: 'amino acid',
75
+ },
76
+ play: async ({ canvas, step }) => {
77
+ await step('Wait for component to render', async () => {
78
+ await canvas.findByText('All segments');
79
+ });
80
+
81
+ await step("Click 'All segments' button", async () => {
82
+ canvas.getByRole('button', { name: 'All segments' }).click();
83
+ await expect(canvas.getByText('Select none')).toBeInTheDocument();
84
+ canvas.getByRole('button', { name: 'Select none' }).click();
85
+ await canvas.findAllByText('No data available for your filters.');
86
+ canvas.getByRole('checkbox', { name: 'S' }).click();
87
+ await canvas.findAllByText('S:Q493E');
88
+ const element = canvas.queryByText(/ORF1a:/);
89
+ await expect(element).not.toBeInTheDocument();
90
+ });
91
+ },
92
+ };
@@ -1,9 +1,10 @@
1
1
  import { type FunctionComponent } from 'preact';
2
- import { type Dispatch, type StateUpdater, useState } from 'preact/hooks';
2
+ import { type Dispatch, type StateUpdater, useMemo, useState } from 'preact/hooks';
3
3
  import z from 'zod';
4
4
 
5
5
  import { computeWastewaterMutationsOverTimeDataPerLocation } from './computeWastewaterMutationsOverTimeDataPerLocation';
6
6
  import { lapisFilterSchema, sequenceTypeSchema } from '../../../types';
7
+ import { Map2dView } from '../../../utils/map2d';
7
8
  import { useLapisUrl } from '../../LapisUrlContext';
8
9
  import { type ColorScale } from '../../components/color-scale-selector';
9
10
  import { ColorScaleSelectorDropdown } from '../../components/color-scale-selector-dropdown';
@@ -13,6 +14,7 @@ import Info, { InfoComponentCode, InfoHeadline1, InfoParagraph } from '../../com
13
14
  import { LoadingDisplay } from '../../components/loading-display';
14
15
  import { NoDataDisplay } from '../../components/no-data-display';
15
16
  import { ResizeContainer } from '../../components/resize-container';
17
+ import { type DisplayedSegment, SegmentSelector } from '../../components/segment-selector';
16
18
  import Tabs from '../../components/tabs';
17
19
  import { type MutationOverTimeDataMap } from '../../mutationsOverTime/MutationOverTimeData';
18
20
  import MutationsOverTimeGrid from '../../mutationsOverTime/mutations-over-time-grid';
@@ -86,28 +88,67 @@ type MutationOverTimeDataPerLocation = {
86
88
  data: MutationOverTimeDataMap;
87
89
  }[];
88
90
 
91
+ function useDisplayedSegments(mutations: MutationOverTimeDataPerLocation) {
92
+ const displayedSegments = useMemo(() => {
93
+ const unique = [
94
+ ...new Set(
95
+ mutations.flatMap(({ data }) => data.getFirstAxisKeys().map((mutation) => mutation.segment || '')),
96
+ ),
97
+ ];
98
+
99
+ return unique.map((segment) => ({ segment, label: segment, checked: true }));
100
+ }, [mutations]);
101
+
102
+ return useState<DisplayedSegment[]>(displayedSegments);
103
+ }
104
+
89
105
  type MutationOverTimeTabsProps = {
90
106
  mutationOverTimeDataPerLocation: MutationOverTimeDataPerLocation;
91
107
  originalComponentProps: WastewaterMutationsOverTimeProps;
92
108
  };
93
109
 
110
+ function getFilteredMutationOverTimeData({
111
+ data,
112
+ displayedSegments,
113
+ }: {
114
+ data: MutationOverTimeDataMap;
115
+ displayedSegments: DisplayedSegment[];
116
+ }): MutationOverTimeDataMap {
117
+ const filteredData = new Map2dView(data);
118
+
119
+ const mutationsToFilterOut = data.getFirstAxisKeys().filter((entry) => {
120
+ return displayedSegments.some((segment) => segment.segment === entry.segment && !segment.checked);
121
+ });
122
+
123
+ mutationsToFilterOut.forEach((entry) => {
124
+ filteredData.deleteRow(entry);
125
+ });
126
+
127
+ return filteredData;
128
+ }
129
+
94
130
  const MutationsOverTimeTabs: FunctionComponent<MutationOverTimeTabsProps> = ({
95
131
  mutationOverTimeDataPerLocation,
96
132
  originalComponentProps,
97
133
  }) => {
98
134
  const [colorScale, setColorScale] = useState<ColorScale>({ min: 0, max: 1, color: 'indigo' });
135
+ const [displayedSegments, setDisplayedSegments] = useDisplayedSegments(mutationOverTimeDataPerLocation);
99
136
 
100
- const tabs = mutationOverTimeDataPerLocation.map(({ location, data }) => ({
101
- title: location,
102
- content: (
103
- <MutationsOverTimeGrid
104
- data={data}
105
- colorScale={colorScale}
106
- pageSizes={originalComponentProps.pageSizes}
107
- sequenceType={originalComponentProps.sequenceType}
108
- />
109
- ),
110
- }));
137
+ const tabs = useMemo(
138
+ () =>
139
+ mutationOverTimeDataPerLocation.map(({ location, data }) => ({
140
+ title: location,
141
+ content: (
142
+ <MutationsOverTimeGrid
143
+ data={getFilteredMutationOverTimeData({ data, displayedSegments })}
144
+ colorScale={colorScale}
145
+ pageSizes={originalComponentProps.pageSizes}
146
+ sequenceType={originalComponentProps.sequenceType}
147
+ />
148
+ ),
149
+ })),
150
+ [mutationOverTimeDataPerLocation, displayedSegments, colorScale, originalComponentProps],
151
+ );
111
152
 
112
153
  const toolbar = (
113
154
  <Toolbar
@@ -115,6 +156,8 @@ const MutationsOverTimeTabs: FunctionComponent<MutationOverTimeTabsProps> = ({
115
156
  setColorScale={setColorScale}
116
157
  originalComponentProps={originalComponentProps}
117
158
  data={mutationOverTimeDataPerLocation}
159
+ displayedSegments={displayedSegments}
160
+ setDisplayedSegments={setDisplayedSegments}
118
161
  />
119
162
  );
120
163
 
@@ -126,12 +169,21 @@ type ToolbarProps = {
126
169
  setColorScale: Dispatch<StateUpdater<ColorScale>>;
127
170
  originalComponentProps: WastewaterMutationsOverTimeProps;
128
171
  data: MutationOverTimeDataPerLocation;
172
+ displayedSegments: DisplayedSegment[];
173
+ setDisplayedSegments: (segments: DisplayedSegment[]) => void;
129
174
  };
130
175
 
131
- const Toolbar: FunctionComponent<ToolbarProps> = ({ colorScale, setColorScale, originalComponentProps }) => {
176
+ const Toolbar: FunctionComponent<ToolbarProps> = ({
177
+ colorScale,
178
+ setColorScale,
179
+ originalComponentProps,
180
+ displayedSegments,
181
+ setDisplayedSegments,
182
+ }) => {
132
183
  return (
133
184
  <>
134
185
  <ColorScaleSelectorDropdown colorScale={colorScale} setColorScale={setColorScale} />
186
+ <SegmentSelector displayedSegments={displayedSegments} setDisplayedSegments={setDisplayedSegments} />
135
187
  <WastewaterMutationsOverTimeInfo originalComponentProps={originalComponentProps} />
136
188
  <Fullscreen />
137
189
  </>
@@ -19,7 +19,9 @@ The mutation annotations can be (optionally) supplied to `gs-app` as a JSON obje
19
19
  description: 'This describes what is special about these mutations.',
20
20
  symbol: '+',
21
21
  nucleotideMutations: ['C44T', 'C774T', 'G24872T', 'T23011-'],
22
+ nucleotidePositions: ['123', '234'],
22
23
  aminoAcidMutations: ['S:501Y', 'S:S31-', 'ORF1a:S4286C'],
24
+ aminoAcidPositions: ['S:123', 'ORF1a:234']
23
25
  },
24
26
  ]"
25
27
  >
@@ -31,3 +33,9 @@ The mutation annotations are then distributed to child components.
31
33
  Whenever we display a mutation (e.g. in the `gs-mutations` table view)
32
34
  we will append the `symbol` of all matching annotations: <span>C44T<sup class='text-red-600'>+</sup></span>.
33
35
  Users can click on the mutation to open a modal that shows the `name` and `description` of the annotation.
36
+
37
+ The annotation can be applied to specific mutations:
38
+
39
+ - `nucleotideMutations: [C44T]` matches only the nucleotide mutation `C44T`,
40
+ - `aminoAcidPositions: [S:123]` matches all amino acid mutations that occur on the gene `S` at position `123`
41
+ - If the pathogen has only one segment, one can omit the segment, writing `123` for any mutation at position `123`.
@@ -51,7 +51,9 @@ const Template: StoryObj<StoryProps> = {
51
51
  description: 'This describes what is special about these mutations.',
52
52
  symbol: '*',
53
53
  nucleotideMutations: ['C44T', 'C774T', 'G24872T', 'T23011-'],
54
+ nucleotidePositions: ['123', '456'],
54
55
  aminoAcidMutations: ['S:501Y', 'S:S31-', 'ORF1a:S4286C'],
56
+ aminoAcidPositions: ['S:123'],
55
57
  },
56
58
  ],
57
59
  },
@@ -53,8 +53,10 @@ export class AppComponent extends LitElement {
53
53
  name: string;
54
54
  description: string;
55
55
  symbol: string;
56
- nucleotideMutations: string[];
57
- aminoAcidMutations: string[];
56
+ nucleotideMutations?: string[];
57
+ nucleotidePositions?: string[];
58
+ aminoAcidMutations?: string[];
59
+ aminoAcidPositions?: string[];
58
60
  }[] = [];
59
61
 
60
62
  /**
@@ -1,12 +1,16 @@
1
1
  import { createContext } from '@lit/context';
2
2
  import z from 'zod';
3
3
 
4
+ const annotations = z.array(z.string());
5
+
4
6
  const mutationAnnotationSchema = z.object({
5
7
  name: z.string(),
6
8
  description: z.string(),
7
9
  symbol: z.string(),
8
- nucleotideMutations: z.array(z.string()),
9
- aminoAcidMutations: z.array(z.string()),
10
+ nucleotideMutations: annotations.optional(),
11
+ nucleotidePositions: annotations.optional(),
12
+ aminoAcidMutations: annotations.optional(),
13
+ aminoAcidPositions: annotations.optional(),
10
14
  });
11
15
  export type MutationAnnotation = z.infer<typeof mutationAnnotationSchema>;
12
16