@genspectrum/dashboard-components 1.12.0 → 1.13.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 (57) hide show
  1. package/README.md +0 -7
  2. package/custom-elements.json +6 -25
  3. package/dist/components.d.ts +30 -36
  4. package/dist/components.js +943 -757
  5. package/dist/components.js.map +1 -1
  6. package/dist/util.d.ts +46 -30
  7. package/package.json +1 -5
  8. package/src/lapisApi/lapisApi.ts +21 -1
  9. package/src/lapisApi/lapisTypes.ts +36 -0
  10. package/src/preact/components/annotated-mutation.tsx +2 -2
  11. package/src/preact/{mutationsOverTime/mutations-over-time-grid.tsx → components/features-over-time-grid.tsx} +45 -52
  12. package/src/preact/genomeViewer/genome-data-viewer.tsx +2 -2
  13. package/src/preact/mutationsOverTime/MutationOverTimeData.ts +6 -4
  14. package/src/preact/mutationsOverTime/__mockData__/aminoAcidMutationsByDay/aminoAcidMutations.json +5482 -0
  15. package/src/preact/mutationsOverTime/__mockData__/aminoAcidMutationsByDay/aminoAcidMutationsOverTime.json +5496 -0
  16. package/src/preact/mutationsOverTime/__mockData__/byWeek/mutationsOverTime.json +7100 -0
  17. package/src/preact/mutationsOverTime/__mockData__/byWeek/nucleotideMutations.json +10122 -0
  18. package/src/preact/mutationsOverTime/__mockData__/defaultMockData/mutationsOverTime.json +12646 -0
  19. package/src/preact/mutationsOverTime/__mockData__/defaultMockData/nucleotideMutations.json +12632 -0
  20. package/src/preact/mutationsOverTime/__mockData__/request1800s/mutationsOverTime.json +16 -0
  21. package/src/preact/mutationsOverTime/__mockData__/request1800s/nucleotideMutations.json +11 -0
  22. package/src/preact/mutationsOverTime/__mockData__/withDisplayMutations/mutationsOverTime.json +52 -0
  23. package/src/preact/mutationsOverTime/getFilteredMutationsOverTime.spec.ts +3 -3
  24. package/src/preact/mutationsOverTime/mutations-over-time-grid-tooltip.tsx +3 -6
  25. package/src/preact/mutationsOverTime/mutations-over-time.stories.tsx +199 -12
  26. package/src/preact/mutationsOverTime/mutations-over-time.tsx +30 -35
  27. package/src/preact/wastewater/mutationsOverTime/wastewater-mutations-over-time.tsx +30 -3
  28. package/src/query/queryDatesInDataset.ts +89 -0
  29. package/src/query/queryMutationsOverTime.spec.ts +526 -548
  30. package/src/query/queryMutationsOverTime.ts +21 -232
  31. package/src/query/queryQueriesOverTime.spec.ts +432 -0
  32. package/src/query/queryQueriesOverTime.ts +125 -0
  33. package/src/utilEntrypoint.ts +3 -1
  34. package/src/utils/mutations.spec.ts +6 -0
  35. package/src/utils/mutations.ts +1 -1
  36. package/src/utils/temporalClass.ts +4 -0
  37. package/src/web-components/visualization/gs-mutations-over-time.spec-d.ts +0 -3
  38. package/src/web-components/visualization/gs-mutations-over-time.stories.ts +283 -17
  39. package/src/web-components/visualization/gs-mutations-over-time.tsx +0 -9
  40. package/standalone-bundle/dashboard-components.js +8935 -8781
  41. package/standalone-bundle/dashboard-components.js.map +1 -1
  42. package/dist/assets/mutationOverTimeWorker-f8Kp0S6V.js.map +0 -1
  43. package/src/preact/mutationsOverTime/__mockData__/aminoAcidMutationsByDay.ts +0 -47170
  44. package/src/preact/mutationsOverTime/__mockData__/byWeek.ts +0 -54026
  45. package/src/preact/mutationsOverTime/__mockData__/defaultMockData.ts +0 -108385
  46. package/src/preact/mutationsOverTime/__mockData__/mockConversion.ts +0 -54
  47. package/src/preact/mutationsOverTime/__mockData__/noDataWhenNoMutationsAreInFilter.ts +0 -23
  48. package/src/preact/mutationsOverTime/__mockData__/noDataWhenThereAreNoDatesInFilter.ts +0 -23
  49. package/src/preact/mutationsOverTime/__mockData__/showsMessageWhenTooManyMutations.ts +0 -65527
  50. package/src/preact/mutationsOverTime/__mockData__/withDisplayMutations.ts +0 -352
  51. package/src/preact/mutationsOverTime/__mockData__/withGaps.ts +0 -298
  52. package/src/preact/mutationsOverTime/mutationOverTimeWorker.mock.ts +0 -33
  53. package/src/preact/mutationsOverTime/mutationOverTimeWorker.ts +0 -29
  54. package/src/preact/webWorkers/useWebWorker.ts +0 -74
  55. package/src/preact/webWorkers/workerFunction.ts +0 -30
  56. package/src/query/queryMutationsOverTimeNewEndpoint.spec.ts +0 -988
  57. package/standalone-bundle/assets/mutationOverTimeWorker-AhhjjklP.js.map +0 -1
