@genspectrum/dashboard-components 0.19.4 → 0.19.5

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/dist/util.d.ts CHANGED
@@ -486,6 +486,19 @@ declare const namedLapisFilterSchema: default_2.ZodObject<{
486
486
  displayName: string;
487
487
  }>;
488
488
 
489
+ export declare type NumberRange = default_2.infer<typeof numberRangeSchema>;
490
+
491
+ declare const numberRangeSchema: default_2.ZodObject<{
492
+ min: default_2.ZodOptional<default_2.ZodNumber>;
493
+ max: default_2.ZodOptional<default_2.ZodNumber>;
494
+ }, "strip", default_2.ZodTypeAny, {
495
+ min?: number | undefined;
496
+ max?: number | undefined;
497
+ }, {
498
+ min?: number | undefined;
499
+ max?: number | undefined;
500
+ }>;
501
+
489
502
  export declare type NumberSequencesOverTimeProps = default_2.infer<typeof numberSequencesOverTimePropsSchema>;
490
503
 
491
504
  declare const numberSequencesOverTimePropsSchema: default_2.ZodObject<{
@@ -1058,11 +1071,7 @@ declare global {
1058
1071
 
1059
1072
  declare global {
1060
1073
  interface HTMLElementTagNameMap {
1061
- 'gs-date-range-filter': DateRangeFilterComponent;
1062
- }
1063
- interface HTMLElementEventMap {
1064
- [gsEventNames.dateRangeFilterChanged]: CustomEvent<Record<string, string>>;
1065
- [gsEventNames.dateRangeOptionChanged]: DateRangeOptionChangedEvent;
1074
+ 'gs-statistics': StatisticsComponent;
1066
1075
  }
1067
1076
  }
1068
1077
 
@@ -1070,7 +1079,7 @@ declare global {
1070
1079
  declare global {
1071
1080
  namespace JSX {
1072
1081
  interface IntrinsicElements {
1073
- 'gs-date-range-filter': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1082
+ 'gs-statistics': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1074
1083
  }
1075
1084
  }
1076
1085
  }
@@ -1078,10 +1087,7 @@ declare global {
1078
1087
 
1079
1088
  declare global {
1080
1089
  interface HTMLElementTagNameMap {
1081
- 'gs-location-filter': LocationFilterComponent;
1082
- }
1083
- interface HTMLElementEventMap {
1084
- [gsEventNames.locationChanged]: LocationChangedEvent;
1090
+ 'gs-wastewater-mutations-over-time': WastewaterMutationsOverTimeComponent;
1085
1091
  }
1086
1092
  }
1087
1093
 
@@ -1089,7 +1095,7 @@ declare global {
1089
1095
  declare global {
1090
1096
  namespace JSX {
1091
1097
  interface IntrinsicElements {
1092
- 'gs-location-filter': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1098
+ 'gs-wastewater-mutations-over-time': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1093
1099
  }
1094
1100
  }
1095
1101
  }
@@ -1097,7 +1103,11 @@ declare global {
1097
1103
 
1098
1104
  declare global {
1099
1105
  interface HTMLElementTagNameMap {
1100
- 'gs-statistics': StatisticsComponent;
1106
+ 'gs-date-range-filter': DateRangeFilterComponent;
1107
+ }
1108
+ interface HTMLElementEventMap {
1109
+ [gsEventNames.dateRangeFilterChanged]: CustomEvent<Record<string, string>>;
1110
+ [gsEventNames.dateRangeOptionChanged]: DateRangeOptionChangedEvent;
1101
1111
  }
1102
1112
  }
1103
1113
 
@@ -1105,7 +1115,7 @@ declare global {
1105
1115
  declare global {
1106
1116
  namespace JSX {
1107
1117
  interface IntrinsicElements {
1108
- 'gs-statistics': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1118
+ 'gs-date-range-filter': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1109
1119
  }
1110
1120
  }
1111
1121
  }
@@ -1132,10 +1142,10 @@ declare global {
1132
1142
 
1133
1143
  declare global {
1134
1144
  interface HTMLElementTagNameMap {
1135
- 'gs-lineage-filter': LineageFilterComponent;
1145
+ 'gs-location-filter': LocationFilterComponent;
1136
1146
  }
1137
1147
  interface HTMLElementEventMap {
1138
- [gsEventNames.lineageFilterChanged]: LineageFilterChangedEvent;
1148
+ [gsEventNames.locationChanged]: LocationChangedEvent;
1139
1149
  }
1140
1150
  }
1141
1151
 
@@ -1143,7 +1153,7 @@ declare global {
1143
1153
  declare global {
1144
1154
  namespace JSX {
1145
1155
  interface IntrinsicElements {
1146
- 'gs-lineage-filter': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1156
+ 'gs-location-filter': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1147
1157
  }
1148
1158
  }
1149
1159
  }
@@ -1170,11 +1180,10 @@ declare global {
1170
1180
 
1171
1181
  declare global {
1172
1182
  interface HTMLElementTagNameMap {
1173
- 'gs-number-range-filter': NumberRangeFilterComponent;
1183
+ 'gs-lineage-filter': LineageFilterComponent;
1174
1184
  }
1175
1185
  interface HTMLElementEventMap {
1176
- [gsEventNames.numberRangeFilterChanged]: NumberRangeFilterChangedEvent;
1177
- [gsEventNames.numberRangeValueChanged]: NumberRangeValueChangedEvent;
1186
+ [gsEventNames.lineageFilterChanged]: LineageFilterChangedEvent;
1178
1187
  }
1179
1188
  }
1180
1189
 
@@ -1182,7 +1191,7 @@ declare global {
1182
1191
  declare global {
1183
1192
  namespace JSX {
1184
1193
  interface IntrinsicElements {
1185
- 'gs-number-range-filter': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1194
+ 'gs-lineage-filter': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1186
1195
  }
1187
1196
  }
1188
1197
  }
@@ -1190,7 +1199,11 @@ declare global {
1190
1199
 
1191
1200
  declare global {
1192
1201
  interface HTMLElementTagNameMap {
1193
- 'gs-wastewater-mutations-over-time': WastewaterMutationsOverTimeComponent;
1202
+ 'gs-number-range-filter': NumberRangeFilterComponent;
1203
+ }
1204
+ interface HTMLElementEventMap {
1205
+ [gsEventNames.numberRangeFilterChanged]: NumberRangeFilterChangedEvent;
1206
+ [gsEventNames.numberRangeValueChanged]: NumberRangeValueChangedEvent;
1194
1207
  }
1195
1208
  }
1196
1209
 
@@ -1198,7 +1211,7 @@ declare global {
1198
1211
  declare global {
1199
1212
  namespace JSX {
1200
1213
  interface IntrinsicElements {
1201
- 'gs-wastewater-mutations-over-time': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1214
+ 'gs-number-range-filter': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1202
1215
  }
1203
1216
  }
1204
1217
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@genspectrum/dashboard-components",
3
- "version": "0.19.4",
3
+ "version": "0.19.5",
4
4
  "description": "GenSpectrum web components for building dashboards",
5
5
  "type": "module",
6
6
  "license": "AGPL-3.0-only",
@@ -51,7 +51,9 @@ export function getFilteredMutationOverTimeData({
51
51
  return true;
52
52
  }
53
53
 
54
- if (applySearchFilter(entry.mutation, sequenceType, mutationFilterValue, annotationProvider)) {
54
+ if (
55
+ mutationOrAnnotationDoNotMatchFilter(entry.mutation, sequenceType, mutationFilterValue, annotationProvider)
56
+ ) {
55
57
  return true;
56
58
  }
57
59
 
@@ -67,7 +69,7 @@ export function getFilteredMutationOverTimeData({
67
69
  return filteredData;
68
70
  }
69
71
 
70
- export function applySearchFilter(
72
+ export function mutationOrAnnotationDoNotMatchFilter(
71
73
  mutation: Mutation,
72
74
  sequenceType: SequenceType,
73
75
  filterValue: string,
@@ -1,5 +1,6 @@
1
1
  import { type Meta, type StoryObj } from '@storybook/preact';
2
- import { expect, userEvent } from '@storybook/test';
2
+ import { expect, userEvent, waitFor } from '@storybook/test';
3
+ import type { Canvas } from '@storybook/types';
3
4
 
4
5
  import { WastewaterMutationsOverTime, type WastewaterMutationsOverTimeProps } from './wastewater-mutations-over-time';
5
6
  import { WISE_DETAILS_ENDPOINT, WISE_LAPIS_URL } from '../../../constants';
@@ -7,6 +8,7 @@ import referenceGenome from '../../../lapisApi/__mockData__/referenceGenome.json
7
8
  import { LapisUrlContextProvider } from '../../LapisUrlContext';
8
9
  import { ReferenceGenomeContext } from '../../ReferenceGenomeContext';
9
10
  import details from './__mockData__/details.json';
11
+ import type { MutationsOverTimeProps } from '../../mutationsOverTime/mutations-over-time';
10
12
 
11
13
  const meta: Meta<WastewaterMutationsOverTimeProps> = {
12
14
  title: 'Wastewater visualization/Wastewater mutations over time',
@@ -108,3 +110,35 @@ export const AminoAcids: StoryObj<WastewaterMutationsOverTimeProps> = {
108
110
  });
109
111
  },
110
112
  };
113
+
114
+ export const UsesMutationFilter: StoryObj<MutationsOverTimeProps> = {
115
+ ...Default,
116
+ play: async ({ canvas, step }) => {
117
+ await expectMutationOnPage(canvas, 'A966C');
118
+
119
+ await step('input filter', async () => {
120
+ const filterButton = canvas.getByRole('button', { name: 'Filter mutations' });
121
+ await userEvent.click(filterButton);
122
+
123
+ const filterInput = canvas.getByPlaceholderText('Filter');
124
+ await userEvent.type(filterInput, '26');
125
+ });
126
+
127
+ await step('should show only matching filter', async () => {
128
+ await expectMutationOnPage(canvas, 'T4026G');
129
+ await expectMutationOnPage(canvas, 'T5260C');
130
+
131
+ await waitFor(async () => {
132
+ const filteredMutation = canvas.queryByText('A966C');
133
+ await expect(filteredMutation).not.toBeInTheDocument();
134
+ });
135
+ });
136
+ },
137
+ };
138
+
139
+ async function expectMutationOnPage(canvas: Canvas, mutation: string) {
140
+ await waitFor(async () => {
141
+ const mutationOnFirstPage = canvas.getAllByText(mutation)[0];
142
+ await expect(mutationOnFirstPage).toBeVisible();
143
+ });
144
+ }
@@ -3,20 +3,23 @@ import { type Dispatch, type StateUpdater, useMemo, useState } from 'preact/hook
3
3
  import z from 'zod';
4
4
 
5
5
  import { computeWastewaterMutationsOverTimeDataPerLocation } from './computeWastewaterMutationsOverTimeDataPerLocation';
6
- import { lapisFilterSchema, sequenceTypeSchema } from '../../../types';
6
+ import { lapisFilterSchema, type SequenceType, sequenceTypeSchema } from '../../../types';
7
7
  import { Map2dView } from '../../../utils/map2d';
8
8
  import { useLapisUrl } from '../../LapisUrlContext';
9
+ import { useMutationAnnotationsProvider } from '../../MutationAnnotationsContext';
9
10
  import { type ColorScale } from '../../components/color-scale-selector';
10
11
  import { ColorScaleSelectorDropdown } from '../../components/color-scale-selector-dropdown';
11
12
  import { ErrorBoundary } from '../../components/error-boundary';
12
13
  import { Fullscreen } from '../../components/fullscreen';
13
14
  import Info, { InfoComponentCode, InfoHeadline1, InfoParagraph } from '../../components/info';
14
15
  import { LoadingDisplay } from '../../components/loading-display';
16
+ import { MutationsOverTimeTextFilter } from '../../components/mutations-over-time-text-filter';
15
17
  import { NoDataDisplay } from '../../components/no-data-display';
16
18
  import { ResizeContainer } from '../../components/resize-container';
17
19
  import { type DisplayedSegment, SegmentSelector } from '../../components/segment-selector';
18
20
  import Tabs from '../../components/tabs';
19
21
  import { type MutationOverTimeDataMap } from '../../mutationsOverTime/MutationOverTimeData';
22
+ import { mutationOrAnnotationDoNotMatchFilter } from '../../mutationsOverTime/getFilteredMutationsOverTimeData';
20
23
  import MutationsOverTimeGrid from '../../mutationsOverTime/mutations-over-time-grid';
21
24
  import { pageSizesSchema } from '../../shared/tanstackTable/pagination';
22
25
  import { PageSizeContextProvider } from '../../shared/tanstackTable/pagination-context';
@@ -111,13 +114,23 @@ type MutationOverTimeTabsProps = {
111
114
  function getFilteredMutationOverTimeData({
112
115
  data,
113
116
  displayedSegments,
117
+ mutationFilterValue,
118
+ annotationProvider,
119
+ sequenceType,
114
120
  }: {
115
121
  data: MutationOverTimeDataMap;
116
122
  displayedSegments: DisplayedSegment[];
123
+ mutationFilterValue: string;
124
+ sequenceType: SequenceType;
125
+ annotationProvider: ReturnType<typeof useMutationAnnotationsProvider>;
117
126
  }): MutationOverTimeDataMap {
118
127
  const filteredData = new Map2dView(data);
119
128
 
120
129
  const mutationsToFilterOut = data.getFirstAxisKeys().filter((entry) => {
130
+ if (mutationOrAnnotationDoNotMatchFilter(entry, sequenceType, mutationFilterValue, annotationProvider)) {
131
+ return true;
132
+ }
133
+
121
134
  return displayedSegments.some((segment) => segment.segment === entry.segment && !segment.checked);
122
135
  });
123
136
 
@@ -132,6 +145,9 @@ const MutationsOverTimeTabs: FunctionComponent<MutationOverTimeTabsProps> = ({
132
145
  mutationOverTimeDataPerLocation,
133
146
  originalComponentProps,
134
147
  }) => {
148
+ const [mutationFilterValue, setMutationFilterValue] = useState('');
149
+ const annotationProvider = useMutationAnnotationsProvider();
150
+
135
151
  const [colorScale, setColorScale] = useState<ColorScale>({ min: 0, max: 1, color: 'indigo' });
136
152
  const [displayedSegments, setDisplayedSegments] = useDisplayedSegments(mutationOverTimeDataPerLocation);
137
153
 
@@ -141,14 +157,28 @@ const MutationsOverTimeTabs: FunctionComponent<MutationOverTimeTabsProps> = ({
141
157
  title: location,
142
158
  content: (
143
159
  <MutationsOverTimeGrid
144
- data={getFilteredMutationOverTimeData({ data, displayedSegments })}
160
+ data={getFilteredMutationOverTimeData({
161
+ data,
162
+ displayedSegments,
163
+ mutationFilterValue,
164
+ annotationProvider,
165
+ sequenceType: originalComponentProps.sequenceType,
166
+ })}
145
167
  colorScale={colorScale}
146
168
  pageSizes={originalComponentProps.pageSizes}
147
169
  sequenceType={originalComponentProps.sequenceType}
148
170
  />
149
171
  ),
150
172
  })),
151
- [mutationOverTimeDataPerLocation, displayedSegments, colorScale, originalComponentProps],
173
+ [
174
+ mutationOverTimeDataPerLocation,
175
+ displayedSegments,
176
+ mutationFilterValue,
177
+ annotationProvider,
178
+ colorScale,
179
+ originalComponentProps.pageSizes,
180
+ originalComponentProps.sequenceType,
181
+ ],
152
182
  );
153
183
 
154
184
  const toolbar = (
@@ -159,6 +189,8 @@ const MutationsOverTimeTabs: FunctionComponent<MutationOverTimeTabsProps> = ({
159
189
  data={mutationOverTimeDataPerLocation}
160
190
  displayedSegments={displayedSegments}
161
191
  setDisplayedSegments={setDisplayedSegments}
192
+ setFilterValue={setMutationFilterValue}
193
+ mutationFilterValue={mutationFilterValue}
162
194
  />
163
195
  );
164
196
 
@@ -176,6 +208,8 @@ type ToolbarProps = {
176
208
  data: MutationOverTimeDataPerLocation;
177
209
  displayedSegments: DisplayedSegment[];
178
210
  setDisplayedSegments: (segments: DisplayedSegment[]) => void;
211
+ mutationFilterValue: string;
212
+ setFilterValue: (filterValue: string) => void;
179
213
  };
180
214
 
181
215
  const Toolbar: FunctionComponent<ToolbarProps> = ({
@@ -184,9 +218,12 @@ const Toolbar: FunctionComponent<ToolbarProps> = ({
184
218
  originalComponentProps,
185
219
  displayedSegments,
186
220
  setDisplayedSegments,
221
+ setFilterValue,
222
+ mutationFilterValue,
187
223
  }) => {
188
224
  return (
189
225
  <>
226
+ <MutationsOverTimeTextFilter setFilterValue={setFilterValue} value={mutationFilterValue} />
190
227
  <ColorScaleSelectorDropdown colorScale={colorScale} setColorScale={setColorScale} />
191
228
  <SegmentSelector
192
229
  displayedSegments={displayedSegments}
@@ -40,3 +40,5 @@ export { TextFilterChangedEvent } from './preact/textFilter/TextFilterChangedEve
40
40
  export type { MutationAnnotations, MutationAnnotation } from './web-components/mutation-annotations-context';
41
41
 
42
42
  export { gsEventNames } from './utils/gsEventNames';
43
+
44
+ export { type NumberRange } from './preact/numberRangeFilter/NumberRangeFilterChangedEvent';