@genspectrum/dashboard-components 0.10.2 → 0.10.4

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 (76) hide show
  1. package/README.md +19 -19
  2. package/custom-elements.json +377 -33
  3. package/dist/assets/mutationOverTimeWorker-BjjkMGzd.js.map +1 -0
  4. package/dist/components.d.ts +217 -59
  5. package/dist/components.js +1365 -177
  6. package/dist/components.js.map +1 -1
  7. package/dist/{dateRangeOption-du8H7LWu.js → dateRangeOption-Doo6WHKu.js} +17 -3
  8. package/dist/dateRangeOption-Doo6WHKu.js.map +1 -0
  9. package/dist/style.css +16 -6
  10. package/dist/util.d.ts +107 -41
  11. package/dist/util.js +1 -1
  12. package/package.json +10 -4
  13. package/src/preact/aggregatedData/aggregate.stories.tsx +14 -0
  14. package/src/preact/aggregatedData/aggregate.tsx +17 -15
  15. package/src/preact/components/error-boundary.stories.tsx +24 -3
  16. package/src/preact/components/error-boundary.tsx +3 -4
  17. package/src/preact/components/error-display.tsx +38 -17
  18. package/src/preact/components/tabs.tsx +2 -2
  19. package/src/preact/dateRangeSelector/date-range-selector.stories.tsx +1 -1
  20. package/src/preact/lineageFilter/lineage-filter.stories.tsx +1 -1
  21. package/src/preact/locationFilter/location-filter.stories.tsx +1 -1
  22. package/src/preact/map/__mockData__/aggregatedGermany.json +83 -0
  23. package/src/preact/map/__mockData__/aggregatedWorld.json +259 -0
  24. package/src/preact/map/__mockData__/germanyMap.json +9083 -0
  25. package/src/preact/map/__mockData__/howToGenerateWorldMap.md +9 -0
  26. package/src/preact/map/__mockData__/worldAtlas.json +497127 -0
  27. package/src/preact/map/leafletStyleModifications.css +3 -0
  28. package/src/preact/map/sequences-by-location-map.tsx +202 -0
  29. package/src/preact/map/sequences-by-location-table.tsx +18 -0
  30. package/src/preact/map/sequences-by-location.stories.tsx +144 -0
  31. package/src/preact/map/sequences-by-location.tsx +151 -0
  32. package/src/preact/map/useGeoJsonMap.tsx +62 -0
  33. package/src/preact/mutationComparison/mutation-comparison.tsx +1 -1
  34. package/src/preact/mutationFilter/mutation-filter.tsx +26 -13
  35. package/src/preact/mutations/mutations.tsx +16 -12
  36. package/src/preact/mutationsOverTime/mutations-over-time.stories.tsx +14 -0
  37. package/src/preact/mutationsOverTime/mutations-over-time.tsx +18 -14
  38. package/src/preact/numberSequencesOverTime/number-sequences-over-time.stories.tsx +14 -0
  39. package/src/preact/numberSequencesOverTime/number-sequences-over-time.tsx +22 -14
  40. package/src/preact/prevalenceOverTime/prevalence-over-time.stories.tsx +14 -0
  41. package/src/preact/prevalenceOverTime/prevalence-over-time.tsx +28 -19
  42. package/src/preact/relativeGrowthAdvantage/relative-growth-advantage.stories.tsx +14 -0
  43. package/src/preact/relativeGrowthAdvantage/relative-growth-advantage.tsx +18 -15
  44. package/src/preact/shared/charts/confideceInterval.ts +10 -8
  45. package/src/preact/shared/charts/getYAxisMax.ts +10 -5
  46. package/src/preact/shared/stories/expectErrorMessage.ts +21 -0
  47. package/src/preact/statistic/statistics.tsx +10 -8
  48. package/src/preact/textInput/text-input.stories.tsx +14 -0
  49. package/src/preact/textInput/text-input.tsx +16 -11
  50. package/src/preact/useQuery.ts +9 -1
  51. package/src/query/queryAggregateData.ts +2 -1
  52. package/src/styles/tailwind.css +1 -1
  53. package/src/types.ts +13 -1
  54. package/src/utilEntrypoint.ts +7 -0
  55. package/src/web-components/app.stories.ts +17 -2
  56. package/src/web-components/app.ts +17 -5
  57. package/src/web-components/input/gs-mutation-filter.stories.ts +2 -0
  58. package/src/web-components/input/gs-text-input.tsx +2 -2
  59. package/src/web-components/introduction.mdx +4 -4
  60. package/src/web-components/visualization/data_visualization_statistical_analysis.mdx +3 -3
  61. package/src/web-components/visualization/gs-mutations-over-time.tsx +1 -3
  62. package/src/web-components/visualization/gs-mutations.tsx +1 -3
  63. package/src/web-components/visualization/gs-number-sequences-over-time.tsx +1 -3
  64. package/src/web-components/visualization/gs-prevalence-over-time.tsx +3 -6
  65. package/src/web-components/visualization/gs-relative-growth-advantage.tsx +1 -5
  66. package/src/web-components/visualization/gs-sequences-by-location.stories.ts +234 -0
  67. package/src/web-components/visualization/gs-sequences-by-location.tsx +253 -0
  68. package/src/web-components/visualization/index.ts +1 -0
  69. package/standalone-bundle/assets/mutationOverTimeWorker-DoUBht2e.js.map +1 -0
  70. package/standalone-bundle/dashboard-components.js +16223 -9292
  71. package/standalone-bundle/dashboard-components.js.map +1 -1
  72. package/standalone-bundle/style.css +1 -1
  73. package/dist/assets/mutationOverTimeWorker-Di6yP1e6.js.map +0 -1
  74. package/dist/dateRangeOption-du8H7LWu.js.map +0 -1
  75. package/src/preact/shared/stories/expectInvalidAttributesErrorMessage.ts +0 -13
  76. package/standalone-bundle/assets/mutationOverTimeWorker-cIyshfj_.js.map +0 -1