package/dist/util.d.ts CHANGED
@@ -734,6 +734,22 @@ export declare type PrevalenceOverTimeView = default_2.infer<typeof prevalenceOv
734
734
 
735
735
  declare const prevalenceOverTimeViewSchema: default_2.ZodUnion<[default_2.ZodLiteral<"table">, default_2.ZodLiteral<"bar">, default_2.ZodLiteral<"line">, default_2.ZodLiteral<"bubble">]>;
736
736
 
737
+ export declare type QueryDefinition = default_2.infer<typeof queryDefinition>;
738
+
739
+ declare const queryDefinition: default_2.ZodObject<{
740
+ displayLabel: default_2.ZodOptional<default_2.ZodString>;
741
+ countQuery: default_2.ZodString;
742
+ coverageQuery: default_2.ZodString;
743
+ }, "strip", default_2.ZodTypeAny, {
744
+ countQuery: string;
745
+ coverageQuery: string;
746
+ displayLabel?: string | undefined;
747
+ }, {
748
+ countQuery: string;
749
+ coverageQuery: string;
750
+ displayLabel?: string | undefined;
751
+ }>;
752
+
737
753
  export declare type RelativeGrowthAdvantageProps = default_2.infer<typeof relativeGrowthAdvantagePropsSchema>;
738
754
 
