@genspectrum/dashboard-components 1.16.0 → 1.17.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 (36) hide show
  1. package/custom-elements.json +3 -3
  2. package/dist/components.d.ts +4 -4
  3. package/dist/components.js +449 -246
  4. package/dist/components.js.map +1 -1
  5. package/dist/util.d.ts +6 -6
  6. package/package.json +1 -1
  7. package/src/preact/MutationAnnotationsContext.tsx +1 -1
  8. package/src/preact/components/csv-download-button.tsx +22 -14
  9. package/src/preact/components/features-over-time-grid.tsx +189 -43
  10. package/src/preact/components/mutations-over-time-mutations-filter.stories.tsx +1 -1
  11. package/src/preact/components/mutations-over-time-mutations-filter.tsx +1 -1
  12. package/src/preact/mutationsOverTime/__mockData__/aminoAcidMutationsByDay/aminoAcidMutationsOverTimePage1.json +52 -0
  13. package/src/preact/mutationsOverTime/__mockData__/byWeek/mutationsOverTimePage1.json +76 -0
  14. package/src/preact/mutationsOverTime/__mockData__/defaultMockData/mockDefaultMutationsOverTimeWithFilter.json +43 -0
  15. package/src/preact/mutationsOverTime/__mockData__/defaultMockData/mutationsOverTimePage1.json +126 -0
  16. package/src/preact/mutationsOverTime/__mockData__/defaultMockData/mutationsOverTimePage2.json +116 -0
  17. package/src/preact/mutationsOverTime/__mockData__/defaultMockData/mutationsOverTimePageSize20.json +216 -0
  18. package/src/preact/mutationsOverTime/getFilteredMutationCodes.spec.ts +236 -0
  19. package/src/preact/mutationsOverTime/{getFilteredMutationsOverTimeData.ts → getFilteredMutationCodes.ts} +29 -44
  20. package/src/preact/mutationsOverTime/mutations-over-time.stories.tsx +128 -23
  21. package/src/preact/mutationsOverTime/mutations-over-time.tsx +139 -74
  22. package/src/preact/mutationsOverTime/useMutationsOverTimePageData.ts +111 -0
  23. package/src/preact/shared/tanstackTable/pagination-context.tsx +5 -2
  24. package/src/preact/shared/tanstackTable/pagination.tsx +11 -9
  25. package/src/preact/shared/tanstackTable/tanstackTable.tsx +7 -4
  26. package/src/preact/wastewater/mutationsOverTime/wastewater-mutations-over-time.tsx +1 -1
  27. package/src/query/queryMutationsOverTime.spec.ts +187 -662
  28. package/src/query/queryMutationsOverTime.ts +46 -33
  29. package/src/utils/useControlledState.ts +15 -0
  30. package/src/web-components/visualization/gs-mutations-over-time.stories.ts +78 -22
  31. package/standalone-bundle/dashboard-components.js +6872 -6690
  32. package/standalone-bundle/dashboard-components.js.map +1 -1
  33. package/src/preact/mutationsOverTime/__mockData__/aminoAcidMutationsByDay/aminoAcidMutationsOverTime.json +0 -5496
  34. package/src/preact/mutationsOverTime/__mockData__/byWeek/mutationsOverTime.json +0 -7100
  35. package/src/preact/mutationsOverTime/__mockData__/defaultMockData/mutationsOverTime.json +0 -12646
  36. package/src/preact/mutationsOverTime/getFilteredMutationsOverTime.spec.ts +0 -417