@@ -0,0 +1,253 @@
1
+ import leafletStyle from 'leaflet/dist/leaflet.css?inline';
2
+ import { unsafeCSS } from 'lit';
3
+ import { customElement, property } from 'lit/decorators.js';
4
+ import type { DetailedHTMLProps, HTMLAttributes } from 'react';
5
+
6
+ import leafletStyleModifications from '../../preact/map/leafletStyleModifications.css?inline';
7
+ import { SequencesByLocation, type SequencesByLocationProps } from '../../preact/map/sequences-by-location';
8
+ import type { Equals, Expect } from '../../utils/typeAssertions';
9
+ import { PreactLitAdapterWithGridJsStyles } from '../PreactLitAdapterWithGridJsStyles';
10
+
11
+ const leafletCss = unsafeCSS(leafletStyle);
12
+ const leafletModificationsCss = unsafeCSS(leafletStyleModifications);
13
+
14
+ /**
15
+ * ## Context
16
+ *
17
+ * This component shows the geographic distribution of sequence data from LAPIS.
18
+ * It displays the count and proportion (number of sample per `lapisLocationField` / number of samples matching the `lapisFilter`)
19
+ * of the data by location.
20
+ *
21
+ * ## Views
22
+ *
23
+ * ### Map View
24
+ *
25
+ * This view displays a chloropleth map based on [Leaflet](https://leafletjs.com/).
26
+ * The component expects a `mapSource` object that specifies where the map data can be downloaded from.
27
+ * We can imagine that we add other map source types later (for example, GeoJSON).
28
+ *
29
+ * #### TopoJSON
30
+ *
31
+ * Suppose you provide this example object as `mapSource`:
32
+ *
33
+ * ```json
34
+ * {
35
+ * "type": "topojson",
36
+ * "url": "https://example.com/map.topojson",
37
+ * "topologyObjectsKey": "myObjectKey"
38
+ * }
39
+ * ```
40
+ *
41
+ * The URL must point to a [TopoJSON file](https://github.com/topojson/topojson) that contains the map data.
42
+ * The TopoJSON file must schematically look like this,
43
+ * where `objects[topologyObjectsKey]` must be a valid GeometryCollection (`objects.myObjectKey` in this example):
44
+ *
45
+ * ```json
46
+ * {
47
+ * "type": "Topology",
48
+ * "objects": {
49
+ * "myObjectKey": {
50
+ * "type": "GeometryCollection",
51
+ * "geometries": [
52
+ * {
53
+ * "type": "Polygon",
54
+ * "properties": {
55
+ * "name": "North Rhine Westphalia"
56
+ * },
57
+ * "id": "DE.NW",
58
+ * "arcs": [...]
59
+ * },
60
+ * ...
61
+ * ]
62
+ * }
63
+ * },
64
+ * "arcs": [...],
65
+ * "transform": {...}
66
+ * }
67
+ * ```
68
+ *
69
+ * You can use any valid TopoJSON file.
70
+ * https://github.com/markmarkoh/datamaps/tree/master/src/js/data contains TopoJSON files for many countries.
71
+ *
72
+ * The `lapisFilter` is used to select the data to display, and it is aggregated by the `lapisLocationField`.
73
+ * This component assumes that every geometry object in the TopoJSON file has a `properties.name` field.
74
+ *
75
+ * The values that LAPIS returns for `lapisLocationField` must match the `properties.name` in the map data.
76
+ * LAPIS entries where `lapisLocationField` is `null` are ignored on the map.
77
+ *
78
+ * The names of the locations in the TopoJSON map and in LAPIS should match.
79
+ * However, there are two cases of misalignment:
80
+ * - If there is a LAPIS location that does not match any location in the TopoJSON map,
81
+ * the component will log a console warning to assist in creating map data that aligns with the LAPIS data.
82
+ * - If a TopoJSON location does not match any LAPIS location for the given filter,
83
+ * no data will be displayed for this location.
84
+ * This is expected, as LAPIS will only return locations where sequences have been collected for that filter.
85
+ *
86
+ * ### Table View
87
+ *
88
+ * This view displays the data in a table format.
89
+ * It is similar to the table view of the `gs-aggregate` component.
90
+ * The table has three columns:
91
+ * - `lapisLocationField`,
92
+ * - `count` (the number of samples in this location matching the `lapisFilter`),
93
+ * - `proportion` (`count` / sum of the `count` of all locations).
94
+ */
95
+ @customElement('gs-sequences-by-location')
96
+ export class SequencesByLocationComponent extends PreactLitAdapterWithGridJsStyles {
97
+ static override styles = [...PreactLitAdapterWithGridJsStyles.styles, leafletCss, leafletModificationsCss];
98
+
99
+ /**
100
+ * LAPIS filter to select the displayed data.
101
+ * If you want to display the distribution over the states of a certain country,
102
+ * you should usually filter by that country here (e.g. { country: 'USA' }).
103
+ */
104
+ @property({ type: Object })
105
+ lapisFilter: Record<string, string | number | null | boolean> = {};
106
+
107
+ /**
108
+ * Required.
109
+ *
110
+ * The location field to aggregate the data by.
111
+ * This should match the selected map location granularity.
112
+ */
113
+ @property({ type: String })
114
+ lapisLocationField: string = '';
115
+
116
+ /**
117
+ * Required when using the map view.
118
+ *
119
+ * The source of the map data. See component level docs for more information.
120
+ */
121
+ @property({ type: Object })
122
+ mapSource: { type: 'topojson'; url: string; topologyObjectsKey: string } | undefined = undefined;
123
+
124
+ /**
125
+ * Enable map navigation (dragging, keyboard navigation, zooming).
126
+ */
127
+ @property({ type: Boolean })
128
+ enableMapNavigation: boolean = true;
129
+
130
+ /**
131
+ * The width of the component.
132
+ * Not that the map in the map view is not responsive
133
+ * (i.e. does not adjust its size when the component is resized).
134
+ *
135
+ * Visit https://genspectrum.github.io/dashboard-components/?path=/docs/components-size-of-components--docs for more information.
136
+ */
137
+ @property({ type: String })
138
+ width: string = '100%';
139
+
140
+ /**
141
+ * The height of the component.
142
+ *
143
+ * Visit https://genspectrum.github.io/dashboard-components/?path=/docs/components-size-of-components--docs for more information.
144
+ */
145
+ @property({ type: String })
146
+ height: string = '700px';
147
+
148
+ /**
149
+ A list of tabs with views that this component should provide.
150
+ */
151
+ @property({ type: Array })
152
+ views: ('map' | 'table')[] = ['map', 'table'];
153
+
154
+ /**
155
+ * The initial zoom level of the map.
156
+ */
157
+ @property({ type: Number })
158
+ zoom: number = 1;
159
+
160
+ /**
161
+ * Initially shift the center of the map in x direction (longitude).
162
+ *
163
+ * `-180` is the International Date Line with the map shifted to the right, `0` is the prime meridian,
164
+ * `180` is the International Date Line with the map shifted to the left.
165
+ */
166
+ @property({ type: Number })
167
+ offsetX: number = 0;
168
+
169
+ /**
170
+ * Initially shift the center of the map in y direction (latitude).
171
+ *
172
+ * `-90` is the South Pole, `0` is the equator, `90` is the North Pole.
173
+ */
174
+ @property({ type: Number })
175
+ offsetY: number = 0;
176
+
177
+ /**
178
+ * The maximum number of rows to display in the table view.
179
+ * Set to `false` to disable pagination. Set to `true` to enable pagination with a default limit (10).
180
+ */
181
+ @property({ type: Object })
182
+ pageSize: boolean | number = false;
183
+
184
+ override render() {
185
+ return (
186
+ <SequencesByLocation
187
+ lapisFilter={this.lapisFilter}
188
+ lapisLocationField={this.lapisLocationField}
189
+ mapSource={this.mapSource}
190
+ enableMapNavigation={this.enableMapNavigation}
191
+ width={this.width}
192
+ height={this.height}
193
+ views={this.views}
194
+ zoom={this.zoom}
195
+ offsetX={this.offsetX}
196
+ offsetY={this.offsetY}
197
+ pageSize={this.pageSize}
198
+ />
199
+ );
200
+ }
201
+ }
202
+
203
+ declare global {
204
+ interface HTMLElementTagNameMap {
205
+ 'gs-sequences-by-location': SequencesByLocationComponent;
206
+ }
207
+ }
208
+
209
+ declare global {
210
+ // eslint-disable-next-line @typescript-eslint/no-namespace
211
+ namespace JSX {
212
+ interface IntrinsicElements {
213
+ 'gs-sequences-by-location': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
214
+ }
215
+ }
216
+ }
217
+
218
+ /* eslint-disable @typescript-eslint/no-unused-vars, no-unused-vars */
219
+ type LapisFilterMatches = Expect<
220
+ Equals<typeof SequencesByLocationComponent.prototype.lapisFilter, SequencesByLocationProps['lapisFilter']>
221
+ >;
222
+ type LapisLocationFieldMatches = Expect<
223
+ Equals<
224
+ typeof SequencesByLocationComponent.prototype.lapisLocationField,
225
+ SequencesByLocationProps['lapisLocationField']
226
+ >
227
+ >;
228
+ type MapSourceMatches = Expect<
229
+ Equals<typeof SequencesByLocationComponent.prototype.mapSource, SequencesByLocationProps['mapSource']>
230
+ >;
231
+ type EnableMapNavigationMatches = Expect<
232
+ Equals<
233
+ typeof SequencesByLocationComponent.prototype.enableMapNavigation,
234
+ SequencesByLocationProps['enableMapNavigation']
235
+ >
236
+ >;
237
+ type WidthMatches = Expect<
238
+ Equals<typeof SequencesByLocationComponent.prototype.width, SequencesByLocationProps['width']>
239
+ >;
240
+ type HeightMatches = Expect<
241
+ Equals<typeof SequencesByLocationComponent.prototype.height, SequencesByLocationProps['height']>
242
+ >;
243
+ type ViewsMatches = Expect<
244
+ Equals<typeof SequencesByLocationComponent.prototype.views, SequencesByLocationProps['views']>
245
+ >;
246
+ type ZoomMatches = Expect<Equals<typeof SequencesByLocationComponent.prototype.zoom, SequencesByLocationProps['zoom']>>;
247
+ type OffsetXMatches = Expect<
248
+ Equals<typeof SequencesByLocationComponent.prototype.offsetX, SequencesByLocationProps['offsetX']>
249
+ >;
250
+ type OffsetYMatches = Expect<
251
+ Equals<typeof SequencesByLocationComponent.prototype.offsetY, SequencesByLocationProps['offsetY']>
252
+ >;
253
+ /* eslint-enable @typescript-eslint/no-unused-vars, no-unused-vars */
@@ -5,4 +5,5 @@ export { RelativeGrowthAdvantageComponent } from './gs-relative-growth-advantage
5
5
  export { AggregateComponent } from './gs-aggregate';
6
6
  export { NumberSequencesOverTimeComponent } from './gs-number-sequences-over-time';
7
7
  export { MutationsOverTimeComponent } from './gs-mutations-over-time';
8
+ export { SequencesByLocationComponent } from './gs-sequences-by-location';
8
9
  export { StatisticsComponent } from './gs-statistics';