@genspectrum/dashboard-components 1.11.0 → 1.11.1

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
@@ -955,7 +955,7 @@ declare global {
955
955
 
956
956
  declare global {
957
957
  interface HTMLElementTagNameMap {
958
- 'gs-wastewater-mutations-over-time': WastewaterMutationsOverTimeComponent;
958
+ 'gs-genome-data-viewer': GenomeDataViewerComponent;
959
959
  }
960
960
  }
961
961
 
@@ -963,7 +963,7 @@ declare global {
963
963
  declare global {
964
964
  namespace JSX {
965
965
  interface IntrinsicElements {
966
- 'gs-wastewater-mutations-over-time': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
966
+ 'gs-genome-data-viewer': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
967
967
  }
968
968
  }
969
969
  }
@@ -971,11 +971,7 @@ declare global {
971
971
 
972
972
  declare global {
973
973
  interface HTMLElementTagNameMap {
974
- 'gs-date-range-filter': DateRangeFilterComponent;
975
- }
976
- interface HTMLElementEventMap {
977
- [gsEventNames.dateRangeFilterChanged]: CustomEvent<Record<string, string>>;
978
- [gsEventNames.dateRangeOptionChanged]: DateRangeOptionChangedEvent;
974
+ 'gs-mutation-comparison-component': MutationComparisonComponent;
979
975
  }
980
976
  }
981
977
 
@@ -983,7 +979,7 @@ declare global {
983
979
  declare global {
984
980
  namespace JSX {
985
981
  interface IntrinsicElements {
986
- 'gs-date-range-filter': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
982
+ 'gs-mutation-comparison-component': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
987
983
  }
988
984
  }
989
985
  }
@@ -991,10 +987,7 @@ declare global {
991
987
 
992
988
  declare global {
993
989
  interface HTMLElementTagNameMap {
994
- 'gs-location-filter': LocationFilterComponent;
995
- }
996
- interface HTMLElementEventMap {
997
- [gsEventNames.locationChanged]: LocationChangedEvent;
990
+ 'gs-mutations': MutationsComponent;
998
991
  }
999
992
  }
1000
993
 
@@ -1002,7 +995,7 @@ declare global {
1002
995
  declare global {
1003
996
  namespace JSX {
1004
997
  interface IntrinsicElements {
1005
- 'gs-location-filter': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
998
+ 'gs-mutations': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1006
999
  }
1007
1000
  }
1008
1001
  }
@@ -1010,10 +1003,7 @@ declare global {
1010
1003
 
1011
1004
  declare global {
1012
1005
  interface HTMLElementTagNameMap {
1013
- 'gs-text-filter': TextFilterComponent;
1014
- }
1015
- interface HTMLElementEventMap {
1016
- [gsEventNames.textFilterChanged]: TextFilterChangedEvent;
1006
+ 'gs-prevalence-over-time': PrevalenceOverTimeComponent;
1017
1007
  }
1018
1008
  }
1019
1009
 
@@ -1021,7 +1011,7 @@ declare global {
1021
1011
  declare global {
1022
1012
  namespace JSX {
1023
1013
  interface IntrinsicElements {
1024
- 'gs-text-filter': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1014
+ 'gs-prevalence-over-time': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1025
1015
  }
1026
1016
  }
1027
1017
  }
@@ -1029,11 +1019,7 @@ declare global {
1029
1019
 
1030
1020
  declare global {
1031
1021
  interface HTMLElementTagNameMap {
1032
- 'gs-lineage-filter': LineageFilterComponent;
1033
- }
1034
- interface HTMLElementEventMap {
1035
- [gsEventNames.lineageFilterChanged]: LineageFilterChangedEvent;
1036
- [gsEventNames.lineageFilterMultiChanged]: LineageMultiFilterChangedEvent;
1022
+ 'gs-aggregate': AggregateComponent;
1037
1023
  }
1038
1024
  }
1039
1025
 
@@ -1041,7 +1027,7 @@ declare global {
1041
1027
  declare global {
1042
1028
  namespace JSX {
1043
1029
  interface IntrinsicElements {
1044
- 'gs-lineage-filter': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1030
+ 'gs-aggregate': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1045
1031
  }
1046
1032
  }
1047
1033
  }
@@ -1049,10 +1035,7 @@ declare global {
1049
1035
 
1050
1036
  declare global {
1051
1037
  interface HTMLElementTagNameMap {
1052
- 'gs-mutation-filter': MutationFilterComponent;
1053
- }
1054
- interface HTMLElementEventMap {
1055
- [gsEventNames.mutationFilterChanged]: CustomEvent<MutationsFilter>;
1038
+ 'gs-number-sequences-over-time': NumberSequencesOverTimeComponent;
1056
1039
  }
1057
1040
  }
1058
1041
 
@@ -1060,7 +1043,7 @@ declare global {
1060
1043
  declare global {
1061
1044
  namespace JSX {
1062
1045
  interface IntrinsicElements {
1063
- 'gs-mutation-filter': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1046
+ 'gs-number-sequences-over-time': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1064
1047
  }
1065
1048
  }
1066
1049
  }
@@ -1068,11 +1051,7 @@ declare global {
1068
1051
 
1069
1052
  declare global {
1070
1053
  interface HTMLElementTagNameMap {
1071
- 'gs-number-range-filter': NumberRangeFilterComponent;
1072
- }
1073
- interface HTMLElementEventMap {
1074
- [gsEventNames.numberRangeFilterChanged]: NumberRangeFilterChangedEvent;
1075
- [gsEventNames.numberRangeValueChanged]: NumberRangeValueChangedEvent;
1054
+ 'gs-relative-growth-advantage': RelativeGrowthAdvantageComponent;
1076
1055
  }
1077
1056
  }
1078
1057
 
@@ -1080,7 +1059,7 @@ declare global {
1080
1059
  declare global {
1081
1060
  namespace JSX {
1082
1061
  interface IntrinsicElements {
1083
- 'gs-number-range-filter': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1062
+ 'gs-relative-growth-advantage': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1084
1063
  }
1085
1064
  }
1086
1065
  }
@@ -1088,7 +1067,7 @@ declare global {
1088
1067
 
1089
1068
  declare global {
1090
1069
  interface HTMLElementTagNameMap {
1091
- 'gs-genome-data-viewer': GenomeDataViewerComponent;
1070
+ 'gs-mutations-over-time': MutationsOverTimeComponent;
1092
1071
  }
1093
1072
  }
1094
1073
 
@@ -1096,7 +1075,7 @@ declare global {
1096
1075
  declare global {
1097
1076
  namespace JSX {
1098
1077
  interface IntrinsicElements {
1099
- 'gs-genome-data-viewer': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1078
+ 'gs-mutations-over-time': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1100
1079
  }
1101
1080
  }
1102
1081
  }
@@ -1104,7 +1083,7 @@ declare global {
1104
1083
 
1105
1084
  declare global {
1106
1085
  interface HTMLElementTagNameMap {
1107
- 'gs-mutation-comparison-component': MutationComparisonComponent;
1086
+ 'gs-sequences-by-location': SequencesByLocationComponent;
1108
1087
  }
1109
1088
  }
1110
1089
 
@@ -1112,7 +1091,7 @@ declare global {
1112
1091
  declare global {
1113
1092
  namespace JSX {
1114
1093
  interface IntrinsicElements {
1115
- 'gs-mutation-comparison-component': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1094
+ 'gs-sequences-by-location': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1116
1095
  }
1117
1096
  }
1118
1097
  }
@@ -1120,7 +1099,7 @@ declare global {
1120
1099
 
1121
1100
  declare global {
1122
1101
  interface HTMLElementTagNameMap {
1123
- 'gs-mutations': MutationsComponent;
1102
+ 'gs-statistics': StatisticsComponent;
1124
1103
  }
1125
1104
  }
1126
1105
 
@@ -1128,7 +1107,7 @@ declare global {
1128
1107
  declare global {
1129
1108
  namespace JSX {
1130
1109
  interface IntrinsicElements {
1131
- 'gs-mutations': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1110
+ 'gs-statistics': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1132
1111
  }
1133
1112
  }
1134
1113
  }
@@ -1136,7 +1115,11 @@ declare global {
1136
1115
 
1137
1116
  declare global {
1138
1117
  interface HTMLElementTagNameMap {
1139
- 'gs-prevalence-over-time': PrevalenceOverTimeComponent;
1118
+ 'gs-date-range-filter': DateRangeFilterComponent;
1119
+ }
1120
+ interface HTMLElementEventMap {
1121
+ [gsEventNames.dateRangeFilterChanged]: CustomEvent<Record<string, string>>;
1122
+ [gsEventNames.dateRangeOptionChanged]: DateRangeOptionChangedEvent;
1140
1123
  }
1141
1124
  }
1142
1125
 
@@ -1144,7 +1127,7 @@ declare global {
1144
1127
  declare global {
1145
1128
  namespace JSX {
1146
1129
  interface IntrinsicElements {
1147
- 'gs-prevalence-over-time': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1130
+ 'gs-date-range-filter': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1148
1131
  }
1149
1132
  }
1150
1133
  }
@@ -1152,7 +1135,10 @@ declare global {
1152
1135
 
1153
1136
  declare global {
1154
1137
  interface HTMLElementTagNameMap {
1155
- 'gs-relative-growth-advantage': RelativeGrowthAdvantageComponent;
1138
+ 'gs-location-filter': LocationFilterComponent;
1139
+ }
1140
+ interface HTMLElementEventMap {
1141
+ [gsEventNames.locationChanged]: LocationChangedEvent;
1156
1142
  }
1157
1143
  }
1158
1144
 
@@ -1160,7 +1146,7 @@ declare global {
1160
1146
  declare global {
1161
1147
  namespace JSX {
1162
1148
  interface IntrinsicElements {
1163
- 'gs-relative-growth-advantage': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1149
+ 'gs-location-filter': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1164
1150
  }
1165
1151
  }
1166
1152
  }
@@ -1168,7 +1154,10 @@ declare global {
1168
1154
 
1169
1155
  declare global {
1170
1156
  interface HTMLElementTagNameMap {
1171
- 'gs-aggregate': AggregateComponent;
1157
+ 'gs-text-filter': TextFilterComponent;
1158
+ }
1159
+ interface HTMLElementEventMap {
1160
+ [gsEventNames.textFilterChanged]: TextFilterChangedEvent;
1172
1161
  }
1173
1162
  }
1174
1163
 
@@ -1176,7 +1165,7 @@ declare global {
1176
1165
  declare global {
1177
1166
  namespace JSX {
1178
1167
  interface IntrinsicElements {
1179
- 'gs-aggregate': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1168
+ 'gs-text-filter': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1180
1169
  }
1181
1170
  }
1182
1171
  }
@@ -1184,7 +1173,10 @@ declare global {
1184
1173
 
1185
1174
  declare global {
1186
1175
  interface HTMLElementTagNameMap {
1187
- 'gs-number-sequences-over-time': NumberSequencesOverTimeComponent;
1176
+ 'gs-mutation-filter': MutationFilterComponent;
1177
+ }
1178
+ interface HTMLElementEventMap {
1179
+ [gsEventNames.mutationFilterChanged]: CustomEvent<MutationsFilter>;
1188
1180
  }
1189
1181
  }
1190
1182
 
@@ -1192,7 +1184,7 @@ declare global {
1192
1184
  declare global {
1193
1185
  namespace JSX {
1194
1186
  interface IntrinsicElements {
1195
- 'gs-number-sequences-over-time': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1187
+ 'gs-mutation-filter': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1196
1188
  }
1197
1189
  }
1198
1190
  }
@@ -1200,7 +1192,11 @@ declare global {
1200
1192
 
1201
1193
  declare global {
1202
1194
  interface HTMLElementTagNameMap {
1203
- 'gs-mutations-over-time': MutationsOverTimeComponent;
1195
+ 'gs-lineage-filter': LineageFilterComponent;
1196
+ }
1197
+ interface HTMLElementEventMap {
1198
+ [gsEventNames.lineageFilterChanged]: LineageFilterChangedEvent;
1199
+ [gsEventNames.lineageFilterMultiChanged]: LineageMultiFilterChangedEvent;
1204
1200
  }
1205
1201
  }
1206
1202
 
@@ -1208,7 +1204,7 @@ declare global {
1208
1204
  declare global {
1209
1205
  namespace JSX {
1210
1206
  interface IntrinsicElements {
1211
- 'gs-mutations-over-time': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1207
+ 'gs-lineage-filter': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1212
1208
  }
1213
1209
  }
1214
1210
  }
@@ -1216,7 +1212,11 @@ declare global {
1216
1212
 
1217
1213
  declare global {
1218
1214
  interface HTMLElementTagNameMap {
1219
- 'gs-sequences-by-location': SequencesByLocationComponent;
1215
+ 'gs-number-range-filter': NumberRangeFilterComponent;
1216
+ }
1217
+ interface HTMLElementEventMap {
1218
+ [gsEventNames.numberRangeFilterChanged]: NumberRangeFilterChangedEvent;
1219
+ [gsEventNames.numberRangeValueChanged]: NumberRangeValueChangedEvent;
1220
1220
  }
1221
1221
  }
1222
1222
 
@@ -1224,7 +1224,7 @@ declare global {
1224
1224
  declare global {
1225
1225
  namespace JSX {
1226
1226
  interface IntrinsicElements {
1227
- 'gs-sequences-by-location': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1227
+ 'gs-number-range-filter': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1228
1228
  }
1229
1229
  }
1230
1230
  }
@@ -1232,7 +1232,7 @@ declare global {
1232
1232
 
1233
1233
  declare global {
1234
1234
  interface HTMLElementTagNameMap {
1235
- 'gs-statistics': StatisticsComponent;
1235
+ 'gs-wastewater-mutations-over-time': WastewaterMutationsOverTimeComponent;
1236
1236
  }
1237
1237
  }
1238
1238
 
@@ -1240,7 +1240,7 @@ declare global {
1240
1240
  declare global {
1241
1241
  namespace JSX {
1242
1242
  interface IntrinsicElements {
1243
- 'gs-statistics': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1243
+ 'gs-wastewater-mutations-over-time': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1244
1244
  }
1245
1245
  }
1246
1246
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@genspectrum/dashboard-components",
3
- "version": "1.11.0",
3
+ "version": "1.11.1",
4
4
  "description": "GenSpectrum web components for building dashboards",
5
5
  "type": "module",
6
6
  "license": "AGPL-3.0-only",
@@ -68,7 +68,7 @@ export function getFilteredMutationOverTimeData({
68
68
  if (hideGaps) {
69
69
  const dateRangesToFilterOut = filteredData.getSecondAxisKeys().filter((dateRange) => {
70
70
  const vals = filteredData.getColumn(dateRange);
71
- return !vals.some((v) => v?.type === 'value' && v.totalCount > 0);
71
+ return !vals.some((v) => (v?.type === 'value' || v?.type === 'valueWithCoverage') && v.totalCount > 0);
72
72
  });
73
73
  dateRangesToFilterOut.forEach((dateRange) => filteredData.deleteColumn(dateRange));
74
74
  }
@@ -65,6 +65,53 @@ export const WithValue: StoryObj<MutationsOverTimeGridTooltipProps> = {
65
65
  play: async ({ canvasElement }) => {
66
66
  const canvas = within(canvasElement);
67
67
 
68
+ await expect(canvas.getByText('50.00%')).toBeVisible();
69
+ await expect(canvas.getByText('100')).toBeVisible();
70
+ await expect(canvas.getByText('have the mutation A500-.')).toBeVisible();
71
+ await expect(canvas.getByText('200')).toBeVisible();
72
+ await expect(canvas.getByText('have coverage at position 500.')).toBeVisible();
73
+ await expect(canvas.getByText('300')).toBeVisible();
74
+ await expect(canvas.getByText('total in this date range.')).toBeVisible();
75
+ },
76
+ };
77
+
78
+ export const WithValueWithZero: StoryObj<MutationsOverTimeGridTooltipProps> = {
79
+ ...Template,
80
+ args: {
81
+ ...Template.args,
82
+ value: {
83
+ type: 'value',
84
+ proportion: 0,
85
+ count: 0,
86
+ totalCount: 300,
87
+ },
88
+ },
89
+ play: async ({ canvasElement }) => {
90
+ const canvas = within(canvasElement);
91
+
92
+ await expect(canvas.getByText('0.00%')).toBeVisible();
93
+ await expect(canvas.getByText('0')).toBeVisible();
94
+ await expect(canvas.getByText('have the mutation A500-.')).toBeVisible();
95
+ await expect(canvas.queryByText('with coverage at position 500.')).not.toBeInTheDocument();
96
+ await expect(canvas.getByText('300')).toBeVisible();
97
+ await expect(canvas.getByText('total in this date range.')).toBeVisible();
98
+ },
99
+ };
100
+
101
+ export const WithValueWithCoverage: StoryObj<MutationsOverTimeGridTooltipProps> = {
102
+ ...Template,
103
+ args: {
104
+ ...Template.args,
105
+ value: {
106
+ type: 'valueWithCoverage',
107
+ count: 100,
108
+ coverage: 200,
109
+ totalCount: 300,
110
+ },
111
+ },
112
+ play: async ({ canvasElement }) => {
113
+ const canvas = within(canvasElement);
114
+
68
115
  await expect(canvas.getByText('50.00%')).toBeVisible();
69
116
  await expect(canvas.getByText('100')).toBeVisible();
70
117
  await expect(canvas.getByText('have the mutation A500- out of')).toBeVisible();
@@ -21,12 +21,26 @@ export const MutationsOverTimeGridTooltip: FunctionComponent<MutationsOverTimeGr
21
21
  }: MutationsOverTimeGridTooltipProps) => {
22
22
  const dateClass = toTemporalClass(date);
23
23
 
24
- const proportionText =
25
- value === null
26
- ? 'No data'
27
- : value.type === 'belowThreshold'
28
- ? `<${formatProportion(MUTATIONS_OVER_TIME_MIN_PROPORTION)}`
29
- : formatProportion(value.proportion);
24
+ let proportionText = 'No data';
25
+
26
+ if (value !== null) {
27
+ switch (value.type) {
28
+ case 'belowThreshold': {
29
+ proportionText = `<${formatProportion(MUTATIONS_OVER_TIME_MIN_PROPORTION)}`;
30
+ break;
31
+ }
32
+ case 'value':
33
+ case 'wastewaterValue': {
34
+ proportionText = formatProportion(value.proportion);
35
+ break;
36
+ }
37
+ case 'valueWithCoverage': {
38
+ // value.coverage will always be non-zero if we're in this case
39
+ proportionText = formatProportion(value.count / value.coverage);
40
+ break;
41
+ }
42
+ }
43
+ }
30
44
 
31
45
  return (
32
46
  <div>
@@ -61,21 +75,50 @@ const TooltipValueCountsDescription: FunctionComponent<{
61
75
  }
62
76
  return (
63
77
  <div className='mt-2'>
64
- {value.type === 'belowThreshold' ? (
65
- <p className='text-gray-600'>
66
- None or less than {formatProportion(MUTATIONS_OVER_TIME_MIN_PROPORTION)} have the mutation.
67
- </p>
68
- ) : (
69
- <>
70
- <p>
71
- {value.count} <span className='text-gray-600'>have the mutation {mutationCode} out of</span>
72
- </p>
73
- <p>
74
- {totalCountWithCoverage(value.count, value.proportion)}{' '}
75
- <span className='text-gray-600'>with coverage at position {mutationPosition}.</span>
76
- </p>
77
- </>
78
- )}
78
+ {(() => {
79
+ switch (value.type) {
80
+ case 'belowThreshold':
81
+ return (
82
+ <p className='text-gray-600'>
83
+ None or less than {formatProportion(MUTATIONS_OVER_TIME_MIN_PROPORTION)} have the
84
+ mutation.
85
+ </p>
86
+ );
87
+
88
+ case 'value':
89
+ return (
90
+ <>
91
+ <p>
92
+ {value.count}{' '}
93
+ <span className='text-gray-600'>have the mutation {mutationCode}.</span>
94
+ </p>
95
+ {value.proportion > 0 && (
96
+ <p>
97
+ {Math.round(value.count / value.proportion)}{' '}
98
+ <span className='text-gray-600'>
99
+ have coverage at position {mutationPosition}.
100
+ </span>
101
+ </p>
102
+ )}
103
+ </>
104
+ );
105
+
106
+ case 'valueWithCoverage':
107
+ return (
108
+ <>
109
+ <p>
110
+ {value.count}{' '}
111
+ <span className='text-gray-600'>have the mutation {mutationCode} out of</span>
112
+ </p>
113
+ <p>
114
+ {value.coverage}{' '}
115
+ <span className='text-gray-600'>with coverage at position {mutationPosition}.</span>
116
+ </p>
117
+ </>
118
+ );
119
+ }
120
+ })()}
121
+
79
122
  <p>
80
123
  {value.totalCount} <span className='text-gray-600'>total in this date range.</span>
81
124
  </p>
@@ -83,13 +126,6 @@ const TooltipValueCountsDescription: FunctionComponent<{
83
126
  );
84
127
  };
85
128
 
86
- function totalCountWithCoverage(count: number, proportion: number) {
87
- if (count === 0) {
88
- return 0;
89
- }
90
- return Math.round(count / proportion);
91
- }
92
-
93
129
  const timeIntervalDisplay = (date: TemporalClass) => {
94
130
  if (date instanceof YearMonthDayClass) {
95
131
  return date.toString();
@@ -4,7 +4,7 @@ import z from 'zod';
4
4
 
5
5
  import { type MutationOverTimeDataMap } from './MutationOverTimeData';
6
6
  import { MutationsOverTimeGridTooltip } from './mutations-over-time-grid-tooltip';
7
- import { type MutationOverTimeMutationValue } from '../../query/queryMutationsOverTime';
7
+ import { getProportion, type MutationOverTimeMutationValue } from '../../query/queryMutationsOverTime';
8
8
  import { type SequenceType } from '../../types';
9
9
  import { type Deletion, type Substitution } from '../../utils/mutations';
10
10
  import { type Temporal } from '../../utils/temporalClass';
@@ -211,7 +211,7 @@ const ProportionCell: FunctionComponent<{
211
211
  colorScale: ColorScale;
212
212
  tooltipPortalTarget: HTMLElement | null;
213
213
  }> = ({ value, mutation, date, tooltipPosition, colorScale, tooltipPortalTarget }) => {
214
- const proportion = value?.type === 'belowThreshold' ? undefined : value?.proportion;
214
+ const proportion = getProportion(value);
215
215
 
216
216
  return (
217
217
  <div className={'py-1 w-full h-full'}>
@@ -12,7 +12,7 @@ import {
12
12
  } from './getFilteredMutationsOverTimeData';
13
13
  import { type MutationOverTimeWorkerResponse } from './mutationOverTimeWorker';
14
14
  import MutationsOverTimeGrid, { customColumnSchema } from './mutations-over-time-grid';
15
- import { type MutationOverTimeQuery } from '../../query/queryMutationsOverTime';
15
+ import { getProportion, type MutationOverTimeQuery } from '../../query/queryMutationsOverTime';
16
16
  import {
17
17
  lapisFilterSchema,
18
18
  sequenceTypeSchema,
@@ -356,7 +356,7 @@ function getDownloadData(filteredData: MutationOverTimeDataMap) {
356
356
  return dates.reduce(
357
357
  (accumulated, date) => {
358
358
  const value = filteredData.get(mutation, date);
359
- const proportion = value?.type === 'value' || value?.type === 'wastewaterValue' ? value.proportion : '';
359
+ const proportion = getProportion(value ?? null) ?? '';
360
360
  return {
361
361
  ...accumulated,
362
362
  [date.dateString]: proportion,
@@ -49,6 +49,12 @@ export type MutationOverTimeMutationValue =
49
49
  count: number;
50
50
  totalCount: number;
51
51
  }
52
+ | {
53
+ type: 'valueWithCoverage';
54
+ count: number;
55
+ coverage: number;
56
+ totalCount: number;
57
+ }
52
58
  | {
53
59
  type: 'wastewaterValue';
54
60
  proportion: number;
@@ -59,6 +65,19 @@ export type MutationOverTimeMutationValue =
59
65
  }
60
66
  | null;
61
67
 
68
+ export function getProportion(value: MutationOverTimeMutationValue) {
69
+ switch (value?.type) {
70
+ case 'value':
71
+ case 'wastewaterValue':
72
+ return value.proportion;
73
+ case 'valueWithCoverage':
74
+ return value.count / value.coverage;
75
+ case 'belowThreshold':
76
+ return undefined;
77
+ }
78
+ return undefined;
79
+ }
80
+
62
81
  const MAX_NUMBER_OF_GRID_COLUMNS = 200;
63
82
  export const MUTATIONS_OVER_TIME_MIN_PROPORTION = 0.001;
64
83
 
@@ -316,9 +335,9 @@ async function queryMutationsOverTimeDataDirectEndpoint(
316
335
  return [
317
336
  date.dateString,
318
337
  {
319
- type: 'value',
320
- proportion: count / coverage,
338
+ type: 'valueWithCoverage',
321
339
  count,
340
+ coverage,
322
341
  totalCount,
323
342
  },
324
343
  ];