@@ -0,0 +1,236 @@
1
+ import { describe, expect, it } from 'vitest';
2
+
3
+ import { getFilteredMutationCodes, type MutationFilter } from './getFilteredMutationCodes';
4
+ import { type DeletionEntry, type SubstitutionEntry } from '../../types';
5
+ import { type Deletion, type Substitution } from '../../utils/mutations';
6
+ import { type MutationAnnotations } from '../../web-components/mutation-annotations-context';
7
+ import { getMutationAnnotationsContext, getMutationAnnotationsProvider } from '../MutationAnnotationsContext';
8
+
9
+ describe('getFilteredMutationCodes', () => {
10
+ it('should filter by displayed segments', () => {
11
+ const result = getFilteredMutationCodes({
12
+ overallMutationData: [someSubstitutionEntry, anotherSubstitutionEntry, someDeletionEntry],
13
+ displayedSegments: [
14
+ { segment: 'someSegment', checked: false, label: 'Some Segment' },
15
+ { segment: 'someOtherSegment', checked: true, label: 'Some Other Segment' },
16
+ ],
17
+ displayedMutationTypes: [],
18
+ proportionInterval,
19
+ mutationFilterValue: { textFilter: '', annotationNameFilter: new Set() },
20
+ sequenceType: 'nucleotide',
21
+ annotationProvider: () => [],
22
+ });
23
+
24
+ expect(result).to.deep.equal([anotherSubstitution.code]);
25
+ });
26
+
27
+ it('should filter by mutation types', () => {
28
+ const result = getFilteredMutationCodes({
29
+ overallMutationData: [someSubstitutionEntry, anotherSubstitutionEntry, someDeletionEntry],
30
+ displayedSegments: [],
31
+ displayedMutationTypes: [
32
+ { type: 'substitution', checked: false, label: 'Substitution' },
33
+ { type: 'deletion', checked: true, label: 'Deletion' },
34
+ ],
35
+ proportionInterval,
36
+ mutationFilterValue: { textFilter: '', annotationNameFilter: new Set() },
37
+ sequenceType: 'nucleotide',
38
+ annotationProvider: () => [],
39
+ });
40
+
41
+ expect(result).to.deep.equal([someDeletion.code]);
42
+ });
43
+
44
+ it('should remove mutations where overall proportion is below filter', () => {
45
+ const result = getFilteredMutationCodes({
46
+ overallMutationData: [
47
+ { ...someSubstitutionEntry, proportion: belowFilter },
48
+ { ...anotherSubstitutionEntry, proportion: inFilter },
49
+ { ...someDeletionEntry, proportion: inFilter },
50
+ ],
51
+ displayedSegments: [],
52
+ displayedMutationTypes: [],
53
+ proportionInterval,
54
+ mutationFilterValue: { textFilter: '', annotationNameFilter: new Set() },
55
+ sequenceType: 'nucleotide',
56
+ annotationProvider: () => [],
57
+ });
58
+
59
+ expect(result).to.deep.equal([anotherSubstitution.code, someDeletion.code]);
60
+ });
61
+
62
+ it('should remove mutations where overall proportion is above filter', () => {
63
+ const result = getFilteredMutationCodes({
64
+ overallMutationData: [
65
+ { ...someSubstitutionEntry, proportion: aboveFilter },
66
+ { ...anotherSubstitutionEntry, proportion: inFilter },
67
+ { ...someDeletionEntry, proportion: inFilter },
68
+ ],
69
+ displayedSegments: [],
70
+ displayedMutationTypes: [],
71
+ proportionInterval,
72
+ mutationFilterValue: { textFilter: '', annotationNameFilter: new Set() },
73
+ sequenceType: 'nucleotide',
74
+ annotationProvider: () => [],
75
+ });
76
+
77
+ expect(result).to.deep.equal([anotherSubstitution.code, someDeletion.code]);
78
+ });
79
+
80
+ it('should not remove mutations where overall proportion is at lower bound of filter', () => {
81
+ const result = getFilteredMutationCodes({
82
+ overallMutationData: [
83
+ { ...someSubstitutionEntry, proportion: atFilterMin },
84
+ { ...anotherSubstitutionEntry, proportion: inFilter },
85
+ { ...someDeletionEntry, proportion: inFilter },
86
+ ],
87
+ displayedSegments: [],
88
+ displayedMutationTypes: [],
89
+ proportionInterval,
90
+ mutationFilterValue: { textFilter: '', annotationNameFilter: new Set() },
91
+ sequenceType: 'nucleotide',
92
+ annotationProvider: () => [],
93
+ });
94
+
95
+ expect(result).to.deep.equal([someSubstitution.code, anotherSubstitution.code, someDeletion.code]);
96
+ });
97
+
98
+ it('should not remove mutations where overall proportion is at upper bound of filter', () => {
99
+ const result = getFilteredMutationCodes({
100
+ overallMutationData: [
101
+ { ...someSubstitutionEntry, proportion: atFilterMax },
102
+ { ...anotherSubstitutionEntry, proportion: inFilter },
103
+ { ...someDeletionEntry, proportion: inFilter },
104
+ ],
105
+ displayedSegments: [],
106
+ displayedMutationTypes: [],
107
+ proportionInterval,
108
+ mutationFilterValue: { textFilter: '', annotationNameFilter: new Set() },
109
+ sequenceType: 'nucleotide',
110
+ annotationProvider: () => [],
111
+ });
112
+
113
+ expect(result).to.deep.equal([someSubstitution.code, anotherSubstitution.code, someDeletion.code]);
114
+ });
115
+
116
+ it('should filter by mutation filter text value', () => {
117
+ const result = getFilteredMutationCodes({
118
+ overallMutationData: [someSubstitutionEntry, anotherSubstitutionEntry, someDeletionEntry],
119
+ displayedSegments: [],
120
+ displayedMutationTypes: [],
121
+ proportionInterval,
122
+ mutationFilterValue: { textFilter: '23T', annotationNameFilter: new Set() },
123
+ sequenceType: 'nucleotide',
124
+ annotationProvider: () => [],
125
+ });
126
+
127
+ expect(result).to.deep.equal([someSubstitution.code]);
128
+ });
129
+
130
+ describe('should filter by annotation', () => {
131
+ const expectFilteredValue = (filterValue: MutationFilter, annotations: MutationAnnotations) => {
132
+ const annotationProvider = getMutationAnnotationsProvider(getMutationAnnotationsContext(annotations));
133
+
134
+ const result = getFilteredMutationCodes({
135
+ overallMutationData: [someSubstitutionEntry, anotherSubstitutionEntry, someDeletionEntry],
136
+ displayedSegments: [],
137
+ displayedMutationTypes: [],
138
+ proportionInterval,
139
+ mutationFilterValue: filterValue,
140
+ sequenceType: 'nucleotide',
141
+ annotationProvider,
142
+ });
143
+
144
+ expect(result).to.deep.equal([someSubstitution.code]);
145
+ };
146
+
147
+ it('with filter value in symbol', () => {
148
+ expectFilteredValue({ textFilter: '#', annotationNameFilter: new Set() }, [
149
+ { name: 'Annotation 1', description: 'Description 1', symbol: '#', nucleotideMutations: ['A123T'] },
150
+ ]);
151
+ });
152
+
153
+ it('with filter value in name', () => {
154
+ expectFilteredValue({ textFilter: 'Annota', annotationNameFilter: new Set() }, [
155
+ { name: 'Annotation 1 #', description: 'Description 1', symbol: '+', nucleotideMutations: ['A123T'] },
156
+ ]);
157
+ });
158
+
159
+ it('with filter value in description', () => {
160
+ expectFilteredValue({ textFilter: 'Descr', annotationNameFilter: new Set() }, [
161
+ { name: 'Annotation 1', description: 'Description 1', symbol: '#', nucleotideMutations: ['A123T'] },
162
+ ]);
163
+ });
164
+
165
+ it('with annotation name filter', () => {
166
+ expectFilteredValue({ textFilter: '', annotationNameFilter: new Set(['Annotation 1']) }, [
167
+ { name: 'Annotation 1', description: 'Description 1', symbol: '#', nucleotideMutations: ['A123T'] },
168
+ ]);
169
+ });
170
+ });
171
+
172
+ it('should not filter by individual time-series proportions below the overall filter', () => {
173
+ const result = getFilteredMutationCodes({
174
+ overallMutationData: [someSubstitutionEntry, anotherSubstitutionEntry, someDeletionEntry],
175
+ displayedSegments: [],
176
+ displayedMutationTypes: [],
177
+ proportionInterval,
178
+ mutationFilterValue: { textFilter: '', annotationNameFilter: new Set() },
179
+ sequenceType: 'nucleotide',
180
+ annotationProvider: () => [],
181
+ });
182
+
183
+ expect(result).to.deep.equal([someSubstitution.code, anotherSubstitution.code, someDeletion.code]);
184
+ });
185
+
186
+ const belowFilter = 0.1;
187
+ const atFilterMin = 0.2;
188
+ const inFilter = 0.5;
189
+ const atFilterMax = 0.9;
190
+ const aboveFilter = 0.99;
191
+ const proportionInterval = { min: atFilterMin, max: atFilterMax };
192
+
193
+ const someSubstitution: Substitution = {
194
+ type: 'substitution',
195
+ valueAtReference: 'A',
196
+ substitutionValue: 'T',
197
+ code: 'A123T',
198
+ segment: 'someSegment',
199
+ position: 123,
200
+ };
201
+ const someSubstitutionEntry: SubstitutionEntry<Substitution> = {
202
+ type: 'substitution',
203
+ mutation: someSubstitution,
204
+ count: 234,
205
+ proportion: inFilter,
206
+ };
207
+
208
+ const anotherSubstitution: Substitution = {
209
+ type: 'substitution',
210
+ valueAtReference: 'G',
211
+ substitutionValue: 'C',
212
+ code: 'G345C',
213
+ segment: 'someOtherSegment',
214
+ position: 345,
215
+ };
216
+ const anotherSubstitutionEntry: SubstitutionEntry<Substitution> = {
217
+ type: 'substitution',
218
+ mutation: anotherSubstitution,
219
+ count: 456,
220
+ proportion: inFilter,
221
+ };
222
+
223
+ const someDeletion: Deletion = {
224
+ type: 'deletion',
225
+ valueAtReference: 'A',
226
+ segment: 'someSegment',
227
+ position: 567,
228
+ code: 'A123-',
229
+ };
230
+ const someDeletionEntry: DeletionEntry<Deletion> = {
231
+ type: 'deletion',
232
+ mutation: someDeletion,
233
+ count: 789,
234
+ proportion: inFilter,
235
+ };
236
+ });
@@ -1,8 +1,6 @@
1
1
  import z from 'zod';
