@genspectrum/dashboard-components 1.13.0 → 1.14.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 (26) hide show
  1. package/custom-elements.json +393 -2
  2. package/dist/components.d.ts +126 -9
  3. package/dist/components.js +701 -163
  4. package/dist/components.js.map +1 -1
  5. package/dist/util.d.ts +146 -11
  6. package/package.json +1 -1
  7. package/src/lapisApi/lapisTypes.ts +1 -1
  8. package/src/preact/queriesOverTime/__mockData__/defaultMockData/queriesOverTime.json +32 -0
  9. package/src/preact/queriesOverTime/__mockData__/manyQueries.json +128 -0
  10. package/src/preact/queriesOverTime/__mockData__/request1800s.json +16 -0
  11. package/src/preact/queriesOverTime/__mockData__/withGaps.json +52 -0
  12. package/src/preact/queriesOverTime/getFilteredQueriesOverTimeData.ts +85 -0
  13. package/src/preact/queriesOverTime/queries-over-time-filter.tsx +25 -0
  14. package/src/preact/queriesOverTime/queries-over-time-grid-tooltip.stories.tsx +134 -0
  15. package/src/preact/queriesOverTime/queries-over-time-grid-tooltip.tsx +123 -0
  16. package/src/preact/queriesOverTime/queries-over-time.stories.tsx +481 -0
  17. package/src/preact/queriesOverTime/queries-over-time.tsx +304 -0
  18. package/src/utilEntrypoint.ts +1 -0
  19. package/src/web-components/visualization/gs-mutations-over-time.spec-d.ts +3 -0
  20. package/src/web-components/visualization/gs-mutations-over-time.tsx +1 -1
  21. package/src/web-components/visualization/gs-queries-over-time.spec-d.ts +38 -0
  22. package/src/web-components/visualization/gs-queries-over-time.stories.ts +288 -0
  23. package/src/web-components/visualization/gs-queries-over-time.tsx +154 -0
  24. package/src/web-components/visualization/index.ts +1 -0
  25. package/standalone-bundle/dashboard-components.js +8509 -8068
  26. package/standalone-bundle/dashboard-components.js.map +1 -1