739
755
  declare const relativeGrowthAdvantagePropsSchema: default_2.ZodObject<{
@@ -987,7 +1003,7 @@ declare global {
987
1003
 
988
1004
  declare global {
989
1005
  interface HTMLElementTagNameMap {
990
- 'gs-mutation-comparison': MutationComparisonComponent;
1006
+ 'gs-mutations': MutationsComponent;
991
1007
  }
992
1008
  }
993
1009
 
@@ -995,7 +1011,7 @@ declare global {
995
1011
  declare global {
996
1012
  namespace JSX {
997
1013
  interface IntrinsicElements {
998
- 'gs-mutation-comparison': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1014
+ 'gs-mutations': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
999
1015
  }
1000
1016
  }
1001
1017
  }
@@ -1003,7 +1019,7 @@ declare global {
1003
1019
 
1004
1020
  declare global {
1005
1021
  interface HTMLElementTagNameMap {
1006
- 'gs-mutations': MutationsComponent;
1022
+ 'gs-prevalence-over-time': PrevalenceOverTimeComponent;
1007
1023
  }
1008
1024
  }
1009
1025
 
@@ -1011,7 +1027,7 @@ declare global {
1011
1027
  declare global {
1012
1028
  namespace JSX {
1013
1029
  interface IntrinsicElements {
1014
- 'gs-mutations': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1030
+ 'gs-prevalence-over-time': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1015
1031
  }
1016
1032
  }
1017
1033
  }
@@ -1019,7 +1035,7 @@ declare global {
1019
1035
 
1020
1036
  declare global {
1021
1037
  interface HTMLElementTagNameMap {
1022
- 'gs-relative-growth-advantage': RelativeGrowthAdvantageComponent;
1038
+ 'gs-mutation-comparison': MutationComparisonComponent;
1023
1039
  }
1024
1040
  }
1025
1041
 
@@ -1027,7 +1043,7 @@ declare global {
1027
1043
  declare global {
1028
1044
  namespace JSX {
1029
1045
  interface IntrinsicElements {
1030
- 'gs-relative-growth-advantage': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1046
+ 'gs-mutation-comparison': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1031
1047
  }
1032
1048
  }
1033
1049
  }
@@ -1035,7 +1051,7 @@ declare global {
1035
1051
 
1036
1052
  declare global {
1037
1053
  interface HTMLElementTagNameMap {
1038
- 'gs-prevalence-over-time': PrevalenceOverTimeComponent;
1054
+ 'gs-relative-growth-advantage': RelativeGrowthAdvantageComponent;
1039
1055
  }
1040
1056
  }
1041
1057
 
@@ -1043,7 +1059,7 @@ declare global {
1043
1059
  declare global {
1044
1060
  namespace JSX {
1045
1061
  interface IntrinsicElements {
1046
- 'gs-prevalence-over-time': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1062
+ 'gs-relative-growth-advantage': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1047
1063
  }
1048
1064
  }
1049
1065
  }
@@ -1051,7 +1067,7 @@ declare global {
1051
1067
 
1052
1068
  declare global {
1053
1069
  interface HTMLElementTagNameMap {
1054
- 'gs-aggregate': AggregateComponent;
1070
+ 'gs-number-sequences-over-time': NumberSequencesOverTimeComponent;
1055
1071
  }
1056
1072
  }
1057
1073
 
@@ -1059,7 +1075,7 @@ declare global {
1059
1075
  declare global {
1060
1076
  namespace JSX {
1061
1077
  interface IntrinsicElements {
1062
- 'gs-aggregate': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1078
+ 'gs-number-sequences-over-time': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1063
1079
  }
1064
1080
  }
1065
1081
  }
@@ -1067,7 +1083,7 @@ declare global {
1067
1083
 
1068
1084
  declare global {
1069
1085
  interface HTMLElementTagNameMap {
1070
- 'gs-number-sequences-over-time': NumberSequencesOverTimeComponent;
1086
+ 'gs-aggregate': AggregateComponent;
1071
1087
  }
1072
1088
  }
1073
1089
 
@@ -1075,7 +1091,7 @@ declare global {
1075
1091
  declare global {
1076
1092
  namespace JSX {
1077
1093
  interface IntrinsicElements {
1078
- 'gs-number-sequences-over-time': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1094
+ 'gs-aggregate': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1079
1095
  }
1080
1096
  }
1081
1097
  }
@@ -1083,7 +1099,7 @@ declare global {
1083
1099
 
1084
1100
  declare global {
1085
1101
  interface HTMLElementTagNameMap {
1086
- 'gs-statistics': StatisticsComponent;
1102
+ 'gs-mutations-over-time': MutationsOverTimeComponent;
1087
1103
  }
1088
1104
  }
1089
1105
 
@@ -1091,7 +1107,7 @@ declare global {
1091
1107
  declare global {
1092
1108
  namespace JSX {
1093
1109
  interface IntrinsicElements {
1094
- 'gs-statistics': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1110
+ 'gs-mutations-over-time': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1095
1111
  }
1096
1112
  }
1097
1113
  }
@@ -1115,7 +1131,7 @@ declare global {
1115
1131
 
1116
1132
  declare global {
1117
1133
  interface HTMLElementTagNameMap {
1118
- 'gs-mutations-over-time': MutationsOverTimeComponent;
1134
+ 'gs-statistics': StatisticsComponent;
1119
1135
  }
1120
1136
  }
1121
1137
 
@@ -1123,7 +1139,7 @@ declare global {
1123
1139
  declare global {
1124
1140
  namespace JSX {
1125
1141
  interface IntrinsicElements {
1126
- 'gs-mutations-over-time': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1142
+ 'gs-statistics': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1127
1143
  }
1128
1144
  }
1129
1145
  }
@@ -1151,10 +1167,10 @@ declare global {
1151
1167
 
1152
1168
  declare global {
1153
1169
  interface HTMLElementTagNameMap {
1154
- 'gs-text-filter': TextFilterComponent;
1170
+ 'gs-location-filter': LocationFilterComponent;
1155
1171
  }
1156
1172
  interface HTMLElementEventMap {
1157
- [gsEventNames.textFilterChanged]: TextFilterChangedEvent;
1173
+ [gsEventNames.locationChanged]: LocationChangedEvent;
1158
1174
  }
1159
1175
  }
1160
1176
 
@@ -1162,7 +1178,7 @@ declare global {
1162
1178
  declare global {
1163
1179
  namespace JSX {
1164
1180
  interface IntrinsicElements {
1165
- 'gs-text-filter': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1181
+ 'gs-location-filter': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1166
1182
  }
1167
1183
  }
1168
1184
  }
@@ -1170,10 +1186,10 @@ declare global {
1170
1186
 
1171
1187
  declare global {
1172
1188
  interface HTMLElementTagNameMap {
1173
- 'gs-location-filter': LocationFilterComponent;
1189
+ 'gs-text-filter': TextFilterComponent;
1174
1190
  }
1175
1191
  interface HTMLElementEventMap {
1176
- [gsEventNames.locationChanged]: LocationChangedEvent;
1192
+ [gsEventNames.textFilterChanged]: TextFilterChangedEvent;
1177
1193
  }
1178
1194
  }
1179
1195
 
@@ -1181,7 +1197,7 @@ declare global {
1181
1197
  declare global {
1182
1198
  namespace JSX {
1183
1199
  interface IntrinsicElements {
1184
- 'gs-location-filter': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1200
+ 'gs-text-filter': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1185
1201
  }
1186
1202
  }
1187
1203
  }
@@ -1208,11 +1224,11 @@ declare global {
1208
1224
 
1209
1225
  declare global {
1210
1226
  interface HTMLElementTagNameMap {
1211
- 'gs-number-range-filter': NumberRangeFilterComponent;
1227
+ 'gs-lineage-filter': LineageFilterComponent;
1212
1228
  }
1213
1229
  interface HTMLElementEventMap {
1214
- [gsEventNames.numberRangeFilterChanged]: NumberRangeFilterChangedEvent;
1215
- [gsEventNames.numberRangeValueChanged]: NumberRangeValueChangedEvent;
1230
+ [gsEventNames.lineageFilterChanged]: LineageFilterChangedEvent;
1231
+ [gsEventNames.lineageFilterMultiChanged]: LineageMultiFilterChangedEvent;
1216
1232
  }
1217
1233
  }
1218
1234
 
@@ -1220,7 +1236,7 @@ declare global {
1220
1236
  declare global {
1221
1237
  namespace JSX {
1222
1238
  interface IntrinsicElements {
1223
- 'gs-number-range-filter': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1239
+ 'gs-lineage-filter': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1224
1240
  }
1225
1241
  }
1226
1242
  }
@@ -1228,11 +1244,11 @@ declare global {
1228
1244
 
1229
1245
  declare global {
1230
1246
  interface HTMLElementTagNameMap {
1231
- 'gs-lineage-filter': LineageFilterComponent;
1247
+ 'gs-number-range-filter': NumberRangeFilterComponent;
1232
1248
  }
1233
1249
  interface HTMLElementEventMap {
1234
- [gsEventNames.lineageFilterChanged]: LineageFilterChangedEvent;
1235
- [gsEventNames.lineageFilterMultiChanged]: LineageMultiFilterChangedEvent;
1250
+ [gsEventNames.numberRangeFilterChanged]: NumberRangeFilterChangedEvent;
1251
+ [gsEventNames.numberRangeValueChanged]: NumberRangeValueChangedEvent;
1236
1252
  }
1237
1253
  }
1238
1254
 
@@ -1240,7 +1256,7 @@ declare global {
1240
1256
  declare global {
1241
1257
  namespace JSX {
1242
1258
  interface IntrinsicElements {
1243
- 'gs-lineage-filter': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1259
+ 'gs-number-range-filter': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1244
1260
  }
1245
1261
  }
1246
1262
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@genspectrum/dashboard-components",
3
- "version": "1.12.0",
3
+ "version": "1.13.0",
4
4
  "description": "GenSpectrum web components for building dashboards",
5
5
  "type": "module",
6
6
  "license": "AGPL-3.0-only",
@@ -16,10 +16,6 @@
16
16
  "access": "public"
17
17
  },
18
18
  "imports": {
19
- "#mutationOverTime": {
20
- "storybook": "./src/preact/mutationsOverTime/mutationOverTimeWorker.mock.ts",
21
- "default": "./src/preact/mutationsOverTime/mutationOverTimeWorker.ts"
22
- },
23
19
  "#*": [
24
20
  "./*",
25
21
  "./*.ts",
@@ -1,3 +1,4 @@
1
+ import { lineageDefinitionResponseSchema } from './LineageDefinition';
1
2
  import { referenceGenomeResponse } from './ReferenceGenome';
2
3
  import {
3
4
  aggregatedResponse,
@@ -11,9 +12,10 @@ import {
11
12
  type ProblemDetail,
12
13
  type MutationsOverTimeRequest,
13
14
  mutationsOverTimeResponse,
15
+ type QueriesOverTimeRequest,
16
+ queriesOverTimeResponse,
14
17
  } from './lapisTypes';
15
18
  import { type SequenceType } from '../types';
16
- import { lineageDefinitionResponseSchema } from './LineageDefinition';
17
19
 
18
20
  export class UnknownLapisError extends Error {
19
21
  constructor(
@@ -136,6 +138,23 @@ export async function fetchMutationsOverTime(
136
138
  return mutationsOverTimeResponse.parse(await response.json());
137
139
  }
138
140
 
141
+ export async function fetchQueriesOverTime(lapisUrl: string, body: QueriesOverTimeRequest, signal?: AbortSignal) {
142
+ const response = await callLapis(
143
+ queriesOverTimeEndpoint(lapisUrl),
144
+ {
145
+ method: 'POST',
146
+ headers: {
147
+ 'Content-Type': 'application/json',
148
+ },
149
+ body: JSON.stringify(body),
150
+ signal,
151
+ },
152
+ 'queries over time',
153
+ );
154
+
155
+ return queriesOverTimeResponse.parse(await response.json());
156
+ }
157
+
139
158
  export async function fetchReferenceGenome(lapisUrl: string, signal?: AbortSignal) {
140
159
  const response = await callLapis(
141
160
  referenceGenomeEndpoint(lapisUrl),
@@ -244,6 +263,7 @@ export const mutationsOverTimeEndpoint = (lapisUrl: string, sequenceType: Sequen
244
263
  ? `${lapisUrl}/component/aminoAcidMutationsOverTime`
245
264
  : `${lapisUrl}/component/nucleotideMutationsOverTime`;
246
265
  };
266
+ export const queriesOverTimeEndpoint = (lapisUrl: string) => `${lapisUrl}/component/queriesOverTime`;
247
267
  export const referenceGenomeEndpoint = (lapisUrl: string) => `${lapisUrl}/sample/referenceGenome`;
248
268
  export const lineageDefinitionEndpoint = (lapisUrl: string, lapisField: string) =>
249
269
  `${lapisUrl}/sample/lineageDefinition/${lapisField}`;
@@ -67,6 +67,42 @@ export const mutationsOverTimeResponse = makeLapisResponse(
67
67
  );
68
68
  export type MutationsOverTimeResponse = z.infer<typeof mutationsOverTimeResponse>;
69
69
 
70
+ const queryDefinition = z.object({
71
+ displayLabel: z.string().optional(),
72
+ countQuery: z.string(),
73
+ coverageQuery: z.string(),
74
+ });
75
+
76
+ export type QueryDefinition = z.infer<typeof queryDefinition>;
77
+
78
+ export const queriesOverTimeRequest = z.object({
79
+ filters: z.record(filterValue),
80
+ queries: z.array(queryDefinition),
81
+ dateRanges: z.array(dateRange),
82
+ dateField: z.string(),
83
+ downloadAsFile: z.boolean().optional(),
84
+ downloadFileBasename: z.string().optional(),
85
+ compression: z.enum(['gzip', 'none']).optional(),
86
+ });
87
+ export type QueriesOverTimeRequest = z.infer<typeof queriesOverTimeRequest>;
88
+
89
+ export const queriesOverTimeResponse = makeLapisResponse(
90
+ z.object({
91
+ queries: z.array(z.string()),
92
+ dateRanges: z.array(dateRange),
93
+ data: z.array(
94
+ z.array(
95
+ z.object({
96
+ count: z.number(),
97
+ coverage: z.number(),
98
+ }),
99
+ ),
100
+ ),
101
+ totalCountsByDateRange: z.array(z.number()),
102
+ }),
103
+ );
104
+ export type QueriesOverTimeResponse = z.infer<typeof queriesOverTimeResponse>;
105
+
70
106
  const insertionCount = z.object({
71
107
  insertion: z.string(),
72
108
  count: z.number(),
@@ -2,11 +2,11 @@ import DOMPurify from 'dompurify';
2
2
  import { useRef } from 'gridjs';
3
3
  import { Fragment, type FunctionComponent, type RefObject } from 'preact';
4
4
 
5
+ import { InfoHeadline1, InfoHeadline2, InfoParagraph } from './info';
6
+ import { ButtonWithModalDialog, useModalRef } from './modal';
5
7
  import type { SequenceType } from '../../types';
6
8
  import type { Deletion, Substitution } from '../../utils/mutations';
7
9
  import { useMutationAnnotationsProvider } from '../MutationAnnotationsContext';
8
- import { InfoHeadline1, InfoHeadline2, InfoParagraph } from './info';
9
- import { ButtonWithModalDialog, useModalRef } from './modal';
10
10
  import { useMutationLinkProvider } from '../MutationLinkTemplateContext';
11
11
 
12
12
  export type AnnotatedMutationProps = {
@@ -1,17 +1,13 @@
1
- import { type FunctionComponent } from 'preact';
1
+ import { type FunctionComponent, type JSX } from 'preact';
2
2
  import { useMemo } from 'preact/hooks';
3
3
  import z from 'zod';
4
4
 
5
- import { type MutationOverTimeDataMap } from './MutationOverTimeData';
6
- import { MutationsOverTimeGridTooltip } from './mutations-over-time-grid-tooltip';
7
- import { getProportion, type MutationOverTimeMutationValue } from '../../query/queryMutationsOverTime';
8
- import { type SequenceType } from '../../types';
9
- import { type Deletion, type Substitution } from '../../utils/mutations';
5
+ import { type ColorScale, getColorWithinScale, getTextColorForScale } from './color-scale-selector';
6
+ import PortalTooltip from './portal-tooltip';
7
+ import { type TooltipPosition } from './tooltip';
8
+ import { getProportion, type ProportionValue } from '../../query/queryMutationsOverTime';
10
9
  import { type Temporal } from '../../utils/temporalClass';
11
- import { AnnotatedMutation } from '../components/annotated-mutation';
12
- import { type ColorScale, getColorWithinScale, getTextColorForScale } from '../components/color-scale-selector';
13
- import PortalTooltip from '../components/portal-tooltip';
14
- import { type TooltipPosition } from '../components/tooltip';
10
+ import { type TemporalDataMap } from '../mutationsOverTime/MutationOverTimeData';
15
11
  import { formatProportion } from '../shared/table/formatProportion';
16
12
  import { type PageSizes, Pagination } from '../shared/tanstackTable/pagination';
17
13
  import { usePageSizeContext } from '../shared/tanstackTable/pagination-context';
@@ -31,55 +27,56 @@ export const customColumnSchema = z.object({
31
27
  });
32
28
  export type CustomColumn = z.infer<typeof customColumnSchema>;
33
29
 
34
- export interface MutationsOverTimeGridProps {
35
- data: MutationOverTimeDataMap;
30
+ export interface FeatureRenderer<D> {
31
+ asString(value: D): string;
32
+ renderRowLabel(value: D): JSX.Element;
33
+ renderTooltip(value: D, temporal: Temporal, proportionValue: ProportionValue | undefined): JSX.Element;
34
+ }
35
+
36
+ export interface FeaturesOverTimeGridProps<F> {
37
+ rowLabelHeader: string;
38
+ data: TemporalDataMap<F>;
36
39
  colorScale: ColorScale;
37
- sequenceType: SequenceType;
38
40
  pageSizes: PageSizes;
39
41
  customColumns?: CustomColumn[];
42
+ featureRenderer: FeatureRenderer<F>;
40
43
  tooltipPortalTarget: HTMLElement | null;
41
44
  }
42
45
 
43
- type RowType = {
44
- mutation: Substitution | Deletion;
45
- values: (MutationOverTimeMutationValue | undefined)[];
46
+ type RowType<F> = {
47
+ feature: F;
48
+ values: (ProportionValue | undefined)[];
46
49
  customValues: (string | number | undefined)[];
47
50
  };
48
51
 
49
52
  const EMPTY_COLUMNS: CustomColumn[] = [];
50
53
 
51
- const MutationsOverTimeGrid: FunctionComponent<MutationsOverTimeGridProps> = ({
54
+ function FeaturesOverTimeGrid<F>({
55
+ rowLabelHeader,
52
56
  data,
53
57
  colorScale,
54
- sequenceType,
55
58
  pageSizes,
56
59
  customColumns = EMPTY_COLUMNS,
60
+ featureRenderer,
57
61
  tooltipPortalTarget,
58
- }) => {
62
+ }: FeaturesOverTimeGridProps<F>) {
59
63
  const tableData = useMemo(() => {
60
- const allMutations = data.getFirstAxisKeys();
61
- return data.getAsArray().map((row, index): RowType => {
62
- const mutation = allMutations[index];
63
- const customValues = customColumns.map((col) => col.values[mutation.code]);
64
- return { mutation, values: [...row], customValues };
64
+ const firstAxisKeys = data.getFirstAxisKeys();
65
+ return data.getAsArray().map((row, index): RowType<F> => {
66
+ const firstAxisKey = firstAxisKeys[index];
67
+ const customValues = customColumns.map((col) => col.values[featureRenderer.asString(firstAxisKey)]);
68
+ return { feature: firstAxisKey, values: [...row], customValues };
65
69
  });
66
- }, [data, customColumns]);
70
+ }, [data, customColumns, featureRenderer]);
67
71
 
68
72
  const columns = useMemo(() => {
69
- const columnHelper = createColumnHelper<RowType>();
73
+ const columnHelper = createColumnHelper<RowType<F>>();
70
74
  const dates = data.getSecondAxisKeys();
71
75
 
72
- const mutationHeader = columnHelper.accessor((row) => row.mutation, {
73
- id: 'mutation',
74
- header: () => <span>Mutation</span>,
75
- cell: ({ getValue }) => {
76
- const value = getValue();
77
- return (
78
- <div className={'text-center'}>
79
- <AnnotatedMutation mutation={value} sequenceType={sequenceType} />
80
- </div>
81
- );
82
- },
76
+ const featureHeader = columnHelper.accessor((row) => row.feature, {
77
+ id: 'feature',
78
+ header: () => <span>{rowLabelHeader}</span>,
79
+ cell: ({ getValue }) => featureRenderer.renderRowLabel(getValue()),
83
80
  });
84
81
 
85
82
  const customColumnHeaders = customColumns.map((customCol, index) => {
@@ -108,12 +105,13 @@ const MutationsOverTimeGrid: FunctionComponent<MutationsOverTimeGridProps> = ({
108
105
  const numberOfRows = table.getRowModel().rows.length;
109
106
  const numberOfColumns = table.getAllColumns().length;
110
107
 
108
+ const tooltip = featureRenderer.renderTooltip(row.original.feature, date, value);
109
+
111
110
  return (
112
111
  <div className={'text-center'}>
113
112
  <ProportionCell
114
113
  value={value ?? null}
115
- date={date}
116
- mutation={row.original.mutation}
114
+ tooltip={tooltip}
117
115
  tooltipPosition={getTooltipPosition(
118
116
  rowIndex -
119
117
  table.getState().pagination.pageIndex * table.getState().pagination.pageSize,
@@ -130,8 +128,8 @@ const MutationsOverTimeGrid: FunctionComponent<MutationsOverTimeGridProps> = ({
130
128
  });
131
129
  });
132
130
 
133
- return [mutationHeader, ...customColumnHeaders, ...dateHeaders];
134
- }, [colorScale, data, sequenceType, customColumns, tooltipPortalTarget]);
131
+ return [featureHeader, ...customColumnHeaders, ...dateHeaders];
132
+ }, [colorScale, data, customColumns, tooltipPortalTarget, featureRenderer, rowLabelHeader]);
135
133
 
136
134
  const { pageSize } = usePageSizeContext();
137
135
  const table = usePreactTable({
@@ -183,7 +181,7 @@ const MutationsOverTimeGrid: FunctionComponent<MutationsOverTimeGridProps> = ({
183
181
  </div>
184
182
  </div>
185
183
  );
186
- };
184
+ }
187
185
 
188
186
  function styleGridHeader(columnIndex: number, numDateColumns: number) {
189
187
  if (columnIndex === 0) {
@@ -204,22 +202,17 @@ function getTooltipPosition(rowIndex: number, rows: number, columnIndex: number,
204
202
  }
205
203
 
206
204
  const ProportionCell: FunctionComponent<{
207
- value: MutationOverTimeMutationValue;
208
- date: Temporal;
209
- mutation: Substitution | Deletion;
205
+ value: ProportionValue;
206
+ tooltip: JSX.Element;
210
207
  tooltipPosition: TooltipPosition;
211
208
  colorScale: ColorScale;
212
209
  tooltipPortalTarget: HTMLElement | null;
213
- }> = ({ value, mutation, date, tooltipPosition, colorScale, tooltipPortalTarget }) => {
210
+ }> = ({ value, tooltip, tooltipPosition, colorScale, tooltipPortalTarget }) => {
214
211
  const proportion = getProportion(value);
215
212
 
216
213
  return (
217
214
  <div className={'py-1 w-full h-full'}>
218
- <PortalTooltip
219
- content={<MutationsOverTimeGridTooltip mutation={mutation} date={date} value={value} />}
220
- position={tooltipPosition}
221
- portalTarget={tooltipPortalTarget}
222
- >
215
+ <PortalTooltip content={tooltip} position={tooltipPosition} portalTarget={tooltipPortalTarget}>
223
216
  <div
224
217
  style={{
225
218
  backgroundColor: getColorWithinScale(proportion, colorScale),
@@ -240,4 +233,4 @@ const ProportionCell: FunctionComponent<{
240
233
  );
241
234
  };
242
235
 
243
- export default MutationsOverTimeGrid;
236
+ export default FeaturesOverTimeGrid;
@@ -2,12 +2,12 @@ import type { FunctionComponent } from 'preact';
2
2
  import { useEffect, useRef, useState } from 'preact/hooks';
3
3
  import z from 'zod';
4
4
 
5
+ import CDSPlot from './CDSPlot';
6
+ import { loadGff3 } from './loadGff3';
5
7
  import { ErrorBoundary } from '../components/error-boundary';
6
8
  import { LoadingDisplay } from '../components/loading-display';
7
9
  import { ResizeContainer } from '../components/resize-container';
8
10
  import { useQuery } from '../useQuery';
9
- import CDSPlot from './CDSPlot';
10
- import { loadGff3 } from './loadGff3';
11
11
 
12
12
  const genomeDataViewerPropsSchema = z.object({
13
13
  gff3Source: z.string().min(1, 'gff3Source cannot be empty'),
@@ -1,5 +1,5 @@
1
1
  import {
2
- type MutationOverTimeMutationValue,
2
+ type ProportionValue,
3
3
  serializeSubstitutionOrDeletion,
4
4
  serializeTemporal,
5
5
  } from '../../query/queryMutationsOverTime';
@@ -7,18 +7,20 @@ import { type Map2d, Map2dBase, type Map2DContents } from '../../utils/map2d';
7
7
  import type { Deletion, Substitution } from '../../utils/mutations';
8
8
  import type { Temporal, TemporalClass } from '../../utils/temporalClass';
9
9
 
10
+ export type TemporalDataMap<D, T extends Temporal | TemporalClass = Temporal> = Map2d<D, T, ProportionValue>;
11
+
10
12
  export type MutationOverTimeDataMap<T extends Temporal | TemporalClass = Temporal> = Map2d<
11
13
  Substitution | Deletion,
12
14
  T,
13
- MutationOverTimeMutationValue
15
+ ProportionValue
14
16
  >;
15
17
 
16
18
  export class BaseMutationOverTimeDataMap<T extends Temporal | TemporalClass = Temporal> extends Map2dBase<
17
19
  Substitution | Deletion,
18
20
  T,
19
- MutationOverTimeMutationValue
21
+ ProportionValue
20
22
  > {
21
- constructor(initialContent?: Map2DContents<Substitution | Deletion, T, MutationOverTimeMutationValue>) {
23
+ constructor(initialContent?: Map2DContents<Substitution | Deletion, T, ProportionValue>) {
22
24
  super(serializeSubstitutionOrDeletion, serializeTemporal, initialContent);
23
25
  }
24
26
  }