@genspectrum/dashboard-components 0.10.1 → 0.10.2

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 (46) hide show
  1. package/custom-elements.json +23 -23
  2. package/dist/assets/{mutationOverTimeWorker-CvZg52rf.js.map → mutationOverTimeWorker-Di6yP1e6.js.map} +1 -1
  3. package/dist/components.d.ts +50 -48
  4. package/dist/components.js +151 -62
  5. package/dist/components.js.map +1 -1
  6. package/dist/{utilEntrypoint-g4DsyhU7.js → dateRangeOption-du8H7LWu.js} +33 -2
  7. package/dist/dateRangeOption-du8H7LWu.js.map +1 -0
  8. package/dist/util.d.ts +101 -59
  9. package/dist/util.js +3 -2
  10. package/package.json +1 -1
  11. package/src/preact/components/color-scale-selector.tsx +7 -3
  12. package/src/preact/components/error-boundary.tsx +39 -5
  13. package/src/preact/components/error-display.tsx +40 -5
  14. package/src/preact/dateRangeSelector/computeInitialValues.spec.ts +8 -2
  15. package/src/preact/dateRangeSelector/computeInitialValues.ts +6 -0
  16. package/src/preact/dateRangeSelector/date-range-selector.stories.tsx +16 -2
  17. package/src/preact/dateRangeSelector/date-range-selector.tsx +20 -15
  18. package/src/preact/dateRangeSelector/dateRangeOption.ts +10 -5
  19. package/src/preact/lineageFilter/lineage-filter.stories.tsx +18 -4
  20. package/src/preact/lineageFilter/lineage-filter.tsx +15 -10
  21. package/src/preact/locationFilter/location-filter.stories.tsx +14 -0
  22. package/src/preact/locationFilter/location-filter.tsx +15 -10
  23. package/src/preact/mutationComparison/mutation-comparison-venn.tsx +17 -18
  24. package/src/preact/mutationComparison/mutation-comparison.tsx +18 -12
  25. package/src/preact/mutationsOverTime/__mockData__/aminoAcidMutationsByDay.ts +1326 -9341
  26. package/src/preact/mutationsOverTime/__mockData__/byWeek.ts +615 -4920
  27. package/src/preact/mutationsOverTime/__mockData__/defaultMockData.ts +2203 -17624
  28. package/src/preact/mutationsOverTime/mutations-over-time-grid.tsx +16 -8
  29. package/src/preact/mutationsOverTime/mutations-over-time.tsx +1 -3
  30. package/src/preact/shared/stories/expectInvalidAttributesErrorMessage.ts +13 -0
  31. package/src/preact/webWorkers/useWebWorker.ts +8 -4
  32. package/src/query/queryMutationsOverTime.spec.ts +12 -27
  33. package/src/query/queryMutationsOverTime.ts +2 -6
  34. package/src/types.ts +19 -6
  35. package/src/utilEntrypoint.ts +8 -0
  36. package/src/utils/map2d.spec.ts +10 -10
  37. package/src/utils/map2d.ts +10 -10
  38. package/src/web-components/input/gs-date-range-selector.stories.ts +2 -2
  39. package/src/web-components/input/gs-date-range-selector.tsx +3 -3
  40. package/src/web-components/input/gs-lineage-filter.tsx +1 -1
  41. package/src/web-components/input/gs-location-filter.tsx +2 -2
  42. package/src/web-components/visualization/gs-aggregate.tsx +2 -2
  43. package/standalone-bundle/assets/{mutationOverTimeWorker-CypX_PYM.js.map → mutationOverTimeWorker-cIyshfj_.js.map} +1 -1
  44. package/standalone-bundle/dashboard-components.js +6668 -6580
  45. package/standalone-bundle/dashboard-components.js.map +1 -1
  46. package/dist/utilEntrypoint-g4DsyhU7.js.map +0 -1
@@ -1,7 +1,33 @@
1
+ import z from "zod";
2
+ const lapisFilterSchema = z.record(z.union([z.string(), z.number(), z.null(), z.boolean()]));
3
+ const namedLapisFilterSchema = z.object({
4
+ lapisFilter: lapisFilterSchema,
5
+ displayName: z.string()
6
+ });
7
+ const sequenceTypeSchema = z.union([z.literal("nucleotide"), z.literal("amino acid")]);
8
+ const views = {
9
+ table: "table",
10
+ venn: "venn"
11
+ };
12
+ const mutationComparisonViewSchema = z.union([z.literal(views.table), z.literal(views.venn)]);
1
13
  const toYYYYMMDD = (date) => {
2
14
  const options = { year: "numeric", month: "2-digit", day: "2-digit" };
3
15
  return date.toLocaleDateString("en-CA", options);
4
16
  };