package/dist/util.d.ts CHANGED
@@ -734,20 +734,139 @@ 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 QueriesOverTimeProps = default_2.infer<typeof queriesOverTimeSchema>;
738
+
739
+ declare const queriesOverTimeSchema: default_2.ZodObject<{
740
+ lapisFilter: default_2.ZodIntersection<default_2.ZodRecord<default_2.ZodString, default_2.ZodUnion<[default_2.ZodString, default_2.ZodArray<default_2.ZodString, "many">, default_2.ZodNumber, default_2.ZodNull, default_2.ZodBoolean, default_2.ZodUndefined]>>, default_2.ZodObject<{
741
+ nucleotideMutations: default_2.ZodOptional<default_2.ZodArray<default_2.ZodString, "many">>;
742
+ aminoAcidMutations: default_2.ZodOptional<default_2.ZodArray<default_2.ZodString, "many">>;
743
+ nucleotideInsertions: default_2.ZodOptional<default_2.ZodArray<default_2.ZodString, "many">>;
744
+ aminoAcidInsertions: default_2.ZodOptional<default_2.ZodArray<default_2.ZodString, "many">>;
745
+ }, "strip", default_2.ZodTypeAny, {
746
+ nucleotideMutations?: string[] | undefined;
747
+ aminoAcidMutations?: string[] | undefined;
748
+ nucleotideInsertions?: string[] | undefined;
749
+ aminoAcidInsertions?: string[] | undefined;
750
+ }, {
751
+ nucleotideMutations?: string[] | undefined;
752
+ aminoAcidMutations?: string[] | undefined;
753
+ nucleotideInsertions?: string[] | undefined;
754
+ aminoAcidInsertions?: string[] | undefined;
755
+ }>>;
756
+ queries: default_2.ZodArray<default_2.ZodObject<{
757
+ displayLabel: default_2.ZodString;
758
+ countQuery: default_2.ZodString;
759
+ coverageQuery: default_2.ZodString;
760
+ }, "strip", default_2.ZodTypeAny, {
761
+ displayLabel: string;
762
+ countQuery: string;
763
+ coverageQuery: string;
764
+ }, {
765
+ displayLabel: string;
766
+ countQuery: string;
767
+ coverageQuery: string;
768
+ }>, "many">;
769
+ views: default_2.ZodArray<default_2.ZodLiteral<"grid">, "many">;
770
+ granularity: default_2.ZodUnion<[default_2.ZodLiteral<"day">, default_2.ZodLiteral<"week">, default_2.ZodLiteral<"month">, default_2.ZodLiteral<"year">]>;
771
+ lapisDateField: default_2.ZodString;
772
+ initialMeanProportionInterval: default_2.ZodObject<{
773
+ min: default_2.ZodNumber;
774
+ max: default_2.ZodNumber;
775
+ }, "strip", default_2.ZodTypeAny, {
776
+ min: number;
777
+ max: number;
778
+ }, {
779
+ min: number;
780
+ max: number;
781
+ }>;
782
+ hideGaps: default_2.ZodOptional<default_2.ZodBoolean>;
783
+ width: default_2.ZodString;
784
+ height: default_2.ZodOptional<default_2.ZodString>;
785
+ pageSizes: default_2.ZodUnion<[default_2.ZodArray<default_2.ZodNumber, "many">, default_2.ZodNumber]>;
786
+ customColumns: default_2.ZodOptional<default_2.ZodArray<default_2.ZodObject<{
787
+ header: default_2.ZodString;
788
+ values: default_2.ZodRecord<default_2.ZodString, default_2.ZodUnion<[default_2.ZodString, default_2.ZodNumber]>>;
789
+ }, "strip", default_2.ZodTypeAny, {
790
+ values: Record<string, string | number>;
791
+ header: string;
792
+ }, {
793
+ values: Record<string, string | number>;
794
+ header: string;
795
+ }>, "many">>;
796
+ }, "strip", default_2.ZodTypeAny, {
797
+ lapisFilter: Record<string, string | number | boolean | string[] | null | undefined> & {
798
+ nucleotideMutations?: string[] | undefined;
799
+ aminoAcidMutations?: string[] | undefined;
800
+ nucleotideInsertions?: string[] | undefined;
801
+ aminoAcidInsertions?: string[] | undefined;
802
+ };
803
+ queries: {
804
+ displayLabel: string;
805
+ countQuery: string;
806
+ coverageQuery: string;
807
+ }[];
808
+ width: string;
809
+ views: "grid"[];
810
+ granularity: "day" | "week" | "month" | "year";
811
+ lapisDateField: string;
812
+ pageSizes: number | number[];
813
+ initialMeanProportionInterval: {
814
+ min: number;
815
+ max: number;
816
+ };
817
+ height?: string | undefined;
818
+ hideGaps?: boolean | undefined;
819
+ customColumns?: {
820
+ values: Record<string, string | number>;
821
+ header: string;
822
+ }[] | undefined;
823
+ }, {
824
+ lapisFilter: Record<string, string | number | boolean | string[] | null | undefined> & {
825
+ nucleotideMutations?: string[] | undefined;
826
+ aminoAcidMutations?: string[] | undefined;
827
+ nucleotideInsertions?: string[] | undefined;
828
+ aminoAcidInsertions?: string[] | undefined;
829
+ };
830
+ queries: {
831
+ displayLabel: string;
832
+ countQuery: string;
833
+ coverageQuery: string;
834
+ }[];
835
+ width: string;
836
+ views: "grid"[];
837
+ granularity: "day" | "week" | "month" | "year";
838
+ lapisDateField: string;
839
+ pageSizes: number | number[];
840
+ initialMeanProportionInterval: {
841
+ min: number;
842
+ max: number;
843
+ };
844
+ height?: string | undefined;
845
+ hideGaps?: boolean | undefined;
846
+ customColumns?: {
847
+ values: Record<string, string | number>;
848
+ header: string;
849
+ }[] | undefined;
850
+ }>;
851
+
852
+ export declare type QueriesOverTimeView = default_2.infer<typeof queriesOverTimeViewSchema>;
853
+
854
+ declare const queriesOverTimeViewSchema: default_2.ZodLiteral<"grid">;
855
+
737
856
  export declare type QueryDefinition = default_2.infer<typeof queryDefinition>;
