@genspectrum/dashboard-components 0.6.13 → 0.6.15

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 (78) hide show
  1. package/README.md +29 -0
  2. package/custom-elements.json +23 -17
  3. package/dist/dashboard-components.js +8237 -37898
  4. package/dist/dashboard-components.js.map +1 -1
  5. package/dist/genspectrum-components.d.ts +26 -8
  6. package/dist/style.css +3 -3
  7. package/package.json +9 -8
  8. package/src/index.ts +8 -0
  9. package/src/lapisApi/lapisApi.ts +15 -7
  10. package/src/operator/FetchAggregatedOperator.ts +2 -7
  11. package/src/preact/components/color-scale-selector-dropdown.stories.tsx +1 -1
  12. package/src/preact/components/error-boundary.stories.tsx +21 -4
  13. package/src/preact/components/error-display.stories.tsx +20 -2
  14. package/src/preact/components/error-display.tsx +64 -10
  15. package/src/preact/components/info.stories.tsx +5 -5
  16. package/src/preact/components/info.tsx +2 -4
  17. package/src/preact/components/percent-intput.tsx +7 -2
  18. package/src/preact/components/proportion-selector.tsx +12 -2
  19. package/src/preact/dateRangeSelector/date-range-selector.stories.tsx +2 -3
  20. package/src/preact/dateRangeSelector/date-range-selector.tsx +4 -4
  21. package/src/preact/lineageFilter/lineage-filter.stories.tsx +2 -3
  22. package/src/preact/lineageFilter/lineage-filter.tsx +2 -2
  23. package/src/preact/locationFilter/fetchAutocompletionList.ts +1 -14
  24. package/src/preact/locationFilter/location-filter.stories.tsx +2 -3
  25. package/src/preact/locationFilter/location-filter.tsx +2 -2
  26. package/src/preact/mutationFilter/mutation-filter.stories.tsx +2 -3
  27. package/src/preact/mutationsOverTime/__mockData__/aggregated_2024_01.json +13 -0
  28. package/src/preact/mutationsOverTime/__mockData__/aggregated_2024_02.json +13 -0
  29. package/src/preact/mutationsOverTime/__mockData__/aggregated_2024_03.json +13 -0
  30. package/src/preact/mutationsOverTime/__mockData__/aggregated_2024_04.json +13 -0
  31. package/src/preact/mutationsOverTime/__mockData__/aggregated_2024_05.json +13 -0
  32. package/src/preact/mutationsOverTime/__mockData__/aggregated_2024_06.json +13 -0
  33. package/src/preact/mutationsOverTime/__mockData__/aggregated_2024_07.json +13 -0
  34. package/src/preact/mutationsOverTime/__mockData__/aggregated_20_01_2024.json +13 -0
  35. package/src/preact/mutationsOverTime/__mockData__/aggregated_21_01_2024.json +13 -0
  36. package/src/preact/mutationsOverTime/__mockData__/aggregated_22_01_2024.json +13 -0
  37. package/src/preact/mutationsOverTime/__mockData__/aggregated_23_01_2024.json +13 -0
  38. package/src/preact/mutationsOverTime/__mockData__/aggregated_24_01_2024.json +13 -0
  39. package/src/preact/mutationsOverTime/__mockData__/aggregated_25_01_2024.json +13 -0
  40. package/src/preact/mutationsOverTime/__mockData__/aggregated_26_01_2024.json +13 -0
  41. package/src/preact/mutationsOverTime/__mockData__/aggregated_tooManyMutations_total.json +13 -0
  42. package/src/preact/mutationsOverTime/__mockData__/aggregated_week3_2024.json +13 -0
  43. package/src/preact/mutationsOverTime/__mockData__/aggregated_week4_2024.json +13 -0
  44. package/src/preact/mutationsOverTime/__mockData__/aggregated_week5_2024.json +13 -0
  45. package/src/preact/mutationsOverTime/__mockData__/aggregated_week6_2024.json +13 -0
  46. package/src/preact/mutationsOverTime/getFilteredMutationsOverTime.spec.ts +56 -8
  47. package/src/preact/mutationsOverTime/mutations-over-time-grid.tsx +4 -2
  48. package/src/preact/mutationsOverTime/mutations-over-time.stories.tsx +135 -0
  49. package/src/preact/prevalenceOverTime/prevalence-over-time.tsx +3 -3
  50. package/src/preact/relativeGrowthAdvantage/relative-growth-advantage.tsx +1 -1
  51. package/src/preact/shared/floating-ui/hooks.ts +1 -1
  52. package/src/preact/textInput/text-input.stories.tsx +2 -3
  53. package/src/preact/textInput/text-input.tsx +2 -2
  54. package/src/query/queryMutationsOverTime.spec.ts +210 -64
  55. package/src/query/queryMutationsOverTime.ts +10 -2
  56. package/src/web-components/app.stories.ts +0 -2
  57. package/src/web-components/errorHandling.mdx +8 -0
  58. package/src/web-components/input/gs-date-range-selector.stories.ts +2 -3
  59. package/src/web-components/input/gs-date-range-selector.tsx +24 -4
  60. package/src/web-components/input/gs-lineage-filter.stories.ts +2 -3
  61. package/src/web-components/input/gs-lineage-filter.tsx +15 -1
  62. package/src/web-components/input/gs-location-filter.stories.ts +2 -3
  63. package/src/web-components/input/gs-location-filter.tsx +13 -1
  64. package/src/web-components/input/gs-mutation-filter.stories.ts +2 -3
  65. package/src/web-components/input/gs-mutation-filter.tsx +1 -0
  66. package/src/web-components/input/gs-text-input.stories.ts +2 -3
  67. package/src/web-components/input/gs-text-input.tsx +13 -1
  68. package/src/web-components/visualization/gs-aggregate.tsx +17 -1
  69. package/src/web-components/visualization/gs-mutation-comparison.tsx +9 -0
  70. package/src/web-components/visualization/gs-mutations-over-time.stories.ts +271 -0
  71. package/src/web-components/visualization/gs-mutations-over-time.tsx +7 -0
  72. package/src/web-components/visualization/gs-mutations.tsx +11 -5
  73. package/src/web-components/visualization/gs-number-sequences-over-time.tsx +15 -0
  74. package/src/web-components/visualization/gs-prevalence-over-time.stories.ts +8 -9
  75. package/src/web-components/visualization/gs-prevalence-over-time.tsx +26 -8
  76. package/src/web-components/visualization/gs-relative-growth-advantage.tsx +43 -5
  77. package/standalone-bundle/dashboard-components.js +30920 -0
  78. package/standalone-bundle/dashboard-components.js.map +1 -0