2
2
 
3
- import { type MutationOverTimeDataMap } from './MutationOverTimeData';
4
3
  import { type SequenceType, type SubstitutionOrDeletionEntry } from '../../types';
5
- import { Map2dView } from '../../utils/map2d';
6
4
  import type { Deletion, Mutation, Substitution } from '../../utils/mutations';
7
5
  import { type useMutationAnnotationsProvider } from '../MutationAnnotationsContext';
8
6
  import type { DisplayedMutationType } from '../components/mutation-type-selector';
@@ -11,7 +9,6 @@ import type { DisplayedSegment } from '../components/segment-selector';
11
9
  export const displayMutationsSchema = z.array(z.string(), {
12
10
  errorMap: () => ({ message: `invalid display mutations` }),
13
11
  });
14
- export type DisplayMutations = z.infer<typeof displayMutationsSchema>;
15
12
 
16
13
  export type MutationFilter = {
17
14
  textFilter: string;
@@ -19,63 +16,51 @@ export type MutationFilter = {
19
16
  };
20
17
 
21
18
  export type GetFilteredMutationOverTimeDataArgs = {
22
- data: MutationOverTimeDataMap;
23
19
  overallMutationData: SubstitutionOrDeletionEntry<Substitution, Deletion>[];
24
20
  displayedSegments: DisplayedSegment[];
25
21
  displayedMutationTypes: DisplayedMutationType[];
26
22
  proportionInterval: { min: number; max: number };
27
- hideGaps: boolean;
28
- displayMutations?: DisplayMutations;
29
23
  mutationFilterValue: MutationFilter;
30
24
  sequenceType: SequenceType;
31
25
  annotationProvider: ReturnType<typeof useMutationAnnotationsProvider>;
32
26
  };
33
27
 
34
- export function getFilteredMutationOverTimeData({
35
- data,
28
+ /**
29
+ * Extracts a list of mutation codes that should be displayed based on the provided filters and overall mutation data.
30
+ */
31
+ export function getFilteredMutationCodes({
36
32
  overallMutationData,
37
33
  displayedSegments,
38
34
  displayedMutationTypes,
39
35
  proportionInterval,
40
- hideGaps,
41
36
  mutationFilterValue,
42
37
  sequenceType,
43
38
  annotationProvider,
44
- }: GetFilteredMutationOverTimeDataArgs) {
45
- const filteredData = new Map2dView(data);
46
-
47
- const mutationsToFilterOut = overallMutationData.filter((entry) => {
48
- if (entry.proportion < proportionInterval.min || entry.proportion > proportionInterval.max) {
49
- return true;
50
- }
51
- if (displayedSegments.some((segment) => segment.segment === entry.mutation.segment && !segment.checked)) {
52
- return true;
53
- }
54
-
55
- if (
56
- mutationOrAnnotationDoNotMatchFilter(entry.mutation, sequenceType, mutationFilterValue, annotationProvider)
57
- ) {
58
- return true;
59
- }
60
-
61
- return displayedMutationTypes.some(
62
- (mutationType) => mutationType.type === entry.mutation.type && !mutationType.checked,
63
- );
64
- });
65
-
66
- mutationsToFilterOut.forEach((entry) => {
67
- filteredData.deleteRow(entry.mutation);
68
- });
69
-
70
- if (hideGaps) {
71
- const dateRangesToFilterOut = filteredData.getSecondAxisKeys().filter((dateRange) => {
72
- const vals = filteredData.getColumn(dateRange);
73
- return !vals.some((v) => (v?.type === 'value' || v?.type === 'valueWithCoverage') && v.totalCount > 0);
74
- });
75
- dateRangesToFilterOut.forEach((dateRange) => filteredData.deleteColumn(dateRange));
76
- }
77
-
78
- return filteredData;
39
+ }: GetFilteredMutationOverTimeDataArgs): string[] {
40
+ return overallMutationData
41
+ .filter((entry) => {
42
+ if (entry.proportion < proportionInterval.min || entry.proportion > proportionInterval.max) {
43
+ return false;
44
+ }
45
+ if (displayedSegments.some((segment) => segment.segment === entry.mutation.segment && !segment.checked)) {
46
+ return false;
47
+ }
48
+
49
+ if (
50
+ mutationOrAnnotationDoNotMatchFilter(
51
+ entry.mutation,
52
+ sequenceType,
53
+ mutationFilterValue,
54
+ annotationProvider,
55
+ )
56
+ ) {
57
+ return false;
58
+ }
59
+ return !displayedMutationTypes.some(
60
+ (mutationType) => mutationType.type === entry.mutation.type && !mutationType.checked,
61
+ );
62
+ })
63
+ .map((e) => e.mutation.code);
79
64
  }
80
65
 
81
66
  export function mutationOrAnnotationDoNotMatchFilter(
@@ -8,7 +8,10 @@ import { type MutationAnnotations } from '../../web-components/mutation-annotati
8
8
  import { LapisUrlContextProvider } from '../LapisUrlContext';
9
9
  import { MutationAnnotationsContextProvider } from '../MutationAnnotationsContext';
10
10
  import { ReferenceGenomeContext } from '../ReferenceGenomeContext';
11
- import mockDefaultMutationsOverTime from './__mockData__/defaultMockData/mutationsOverTime.json';
11
+ import mockDefaultMutationsOverTimeWithFilter from './__mockData__/defaultMockData/mockDefaultMutationsOverTimeWithFilter.json';
12
+ import mockDefaultMutationsOverTimePage1 from './__mockData__/defaultMockData/mutationsOverTimePage1.json';
13
+ import mockDefaultMutationsOverTimePage2 from './__mockData__/defaultMockData/mutationsOverTimePage2.json';
14
+ import mockDefaultMutationsOverTimePageSize20 from './__mockData__/defaultMockData/mutationsOverTimePageSize20.json';
12
15
  import mockDefaultNucleotideMutations from './__mockData__/defaultMockData/nucleotideMutations.json';
13
16
  import mock1800sMutationsOverTime from './__mockData__/request1800s/mutationsOverTime.json';
14
17
  import mock1800sNucleotideMutations from './__mockData__/request1800s/nucleotideMutations.json';
@@ -64,13 +67,10 @@ const meta: Meta<MutationsOverTimeProps> = {
64
67
  },
65
68
  {
66
69
  matcher: {
70
+ name: 'page1',
67
71
  url: `${LAPIS_URL}/component/nucleotideMutationsOverTime`,
68
72
  body: {
69
- filters: {
70
- pangoLineage: 'JN.1*',
71
- dateFrom: '2024-01-15',
72
- dateTo: '2024-07-10',
73
- },
73
+ filters: { pangoLineage: 'JN.1*', dateFrom: '2024-01-15', dateTo: '2024-07-10' },
74
74
  dateRanges: [
75
75
  { dateFrom: '2024-01-01', dateTo: '2024-01-31' },
76
76
  { dateFrom: '2024-02-01', dateTo: '2024-02-29' },
@@ -80,12 +80,125 @@ const meta: Meta<MutationsOverTimeProps> = {
80
80
  { dateFrom: '2024-06-01', dateTo: '2024-06-30' },
81
81
  { dateFrom: '2024-07-01', dateTo: '2024-07-31' },
82
82
  ],
83
+ includeMutations: [
84
+ 'C44T',
85
+ 'C774T',
86
+ 'C7113T',
87
+ 'C12616T',
88
+ 'A13121T',
89
+ 'G15372T',
90
+ 'G17334T',
91
+ 'T18453C',
92
+ 'A19722G',
93
+ 'T21653-',
94
+ ],
83
95
  dateField: 'date',
84
96
  },
85
- matchPartialBody: true, // includeMutations left out
86
97
  response: {
87
98
  status: 200,
88
- body: mockDefaultMutationsOverTime,
99
+ body: mockDefaultMutationsOverTimePage1,
100
+ },
101
+ },
102
+ },
103
+ {
104
+ matcher: {
105
+ name: 'page2',
106
+ url: `${LAPIS_URL}/component/nucleotideMutationsOverTime`,
107
+ body: {
108
+ filters: { pangoLineage: 'JN.1*', dateFrom: '2024-01-15', dateTo: '2024-07-10' },
109
+ dateRanges: [
110
+ { dateFrom: '2024-01-01', dateTo: '2024-01-31' },
111
+ { dateFrom: '2024-02-01', dateTo: '2024-02-29' },
112
+ { dateFrom: '2024-03-01', dateTo: '2024-03-31' },
113
+ { dateFrom: '2024-04-01', dateTo: '2024-04-30' },
114
+ { dateFrom: '2024-05-01', dateTo: '2024-05-31' },
115
+ { dateFrom: '2024-06-01', dateTo: '2024-06-30' },
116
+ { dateFrom: '2024-07-01', dateTo: '2024-07-31' },
117
+ ],
118
+ includeMutations: [
119
+ 'C21654-',
120
+ 'T21655-',
121
+ 'G22111T',
122
+ 'G22599C',
123
+ 'T22928C',
124
+ 'T23011-',
125
+ 'C23039G',
126
+ 'C23277T',
127
+ 'G24872T',
128
+ ],
129
+ dateField: 'date',
130
+ },
131
+ response: {
132
+ status: 200,
133
+ body: mockDefaultMutationsOverTimePage2,
134
+ },
135
+ },
136
+ },
137
+ {
138
+ matcher: {
139
+ name: 'page1-pageSize20',
140
+ url: `${LAPIS_URL}/component/nucleotideMutationsOverTime`,
141
+ body: {
142
+ filters: { pangoLineage: 'JN.1*', dateFrom: '2024-01-15', dateTo: '2024-07-10' },
143
+ dateRanges: [
144
+ { dateFrom: '2024-01-01', dateTo: '2024-01-31' },
145
+ { dateFrom: '2024-02-01', dateTo: '2024-02-29' },
146
+ { dateFrom: '2024-03-01', dateTo: '2024-03-31' },
147
+ { dateFrom: '2024-04-01', dateTo: '2024-04-30' },
148
+ { dateFrom: '2024-05-01', dateTo: '2024-05-31' },
149
+ { dateFrom: '2024-06-01', dateTo: '2024-06-30' },
150
+ { dateFrom: '2024-07-01', dateTo: '2024-07-31' },
151
+ ],
152
+ includeMutations: [
153
+ 'C44T',
154
+ 'C774T',
155
+ 'C7113T',
156
+ 'C12616T',
157
+ 'A13121T',
158
+ 'G15372T',
159
+ 'G17334T',
160
+ 'T18453C',
161
+ 'A19722G',
162
+ 'T21653-',
163
+ 'C21654-',
164
+ 'T21655-',
165
+ 'G22111T',
166
+ 'G22599C',
167
+ 'T22928C',
168
+ 'T23011-',
169
+ 'C23039G',
170
+ 'C23277T',
171
+ 'G24872T',
172
+ ],
173
+ dateField: 'date',
174
+ },
175
+ response: {
176
+ status: 200,
177
+ body: mockDefaultMutationsOverTimePageSize20,
178
+ },
179
+ },
180
+ },
181
+ {
182
+ matcher: {
183
+ name: 'withFilter',
184
+ url: `${LAPIS_URL}/component/nucleotideMutationsOverTime`,
185
+ body: {
186
+ filters: { pangoLineage: 'JN.1*', dateFrom: '2024-01-15', dateTo: '2024-07-10' },
187
+ dateRanges: [
188
+ { dateFrom: '2024-01-01', dateTo: '2024-01-31' },
189
+ { dateFrom: '2024-02-01', dateTo: '2024-02-29' },
190
+ { dateFrom: '2024-03-01', dateTo: '2024-03-31' },
191
+ { dateFrom: '2024-04-01', dateTo: '2024-04-30' },
192
+ { dateFrom: '2024-05-01', dateTo: '2024-05-31' },
193
+ { dateFrom: '2024-06-01', dateTo: '2024-06-30' },
194
+ { dateFrom: '2024-07-01', dateTo: '2024-07-31' },
195
+ ],
196
+ includeMutations: ['T21653-', 'T21655-'],
197
+ dateField: 'date',
198
+ },
199
+ response: {
200
+ status: 200,
201
+ body: mockDefaultMutationsOverTimeWithFilter,
89
202
  },
90
203
  },
91
204
  },
@@ -270,7 +383,7 @@ export const UsesPagination: StoryObj<MutationsOverTimeProps> = {
270
383
  ...Default,
271
384
  play: async ({ canvas, step }) => {
272
385
  const mutationOnFirstPage = 'C44T';
273
- const mutationOnSecondPage = 'T21653-';
386
+ const mutationOnSecondPage = 'C21654-';
274
387
  await expectMutationOnPage(canvas, mutationOnFirstPage);
275
388
 
276
389
  await step('Navigate to next page', async () => {
@@ -279,11 +392,8 @@ export const UsesPagination: StoryObj<MutationsOverTimeProps> = {
279
392
  await expectMutationOnPage(canvas, mutationOnSecondPage);
280
393
  });
281
394
 
282
- await step('Use goto page input', async () => {
283
- const gotoPageInput = canvas.getByRole('spinbutton', { name: 'Enter page number to go to' });
284
- await userEvent.clear(gotoPageInput);
285
- await userEvent.type(gotoPageInput, '1');
286
- await userEvent.tab();
395
+ await step('Go to previous page', async () => {
396
+ canvas.getByRole('button', { name: 'Previous page' }).click();
287
397
 
288
398
  await expectMutationOnPage(canvas, mutationOnFirstPage);
289
399
  });
@@ -448,16 +558,11 @@ export const ShowsNoDataMessageForStrictFilters: StoryObj<MutationsOverTimeProps
448
558
  play: async ({ canvas }) => {
449
559
  await waitFor(() => expect(canvas.getByText('Grid')).toBeVisible(), { timeout: 10000 });
450
560
 
451
- const button = canvas.getByRole('button', { name: 'Mean proportion 5.0% - 90.0%' });
452
- await userEvent.click(button);
453
-
454
- const minInput = canvas.getAllByLabelText('%')[0];
455
- await userEvent.clear(minInput);
456
- await userEvent.type(minInput, '40');
561
+ const filterButton = canvas.getByRole('button', { name: 'Filter mutations' });
562
+ await userEvent.click(filterButton);
457
563
 
458
- const maxInput = canvas.getAllByLabelText('%')[1];
459
- await userEvent.clear(maxInput);
460
- await userEvent.type(maxInput, '41');
564
+ const filterInput = canvas.getByPlaceholderText('Filter');
565
+ await userEvent.type(filterInput, 'too strict');
461
566
 
462
567
  await waitFor(
463
568
  () => expect(canvas.getByText('No data available for your filters.', { exact: false })).toBeVisible(),