738
857
 
739
858
  declare const queryDefinition: default_2.ZodObject<{
740
- displayLabel: default_2.ZodOptional<default_2.ZodString>;
859
+ displayLabel: default_2.ZodString;
741
860
  countQuery: default_2.ZodString;
742
861
  coverageQuery: default_2.ZodString;
743
862
  }, "strip", default_2.ZodTypeAny, {
863
+ displayLabel: string;
744
864
  countQuery: string;
745
865
  coverageQuery: string;
746
- displayLabel?: string | undefined;
747
866
  }, {
867
+ displayLabel: string;
748
868
  countQuery: string;
749
869
  coverageQuery: string;
750
- displayLabel?: string | undefined;
751
870
  }>;
752
871
 
753
872
  export declare type RelativeGrowthAdvantageProps = default_2.infer<typeof relativeGrowthAdvantagePropsSchema>;
@@ -1019,7 +1138,7 @@ declare global {
1019
1138
 
1020
1139
  declare global {
1021
1140
  interface HTMLElementTagNameMap {
1022
- 'gs-prevalence-over-time': PrevalenceOverTimeComponent;
1141
+ 'gs-mutation-comparison': MutationComparisonComponent;
1023
1142
  }
1024
1143
  }
1025
1144
 
@@ -1027,7 +1146,7 @@ declare global {
1027
1146
  declare global {
1028
1147
  namespace JSX {
1029
1148
  interface IntrinsicElements {
1030
- 'gs-prevalence-over-time': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1149
+ 'gs-mutation-comparison': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1031
1150
  }
1032
1151
  }
1033
1152
  }
@@ -1035,7 +1154,7 @@ declare global {
1035
1154
 
1036
1155
  declare global {
1037
1156
  interface HTMLElementTagNameMap {
1038
- 'gs-mutation-comparison': MutationComparisonComponent;
1157
+ 'gs-prevalence-over-time': PrevalenceOverTimeComponent;
1039
1158
  }
1040
1159
  }
1041
1160
 
@@ -1043,7 +1162,7 @@ declare global {
1043
1162
  declare global {
1044
1163
  namespace JSX {
1045
1164
  interface IntrinsicElements {
1046
- 'gs-mutation-comparison': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1165
+ 'gs-prevalence-over-time': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1047
1166
  }
1048
1167
  }
1049
1168
  }
@@ -1067,7 +1186,7 @@ declare global {
1067
1186
 
1068
1187
  declare global {
1069
1188
  interface HTMLElementTagNameMap {
1070
- 'gs-number-sequences-over-time': NumberSequencesOverTimeComponent;
1189
+ 'gs-aggregate': AggregateComponent;
1071
1190
  }
1072
1191
  }
1073
1192
 
@@ -1075,7 +1194,7 @@ declare global {
1075
1194
  declare global {
1076
1195
  namespace JSX {
1077
1196
  interface IntrinsicElements {
1078
- 'gs-number-sequences-over-time': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1197
+ 'gs-aggregate': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1079
1198
  }
1080
1199
  }
1081
1200
  }
@@ -1083,7 +1202,7 @@ declare global {
1083
1202
 
1084
1203
  declare global {
1085
1204
  interface HTMLElementTagNameMap {
1086
- 'gs-aggregate': AggregateComponent;
1205
+ 'gs-number-sequences-over-time': NumberSequencesOverTimeComponent;
1087
1206
  }
1088
1207
  }
1089
1208
 
@@ -1091,7 +1210,7 @@ declare global {
1091
1210
  declare global {
1092
1211
  namespace JSX {
1093
1212
  interface IntrinsicElements {
1094
- 'gs-aggregate': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1213
+ 'gs-number-sequences-over-time': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1095
1214
  }
1096
1215
  }
1097
1216
  }
@@ -1113,6 +1232,22 @@ declare global {
1113
1232
  }
1114
1233
 
1115
1234
 
1235
+ declare global {
1236
+ interface HTMLElementTagNameMap {
1237
+ 'gs-queries-over-time': QueriesOverTimeComponent;
1238
+ }
1239
+ }
1240
+
1241
+
1242
+ declare global {
1243
+ namespace JSX {
1244
+ interface IntrinsicElements {
1245
+ 'gs-queries-over-time': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1246
+ }
1247
+ }
1248
+ }
1249
+
1250
+
1116
1251
  declare global {
1117
1252
  interface HTMLElementTagNameMap {
1118
1253
  'gs-sequences-by-location': SequencesByLocationComponent;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@genspectrum/dashboard-components",
3
- "version": "1.13.0",
3
+ "version": "1.14.0",
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 const mutationsOverTimeResponse = makeLapisResponse(
68
68
  export type MutationsOverTimeResponse = z.infer<typeof mutationsOverTimeResponse>;
69
69
 
70
70
  const queryDefinition = z.object({
71
- displayLabel: z.string().optional(),
71
+ displayLabel: z.string(),
72
72
  countQuery: z.string(),
73
73
  coverageQuery: z.string(),
74
74
  });
@@ -0,0 +1,32 @@
1
+ {
2
+ "data": {
3
+ "queries": ["S:F456L (single mutation)", "R346T + F456L (combination)", "C22916T or T22917G (nucleotide OR)"],
4
+ "dateRanges": [
5
+ { "dateFrom": "2024-01-01", "dateTo": "2024-01-31" },
6
+ { "dateFrom": "2024-02-01", "dateTo": "2024-02-29" },
7
+ { "dateFrom": "2024-03-01", "dateTo": "2024-03-31" },
8
+ { "dateFrom": "2024-04-01", "dateTo": "2024-04-30" }
9
+ ],
10
+ "data": [
11
+ [
12
+ { "count": 450, "coverage": 1000 },
13
+ { "count": 380, "coverage": 950 },
14
+ { "count": 280, "coverage": 900 },
15
+ { "count": 120, "coverage": 850 }
16
+ ],
17
+ [
18
+ { "count": 320, "coverage": 1000 },
19
+ { "count": 420, "coverage": 950 },
20
+ { "count": 380, "coverage": 900 },
21
+ { "count": 290, "coverage": 850 }
22
+ ],
23
+ [
24
+ { "count": 80, "coverage": 1000 },
25
+ { "count": 150, "coverage": 950 },
26
+ { "count": 340, "coverage": 900 },
27
+ { "count": 480, "coverage": 850 }
28
+ ]
29
+ ],
30
+ "totalCountsByDateRange": [1000, 950, 900, 850]
31
+ }
32
+ }
@@ -0,0 +1,128 @@
1
+ {
2
+ "data": {
3
+ "queries": [
4
+ "S:F456L",
5
+ "S:R346T",
6
+ "S:Q493E",
7
+ "S:F486P",
8
+ "S:N460K",
9
+ "S:L455F",
10
+ "S:L455S",
11
+ "S:T572I",
12
+ "S:R190S",
13
+ "S:R190T",
14
+ "S:K478T",
15
+ "S:T22N",
16
+ "S:S31P",
17
+ "ORF1b:S997L",
18
+ "C875A"
19
+ ],
20
+ "dateRanges": [
21
+ { "dateFrom": "2024-01-01", "dateTo": "2024-01-31" },
22
+ { "dateFrom": "2024-02-01", "dateTo": "2024-02-29" },
23
+ { "dateFrom": "2024-03-01", "dateTo": "2024-03-31" },
24
+ { "dateFrom": "2024-04-01", "dateTo": "2024-04-30" }
25
+ ],
26
+ "data": [
27
+ [
28
+ { "count": 450, "coverage": 1000 },
29
+ { "count": 380, "coverage": 950 },
30
+ { "count": 280, "coverage": 900 },
31
+ { "count": 120, "coverage": 850 }
32
+ ],
33
+ [
34
+ { "count": 320, "coverage": 1000 },
35
+ { "count": 420, "coverage": 950 },
36
+ { "count": 380, "coverage": 900 },
37
+ { "count": 290, "coverage": 850 }
38
+ ],
39
+ [
40
+ { "count": 80, "coverage": 1000 },
41
+ { "count": 150, "coverage": 950 },
42
+ { "count": 340, "coverage": 900 },
43
+ { "count": 480, "coverage": 850 }
44
+ ],
45
+ [
46
+ { "count": 200, "coverage": 1000 },
47
+ { "count": 250, "coverage": 950 },
48
+ { "count": 300, "coverage": 900 },
49
+ { "count": 350, "coverage": 850 }
50
+ ],
51
+ [
52
+ { "count": 150, "coverage": 1000 },
53
+ { "count": 200, "coverage": 950 },
54
+ { "count": 250, "coverage": 900 },
55
+ { "count": 300, "coverage": 850 }
56
+ ],
57
+ [
58
+ { "count": 100, "coverage": 1000 },
59
+ { "count": 150, "coverage": 950 },
60
+ { "count": 200, "coverage": 900 },
61
+ { "count": 250, "coverage": 850 }
62
+ ],
63
+ [
64
+ { "count": 180, "coverage": 1000 },
65
+ { "count": 220, "coverage": 950 },
66
+ { "count": 260, "coverage": 900 },
67
+ { "count": 300, "coverage": 850 }
68
+ ],
69
+ [
70
+ { "count": 220, "coverage": 1000 },
71
+ { "count": 280, "coverage": 950 },
72
+ { "count": 340, "coverage": 900 },
73
+ { "count": 400, "coverage": 850 }
74
+ ],
75
+ [
76
+ { "count": 90, "coverage": 1000 },
77
+ { "count": 120, "coverage": 950 },
78
+ { "count": 150, "coverage": 900 },
79
+ { "count": 180, "coverage": 850 }
80
+ ],
81
+ [
82
+ { "count": 300, "coverage": 1000 },
83
+ { "count": 350, "coverage": 950 },
84
+ { "count": 400, "coverage": 900 },
85
+ { "count": 450, "coverage": 850 }
86
+ ],
87
+ [
88
+ { "count": 50, "coverage": 1000 },
89
+ { "count": 75, "coverage": 950 },
90
+ { "count": 100, "coverage": 900 },
91
+ { "count": 125, "coverage": 850 }
92
+ ],
93
+ [
94
+ { "count": 270, "coverage": 1000 },
95
+ { "count": 310, "coverage": 950 },
96
+ { "count": 350, "coverage": 900 },
97
+ { "count": 390, "coverage": 850 }
98
+ ],
99
+ [
100
+ { "count": 160, "coverage": 1000 },
101
+ { "count": 200, "coverage": 950 },
102
+ { "count": 240, "coverage": 900 },
103
+ { "count": 280, "coverage": 850 }
104
+ ],
105
+ [
106
+ { "count": 400, "coverage": 1000 },
107
+ { "count": 420, "coverage": 950 },
108
+ { "count": 440, "coverage": 900 },
109
+ { "count": 460, "coverage": 850 }
110
+ ],
111
+ [
112
+ { "count": 330, "coverage": 1000 },
113
+ { "count": 360, "coverage": 950 },
114
+ { "count": 390, "coverage": 900 },
115
+ { "count": 420, "coverage": 850 }
116
+ ]
117
+ ],
118
+ "totalCountsByDateRange": [1000, 950, 900, 850]
119
+ },
120
+ "info": {
121
+ "dataVersion": "test",
122
+ "requestId": "test-request-many-queries",
123
+ "requestInfo": "Mock data with many queries for testing pagination",
124
+ "reportTo": "https://github.com/GenSpectrum/LAPIS/issues",
125
+ "lapisVersion": "test",
126
+ "siloVersion": "test"
127
+ }
128
+ }
@@ -0,0 +1,16 @@
1
+ {
2
+ "data": {
3
+ "queries": [],
4
+ "dateRanges": [{ "dateFrom": "1800-01-01", "dateTo": "1800-12-31" }],
5
+ "data": [],
6
+ "totalCountsByDateRange": []
7
+ },
8
+ "info": {
9
+ "dataVersion": null,
10
+ "requestId": "test-request-1800s",
11
+ "requestInfo": "Mock data for 1800s empty dataset test",
12
+ "reportTo": "https://github.com/GenSpectrum/LAPIS/issues",
13
+ "lapisVersion": "test",
14
+ "siloVersion": "test"
15
+ }
16
+ }
@@ -0,0 +1,52 @@
1
+ {
2
+ "data": {
3
+ "queries": ["S:F456L", "S:R346T", "S:Q493E"],
4
+ "dateRanges": [
5
+ { "dateFrom": "2024-01-01", "dateTo": "2024-01-31" },
6
+ { "dateFrom": "2024-02-01", "dateTo": "2024-02-29" },
7
+ { "dateFrom": "2024-03-01", "dateTo": "2024-03-31" },
8
+ { "dateFrom": "2024-04-01", "dateTo": "2024-04-30" },
9
+ { "dateFrom": "2024-05-01", "dateTo": "2024-05-31" },
10
+ { "dateFrom": "2024-06-01", "dateTo": "2024-06-30" },
11
+ { "dateFrom": "2024-07-01", "dateTo": "2024-07-31" }
12
+ ],
13
+ "data": [
14
+ [
15
+ { "count": 100, "coverage": 1000 },
16
+ { "count": 150, "coverage": 1050 },
17
+ { "count": 200, "coverage": 1100 },
18
+ { "count": 0, "coverage": 0 },
19
+ { "count": 250, "coverage": 1200 },
20
+ { "count": 300, "coverage": 1250 },
21
+ { "count": 350, "coverage": 1300 }
22
+ ],
23
+ [
24
+ { "count": 200, "coverage": 1000 },
25
+ { "count": 250, "coverage": 1050 },
26
+ { "count": 300, "coverage": 1100 },
27
+ { "count": 0, "coverage": 0 },
28
+ { "count": 350, "coverage": 1200 },
29
+ { "count": 400, "coverage": 1250 },
30
+ { "count": 450, "coverage": 1300 }
31
+ ],
32
+ [
33
+ { "count": 50, "coverage": 1000 },
34
+ { "count": 75, "coverage": 1050 },
35
+ { "count": 100, "coverage": 1100 },
36
+ { "count": 0, "coverage": 0 },
37
+ { "count": 125, "coverage": 1200 },
38
+ { "count": 150, "coverage": 1250 },
39
+ { "count": 175, "coverage": 1300 }
40
+ ]
41
+ ],
42
+ "totalCountsByDateRange": [1000, 1050, 1100, 0, 1200, 1250, 1300]
43
+ },
44
+ "info": {
45
+ "dataVersion": "test",
46
+ "requestId": "test-request-gaps",
47
+ "requestInfo": "Mock data with gaps for testing hide gaps functionality",
48
+ "reportTo": "https://github.com/GenSpectrum/LAPIS/issues",
49
+ "lapisVersion": "test",
50
+ "siloVersion": "test"
51
+ }
52
+ }
@@ -0,0 +1,85 @@
1
+ import { type ProportionValue } from '../../query/queryMutationsOverTime';
2
+ import { serializeQuery, serializeTemporal } from '../../query/queryQueriesOverTime';
3
+ import { Map2dBase, Map2dView, type Map2DContents } from '../../utils/map2d';
4
+ import { type Temporal } from '../../utils/temporalClass';
5
+
6
+ export type QueryFilter = {
7
+ textFilter: string;
8
+ };
9
+
10
+ export type GetFilteredQueryOverTimeDataArgs = {
11
+ data: Map2DContents<string, Temporal, ProportionValue>;
12
+ proportionInterval: { min: number; max: number };
13
+ hideGaps: boolean;
14
+ queryFilterValue: QueryFilter;
15
+ };
16
+
17
+ /**
18
+ * Create a Map2d wrapper for query over time data.
19
+ * Uses displayLabel strings as the first axis (queries) and Temporal objects as the second axis (dates).
20
+ */
21
+ export class QueryOverTimeDataMap extends Map2dBase<string, Temporal, ProportionValue> {
22
+ constructor(initialContent: Map2DContents<string, Temporal, ProportionValue>) {
23
+ super(serializeQuery, serializeTemporal, initialContent);
24
+ }
25
+ }
26
+
27
+ export function getFilteredQueryOverTimeData({
28
+ data,
29
+ proportionInterval,
30
+ hideGaps,
31
+ queryFilterValue,
32
+ }: GetFilteredQueryOverTimeDataArgs) {
33
+ const dataMap = new QueryOverTimeDataMap(data);
34
+ const filteredData = new Map2dView(dataMap);
35
+
36
+ const queries = filteredData.getFirstAxisKeys();
37
+ const dates = filteredData.getSecondAxisKeys();
38
+
39
+ const queriesToFilterOut = queries.filter((query) => {
40
+ // Calculate overall proportion for this query
41
+ let totalCount = 0;
42
+ let totalCoverage = 0;
43
+
44
+ dates.forEach((date) => {
45
+ const value = filteredData.get(query, date);
46
+ if (value?.type === 'valueWithCoverage') {
47
+ totalCount += value.count;
48
+ totalCoverage += value.coverage;
49
+ }
50
+ });
51
+
52
+ const overallProportion = totalCoverage > 0 ? totalCount / totalCoverage : 0;
53
+
54
+ // Filter by proportion interval
55
+ if (overallProportion < proportionInterval.min || overallProportion > proportionInterval.max) {
56
+ return true;
57
+ }
58
+
59
+ // Filter by text (case-insensitive search in displayLabel)
60
+ if (
61
+ queryFilterValue.textFilter !== '' &&
62
+ !query.toLowerCase().includes(queryFilterValue.textFilter.toLowerCase())
63
+ ) {
64
+ return true;
65
+ }
66
+
67
+ return false;
68
+ });
69
+
70
+ // Remove filtered queries from the data view
71
+ queriesToFilterOut.forEach((query) => {
72
+ filteredData.deleteRow(query);
73
+ });
74
+
75
+ // Hide gaps (columns with no data)
76
+ if (hideGaps) {
77
+ const dateRangesToFilterOut = filteredData.getSecondAxisKeys().filter((dateRange) => {
78
+ const vals = filteredData.getColumn(dateRange);
79
+ return !vals.some((v) => (v?.type === 'value' || v?.type === 'valueWithCoverage') && v.totalCount > 0);
80
+ });
81
+ dateRangesToFilterOut.forEach((dateRange) => filteredData.deleteColumn(dateRange));
82
+ }
83
+
84
+ return filteredData;
85
+ }
@@ -0,0 +1,25 @@
1
+ import { type FunctionComponent } from 'preact';
2
+ import { type Dispatch, type StateUpdater } from 'preact/hooks';
3
+
4
+ import { type QueryFilter } from './getFilteredQueriesOverTimeData';
5
+
6
+ type QueriesOverTimeFilterProps = {
7
+ value: QueryFilter;
8
+ setFilterValue: Dispatch<StateUpdater<QueryFilter>>;
9
+ };
10
+
11
+ export const QueriesOverTimeFilter: FunctionComponent<QueriesOverTimeFilterProps> = ({ value, setFilterValue }) => {
12
+ return (
13
+ <input
14
+ type='text'
15
+ placeholder='Filter queries...'
16
+ className='input input-xs input-bordered w-40'
17
+ value={value.textFilter}
18
+ onInput={(e) =>
19
+ setFilterValue({
20
+ textFilter: (e.target as HTMLInputElement).value,
21
+ })
22
+ }
23
+ />
24
+ );
25
+ };