@@ -1,8 +1,16 @@
1
1
  import { type Meta, type StoryObj } from '@storybook/preact';
2
2
  import { expect, waitFor } from '@storybook/test';
3
3
 
4
+ import aggregated_01 from './__mockData__/aggregated_2024_01.json';
5
+ import aggregated_02 from './__mockData__/aggregated_2024_02.json';
6
+ import aggregated_03 from './__mockData__/aggregated_2024_03.json';
7
+ import aggregated_04 from './__mockData__/aggregated_2024_04.json';
8
+ import aggregated_05 from './__mockData__/aggregated_2024_05.json';
9
+ import aggregated_06 from './__mockData__/aggregated_2024_06.json';
10
+ import aggregated_07 from './__mockData__/aggregated_2024_07.json';
4
11
  import aggregated_date from './__mockData__/aggregated_date.json';
5
12
  import aggregated_tooManyMutations from './__mockData__/aggregated_tooManyMutations.json';
13
+ import aggregated_tooManyMutations_total from './__mockData__/aggregated_tooManyMutations_total.json';
6
14
  import nucleotideMutation_01 from './__mockData__/nucleotideMutations_2024_01.json';
7
15
  import nucleotideMutation_02 from './__mockData__/nucleotideMutations_2024_02.json';
8
16
  import nucleotideMutation_03 from './__mockData__/nucleotideMutations_2024_03.json';
@@ -91,6 +99,118 @@ export const Default: StoryObj<MutationsOverTimeProps> = {
91
99
  body: aggregated_date,
92
100
  },
93
101
  },