17
+ const dateRangeOptionSchema = z.object({
18
+ /** The label of the date range option that will be shown to the user */
19
+ label: z.string(),
20
+ /**
21
+ * The start date of the date range in the format `YYYY-MM-DD`.
22
+ * If not set, the date range selector will default to the `earliestDate` property.
23
+ */
24
+ dateFrom: z.string().date().optional(),
25
+ /**
26
+ * The end date of the date range in the format `YYYY-MM-DD`.
27
+ * If not set, the date range selector will default to the current date.
28
+ */
29
+ dateTo: z.string().date().optional()
30
+ });
5
31
  class DateRangeOptionChangedEvent extends CustomEvent {
6
32
  constructor(detail) {
7
33
  super("gs-date-range-option-changed", {
@@ -55,7 +81,12 @@ const dateRangeOptionPresets = {
55
81
  };
56
82
  export {
57
83
  DateRangeOptionChangedEvent as D,
84
+ dateRangeOptionSchema as a,
58
85
  dateRangeOptionPresets as d,
59
- toYYYYMMDD as t
86
+ mutationComparisonViewSchema as m,
87
+ namedLapisFilterSchema as n,
88
+ sequenceTypeSchema as s,
89
+ toYYYYMMDD as t,
90
+ views as v
60
91
  };
61
- //# sourceMappingURL=utilEntrypoint-g4DsyhU7.js.map
92
+ //# sourceMappingURL=dateRangeOption-du8H7LWu.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dateRangeOption-du8H7LWu.js","sources":["../src/types.ts","../src/preact/dateRangeSelector/dateConversion.ts","../src/preact/dateRangeSelector/dateRangeOption.ts"],"sourcesContent":["import z from 'zod';\n\nimport {\n type Deletion,\n type DeletionClass,\n type Insertion,\n type InsertionClass,\n type Substitution,\n type SubstitutionClass,\n} from './utils/mutations';\n\nexport const lapisFilterSchema = z.record(z.union([z.string(), z.number(), z.null(), z.boolean()]));\nexport type LapisFilter = z.infer<typeof lapisFilterSchema>;\n\nexport const namedLapisFilterSchema = z.object({\n lapisFilter: lapisFilterSchema,\n displayName: z.string(),\n});\nexport type NamedLapisFilter = z.infer<typeof namedLapisFilterSchema>;\n\nexport type TemporalGranularity = 'day' | 'week' | 'month' | 'year';\n\nexport const sequenceTypeSchema = z.union([z.literal('nucleotide'), z.literal('amino acid')]);\nexport type SequenceType = z.infer<typeof sequenceTypeSchema>;\n\nexport type SubstitutionOrDeletion = 'substitution' | 'deletion';\n\nexport type MutationType = SubstitutionOrDeletion | 'insertion';\n\nexport type SubstitutionEntry<T extends Substitution = SubstitutionClass> = {\n type: 'substitution';\n mutation: T;\n count: number;\n proportion: number;\n};\n\nexport type DeletionEntry<T extends Deletion = DeletionClass> = {\n type: 'deletion';\n mutation: T;\n count: number;\n proportion: number;\n};\n\nexport type InsertionEntry<T extends Insertion = InsertionClass> = { type: 'insertion'; mutation: T; count: number };\n\nexport type SubstitutionOrDeletionEntry<\n S extends Substitution = SubstitutionClass,\n D extends Deletion = DeletionClass,\n> = SubstitutionEntry<S> | DeletionEntry<D>;\n\nexport type MutationEntry = SubstitutionEntry | DeletionEntry | InsertionEntry;\n\nexport const views = {\n table: 'table',\n venn: 'venn',\n} as const;\n\nexport const mutationComparisonViewSchema = z.union([z.literal(views.table), z.literal(views.venn)]);\nexport type MutationComparisonView = z.infer<typeof mutationComparisonViewSchema>;\n","export const toYYYYMMDD = (date: Date) => {\n const options: Intl.DateTimeFormatOptions = { year: 'numeric', month: '2-digit', day: '2-digit' };\n return date.toLocaleDateString('en-CA', options);\n};\n","import z from 'zod';\n\nimport { toYYYYMMDD } from './dateConversion';\n\n/**\n * A date range option that can be used in the `gs-date-range-selector` component.\n */\nexport const dateRangeOptionSchema = z.object({\n /** The label of the date range option that will be shown to the user */\n label: z.string(),\n /**\n * The start date of the date range in the format `YYYY-MM-DD`.\n * If not set, the date range selector will default to the `earliestDate` property.\n */\n dateFrom: z.string().date().optional(),\n /**\n * The end date of the date range in the format `YYYY-MM-DD`.\n * If not set, the date range selector will default to the current date.\n */\n dateTo: z.string().date().optional(),\n});\n\nexport type DateRangeOption = z.infer<typeof dateRangeOptionSchema>;\n\nexport type DateRangeSelectOption = string | { dateFrom: string; dateTo: string };\n\nexport class DateRangeOptionChangedEvent extends CustomEvent<DateRangeSelectOption> {\n constructor(detail: DateRangeSelectOption) {\n super('gs-date-range-option-changed', {\n detail,\n bubbles: true,\n composed: true,\n });\n }\n}\n\nconst today = new Date();\n\nconst twoWeeksAgo = new Date();\ntwoWeeksAgo.setDate(today.getDate() - 14);\n\nconst lastMonth = new Date(today);\nlastMonth.setMonth(today.getMonth() - 1);\n\nconst last2Months = new Date(today);\nlast2Months.setMonth(today.getMonth() - 2);\n\nconst last3Months = new Date(today);\nlast3Months.setMonth(today.getMonth() - 3);\n\nconst last6Months = new Date(today);\nlast6Months.setMonth(today.getMonth() - 6);\n\nconst lastYear = new Date(today);\nlastYear.setFullYear(today.getFullYear() - 1);\n\n/**\n * Presets for the `gs-date-range-selector` component that can be used as `dateRangeOptions`.\n */\nexport const dateRangeOptionPresets = {\n last2Weeks: {\n label: 'Last 2 weeks',\n dateFrom: toYYYYMMDD(twoWeeksAgo),\n },\n lastMonth: {\n label: 'Last month',\n dateFrom: toYYYYMMDD(lastMonth),\n },\n last2Months: {\n label: 'Last 2 months',\n dateFrom: toYYYYMMDD(last2Months),\n },\n last3Months: {\n label: 'Last 3 months',\n dateFrom: toYYYYMMDD(last3Months),\n },\n last6Months: {\n label: 'Last 6 months',\n dateFrom: toYYYYMMDD(last6Months),\n },\n lastYear: {\n label: 'Last year',\n dateFrom: toYYYYMMDD(lastYear),\n },\n allTimes: {\n label: 'All times',\n },\n} satisfies Record<string, DateRangeOption>;\n"],"names":[],"mappings":";AAWO,MAAM,oBAAoB,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE,OAAO,GAAG,EAAE,OAAO,GAAG,EAAE,KAAK,GAAG,EAAE,QAAQ,CAAC,CAAC,CAAC;AAGrF,MAAA,yBAAyB,EAAE,OAAO;AAAA,EAC3C,aAAa;AAAA,EACb,aAAa,EAAE,OAAO;AAC1B,CAAC;AAKM,MAAM,qBAAqB,EAAE,MAAM,CAAC,EAAE,QAAQ,YAAY,GAAG,EAAE,QAAQ,YAAY,CAAC,CAAC;AA8BrF,MAAM,QAAQ;AAAA,EACjB,OAAO;AAAA,EACP,MAAM;AACV;AAEO,MAAM,+BAA+B,EAAE,MAAM,CAAC,EAAE,QAAQ,MAAM,KAAK,GAAG,EAAE,QAAQ,MAAM,IAAI,CAAC,CAAC;ACzDtF,MAAA,aAAa,CAAC,SAAe;AACtC,QAAM,UAAsC,EAAE,MAAM,WAAW,OAAO,WAAW,KAAK;AAC/E,SAAA,KAAK,mBAAmB,SAAS,OAAO;AACnD;ACIa,MAAA,wBAAwB,EAAE,OAAO;AAAA;AAAA,EAE1C,OAAO,EAAE,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA,EAKhB,UAAU,EAAE,OAAS,EAAA,KAAA,EAAO,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA,EAKrC,QAAQ,EAAE,OAAS,EAAA,KAAA,EAAO,SAAS;AACvC,CAAC;AAMM,MAAM,oCAAoC,YAAmC;AAAA,EAChF,YAAY,QAA+B;AACvC,UAAM,gCAAgC;AAAA,MAClC;AAAA,MACA,SAAS;AAAA,MACT,UAAU;AAAA,IAAA,CACb;AAAA,EACL;AACJ;AAEA,MAAM,4BAAY;AAElB,MAAM,kCAAkB;AACxB,YAAY,QAAQ,MAAM,QAAQ,IAAI,EAAE;AAExC,MAAM,YAAY,IAAI,KAAK,KAAK;AAChC,UAAU,SAAS,MAAM,SAAS,IAAI,CAAC;AAEvC,MAAM,cAAc,IAAI,KAAK,KAAK;AAClC,YAAY,SAAS,MAAM,SAAS,IAAI,CAAC;AAEzC,MAAM,cAAc,IAAI,KAAK,KAAK;AAClC,YAAY,SAAS,MAAM,SAAS,IAAI,CAAC;AAEzC,MAAM,cAAc,IAAI,KAAK,KAAK;AAClC,YAAY,SAAS,MAAM,SAAS,IAAI,CAAC;AAEzC,MAAM,WAAW,IAAI,KAAK,KAAK;AAC/B,SAAS,YAAY,MAAM,YAAY,IAAI,CAAC;AAKrC,MAAM,yBAAyB;AAAA,EAClC,YAAY;AAAA,IACR,OAAO;AAAA,IACP,UAAU,WAAW,WAAW;AAAA,EACpC;AAAA,EACA,WAAW;AAAA,IACP,OAAO;AAAA,IACP,UAAU,WAAW,SAAS;AAAA,EAClC;AAAA,EACA,aAAa;AAAA,IACT,OAAO;AAAA,IACP,UAAU,WAAW,WAAW;AAAA,EACpC;AAAA,EACA,aAAa;AAAA,IACT,OAAO;AAAA,IACP,UAAU,WAAW,WAAW;AAAA,EACpC;AAAA,EACA,aAAa;AAAA,IACT,OAAO;AAAA,IACP,UAAU,WAAW,WAAW;AAAA,EACpC;AAAA,EACA,UAAU;AAAA,IACN,OAAO;AAAA,IACP,UAAU,WAAW,QAAQ;AAAA,EACjC;AAAA,EACA,UAAU;AAAA,IACN,OAAO;AAAA,EACX;AACJ;"}
package/dist/util.d.ts CHANGED
@@ -1,20 +1,6 @@
1
- /**
2
- * A date range option that can be used in the `gs-date-range-selector` component.
3
- */
4
- export declare type DateRangeOption = {
5
- /** The label of the date range option that will be shown to the user */
6
- label: string;
7
- /**
8
- * The start date of the date range in the format `YYYY-MM-DD`.
9
- * If not set, the date range selector will default to the `earliestDate` property.
10
- */
11
- dateFrom?: string;
12
- /**
13
- * The end date of the date range in the format `YYYY-MM-DD`.
14
- * If not set, the date range selector will default to the current date.
15
- */
16
- dateTo?: string;
17
- };
1
+ import { default as default_2 } from 'zod';
2
+
3
+ export declare type DateRangeOption = default_2.infer<typeof dateRangeOptionSchema>;
18
4
 
19
5
  export declare class DateRangeOptionChangedEvent extends CustomEvent<DateRangeSelectOption> {
20
6
  constructor(detail: DateRangeSelectOption);
@@ -53,11 +39,67 @@ export declare const dateRangeOptionPresets: {
53
39
  };
54
40
  };
55
41
 
42
+ /**
43
+ * A date range option that can be used in the `gs-date-range-selector` component.
44
+ */
45
+ declare const dateRangeOptionSchema: default_2.ZodObject<{
46
+ /** The label of the date range option that will be shown to the user */
47
+ label: default_2.ZodString;
48
+ /**
49
+ * The start date of the date range in the format `YYYY-MM-DD`.
50
+ * If not set, the date range selector will default to the `earliestDate` property.
51
+ */
52
+ dateFrom: default_2.ZodOptional<default_2.ZodString>;
53
+ /**
54
+ * The end date of the date range in the format `YYYY-MM-DD`.
55
+ * If not set, the date range selector will default to the current date.
56
+ */
57
+ dateTo: default_2.ZodOptional<default_2.ZodString>;
58
+ }, "strip", default_2.ZodTypeAny, {
59
+ label: string;
60
+ dateFrom?: string | undefined;
61
+ dateTo?: string | undefined;
62
+ }, {
63
+ label: string;
64
+ dateFrom?: string | undefined;
65
+ dateTo?: string | undefined;
66
+ }>;
67
+
56
68
  export declare type DateRangeSelectOption = string | {
57
69
  dateFrom: string;
58
70
  dateTo: string;
59
71
  };
60
72
 
73
+ export declare type LapisFilter = default_2.infer<typeof lapisFilterSchema>;
74
+
75
+ declare const lapisFilterSchema: default_2.ZodRecord<default_2.ZodString, default_2.ZodUnion<[default_2.ZodString, default_2.ZodNumber, default_2.ZodNull, default_2.ZodBoolean]>>;
76
+
77
+ export declare type MutationComparisonView = default_2.infer<typeof mutationComparisonViewSchema>;
78
+
79
+ declare const mutationComparisonViewSchema: default_2.ZodUnion<[default_2.ZodLiteral<"table">, default_2.ZodLiteral<"venn">]>;
80
+
81
+ export declare type NamedLapisFilter = default_2.infer<typeof namedLapisFilterSchema>;
82
+
83
+ declare const namedLapisFilterSchema: default_2.ZodObject<{
84
+ lapisFilter: default_2.ZodRecord<default_2.ZodString, default_2.ZodUnion<[default_2.ZodString, default_2.ZodNumber, default_2.ZodNull, default_2.ZodBoolean]>>;
85
+ displayName: default_2.ZodString;
86
+ }, "strip", default_2.ZodTypeAny, {
87
+ lapisFilter: Record<string, string | number | boolean | null>;
88
+ displayName: string;
89
+ }, {
90
+ lapisFilter: Record<string, string | number | boolean | null>;
91
+ displayName: string;
92
+ }>;
93
+
94
+ export declare type SequenceType = default_2.infer<typeof sequenceTypeSchema>;
95
+
96
+ declare const sequenceTypeSchema: default_2.ZodUnion<[default_2.ZodLiteral<"nucleotide">, default_2.ZodLiteral<"amino acid">]>;
97
+
98
+ export declare const views: {
99
+ readonly table: "table";
100
+ readonly venn: "venn";
101
+ };
102
+
61
103
  export { }
62
104
 
63
105
 
@@ -86,7 +128,11 @@ declare global {
86
128
 
87
129
  declare global {
88
130
  interface HTMLElementTagNameMap {
89
- 'gs-mutation-comparison-component': MutationComparisonComponent;
131
+ 'gs-date-range-selector': DateRangeSelectorComponent;
132
+ }
133
+ interface HTMLElementEventMap {
134
+ 'gs-date-range-filter-changed': CustomEvent<Record<string, string>>;
135
+ 'gs-date-range-option-changed': DateRangeOptionChangedEvent;
90
136
  }
91
137
  }
92
138
 
@@ -94,7 +140,7 @@ declare global {
94
140
  declare global {
95
141
  namespace JSX {
96
142
  interface IntrinsicElements {
97
- 'gs-mutation-comparison-component': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
143
+ 'gs-date-range-selector': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
98
144
  }
99
145
  }
100
146
  }
@@ -102,7 +148,10 @@ declare global {
102
148
 
103
149
  declare global {
104
150
  interface HTMLElementTagNameMap {
105
- 'gs-mutations-component': MutationsComponent;
151
+ 'gs-location-filter': LocationFilterComponent;
152
+ }
153
+ interface HTMLElementEventMap {
154
+ 'gs-location-changed': CustomEvent<Record<string, string>>;
106
155
  }
107
156
  }
108
157
 
@@ -110,7 +159,7 @@ declare global {
110
159
  declare global {
111
160
  namespace JSX {
112
161
  interface IntrinsicElements {
113
- 'gs-mutations-component': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
162
+ 'gs-location-filter': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
114
163
  }
115
164
  }
116
165
  }
@@ -118,7 +167,10 @@ declare global {
118
167
 
119
168
  declare global {
120
169
  interface HTMLElementTagNameMap {
121
- 'gs-prevalence-over-time': PrevalenceOverTimeComponent;
170
+ 'gs-text-input': TextInputComponent;
171
+ }
172
+ interface HTMLElementEventMap {
173
+ 'gs-text-input-changed': CustomEvent<Record<string, string>>;
122
174
  }
123
175
  }
124
176
 
@@ -126,7 +178,7 @@ declare global {
126
178
  declare global {
127
179
  namespace JSX {
128
180
  interface IntrinsicElements {
129
- 'gs-prevalence-over-time': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
181
+ 'gs-text-input': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
130
182
  }
131
183
  }
132
184
  }
@@ -134,7 +186,10 @@ declare global {
134
186
 
135
187
  declare global {
136
188
  interface HTMLElementTagNameMap {
137
- 'gs-relative-growth-advantage': RelativeGrowthAdvantageComponent;
189
+ 'gs-mutation-filter': MutationFilterComponent;
190
+ }
191
+ interface HTMLElementEventMap {
192
+ 'gs-mutation-filter-changed': CustomEvent<SelectedMutationFilterStrings>;
138
193
  }
139
194
  }
140
195
 
@@ -142,7 +197,7 @@ declare global {
142
197
  declare global {
143
198
  namespace JSX {
144
199
  interface IntrinsicElements {
145
- 'gs-relative-growth-advantage': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
200
+ 'gs-mutation-filter': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
146
201
  }
147
202
  }
148
203
  }
@@ -150,7 +205,10 @@ declare global {
150
205
 
151
206
  declare global {
152
207
  interface HTMLElementTagNameMap {
153
- 'gs-aggregate-component': AggregateComponent;
208
+ 'gs-lineage-filter': LineageFilterComponent;
209
+ }
210
+ interface HTMLElementEventMap {
211
+ 'gs-lineage-filter-changed': CustomEvent<Record<string, string>>;
154
212
  }
155
213
  }
156
214
 
@@ -158,7 +216,7 @@ declare global {
158
216
  declare global {
159
217
  namespace JSX {
160
218
  interface IntrinsicElements {
161
- 'gs-aggregate-component': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
219
+ 'gs-lineage-filter': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
162
220
  }
163
221
  }
164
222
  }
@@ -166,7 +224,7 @@ declare global {
166
224
 
167
225
  declare global {
168
226
  interface HTMLElementTagNameMap {
169
- 'gs-number-sequences-over-time': NumberSequencesOverTimeComponent;
227
+ 'gs-mutation-comparison-component': MutationComparisonComponent;
170
228
  }
171
229
  }
172
230
 
@@ -174,7 +232,7 @@ declare global {
174
232
  declare global {
175
233
  namespace JSX {
176
234
  interface IntrinsicElements {
177
- 'gs-number-sequences-over-time': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
235
+ 'gs-mutation-comparison-component': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
178
236
  }
179
237
  }
180
238
  }
@@ -182,7 +240,7 @@ declare global {
182
240
 
183
241
  declare global {
184
242
  interface HTMLElementTagNameMap {
185
- 'gs-mutations-over-time': MutationsOverTimeComponent;
243
+ 'gs-mutations-component': MutationsComponent;
186
244
  }
187
245
  }
188
246
 
@@ -190,7 +248,7 @@ declare global {
190
248
  declare global {
191
249
  namespace JSX {
192
250
  interface IntrinsicElements {
193
- 'gs-mutations-over-time': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
251
+ 'gs-mutations-component': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
194
252
  }
195
253
  }
196
254
  }
@@ -198,7 +256,7 @@ declare global {
198
256
 
199
257
  declare global {
200
258
  interface HTMLElementTagNameMap {
201
- 'gs-statistics': StatisticsComponent;
259
+ 'gs-prevalence-over-time': PrevalenceOverTimeComponent;
202
260
  }
203
261
  }
204
262
 
@@ -206,7 +264,7 @@ declare global {
206
264
  declare global {
207
265
  namespace JSX {
208
266
  interface IntrinsicElements {
209
- 'gs-statistics': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
267
+ 'gs-prevalence-over-time': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
210
268
  }
211
269
  }
212
270
  }
@@ -214,11 +272,7 @@ declare global {
214
272
 
215
273
  declare global {
216
274
  interface HTMLElementTagNameMap {
217
- 'gs-date-range-selector': DateRangeSelectorComponent;
218
- }
219
- interface HTMLElementEventMap {
220
- 'gs-date-range-filter-changed': CustomEvent<Record<string, string>>;
221
- 'gs-date-range-option-changed': DateRangeOptionChangedEvent;
275
+ 'gs-relative-growth-advantage': RelativeGrowthAdvantageComponent;
222
276
  }
223
277
  }
224
278
 
@@ -226,7 +280,7 @@ declare global {
226
280
  declare global {
227
281
  namespace JSX {
228
282
  interface IntrinsicElements {
229
- 'gs-date-range-selector': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
283
+ 'gs-relative-growth-advantage': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
230
284
  }
231
285
  }
232
286
  }
@@ -234,10 +288,7 @@ declare global {
234
288
 
235
289
  declare global {
236
290
  interface HTMLElementTagNameMap {
237
- 'gs-location-filter': LocationFilterComponent;
238
- }
239
- interface HTMLElementEventMap {
240
- 'gs-location-changed': CustomEvent<Record<string, string>>;
291
+ 'gs-aggregate': AggregateComponent;
241
292
  }
242
293
  }
243
294
 
@@ -245,7 +296,7 @@ declare global {
245
296
  declare global {
246
297
  namespace JSX {
247
298
  interface IntrinsicElements {
248
- 'gs-location-filter': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
299
+ 'gs-aggregate': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
249
300
  }
250
301
  }
251
302
  }
@@ -253,10 +304,7 @@ declare global {
253
304
 
254
305
  declare global {
255
306
  interface HTMLElementTagNameMap {
256
- 'gs-text-input': TextInputComponent;
257
- }
258
- interface HTMLElementEventMap {
259
- 'gs-text-input-changed': CustomEvent<Record<string, string>>;
307
+ 'gs-number-sequences-over-time': NumberSequencesOverTimeComponent;
260
308
  }
261
309
  }
262
310
 
@@ -264,7 +312,7 @@ declare global {
264
312
  declare global {
265
313
  namespace JSX {
266
314
  interface IntrinsicElements {
267
- 'gs-text-input': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
315
+ 'gs-number-sequences-over-time': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
268
316
  }
269
317
  }
270
318
  }
@@ -272,10 +320,7 @@ declare global {
272
320
 
273
321
  declare global {
274
322
  interface HTMLElementTagNameMap {
275
- 'gs-mutation-filter': MutationFilterComponent;
276
- }
277
- interface HTMLElementEventMap {
278
- 'gs-mutation-filter-changed': CustomEvent<SelectedMutationFilterStrings>;
323
+ 'gs-mutations-over-time': MutationsOverTimeComponent;
279
324
  }
280
325
  }
281
326
 
@@ -283,7 +328,7 @@ declare global {
283
328
  declare global {
284
329
  namespace JSX {
285
330
  interface IntrinsicElements {
286
- 'gs-mutation-filter': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
331
+ 'gs-mutations-over-time': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
287
332
  }
288
333
  }
289
334
  }
@@ -291,10 +336,7 @@ declare global {
291
336
 
292
337
  declare global {
293
338
  interface HTMLElementTagNameMap {
294
- 'gs-lineage-filter': LineageFilterComponent;
295
- }
296
- interface HTMLElementEventMap {
297
- 'gs-lineage-filter-changed': CustomEvent<Record<string, string>>;
339
+ 'gs-statistics': StatisticsComponent;
298
340
  }
299
341
  }
300
342
 
@@ -302,7 +344,7 @@ declare global {
302
344
  declare global {
303
345
  namespace JSX {
304
346
  interface IntrinsicElements {
305
- 'gs-lineage-filter': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
347
+ 'gs-statistics': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
306
348
  }
307
349
  }
308
350
  }
package/dist/util.js CHANGED
@@ -1,6 +1,7 @@
1
- import { D, d } from "./utilEntrypoint-g4DsyhU7.js";
1
+ import { D, d, v } from "./dateRangeOption-du8H7LWu.js";
2
2
  export {
3
3
  D as DateRangeOptionChangedEvent,
4
- d as dateRangeOptionPresets
4
+ d as dateRangeOptionPresets,
5
+ v as views
5
6
  };
6
7
  //# sourceMappingURL=util.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@genspectrum/dashboard-components",
3
- "version": "0.10.1",
3
+ "version": "0.10.2",
4
4
  "description": "GenSpectrum web components for building dashboards",
5
5
  "type": "module",
6
6
  "license": "AGPL-3.0-only",
@@ -54,7 +54,11 @@ export const ColorScaleSelector: FunctionComponent<ColorScaleSelectorProps> = ({
54
54
  );
55
55
  };
56
56
 
57
- export const getColorWithingScale = (value: number, colorScale: ColorScale) => {
57
+ export const getColorWithingScale = (value: number | undefined, colorScale: ColorScale) => {
58
+ if (value === undefined) {
59
+ return 'lightgrey';
60
+ }
61
+
58
62
  if (colorScale.min === colorScale.max) {
59
63
  return singleGraphColorRGBByName(colorScale.color, 0);
60
64
  }
@@ -66,8 +70,8 @@ export const getColorWithingScale = (value: number, colorScale: ColorScale) => {
66
70
  return singleGraphColorRGBByName(colorScale.color, alpha);
67
71
  };
68
72
 
69
- export const getTextColorForScale = (value: number, colorScale: ColorScale) => {
70
- if (colorScale.min === colorScale.max) {
73
+ export const getTextColorForScale = (value: number | undefined, colorScale: ColorScale) => {
74
+ if (value === undefined || colorScale.min === colorScale.max) {
71
75
  return 'black';
72
76
  }
73
77
 
@@ -1,16 +1,26 @@
1
- import type { FunctionComponent } from 'preact';
2
- import { useErrorBoundary } from 'preact/hooks';
1
+ import { type RenderableProps } from 'preact';
2
+ import { useErrorBoundary, useMemo } from 'preact/hooks';
3
+ import { type ZodSchema } from 'zod';
3
4
 
4
- import { ErrorDisplay, type ErrorDisplayProps } from './error-display';
5
+ import { ErrorDisplay, type ErrorDisplayProps, InvalidPropsError } from './error-display';
5
6
  import { ResizeContainer, type Size } from './resize-container';
6
7
 
7
- type ErrorBoundaryProps = {
8
+ type ErrorBoundaryProps<T> = {
8
9
  size: Size;
9
10
  layout?: ErrorDisplayProps['layout'];
11
+ componentProps?: T;
12
+ schema?: ZodSchema<T>;
10
13
  };
11
14
 
12
- export const ErrorBoundary: FunctionComponent<ErrorBoundaryProps> = ({ size, layout, children }) => {
15
+ export const ErrorBoundary = <T extends Record<string, unknown>>({
16
+ size,
17
+ layout,
18
+ componentProps,
19
+ schema,
20
+ children,
21
+ }: RenderableProps<ErrorBoundaryProps<T>>) => {
13
22
  const [internalError, resetError] = useErrorBoundary();
23
+ const componentPropsParseError = useCheckComponentProps(schema, componentProps);
14
24
 
15
25
  if (internalError) {
16
26
  return (
@@ -20,5 +30,29 @@ export const ErrorBoundary: FunctionComponent<ErrorBoundaryProps> = ({ size, lay
20
30
  );
21
31
  }
22
32
 
33
+ if (componentPropsParseError !== undefined) {
34
+ return (
35
+ <ResizeContainer size={size}>
36
+ <ErrorDisplay error={componentPropsParseError} layout={layout} />
37
+ </ResizeContainer>
38
+ );
39
+ }
40
+
23
41
  return <>{children}</>;
24
42
  };
43
+
44
+ // TODO #554 - make both arguments required once all components validate their props
45
+ function useCheckComponentProps<T extends Record<string, unknown>>(schema?: ZodSchema<T>, componentProps?: T) {
46
+ return useMemo(() => {
47
+ if (schema === undefined || componentProps === undefined) {
48
+ return undefined;
49
+ }
50
+
51
+ const parseResult = schema.safeParse(componentProps);
52
+ if (parseResult.success) {
53
+ return undefined;
54
+ }
55
+
56
+ return new InvalidPropsError(parseResult.error, componentProps);
57
+ }, [componentProps, schema]);
58
+ }
@@ -1,5 +1,6 @@
1
1
  import { type FunctionComponent } from 'preact';
2
2
  import { useEffect, useRef } from 'preact/hooks';
3
+ import { type ZodError } from 'zod';
3
4
 
4
5
  import { LapisError, UnknownLapisError } from '../../lapisApi/lapisApi';
5
6
 
@@ -24,9 +25,19 @@ export class UserFacingError extends Error {
24
25
  }
25
26
  }
26
27
 
28
+ export class InvalidPropsError extends Error {
29
+ constructor(
30
+ public readonly zodError: ZodError,
31
+ public readonly componentProps: Record<string, unknown>,
32
+ ) {
33
+ super(zodError.message);
34
+ this.name = 'InvalidPropsError';
35
+ }
36
+ }
37
+
27
38
  export type ErrorDisplayProps = {
28
39
  error: Error;
29
- resetError: () => void;
40
+ resetError?: () => void;
30
41
  layout?: 'horizontal' | 'vertical';
31
42
  };
32
43
 
@@ -76,10 +87,12 @@ export const ErrorDisplay: FunctionComponent<ErrorDisplayProps> = ({ error, rese
76
87
  )}
77
88
  </div>
78
89
  </div>
79
- <button onClick={resetError} className='btn btn-sm flex items-center m-4'>
80
- <span className='iconify mdi--reload text-lg' />
81
- Try again
82
- </button>
90
+ {resetError !== undefined && (
91
+ <button onClick={resetError} className='btn btn-sm flex items-center m-4'>
92
+ <span className='iconify mdi--reload text-lg' />
93
+ Try again
94
+ </button>
95
+ )}
83
96
  </div>
84
97
  );
85
98
  };
@@ -115,5 +128,27 @@ function getDisplayedErrorMessage(error: Error) {
115
128
  };
116
129
  }
117
130
 
131
+ if (error instanceof InvalidPropsError) {
132
+ const firstError = error.zodError.errors[0];
133
+ let message = error.zodError.issues
134
+ .map((issue) => {
135
+ const actual =
136
+ issue.path[0] in error.componentProps
137
+ ? ` '${JSON.stringify(error.componentProps[issue.path[0]])}'`
138
+ : '';
139
+ return `Unexpected value${actual} for "${issue.path.join('.')}": ${issue.message}`;
140
+ })
141
+ .join(' - ');
142
+
143
+ if (firstError.code === 'invalid_type' && firstError.received === 'null') {
144
+ message = `Is the "${firstError.path[0]}" attribute in the HTML of the correct type? ${message}`;
145
+ }
146
+
147
+ return {
148
+ headline: 'Error - Invalid component attributes',
149
+ details: { headline: 'Invalid component attributes', message },
150
+ };
151
+ }
152
+
118
153
  return { headline: 'Error', details: undefined };
119
154
  }
@@ -50,9 +50,15 @@ describe('computeInitialValues', () => {
50
50
  expectDateMatches(result.initialSelectedDateTo, today);
51
51
  });
52
52
 
53
- it('should fall back to default when initial value is unknown', () => {
53
+ it('should throw when initial value is unknown', () => {
54
+ expect(() =>
55
+ computeInitialValues('not a known value', undefined, undefined, earliestDate, dateRangeOptions),
56
+ ).toThrowError(/Invalid initialValue "not a known value", It must be one of/);
57
+ });
58
+
59
+ it('should throw when initial value is set but no options are provided', () => {
54
60
  expect(() => computeInitialValues('not a known value', undefined, undefined, earliestDate, [])).toThrowError(
55
- /Invalid initialValue "not a known value", It must be one of/,
61
+ /There are no selectable options/,
56
62
  );
57
63
  });
58
64
 
@@ -18,6 +18,12 @@ export function computeInitialValues(
18
18
  const initialSelectedDateRange = selectableOptions.find((option) => option.value === initialValue)?.value;
19
19
 
20
20
  if (initialValue !== undefined && initialSelectedDateRange === undefined) {
21
+ if (selectableOptions.length === 0) {
22
+ throw new UserFacingError(
23
+ 'Invalid initialValue',
24
+ 'There are no selectable options, but initialValue is set.',
25
+ );
26
+ }
21
27
  throw new UserFacingError(
22
28
  'Invalid initialValue',
23
29
  `Invalid initialValue "${initialValue}", It must be one of ${selectableOptions.map((option) => `'${option.value}'`).join(', ')}`,
@@ -8,6 +8,7 @@ import { previewHandles } from '../../../.storybook/preview';
8
8
  import { LAPIS_URL } from '../../constants';
9
9
  import { LapisUrlContext } from '../LapisUrlContext';
10
10
  import { dateRangeOptionPresets } from './dateRangeOption';
11
+ import { expectInvalidAttributesErrorMessage } from '../shared/stories/expectInvalidAttributesErrorMessage';
11
12
 
12
13
  const earliestDate = '1970-01-01';
13
14
 
@@ -55,8 +56,8 @@ const meta: Meta<DateRangeSelectorProps> = {
55
56
  initialValue: dateRangeOptionPresets.lastMonth.label,
56
57
  dateColumn: 'aDateColumn',
57
58
  width: '100%',
58
- initialDateFrom: '',
59
- initialDateTo: '',
59
+ initialDateFrom: undefined,
60
+ initialDateTo: undefined,
60
61
  },
61
62
  };
62
63
 
@@ -221,6 +222,19 @@ export const HandlesInvalidInitialDateFrom: StoryObj<DateRangeSelectorProps> = {
221
222
  },
222
223
  };
223
224
 
225
+ export const WithNoDateColumn: StoryObj<DateRangeSelectorProps> = {
226
+ ...Primary,
227
+ args: {
228
+ ...Primary.args,
229
+ dateColumn: '',
230
+ },
231
+ play: async ({ canvasElement, step }) => {
232
+ step('expect error message', async () => {
233
+ await expectInvalidAttributesErrorMessage(canvasElement, 'String must contain at least 1 character(s)');
234
+ });
235
+ },
236
+ };
237
+
224
238
  async function prepare(canvasElement: HTMLElement, step: StepFunction<PreactRenderer, unknown>) {
225
239
  const canvas = within(canvasElement);
226
240