102
+ {
103
+ matcher: {
104
+ name: 'aggregated_01',
105
+ url: AGGREGATED_ENDPOINT,
106
+ body: {
107
+ dateFrom: '2024-01-01',
108
+ dateTo: '2024-01-31',
109
+ fields: [],
110
+ pangoLineage: 'JN.1*',
111
+ },
112
+ },
113
+ response: {
114
+ status: 200,
115
+ body: aggregated_01,
116
+ },
117
+ },
118
+ {
119
+ matcher: {
120
+ name: 'aggregated_02',
121
+ url: AGGREGATED_ENDPOINT,
122
+ body: {
123
+ dateFrom: '2024-02-01',
124
+ dateTo: '2024-02-29',
125
+ fields: [],
126
+ pangoLineage: 'JN.1*',
127
+ },
128
+ },
129
+ response: {
130
+ status: 200,
131
+ body: aggregated_02,
132
+ },
133
+ },
134
+ {
135
+ matcher: {
136
+ name: 'aggregated_03',
137
+ url: AGGREGATED_ENDPOINT,
138
+ body: {
139
+ dateFrom: '2024-03-01',
140
+ dateTo: '2024-03-31',
141
+ fields: [],
142
+ pangoLineage: 'JN.1*',
143
+ },
144
+ },
145
+ response: {
146
+ status: 200,
147
+ body: aggregated_03,
148
+ },
149
+ },
150
+ {
151
+ matcher: {
152
+ name: 'aggregated_04',
153
+ url: AGGREGATED_ENDPOINT,
154
+ body: {
155
+ dateFrom: '2024-04-01',
156
+ dateTo: '2024-04-30',
157
+ fields: [],
158
+ pangoLineage: 'JN.1*',
159
+ },
160
+ },
161
+ response: {
162
+ status: 200,
163
+ body: aggregated_04,
164
+ },
165
+ },
166
+ {
167
+ matcher: {
168
+ name: 'aggregated_05',
169
+ url: AGGREGATED_ENDPOINT,
170
+ body: {
171
+ dateFrom: '2024-05-01',
172
+ dateTo: '2024-05-31',
173
+ fields: [],
174
+ pangoLineage: 'JN.1*',
175
+ },
176
+ },
177
+ response: {
178
+ status: 200,
179
+ body: aggregated_05,
180
+ },
181
+ },
182
+ {
183
+ matcher: {
184
+ name: 'aggregated_06',
185
+ url: AGGREGATED_ENDPOINT,
186
+ body: {
187
+ dateFrom: '2024-06-01',
188
+ dateTo: '2024-06-30',
189
+ fields: [],
190
+ pangoLineage: 'JN.1*',
191
+ },
192
+ },
193
+ response: {
194
+ status: 200,
195
+ body: aggregated_06,
196
+ },
197
+ },
198
+ {
199
+ matcher: {
200
+ name: 'aggregated_07',
201
+ url: AGGREGATED_ENDPOINT,
202
+ body: {
203
+ dateFrom: '2024-07-01',
204
+ dateTo: '2024-07-31',
205
+ fields: [],
206
+ pangoLineage: 'JN.1*',
207
+ },
208
+ },
209
+ response: {
210
+ status: 200,
211
+ body: aggregated_07,
212
+ },
213
+ },
94
214
  {
95
215
  matcher: {
96
216
  name: 'nucleotideMutations_overall',
@@ -253,6 +373,21 @@ export const ShowsMessageWhenTooManyMutations: StoryObj<MutationsOverTimeProps>
253
373
  body: aggregated_tooManyMutations,
254
374
  },
255
375
  },
376
+ {
377
+ matcher: {
378
+ name: 'aggregated_total',
379
+ url: AGGREGATED_ENDPOINT,
380
+ body: {
381
+ dateFrom: '2023-01-01',
382
+ dateTo: '2023-12-31',
383
+ fields: [],
384
+ },
385
+ },
386
+ response: {
387
+ status: 200,
388
+ body: aggregated_tooManyMutations_total,
389
+ },
390
+ },
256
391
  {
257
392
  matcher: {
258
393
  name: 'nucleotideMutations',
@@ -38,8 +38,8 @@ export interface PrevalenceOverTimeProps {
38
38
  confidenceIntervalMethods: ConfidenceIntervalMethod[];
39
39
  lapisDateField: string;
40
40
  pageSize: boolean | number;
41
- yAxisMaxLinear?: AxisMax;
42
- yAxisMaxLogarithmic?: AxisMax;
41
+ yAxisMaxLinear: AxisMax;
42
+ yAxisMaxLogarithmic: AxisMax;
43
43
  }
44
44
 
45
45
  export const PrevalenceOverTime: FunctionComponent<PrevalenceOverTimeProps> = (componentProps) => {
@@ -69,7 +69,7 @@ export const PrevalenceOverTimeInner: FunctionComponent<PrevalenceOverTimeProps>
69
69
  lapis,
70
70
  lapisDateField,
71
71
  ),
72
- [lapis, numeratorFilter, denominatorFilter, granularity, smoothingWindow],
72
+ [lapis, numeratorFilter, denominatorFilter, granularity, smoothingWindow, lapisDateField],
73
73
  );
74
74
 
75
75
  if (isLoading) {
@@ -66,7 +66,7 @@ export const RelativeGrowthAdvantageInner: FunctionComponent<RelativeGrowthAdvan
66
66
 
67
67
  const { data, error, isLoading } = useQuery(
68
68
  () => queryRelativeGrowthAdvantage(numeratorFilter, denominatorFilter, generationTime, lapis, lapisDateField),
69
- [lapis, numeratorFilter, denominatorFilter, generationTime, views],
69
+ [lapis, numeratorFilter, denominatorFilter, generationTime, views, lapisDateField],
70
70
  );
71
71
 
72
72
  if (isLoading) {
@@ -9,7 +9,7 @@ export function useFloatingUi(
9
9
  middleware?: Array<Middleware | null | undefined | false>,
10
10
  placement?: Placement,
11
11
  ) {
12
- const cleanupRef = useRef<Function | null>(null);
12
+ const cleanupRef = useRef<() => void | null>(null);
13
13
 
14
14
  useEffect(() => {
15
15
  if (!referenceRef.current || !floatingRef.current) {
@@ -1,9 +1,9 @@
1
- import { withActions } from '@storybook/addon-actions/decorator';
2
1
  import { type Meta, type StoryObj } from '@storybook/preact';
3
2
  import { expect, waitFor, within } from '@storybook/test';
4
3
 
5
4
  import data from './__mockData__/aggregated_hosts.json';
6
5
  import { TextInput, type TextInputProps } from './text-input';
6
+ import { previewHandles } from '../../../.storybook/preview';
7
7
  import { AGGREGATED_ENDPOINT, LAPIS_URL } from '../../constants';
8
8
  import { LapisUrlContext } from '../LapisUrlContext';
9
9
 
@@ -12,7 +12,7 @@ const meta: Meta<TextInputProps> = {
12
12
  component: TextInput,
13
13
  parameters: {
14
14
  actions: {
15
- handles: ['gs-text-input-changed'],
15
+ handles: ['gs-text-input-changed', ...previewHandles],
16
16
  },
17
17
  fetchMock: {
18
18
  mocks: [
@@ -32,7 +32,6 @@ const meta: Meta<TextInputProps> = {
32
32
  ],
33
33
  },
34
34
  },
35
- decorators: [withActions],
36
35
  argTypes: {
37
36
  lapisField: {
38
37
  control: {
@@ -12,8 +12,8 @@ import { useQuery } from '../useQuery';
12
12
 
13
13
  export interface TextInputInnerProps {
14
14
  lapisField: string;
15
- placeholderText?: string;
16
- initialValue?: string;
15
+ placeholderText: string;
16
+ initialValue: string;
17
17
  }
18
18
 
19
19
  export interface TextInputProps extends TextInputInnerProps {
@@ -8,15 +8,44 @@ describe('queryMutationsOverTime', () => {
8
8
  const lapisFilter = { field1: 'value1', field2: 'value2' };
9
9
  const dateField = 'dateField';
10
10
 
11
- lapisRequestMocks.aggregated(
12
- { ...lapisFilter, fields: [dateField] },
11
+ lapisRequestMocks.multipleAggregated([
13
12
  {
14
- data: [
15
- { count: 1, [dateField]: '2023-01-01' },
16
- { count: 2, [dateField]: '2023-01-03' },
17
- ],
13
+ body: { ...lapisFilter, fields: [dateField] },
14
+ response: {
15
+ data: [
16
+ { count: 1, [dateField]: '2023-01-01' },
17
+ { count: 2, [dateField]: '2023-01-03' },
18
+ ],
19
+ },
18
20
  },
19
- );
21
+ {
22
+ body: {
23
+ ...lapisFilter,
24
+ dateFieldFrom: '2023-01-01',
25
+ dateFieldTo: '2023-01-01',
26
+ fields: [],
27
+ },
28
+ response: { data: [{ count: 11 }] },
29
+ },
30
+ {
31
+ body: {
32
+ ...lapisFilter,
33
+ dateFieldFrom: '2023-01-02',
34
+ dateFieldTo: '2023-01-02',
35
+ fields: [],
36
+ },
37
+ response: { data: [{ count: 12 }] },
38
+ },
39
+ {
40
+ body: {
41
+ ...lapisFilter,
42
+ dateFieldFrom: '2023-01-03',
43
+ dateFieldTo: '2023-01-03',
44
+ fields: [],
45
+ },
46
+ response: { data: [{ count: 13 }] },
47
+ },
48
+ ]);
20
49
 
21
50
  lapisRequestMocks.multipleMutations(
22
51
  [
@@ -53,16 +82,16 @@ describe('queryMutationsOverTime', () => {
53
82
 
54
83
  const result = await queryMutationsOverTimeData(lapisFilter, 'nucleotide', DUMMY_LAPIS_URL, dateField, 'day');
55
84
 
56
- expect(result.getAsArray({ count: 0, proportion: 0 })).to.deep.equal([
85
+ expect(result.getAsArray({ count: 0, proportion: 0, totalCount: 0 })).to.deep.equal([
57
86
  [
58
- { proportion: 0.1, count: 1 },
59
- { proportion: 0.2, count: 2 },
60
- { proportion: 0.3, count: 3 },
87
+ { proportion: 0.1, count: 1, totalCount: 11 },
88
+ { proportion: 0.2, count: 2, totalCount: 12 },
89
+ { proportion: 0.3, count: 3, totalCount: 13 },
61
90
  ],
62
91
  [
63
- { proportion: 0.4, count: 4 },
64
- { proportion: 0, count: 0 },
65
- { proportion: 0, count: 0 },
92
+ { proportion: 0.4, count: 4, totalCount: 11 },
93
+ { proportion: 0, count: 0, totalCount: 0 },
94
+ { proportion: 0, count: 0, totalCount: 0 },
66
95
  ],
67
96
  ]);
68
97
 
@@ -80,15 +109,44 @@ describe('queryMutationsOverTime', () => {
80
109
  const lapisFilter = { field1: 'value1', field2: 'value2' };
81
110
  const dateField = 'dateField';
82
111
 
83
- lapisRequestMocks.aggregated(
84
- { ...lapisFilter, fields: [dateField] },
112
+ lapisRequestMocks.multipleAggregated([
113
+ {
114
+ body: { ...lapisFilter, fields: [dateField] },
115
+ response: {
116
+ data: [
117
+ { count: 1, [dateField]: '2023-01-01' },
118
+ { count: 2, [dateField]: '2023-01-03' },
119
+ ],
120
+ },
121
+ },
85
122
  {
86
- data: [
87
- { count: 1, [dateField]: '2023-01-01' },
88
- { count: 2, [dateField]: '2023-01-03' },
89
- ],
123
+ body: {
124
+ ...lapisFilter,
125
+ dateFieldFrom: '2023-01-01',
126
+ dateFieldTo: '2023-01-01',
127
+ fields: [],
128
+ },
129
+ response: { data: [{ count: 11 }] },
90
130
  },
91
- );
131
+ {
132
+ body: {
133
+ ...lapisFilter,
134
+ dateFieldFrom: '2023-01-02',
135
+ dateFieldTo: '2023-01-02',
136
+ fields: [],
137
+ },
138
+ response: { data: [{ count: 12 }] },
139
+ },
140
+ {
141
+ body: {
142
+ ...lapisFilter,
143
+ dateFieldFrom: '2023-01-03',
144
+ dateFieldTo: '2023-01-03',
145
+ fields: [],
146
+ },
147
+ response: { data: [{ count: 13 }] },
148
+ },
149
+ ]);
92
150
 
93
151
  lapisRequestMocks.multipleMutations(
94
152
  [
@@ -125,16 +183,16 @@ describe('queryMutationsOverTime', () => {
125
183
 
126
184
  const result = await queryMutationsOverTimeData(lapisFilter, 'nucleotide', DUMMY_LAPIS_URL, dateField, 'day');
127
185
 
128
- expect(result.getAsArray({ count: 0, proportion: 0 })).to.deep.equal([
186
+ expect(result.getAsArray({ count: 0, proportion: 0, totalCount: 0 })).to.deep.equal([
129
187
  [
130
- { proportion: 0.1, count: 1 },
131
- { proportion: 0.3, count: 3 },
132
- { proportion: 0, count: 0 },
188
+ { proportion: 0.1, count: 1, totalCount: 11 },
189
+ { proportion: 0.3, count: 3, totalCount: 13 },
190
+ { proportion: 0, count: 0, totalCount: 0 },
133
191
  ],
134
192
  [
135
- { proportion: 0.4, count: 4 },
136
- { proportion: 0, count: 0 },
137
- { proportion: 0, count: 0 },
193
+ { proportion: 0.4, count: 4, totalCount: 11 },
194
+ { proportion: 0, count: 0, totalCount: 0 },
195
+ { proportion: 0, count: 0, totalCount: 0 },
138
196
  ],
139
197
  ]);
140
198
 
@@ -152,15 +210,44 @@ describe('queryMutationsOverTime', () => {
152
210
  const lapisFilter = { field1: 'value1', field2: 'value2' };
153
211
  const dateField = 'dateField';
154
212
 
155
- lapisRequestMocks.aggregated(
156
- { ...lapisFilter, fields: [dateField] },
213
+ lapisRequestMocks.multipleAggregated([
214
+ {
215
+ body: { ...lapisFilter, fields: [dateField] },
216
+ response: {
217
+ data: [
218
+ { count: 1, [dateField]: '2023-01-01' },
219
+ { count: 2, [dateField]: '2023-01-03' },
220
+ ],
221
+ },
222
+ },
157
223
  {
158
- data: [
159
- { count: 1, [dateField]: '2023-01-01' },
160
- { count: 2, [dateField]: '2023-01-03' },
161
- ],
224
+ body: {
225
+ ...lapisFilter,
226
+ dateFieldFrom: '2023-01-01',
227
+ dateFieldTo: '2023-01-01',
228
+ fields: [],
229
+ },
230
+ response: { data: [{ count: 11 }] },
162
231
  },
163
- );
232
+ {
233
+ body: {
234
+ ...lapisFilter,
235
+ dateFieldFrom: '2023-01-02',
236
+ dateFieldTo: '2023-01-02',
237
+ fields: [],
238
+ },
239
+ response: { data: [{ count: 12 }] },
240
+ },
241
+ {
242
+ body: {
243
+ ...lapisFilter,
244
+ dateFieldFrom: '2023-01-03',
245
+ dateFieldTo: '2023-01-03',
246
+ fields: [],
247
+ },
248
+ response: { data: [{ count: 13 }] },
249
+ },
250
+ ]);
164
251
 
165
252
  lapisRequestMocks.multipleMutations(
166
253
  [
@@ -197,7 +284,7 @@ describe('queryMutationsOverTime', () => {
197
284
 
198
285
  const result = await queryMutationsOverTimeData(lapisFilter, 'nucleotide', DUMMY_LAPIS_URL, dateField, 'day');
199
286
 
200
- expect(result.getAsArray({ count: 0, proportion: 0 })).to.deep.equal([]);
287
+ expect(result.getAsArray({ count: 0, proportion: 0, totalCount: 0 })).to.deep.equal([]);
201
288
  expect(result.getFirstAxisKeys()).to.deep.equal([]);
202
289
  expect(result.getSecondAxisKeys()).to.deep.equal([]);
203
290
  });
@@ -206,15 +293,35 @@ describe('queryMutationsOverTime', () => {
206
293
  const dateField = 'dateField';
207
294
  const lapisFilter = { field1: 'value1', field2: 'value2', [`${dateField}From`]: '2023-01-02' };
208
295
 
209
- lapisRequestMocks.aggregated(
210
- { ...lapisFilter, fields: [dateField] },
296
+ lapisRequestMocks.multipleAggregated([
297
+ {
298
+ body: { ...lapisFilter, fields: [dateField] },
299
+ response: {
300
+ data: [
301
+ { count: 1, [dateField]: '2023-01-01' },
302
+ { count: 2, [dateField]: '2023-01-03' },
303
+ ],
304
+ },
305
+ },
211
306
  {
212
- data: [
213
- { count: 1, [dateField]: '2023-01-01' },
214
- { count: 2, [dateField]: '2023-01-03' },
215
- ],
307
+ body: {
308
+ ...lapisFilter,
309
+ dateFieldFrom: '2023-01-02',
310
+ dateFieldTo: '2023-01-02',
311
+ fields: [],
312
+ },
313
+ response: { data: [{ count: 11 }] },
216
314
  },
217
- );
315
+ {
316
+ body: {
317
+ ...lapisFilter,
318
+ dateFieldFrom: '2023-01-03',
319
+ dateFieldTo: '2023-01-03',
320
+ fields: [],
321
+ },
322
+ response: { data: [{ count: 12 }] },
323
+ },
324
+ ]);
218
325
 
219
326
  lapisRequestMocks.multipleMutations(
220
327
  [
@@ -242,10 +349,10 @@ describe('queryMutationsOverTime', () => {
242
349
 
243
350
  const result = await queryMutationsOverTimeData(lapisFilter, 'nucleotide', DUMMY_LAPIS_URL, dateField, 'day');
244
351
 
245
- expect(result.getAsArray({ count: 0, proportion: 0 })).to.deep.equal([
352
+ expect(result.getAsArray({ count: 0, proportion: 0, totalCount: 0 })).to.deep.equal([
246
353
  [
247
- { proportion: 0.2, count: 2 },
248
- { proportion: 0.3, count: 3 },
354
+ { proportion: 0.2, count: 2, totalCount: 11 },
355
+ { proportion: 0.3, count: 3, totalCount: 12 },
249
356
  ],
250
357
  ]);
251
358
 
@@ -261,15 +368,35 @@ describe('queryMutationsOverTime', () => {
261
368
  const dateField = 'dateField';
262
369
  const lapisFilter = { field1: 'value1', field2: 'value2', [`${dateField}To`]: '2023-01-02' };
263
370
 
264
- lapisRequestMocks.aggregated(
265
- { ...lapisFilter, fields: [dateField] },
371
+ lapisRequestMocks.multipleAggregated([
266
372
  {
267
- data: [
268
- { count: 1, [dateField]: '2023-01-01' },
269
- { count: 2, [dateField]: '2023-01-03' },
270
- ],
373
+ body: { ...lapisFilter, fields: [dateField] },
374
+ response: {
375
+ data: [
376
+ { count: 1, [dateField]: '2023-01-01' },
377
+ { count: 2, [dateField]: '2023-01-03' },
378
+ ],
379
+ },
271
380
  },
272
- );
381
+ {
382
+ body: {
383
+ ...lapisFilter,
384
+ dateFieldFrom: '2023-01-01',
385
+ dateFieldTo: '2023-01-01',
386
+ fields: [],
387
+ },
388
+ response: { data: [{ count: 11 }] },
389
+ },
390
+ {
391
+ body: {
392
+ ...lapisFilter,
393
+ dateFieldFrom: '2023-01-02',
394
+ dateFieldTo: '2023-01-02',
395
+ fields: [],
396
+ },
397
+ response: { data: [{ count: 12 }] },
398
+ },
399
+ ]);
273
400
 
274
401
  lapisRequestMocks.multipleMutations(
275
402
  [
@@ -297,10 +424,10 @@ describe('queryMutationsOverTime', () => {
297
424
 
298
425
  const result = await queryMutationsOverTimeData(lapisFilter, 'nucleotide', DUMMY_LAPIS_URL, dateField, 'day');
299
426
 
300
- expect(result.getAsArray({ count: 0, proportion: 0 })).to.deep.equal([
427
+ expect(result.getAsArray({ count: 0, proportion: 0, totalCount: 0 })).to.deep.equal([
301
428
  [
302
- { proportion: 0.1, count: 1 },
303
- { proportion: 0.2, count: 2 },
429
+ { proportion: 0.1, count: 1, totalCount: 11 },
430
+ { proportion: 0.2, count: 2, totalCount: 12 },
304
431
  ],
305
432
  ]);
306
433
 
@@ -316,15 +443,26 @@ describe('queryMutationsOverTime', () => {
316
443
  const dateField = 'dateField';
317
444
  const lapisFilter = { field1: 'value1', field2: 'value2', [dateField]: '2023-01-02' };
318
445
 
319
- lapisRequestMocks.aggregated(
320
- { ...lapisFilter, fields: [dateField] },
446
+ lapisRequestMocks.multipleAggregated([
321
447
  {
322
- data: [
323
- { count: 1, [dateField]: '2023-01-01' },
324
- { count: 2, [dateField]: '2023-01-03' },
325
- ],
448
+ body: { ...lapisFilter, fields: [dateField] },
449
+ response: {
450
+ data: [
451
+ { count: 1, [dateField]: '2023-01-01' },
452
+ { count: 2, [dateField]: '2023-01-03' },
453
+ ],
454
+ },
326
455
  },
327
- );
456
+ {
457
+ body: {
458
+ ...lapisFilter,
459
+ dateFieldFrom: '2023-01-02',
460
+ dateFieldTo: '2023-01-02',
461
+ fields: [],
462
+ },
463
+ response: { data: [{ count: 11 }] },
464
+ },
465
+ ]);
328
466
 
329
467
  lapisRequestMocks.multipleMutations(
330
468
  [
@@ -343,7 +481,15 @@ describe('queryMutationsOverTime', () => {
343
481
 
344
482
  const result = await queryMutationsOverTimeData(lapisFilter, 'nucleotide', DUMMY_LAPIS_URL, dateField, 'day');
345
483
 
346
- expect(result.getAsArray({ count: 0, proportion: 0 })).to.deep.equal([[{ proportion: 0.2, count: 2 }]]);
484
+ expect(result.getAsArray({ count: 0, proportion: 0, totalCount: 0 })).to.deep.equal([
485
+ [
486
+ {
487
+ proportion: 0.2,
488
+ count: 2,
489
+ totalCount: 11,
490
+ },
491
+ ],
492
+ ]);
347
493
 
348
494
  const sequences = result.getFirstAxisKeys();
349
495
  expect(sequences[0].code).toBe('sequenceName:A123T');
@@ -25,9 +25,10 @@ import {
25
25
  export type MutationOverTimeData = {
26
26
  date: Temporal;
27
27
  mutations: SubstitutionOrDeletionEntry[];
28
+ totalCount: number;
28
29
  };
29
30
 
30
- export type MutationOverTimeMutationValue = { proportion: number; count: number };
31
+ export type MutationOverTimeMutationValue = { proportion: number; count: number; totalCount: number };
31
32
  export type MutationOverTimeDataGroupedByMutation = Map2d<
32
33
  Substitution | Deletion,
33
34
  Temporal,
@@ -75,9 +76,11 @@ export async function queryMutationsOverTimeData(
75
76
  };
76
77
 
77
78
  const data = await fetchAndPrepareSubstitutionsOrDeletions(filter, sequenceType).evaluate(lapis, signal);
79
+ const totalCountQuery = await getTotalNumberOfSequencesInDateRange(filter).evaluate(lapis, signal);
78
80
  return {
79
81
  date,
80
82
  mutations: data.content,
83
+ totalCount: totalCountQuery.content[0].count,
81
84
  };
82
85
  });
83
86
 
@@ -164,6 +167,7 @@ export function groupByMutation(data: MutationOverTimeData[]) {
164
167
  dataArray.set(mutationEntry.mutation, mutationData.date, {
165
168
  count: mutationEntry.count,
166
169
  proportion: mutationEntry.proportion,
170
+ totalCount: mutationData.totalCount,
167
171
  });
168
172
  });
169
173
  });
@@ -181,8 +185,12 @@ function addZeroValuesForDatesWithNoMutationData(
181
185
  const someMutation = dataArray.getFirstAxisKeys()[0];
182
186
  data.forEach((mutationData) => {
183
187
  if (mutationData.mutations.length === 0) {
184
- dataArray.set(someMutation, mutationData.date, { count: 0, proportion: 0 });
188
+ dataArray.set(someMutation, mutationData.date, { count: 0, proportion: 0, totalCount: 0 });
185
189
  }
186
190
  });
187
191
  }
188
192
  }
193
+
194
+ function getTotalNumberOfSequencesInDateRange(filter: LapisFilter) {
195
+ return new FetchAggregatedOperator<{ count: number }>(filter);
196
+ }
@@ -1,5 +1,4 @@
1
1
  import { consume } from '@lit/context';
2
- import { withActions } from '@storybook/addon-actions/decorator';
3
2
  import { expect, waitFor, within } from '@storybook/test';
4
3
  import type { Meta, StoryObj } from '@storybook/web-components';
5
4
  import { html, LitElement } from 'lit';
@@ -30,7 +29,6 @@ const meta: Meta = {
30
29
  codeExample,
31
30
  },
32
31
  }),
33
- decorators: [withActions],
34
32
  tags: ['autodocs'],
35
33
  };
36
34