@genspectrum/dashboard-components 0.1.5 → 0.3.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 (69) hide show
  1. package/custom-elements.json +1161 -928
  2. package/dist/dashboard-components.js +663 -237
  3. package/dist/dashboard-components.js.map +1 -1
  4. package/dist/genspectrum-components.d.ts +177 -140
  5. package/dist/style.css +247 -50
  6. package/package.json +2 -3
  7. package/src/constants.ts +1 -1
  8. package/src/lapisApi/lapisApi.ts +46 -2
  9. package/src/lapisApi/lapisTypes.ts +14 -0
  10. package/src/preact/aggregatedData/aggregate.stories.tsx +4 -2
  11. package/src/preact/aggregatedData/aggregate.tsx +31 -29
  12. package/src/preact/components/error-boundary.stories.tsx +54 -0
  13. package/src/preact/components/error-boundary.tsx +22 -0
  14. package/src/preact/components/error-display.stories.tsx +32 -4
  15. package/src/preact/components/error-display.tsx +48 -1
  16. package/src/preact/components/loading-display.stories.tsx +6 -6
  17. package/src/preact/components/loading-display.tsx +1 -1
  18. package/src/preact/components/no-data-display.tsx +5 -1
  19. package/src/preact/components/resize-container.tsx +5 -14
  20. package/src/preact/dateRangeSelector/date-range-selector.stories.tsx +19 -0
  21. package/src/preact/dateRangeSelector/date-range-selector.tsx +38 -7
  22. package/src/preact/locationFilter/fetchAutocompletionList.ts +15 -1
  23. package/src/preact/locationFilter/location-filter.stories.tsx +23 -6
  24. package/src/preact/locationFilter/location-filter.tsx +28 -18
  25. package/src/preact/mutationComparison/mutation-comparison.stories.tsx +6 -3
  26. package/src/preact/mutationComparison/mutation-comparison.tsx +33 -32
  27. package/src/preact/mutationComparison/queryMutationData.ts +2 -3
  28. package/src/preact/mutationFilter/mutation-filter.stories.tsx +18 -3
  29. package/src/preact/mutationFilter/mutation-filter.tsx +26 -7
  30. package/src/preact/mutations/mutations.stories.tsx +6 -3
  31. package/src/preact/mutations/mutations.tsx +28 -26
  32. package/src/preact/prevalenceOverTime/prevalence-over-time.stories.tsx +14 -7
  33. package/src/preact/prevalenceOverTime/prevalence-over-time.tsx +50 -32
  34. package/src/preact/relativeGrowthAdvantage/relative-growth-advantage.stories.tsx +6 -3
  35. package/src/preact/relativeGrowthAdvantage/relative-growth-advantage.tsx +46 -32
  36. package/src/preact/textInput/text-input.stories.tsx +26 -0
  37. package/src/preact/textInput/text-input.tsx +25 -3
  38. package/src/query/queryPrevalenceOverTime.ts +4 -10
  39. package/src/types.ts +4 -1
  40. package/src/web-components/ResizeContainer.mdx +13 -0
  41. package/src/web-components/app.stories.ts +1 -2
  42. package/src/web-components/app.ts +7 -3
  43. package/src/web-components/index.ts +1 -1
  44. package/src/web-components/input/{date-range-selector-component.stories.ts → gs-date-range-selector.stories.ts} +29 -4
  45. package/src/web-components/input/{date-range-selector-component.tsx → gs-date-range-selector.tsx} +32 -10
  46. package/src/web-components/input/{location-filter-component.stories.ts → gs-location-filter.stories.ts} +32 -5
  47. package/src/web-components/input/{location-filter-component.tsx → gs-location-filter.tsx} +11 -1
  48. package/src/web-components/input/{mutation-filter-component.stories.ts → gs-mutation-filter.stories.ts} +23 -4
  49. package/src/web-components/input/gs-mutation-filter.tsx +126 -0
  50. package/src/web-components/input/{text-input-component.stories.ts → gs-text-input.stories.ts} +34 -6
  51. package/src/web-components/input/{text-input-component.tsx → gs-text-input.tsx} +16 -4
  52. package/src/web-components/input/index.ts +4 -4
  53. package/src/web-components/input/introduction.mdx +11 -0
  54. package/src/web-components/introduction.mdx +15 -0
  55. package/src/web-components/visualization/data_visualization_statistical_analysis.mdx +26 -0
  56. package/src/web-components/{display/aggregate-component.stories.ts → visualization/gs-aggregate.stories.ts} +23 -11
  57. package/src/web-components/visualization/gs-aggregate.tsx +88 -0
  58. package/src/web-components/{display/mutation-comparison-component.stories.ts → visualization/gs-mutation-comparison.stories.ts} +21 -16
  59. package/src/web-components/{display/mutation-comparison-component.tsx → visualization/gs-mutation-comparison.tsx} +27 -18
  60. package/src/web-components/{display/mutations-component.stories.ts → visualization/gs-mutations.stories.ts} +20 -15
  61. package/src/web-components/{display/mutations-component.tsx → visualization/gs-mutations.tsx} +20 -10
  62. package/src/web-components/{display/prevalence-over-time-component.stories.ts → visualization/gs-prevalence-over-time.stories.ts} +29 -20
  63. package/src/web-components/{display/prevalence-over-time-component.tsx → visualization/gs-prevalence-over-time.tsx} +47 -22
  64. package/src/web-components/{display/relative-growth-advantage-component.stories.ts → visualization/gs-relative-growth-advantage.stories.ts} +12 -7
  65. package/src/web-components/{display/relative-growth-advantage-component.tsx → visualization/gs-relative-growth-advantage.tsx} +21 -9
  66. package/src/web-components/visualization/index.ts +5 -0
  67. package/src/web-components/display/aggregate-component.tsx +0 -72
  68. package/src/web-components/display/index.ts +0 -5
  69. package/src/web-components/input/mutation-filter-component.tsx +0 -83
@@ -54,6 +54,12 @@
54
54
  }
55
55
  ]
56
56
  },
57
+ {
58
+ "kind": "javascript-module",
59
+ "path": "src/web-components/ResizeContainer.mdx",
60
+ "declarations": [],
61
+ "exports": []
62
+ },
57
63
  {
58
64
  "kind": "javascript-module",
59
65
  "path": "src/web-components/app.stories.ts",
@@ -64,7 +70,7 @@
64
70
  "type": {
65
71
  "text": "Meta"
66
72
  },
67
- "default": "{ title: 'Wrapper/App', component: 'gs-app', parameters: withComponentDocs({ fetchMock: {}, componentDocs: { tag: 'gs-app', opensShadowDom: false, expectsChildren: true, codeExample, }, }), decorators: [withActions], tags: ['autodocs'], }"
73
+ "default": "{ title: 'Wrapper/App', component: 'gs-app', parameters: withComponentDocs({ fetchMock: {}, componentDocs: { opensShadowDom: false, expectsChildren: true, codeExample, }, }), decorators: [withActions], tags: ['autodocs'], }"
68
74
  },
69
75
  {
70
76
  "kind": "variable",
@@ -88,7 +94,7 @@
88
94
  "type": {
89
95
  "text": "StoryObj<{ lapis: string }>"
90
96
  },
91
- "default": "{ ...Template, args: { lapis: 'definitely-not-a-valid-url', }, play: async ({ canvasElement }) => { const canvas = within(canvasElement); await waitFor(() => { expect(canvas.getByText('Error')).toBeVisible(); }); }, }"
97
+ "default": "{ ...Template, args: { lapis: 'definitely-not-a-valid-url', }, play: async ({ canvasElement }) => { const canvas = within(canvasElement); await waitFor(() => { expect(canvas.getByText('Error', { exact: false })).toBeVisible(); }); }, }"
92
98
  },
93
99
  {
94
100
  "kind": "class",
@@ -179,7 +185,7 @@
179
185
  "text": "string"
180
186
  },
181
187
  "default": "''",
182
- "description": "The URL of the LAPIS instance that all children of this component will use.",
188
+ "description": "Required.\n\nThe URL of the LAPIS instance that all children of this component will use.",
183
189
  "attribute": "lapis"
184
190
  }
185
191
  ],
@@ -190,7 +196,7 @@
190
196
  "text": "string"
191
197
  },
192
198
  "default": "''",
193
- "description": "The URL of the LAPIS instance that all children of this component will use.",
199
+ "description": "Required.\n\nThe URL of the LAPIS instance that all children of this component will use.",
194
200
  "fieldName": "lapis"
195
201
  }
196
202
  ],
@@ -223,23 +229,54 @@
223
229
  },
224
230
  {
225
231
  "kind": "javascript-module",
226
- "path": "src/web-components/display/aggregate-component.stories.ts",
232
+ "path": "src/web-components/index.ts",
233
+ "declarations": [],
234
+ "exports": [
235
+ {
236
+ "kind": "js",
237
+ "name": "App",
238
+ "declaration": {
239
+ "name": "App",
240
+ "module": "./app.js"
241
+ }
242
+ },
243
+ {
244
+ "kind": "js",
245
+ "name": "*",
246
+ "declaration": {
247
+ "name": "*",
248
+ "package": "./visualization"
249
+ }
250
+ },
251
+ {
252
+ "kind": "js",
253
+ "name": "*",
254
+ "declaration": {
255
+ "name": "*",
256
+ "package": "./input"
257
+ }
258
+ }
259
+ ]
260
+ },
261
+ {
262
+ "kind": "javascript-module",
263
+ "path": "src/web-components/input/gs-date-range-selector.stories.ts",
227
264
  "declarations": [
228
265
  {
229
266
  "kind": "variable",
230
267
  "name": "meta",
231
268
  "type": {
232
- "text": "Meta<AggregateProps>"
269
+ "text": "Meta<Required<DateRangeSelectorProps<'CustomDateRange'>>>"
233
270
  },
234
- "default": "{ title: 'Visualization/Aggregate', component: 'gs-aggregate-component', argTypes: { fields: [{ control: 'object' }], views: { options: ['table'], control: { type: 'check' }, }, size: [{ control: 'object' }], headline: { control: 'text' }, }, parameters: withComponentDocs({ fetchMock: { mocks: [ { matcher: { name: 'aggregatedData', url: AGGREGATED_ENDPOINT, body: { fields: ['division', 'host'], country: 'USA', }, }, response: { status: 200, body: aggregatedData, }, }, ], }, componentDocs: { tag: 'gs-aggregate-component', opensShadowDom: true, expectsChildren: false, codeExample: `<gs-aggregate-component fields='[\"division\", \"host\"]' filter='{\"country\": \"USA\"}' views='[\"table\"]'></gs-aggregate-component>`, }, }), tags: ['autodocs'], }"
271
+ "default": "{ title: 'Input/DateRangeSelector', component: 'gs-date-range-selector', parameters: withComponentDocs({ actions: { handles: ['gs-date-range-changed'], }, fetchMock: {}, componentDocs: { opensShadowDom: true, expectsChildren: false, codeExample, }, }), argTypes: { initialValue: { control: { type: 'select', }, options: [ PRESET_VALUE_CUSTOM, PRESET_VALUE_ALL_TIMES, PRESET_VALUE_LAST_2_WEEKS, PRESET_VALUE_LAST_MONTH, PRESET_VALUE_LAST_2_MONTHS, PRESET_VALUE_LAST_3_MONTHS, PRESET_VALUE_LAST_6_MONTHS, 'CustomDateRange', ], }, dateColumn: { control: { type: 'text' } }, customSelectOptions: { control: { type: 'object', }, }, earliestDate: { control: { type: 'text', }, }, width: { control: { type: 'text', }, }, }, args: { customSelectOptions: [{ label: 'CustomDateRange', dateFrom: '2021-01-01', dateTo: '2021-12-31' }], earliestDate: '1970-01-01', initialValue: PRESET_VALUE_LAST_6_MONTHS, dateColumn: 'aDateColumn', width: '100%', }, decorators: [withActions], tags: ['autodocs'], }"
235
272
  },
236
273
  {
237
274
  "kind": "variable",
238
- "name": "Table",
275
+ "name": "DateRangeSelectorStory",
239
276
  "type": {
240
- "text": "StoryObj<AggregateProps>"
277
+ "text": "StoryObj<Required<DateRangeSelectorProps<'CustomDateRange'>>>"
241
278
  },
242
- "default": "{ render: (args) => html` <gs-app lapis=\"${LAPIS_URL}\"> <gs-aggregate-component .fields=${args.fields} .filter=${args.filter} .views=${args.views} .size=${args.size} .headline=${args.headline} ></gs-aggregate-component> </gs-app> `, args: { fields: ['division', 'host'], views: ['table'], filter: { country: 'USA', }, size: { width: '100%', height: '700px' }, headline: 'Aggregate', }, }"
279
+ "default": "{ render: (args) => html` <gs-app lapis=\"${LAPIS_URL}\"> <div class=\"max-w-screen-lg\"> <gs-date-range-selector .customSelectOptions=${args.customSelectOptions} .earliestDate=${args.earliestDate} .initialValue=${args.initialValue} .width=${args.width} .dateColumn=${args.dateColumn} ></gs-date-range-selector> </div> </gs-app>`, play: async ({ canvasElement, step }) => { const canvas = await withinShadowRoot(canvasElement, 'gs-date-range-selector'); const dateTo = () => canvas.getByPlaceholderText('Date to'); await step('Expect last 6 months to be selected', async () => { await expect(canvas.getByRole('combobox')).toHaveValue('last6Months'); await waitFor(() => { expect(dateTo()).toHaveValue(toYYYYMMDD(new Date())); }); }); // Due to the limitations of storybook testing which does not fire an event, // when selecting a value from the dropdown we can't test the fired event here. // An e2e test (using playwright) for that can be found in tests/dateRangeSelector.spec.ts }, }"
243
280
  }
244
281
  ],
245
282
  "exports": [
@@ -248,227 +285,205 @@
248
285
  "name": "default",
249
286
  "declaration": {
250
287
  "name": "meta",
251
- "module": "src/web-components/display/aggregate-component.stories.ts"
288
+ "module": "src/web-components/input/gs-date-range-selector.stories.ts"
252
289
  }
253
290
  },
254
291
  {
255
292
  "kind": "js",
256
- "name": "Table",
293
+ "name": "DateRangeSelectorStory",
257
294
  "declaration": {
258
- "name": "Table",
259
- "module": "src/web-components/display/aggregate-component.stories.ts"
295
+ "name": "DateRangeSelectorStory",
296
+ "module": "src/web-components/input/gs-date-range-selector.stories.ts"
260
297
  }
261
298
  }
262
299
  ]
263
300
  },
264
301
  {
265
302
  "kind": "javascript-module",
266
- "path": "src/web-components/display/aggregate-component.tsx",
303
+ "path": "src/web-components/input/gs-date-range-selector.tsx",
267
304
  "declarations": [
268
305
  {
269
306
  "kind": "class",
270
- "description": "## Context\n\nThis component displays aggregated data, which can provide an overview of the underlying data.\n\nIt expects a list of fields to aggregate by and a filter to apply to the data.",
271
- "name": "AggregateComponent",
307
+ "description": "## Context\nThis component is a group of input fields designed to specify date range filters\nfor a given date column of this Lapis instance. It consists of 3 fields:\n\n- a select field to choose a predefined date range,\n- an input field with an attached date picker for the start date,\n- an input field with an attached date picker for the end date.\n\nSetting a value in the select field will overwrite the previous values of the start and end date.\nSetting a value in either of the date pickers will set the select field to \"custom\",\nwhich represents an arbitrary date range.",
308
+ "name": "DateRangeSelectorComponent",
272
309
  "members": [
273
310
  {
274
311
  "kind": "field",
275
- "name": "fields",
312
+ "name": "customSelectOptions",
276
313
  "type": {
277
- "text": "string[]"
314
+ "text": "{ label: string; dateFrom: string; dateTo: string }[]"
278
315
  },
279
316
  "default": "[]",
280
- "description": "The fields to aggregate by.",
281
- "attribute": "fields"
317
+ "description": "An array of custom options that the select field should provide,\nin addition to the predefined options.\nThe `label` will be shown to the user, and it will be available as `initialValue`.\nThe dates must be in the format `YYYY-MM-DD`.",
318
+ "attribute": "customSelectOptions"
282
319
  },
283
320
  {
284
321
  "kind": "field",
285
- "name": "views",
322
+ "name": "earliestDate",
286
323
  "type": {
287
- "text": "View[]"
324
+ "text": "string"
288
325
  },
289
- "default": "['table']",
290
- "description": "The views are used to display the aggregated data.\nIn the table view, the data is presented in a table format where each field is a column,\nalong with the aggregated value and its proportion.\nThe proportion represents the ratio of the aggregated value to the total count of the data\n(considering the applied filter).",
291
- "attribute": "views"
326
+ "default": "'1900-01-01'",
327
+ "description": "The `dateFrom` value to use in the `allTimes` preset in the format `YYYY-MM-DD`.",
328
+ "attribute": "earliestDate"
292
329
  },
293
330
  {
294
331
  "kind": "field",
295
- "name": "filter",
332
+ "name": "initialValue",
296
333
  "type": {
297
- "text": "LapisFilter"
334
+ "text": "'custom'\n | 'allTimes'\n | 'last2Weeks'\n | 'lastMonth'\n | 'last2Months'\n | 'last3Months'\n | 'last6Months'\n | string"
298
335
  },
299
- "default": "{}",
300
- "description": "The filter to apply to the data.",
301
- "attribute": "filter"
336
+ "default": "'last6Months'",
337
+ "description": "The initial value to use for this date range selector.\nMust be a valid label from the preset labels or a `label` given in the `customSelectOptions`.\n\nIf the value is invalid, the component will default to `'last6Months'`.",
338
+ "attribute": "initialValue"
302
339
  },
303
340
  {
304
341
  "kind": "field",
305
- "name": "size",
342
+ "name": "width",
306
343
  "type": {
307
- "text": "{ width?: string; height?: string } | undefined"
344
+ "text": "string"
308
345
  },
309
- "default": "undefined",
310
- "description": "The size of the component.\n\nIf not set, the component will take the full width of its container with height 700px.\n\nThe width and height should be a string with a unit in css style, e.g. '100%', '500px' or '50vh'.\nIf the unit is %, the size will be relative to the container of the component.",
311
- "attribute": "size"
346
+ "default": "'100%'",
347
+ "description": "The width of the component.\n\nVisit https://genspectrum.github.io/dashboards/?path=/docs/components-size-of-components--docs for more information.",
348
+ "attribute": "width"
312
349
  },
313
350
  {
314
351
  "kind": "field",
315
- "name": "headline",
352
+ "name": "dateColumn",
316
353
  "type": {
317
- "text": "string | undefined"
354
+ "text": "string"
318
355
  },
319
- "default": "'Aggregate'",
320
- "description": "The headline of the component. Set to an empty string to hide the headline.",
321
- "attribute": "headline"
356
+ "default": "'date'",
357
+ "description": "The name of the column in LAPIS that contains the date information.",
358
+ "attribute": "dateColumn"
359
+ }
360
+ ],
361
+ "events": [
362
+ {
363
+ "type": {
364
+ "text": "CustomEvent<{ `${dateColumn}From`: string; `${dateColumn}To`: string; }>"
365
+ },
366
+ "description": "Fired when: - The select field is changed, - A date is selected in either of the date pickers, - A date was typed into either of the date input fields, and the input field loses focus (\"on blur\"). Contains the dates in the format `YYYY-MM-DD`. Example: For `dateColumn = yourDate`, an event with `detail` containing ``` { yourDataFrom: \"2021-01-01\", yourDataTo: \"2021-12-31\" } ``` will be fired.",
367
+ "name": "gs-date-range-changed"
322
368
  }
323
369
  ],
324
370
  "attributes": [
325
371
  {
326
- "name": "fields",
372
+ "name": "customSelectOptions",
327
373
  "type": {
328
- "text": "string[]"
374
+ "text": "{ label: string; dateFrom: string; dateTo: string }[]"
329
375
  },
330
376
  "default": "[]",
331
- "description": "The fields to aggregate by.",
332
- "fieldName": "fields"
377
+ "description": "An array of custom options that the select field should provide,\nin addition to the predefined options.\nThe `label` will be shown to the user, and it will be available as `initialValue`.\nThe dates must be in the format `YYYY-MM-DD`.",
378
+ "fieldName": "customSelectOptions"
333
379
  },
334
380
  {
335
- "name": "views",
381
+ "name": "earliestDate",
336
382
  "type": {
337
- "text": "View[]"
383
+ "text": "string"
338
384
  },
339
- "default": "['table']",
340
- "description": "The views are used to display the aggregated data.\nIn the table view, the data is presented in a table format where each field is a column,\nalong with the aggregated value and its proportion.\nThe proportion represents the ratio of the aggregated value to the total count of the data\n(considering the applied filter).",
341
- "fieldName": "views"
385
+ "default": "'1900-01-01'",
386
+ "description": "The `dateFrom` value to use in the `allTimes` preset in the format `YYYY-MM-DD`.",
387
+ "fieldName": "earliestDate"
342
388
  },
343
389
  {
344
- "name": "filter",
390
+ "name": "initialValue",
345
391
  "type": {
346
- "text": "LapisFilter"
392
+ "text": "'custom'\n | 'allTimes'\n | 'last2Weeks'\n | 'lastMonth'\n | 'last2Months'\n | 'last3Months'\n | 'last6Months'\n | string"
347
393
  },
348
- "default": "{}",
349
- "description": "The filter to apply to the data.",
350
- "fieldName": "filter"
394
+ "default": "'last6Months'",
395
+ "description": "The initial value to use for this date range selector.\nMust be a valid label from the preset labels or a `label` given in the `customSelectOptions`.\n\nIf the value is invalid, the component will default to `'last6Months'`.",
396
+ "fieldName": "initialValue"
351
397
  },
352
398
  {
353
- "name": "size",
399
+ "name": "width",
354
400
  "type": {
355
- "text": "{ width?: string; height?: string } | undefined"
401
+ "text": "string"
356
402
  },
357
- "default": "undefined",
358
- "description": "The size of the component.\n\nIf not set, the component will take the full width of its container with height 700px.\n\nThe width and height should be a string with a unit in css style, e.g. '100%', '500px' or '50vh'.\nIf the unit is %, the size will be relative to the container of the component.",
359
- "fieldName": "size"
403
+ "default": "'100%'",
404
+ "description": "The width of the component.\n\nVisit https://genspectrum.github.io/dashboards/?path=/docs/components-size-of-components--docs for more information.",
405
+ "fieldName": "width"
360
406
  },
361
407
  {
362
- "name": "headline",
408
+ "name": "dateColumn",
363
409
  "type": {
364
- "text": "string | undefined"
410
+ "text": "string"
365
411
  },
366
- "default": "'Aggregate'",
367
- "description": "The headline of the component. Set to an empty string to hide the headline.",
368
- "fieldName": "headline"
412
+ "default": "'date'",
413
+ "description": "The name of the column in LAPIS that contains the date information.",
414
+ "fieldName": "dateColumn"
369
415
  }
370
416
  ],
371
417
  "superclass": {
372
- "name": "PreactLitAdapterWithGridJsStyles",
373
- "module": "/src/web-components/PreactLitAdapterWithGridJsStyles"
418
+ "name": "PreactLitAdapter",
419
+ "module": "/src/web-components/PreactLitAdapter"
374
420
  },
375
- "tagName": "gs-aggregate-component",
421
+ "tagName": "gs-date-range-selector",
376
422
  "customElement": true
377
423
  }
378
424
  ],
379
425
  "exports": [
380
426
  {
381
427
  "kind": "js",
382
- "name": "AggregateComponent",
428
+ "name": "DateRangeSelectorComponent",
383
429
  "declaration": {
384
- "name": "AggregateComponent",
385
- "module": "src/web-components/display/aggregate-component.tsx"
430
+ "name": "DateRangeSelectorComponent",
431
+ "module": "src/web-components/input/gs-date-range-selector.tsx"
386
432
  }
387
433
  },
388
434
  {
389
435
  "kind": "custom-element-definition",
390
- "name": "gs-aggregate-component",
436
+ "name": "gs-date-range-selector",
391
437
  "declaration": {
392
- "name": "AggregateComponent",
393
- "module": "src/web-components/display/aggregate-component.tsx"
438
+ "name": "DateRangeSelectorComponent",
439
+ "module": "src/web-components/input/gs-date-range-selector.tsx"
394
440
  }
395
441
  }
396
442
  ]
397
443
  },
398
444
  {
399
445
  "kind": "javascript-module",
400
- "path": "src/web-components/display/index.ts",
401
- "declarations": [],
402
- "exports": [
403
- {
404
- "kind": "js",
405
- "name": "MutationComparisonComponent",
406
- "declaration": {
407
- "name": "MutationComparisonComponent",
408
- "module": "./mutation-comparison-component"
409
- }
410
- },
411
- {
412
- "kind": "js",
413
- "name": "MutationsComponent",
414
- "declaration": {
415
- "name": "MutationsComponent",
416
- "module": "./mutations-component"
417
- }
418
- },
446
+ "path": "src/web-components/input/gs-location-filter.stories.ts",
447
+ "declarations": [
419
448
  {
420
- "kind": "js",
421
- "name": "PrevalenceOverTimeComponent",
422
- "declaration": {
423
- "name": "PrevalenceOverTimeComponent",
424
- "module": "./prevalence-over-time-component"
425
- }
449
+ "kind": "variable",
450
+ "name": "meta",
451
+ "type": {
452
+ "text": "Meta"
453
+ },
454
+ "default": "{ title: 'Input/Location filter', component: 'gs-location-filter', parameters: withComponentDocs({ actions: { handles: ['gs-location-changed'], }, componentDocs: { opensShadowDom: true, expectsChildren: false, codeExample, }, }), argTypes: { fields: { control: { type: 'object', }, }, initialValue: { control: { type: 'text', }, }, width: { control: { type: 'text', }, }, }, decorators: [withActions], tags: ['autodocs'], }"
426
455
  },
427
456
  {
428
- "kind": "js",
429
- "name": "RelativeGrowthAdvantageComponent",
430
- "declaration": {
431
- "name": "RelativeGrowthAdvantageComponent",
432
- "module": "./relative-growth-advantage-component"
433
- }
457
+ "kind": "variable",
458
+ "name": "LocationFilter",
459
+ "type": {
460
+ "text": "StoryObj<LocationFilterProps>"
461
+ },
462
+ "default": "{ ...Template, parameters: { fetchMock: { mocks: [ { matcher: aggregatedEndpointMatcher, response: { status: 200, body: data, }, }, ], }, }, play: async ({ canvasElement }) => { const canvas = await withinShadowRoot(canvasElement, 'gs-location-filter'); await waitFor(() => { return expect(canvas.getByRole('combobox')).toBeEnabled(); }); }, }"
434
463
  },
435
- {
436
- "kind": "js",
437
- "name": "AggregateComponent",
438
- "declaration": {
439
- "name": "AggregateComponent",
440
- "module": "./aggregate-component"
441
- }
442
- }
443
- ]
444
- },
445
- {
446
- "kind": "javascript-module",
447
- "path": "src/web-components/display/mutation-comparison-component.stories.ts",
448
- "declarations": [
449
464
  {
450
465
  "kind": "variable",
451
- "name": "meta",
466
+ "name": "DelayToShowLoadingState",
452
467
  "type": {
453
- "text": "Meta<MutationComparisonProps>"
468
+ "text": "StoryObj<LocationFilterProps>"
454
469
  },
455
- "default": "{ title: 'Visualization/Mutation comparison', component: 'gs-mutation-comparison-component', argTypes: { variants: { control: 'object' }, sequenceType: { options: ['nucleotide', 'amino acid'], control: { type: 'radio' }, }, views: { options: ['table', 'venn'], control: { type: 'check' }, }, size: { control: 'object' }, headline: { control: 'text' }, }, parameters: withComponentDocs({ componentDocs: { tag: 'gs-mutation-comparison-component', opensShadowDom: true, expectsChildren: false, codeExample, }, }), tags: ['autodocs'], }"
470
+ "default": "{ ...Template, parameters: { fetchMock: { mocks: [ { matcher: aggregatedEndpointMatcher, response: { status: 200, body: data, }, options: { delay: 5000, }, }, ], }, }, }"
456
471
  },
457
472
  {
458
473
  "kind": "variable",
459
- "name": "Default",
474
+ "name": "FetchingLocationsFails",
460
475
  "type": {
461
- "text": "StoryObj<MutationComparisonProps>"
476
+ "text": "StoryObj<LocationFilterProps>"
462
477
  },
463
- "default": "{ ...Template, args: { variants: [ { displayName: 'Some variant', lapisFilter: { country: 'Switzerland', pangoLineage: 'B.1.1.7', dateTo }, }, { displayName: 'Other variant', lapisFilter: { country: 'Switzerland', pangoLineage: 'B.1.1.7', dateFrom, dateTo, }, }, ], sequenceType: 'nucleotide', views: ['table', 'venn'], size: { width: '100%', height: '700px' }, headline: 'Mutation comparison', }, parameters: { fetchMock: { mocks: [ { matcher: { name: 'nucleotideMutationsSomeVariant', url: NUCLEOTIDE_MUTATIONS_ENDPOINT, body: { country: 'Switzerland', pangoLineage: 'B.1.1.7', dateTo, minProportion: 0, }, }, response: { status: 200, body: nucleotideMutationsSomeVariant, }, }, { matcher: { name: 'nucleotideMutationsOtherVariant', url: NUCLEOTIDE_MUTATIONS_ENDPOINT, body: { country: 'Switzerland', pangoLineage: 'B.1.1.7', dateFrom, dateTo, minProportion: 0, }, }, response: { status: 200, body: nucleotideMutationsOtherVariant, }, }, ], }, }, play: async ({ canvasElement, step }) => { const canvas = await withinShadowRoot(canvasElement, 'gs-mutation-comparison-component'); await step('Min and max proportions should be 50% and 100%', async () => { const minInput = () => canvas.getAllByLabelText('%')[0]; const maxInput = () => canvas.getAllByLabelText('%')[1]; await waitFor(() => expect(minInput()).toHaveValue(50)); await waitFor(() => expect(maxInput()).toHaveValue(100)); }); }, }"
478
+ "default": "{ ...Template, parameters: { fetchMock: { mocks: [ { matcher: aggregatedEndpointMatcher, response: { status: 400, body: { error: { status: 400, detail: 'Dummy error message from mock LAPIS', type: 'about:blank' }, }, }, }, ], }, }, play: async ({ canvasElement }) => { const canvas = await withinShadowRoot(canvasElement, 'gs-location-filter'); await waitFor(() => expect(canvas.getByText('Oops! Something went wrong.', { exact: false })).toBeInTheDocument(), ); }, }"
464
479
  },
465
480
  {
466
481
  "kind": "variable",
467
- "name": "VennDiagram",
482
+ "name": "FiresEvent",
468
483
  "type": {
469
- "text": "StoryObj<MutationComparisonProps>"
484
+ "text": "StoryObj<LocationFilterProps>"
470
485
  },
471
- "default": "{ ...Default, play: async ({ canvasElement, step }) => { const canvas = await withinShadowRoot(canvasElement, 'gs-mutation-comparison-component'); await step('Switch to Venn diagram view', async () => { await waitFor(() => expect(canvas.getByRole('button', { name: 'Venn' })).toBeInTheDocument()); await fireEvent.click(canvas.getByRole('button', { name: 'Venn' })); await waitFor(() => expect( canvas.getByText('You have no elements selected. Click in the venn diagram to select.'), ).toBeVisible(), ); }); }, }"
486
+ "default": "{ ...Template, parameters: { fetchMock: { mocks: [ { matcher: aggregatedEndpointMatcher, response: { status: 200, body: data, }, }, ], }, }, play: async ({ canvasElement, step }) => { const canvas = await withinShadowRoot(canvasElement, 'gs-location-filter'); const submitButton = () => canvas.getByRole('button', { name: 'Submit' }); const inputField = () => canvas.getByRole('combobox'); const listenerMock = fn(); await step('Setup event listener mock', async () => { canvasElement.addEventListener('gs-location-changed', listenerMock); }); await step('wait until data is loaded', async () => { await waitFor(() => { return expect(inputField()).toBeEnabled(); }); }); await step('Input invalid location', async () => { await userEvent.type(inputField(), 'Not / A / Location'); await userEvent.click(submitButton()); await expect(listenerMock).not.toHaveBeenCalled(); await userEvent.type(inputField(), '{backspace>18/}'); }); await step('Select Asia', async () => { await userEvent.type(inputField(), 'Asia'); await userEvent.click(submitButton()); await expect(listenerMock).toHaveBeenCalledWith( expect.objectContaining({ detail: { region: 'Asia', }, }), ); }); await step('Select Asia / Bangladesh / Rajshahi / Chapainawabgonj', async () => { await userEvent.type(inputField(), ' / Bangladesh / Rajshahi / Chapainawabgonj'); await userEvent.click(submitButton()); await expect(listenerMock).toHaveBeenCalledWith( expect.objectContaining({ detail: { region: 'Asia', country: 'Bangladesh', division: 'Rajshahi', location: 'Chapainawabgonj', }, }), ); }); }, }"
472
487
  }
473
488
  ],
474
489
  "exports": [
@@ -477,196 +492,183 @@
477
492
  "name": "default",
478
493
  "declaration": {
479
494
  "name": "meta",
480
- "module": "src/web-components/display/mutation-comparison-component.stories.ts"
495
+ "module": "src/web-components/input/gs-location-filter.stories.ts"
481
496
  }
482
497
  },
483
498
  {
484
499
  "kind": "js",
485
- "name": "Default",
500
+ "name": "LocationFilter",
486
501
  "declaration": {
487
- "name": "Default",
488
- "module": "src/web-components/display/mutation-comparison-component.stories.ts"
502
+ "name": "LocationFilter",
503
+ "module": "src/web-components/input/gs-location-filter.stories.ts"
489
504
  }
490
505
  },
491
506
  {
492
507
  "kind": "js",
493
- "name": "VennDiagram",
508
+ "name": "DelayToShowLoadingState",
494
509
  "declaration": {
495
- "name": "VennDiagram",
496
- "module": "src/web-components/display/mutation-comparison-component.stories.ts"
510
+ "name": "DelayToShowLoadingState",
511
+ "module": "src/web-components/input/gs-location-filter.stories.ts"
512
+ }
513
+ },
514
+ {
515
+ "kind": "js",
516
+ "name": "FetchingLocationsFails",
517
+ "declaration": {
518
+ "name": "FetchingLocationsFails",
519
+ "module": "src/web-components/input/gs-location-filter.stories.ts"
520
+ }
521
+ },
522
+ {
523
+ "kind": "js",
524
+ "name": "FiresEvent",
525
+ "declaration": {
526
+ "name": "FiresEvent",
527
+ "module": "src/web-components/input/gs-location-filter.stories.ts"
497
528
  }
498
529
  }
499
530
  ]
500
531
  },
501
532
  {
502
533
  "kind": "javascript-module",
503
- "path": "src/web-components/display/mutation-comparison-component.tsx",
534
+ "path": "src/web-components/input/gs-location-filter.tsx",
504
535
  "declarations": [
505
536
  {
506
537
  "kind": "class",
507
- "description": "This component allows to compare mutations between different variants.\nA variant is defined by its LAPIS filter.\n\nIt only shows substitutions and deletions, it does not show insertions.\n\n## Views\n\n### Table View\n\nThe table view shows mutations\nand the proportions with which the mutation occurs in the respective variant.\nIt only shows mutations that are present in at least one of the variants\nand where the proportion is within the selected proportion interval for at least one variant.\n\n### Venn View\n\nThe Venn view shows the overlap of mutations between the variants in a Venn diagram.\nA variant is considered to have a certain mutation,\nif the proportion of the mutation in the variant is within the selected proportion interval.\nThus, changing the proportion interval may change a mutations from being \"common\" between variant\nto being \"for one variant only\".",
508
- "name": "MutationComparisonComponent",
538
+ "description": "## Context\n\nThis component provides an input field to specify filters for locations.\n\nIt expects a list of fields that form a strict hierarchical order, such as continent, country, and city.\nThe component retrieves a list of all possible values for these fields from the Lapis instance.\nThis list is then utilized to display autocomplete suggestions and to validate the input.\n\nGiven `fields` are `['field1', 'field2', ..., 'fieldN']`,\nthen valid values for the location filter must be in the form `valueForField1 / valueForField2 / ... / valueForFieldK`,\nwhere `1 <= K <= N`.\nValues for the fields `i > K` are considered `undefined`.",
539
+ "name": "LocationFilterComponent",
509
540
  "members": [
510
541
  {
511
542
  "kind": "field",
512
- "name": "variants",
513
- "type": {
514
- "text": "{\n lapisFilter: Record<string, string | number | null | boolean>;\n displayName: string;\n }[]"
515
- },
516
- "default": "[]",
517
- "description": "An array of variants to compare.\n\nThe `lapisFilter` will be sent as is to LAPIS to filter the mutation data.\nIt must be a valid LAPIS filter object.\n\nThe `displayName` will be used as the label for the variant in the views.\nIt should be human-readable.",
518
- "attribute": "variants"
519
- },
520
- {
521
- "kind": "field",
522
- "name": "sequenceType",
543
+ "name": "initialValue",
523
544
  "type": {
524
- "text": "'nucleotide' | 'amino acid'"
545
+ "text": "string"
525
546
  },
526
- "default": "'nucleotide'",
527
- "description": "The type of the sequence for which the mutations should be shown.",
528
- "attribute": "sequenceType"
547
+ "default": "''",
548
+ "description": "The initial value to use for this location filter.\nMust be of the form `valueForField1 / valueForField2 / ... / valueForFieldN`.",
549
+ "attribute": "initialValue"
529
550
  },
530
551
  {
531
552
  "kind": "field",
532
- "name": "views",
553
+ "name": "fields",
533
554
  "type": {
534
- "text": "('table' | 'venn')[]"
555
+ "text": "string[]"
535
556
  },
536
- "default": "['table']",
537
- "description": "A list of tabs with views that this component should provide.",
538
- "attribute": "views"
557
+ "default": "[]",
558
+ "description": "Required.\n\nThe fields to display in the location filter, in hierarchical order.\nThe top-level field should be the first entry in the array.\nThis component assumes that the values for each field form a strict hierarchy\n(e.g., `fields = ['continent', 'country', 'city']`).",
559
+ "attribute": "fields"
539
560
  },
540
561
  {
541
562
  "kind": "field",
542
- "name": "size",
563
+ "name": "width",
543
564
  "type": {
544
- "text": "{ width?: string; height?: string } | undefined"
565
+ "text": "string"
545
566
  },
546
- "default": "undefined",
547
- "description": "The size of the component.\n\nIf not set, the component will take the full width of its container with height 700px.\n\nThe width and height should be a string with a unit in css style, e.g. '100%', '500px' or '50vh'.\nIf the unit is %, the size will be relative to the container of the component.",
548
- "attribute": "size"
549
- },
567
+ "default": "'100%'",
568
+ "description": "The width of the component.\n\nVisit https://genspectrum.github.io/dashboards/?path=/docs/components-size-of-components--docs for more information.",
569
+ "attribute": "width"
570
+ }
571
+ ],
572
+ "events": [
550
573
  {
551
- "kind": "field",
552
- "name": "headline",
553
574
  "type": {
554
- "text": "string | undefined"
575
+ "text": "CustomEvent<Record<string, string>>"
555
576
  },
556
- "default": "'Mutation comparison'",
557
- "description": "The headline of the component. Set to an empty string to hide the headline.",
558
- "attribute": "headline"
577
+ "description": "Fired when the field is submitted with a valid location value. The `details` of this event contain an object with all `fields` as keys and the corresponding values as values, if they are not `undefined`. Example: ``` { continent: \"Asia\", country: \"China\", city: \"Beijing\" } ```",
578
+ "name": "gs-location-changed"
559
579
  }
560
580
  ],
561
581
  "attributes": [
562
582
  {
563
- "name": "variants",
583
+ "name": "initialValue",
564
584
  "type": {
565
- "text": "{\n lapisFilter: Record<string, string | number | null | boolean>;\n displayName: string;\n }[]"
585
+ "text": "string"
566
586
  },
567
- "default": "[]",
568
- "description": "An array of variants to compare.\n\nThe `lapisFilter` will be sent as is to LAPIS to filter the mutation data.\nIt must be a valid LAPIS filter object.\n\nThe `displayName` will be used as the label for the variant in the views.\nIt should be human-readable.",
569
- "fieldName": "variants"
570
- },
571
- {
572
- "name": "sequenceType",
573
- "type": {
574
- "text": "'nucleotide' | 'amino acid'"
575
- },
576
- "default": "'nucleotide'",
577
- "description": "The type of the sequence for which the mutations should be shown.",
578
- "fieldName": "sequenceType"
579
- },
580
- {
581
- "name": "views",
582
- "type": {
583
- "text": "('table' | 'venn')[]"
584
- },
585
- "default": "['table']",
586
- "description": "A list of tabs with views that this component should provide.",
587
- "fieldName": "views"
587
+ "default": "''",
588
+ "description": "The initial value to use for this location filter.\nMust be of the form `valueForField1 / valueForField2 / ... / valueForFieldN`.",
589
+ "fieldName": "initialValue"
588
590
  },
589
591
  {
590
- "name": "size",
592
+ "name": "fields",
591
593
  "type": {
592
- "text": "{ width?: string; height?: string } | undefined"
594
+ "text": "string[]"
593
595
  },
594
- "default": "undefined",
595
- "description": "The size of the component.\n\nIf not set, the component will take the full width of its container with height 700px.\n\nThe width and height should be a string with a unit in css style, e.g. '100%', '500px' or '50vh'.\nIf the unit is %, the size will be relative to the container of the component.",
596
- "fieldName": "size"
596
+ "default": "[]",
597
+ "description": "Required.\n\nThe fields to display in the location filter, in hierarchical order.\nThe top-level field should be the first entry in the array.\nThis component assumes that the values for each field form a strict hierarchy\n(e.g., `fields = ['continent', 'country', 'city']`).",
598
+ "fieldName": "fields"
597
599
  },
598
600
  {
599
- "name": "headline",
601
+ "name": "width",
600
602
  "type": {
601
- "text": "string | undefined"
603
+ "text": "string"
602
604
  },
603
- "default": "'Mutation comparison'",
604
- "description": "The headline of the component. Set to an empty string to hide the headline.",
605
- "fieldName": "headline"
605
+ "default": "'100%'",
606
+ "description": "The width of the component.\n\nVisit https://genspectrum.github.io/dashboards/?path=/docs/components-size-of-components--docs for more information.",
607
+ "fieldName": "width"
606
608
  }
607
609
  ],
608
610
  "superclass": {
609
- "name": "PreactLitAdapterWithGridJsStyles",
610
- "module": "/src/web-components/PreactLitAdapterWithGridJsStyles"
611
+ "name": "PreactLitAdapter",
612
+ "module": "/src/web-components/PreactLitAdapter"
611
613
  },
612
- "tagName": "gs-mutation-comparison-component",
614
+ "tagName": "gs-location-filter",
613
615
  "customElement": true
614
616
  }
615
617
  ],
616
618
  "exports": [
617
619
  {
618
620
  "kind": "js",
619
- "name": "MutationComparisonComponent",
621
+ "name": "LocationFilterComponent",
620
622
  "declaration": {
621
- "name": "MutationComparisonComponent",
622
- "module": "src/web-components/display/mutation-comparison-component.tsx"
623
+ "name": "LocationFilterComponent",
624
+ "module": "src/web-components/input/gs-location-filter.tsx"
623
625
  }
624
626
  },
625
627
  {
626
628
  "kind": "custom-element-definition",
627
- "name": "gs-mutation-comparison-component",
629
+ "name": "gs-location-filter",
628
630
  "declaration": {
629
- "name": "MutationComparisonComponent",
630
- "module": "src/web-components/display/mutation-comparison-component.tsx"
631
+ "name": "LocationFilterComponent",
632
+ "module": "src/web-components/input/gs-location-filter.tsx"
631
633
  }
632
634
  }
633
635
  ]
634
636
  },
635
637
  {
636
638
  "kind": "javascript-module",
637
- "path": "src/web-components/display/mutations-component.stories.ts",
639
+ "path": "src/web-components/input/gs-mutation-filter.stories.ts",
638
640
  "declarations": [
639
641
  {
640
642
  "kind": "variable",
641
643
  "name": "meta",
642
644
  "type": {
643
- "text": "Meta<MutationsProps>"
645
+ "text": "Meta<MutationFilterProps>"
644
646
  },
645
- "default": "{ title: 'Visualization/Mutations', component: 'gs-mutations-component', argTypes: { variant: { control: 'object' }, sequenceType: { options: ['nucleotide', 'amino acid'], control: { type: 'radio' }, }, views: { options: ['table', 'grid', 'insertions'], control: { type: 'check' }, }, size: { control: 'object' }, headline: { control: 'text' }, }, args: { variant: { country: 'Switzerland', pangoLineage: 'B.1.1.7', dateTo: '2022-01-01' }, sequenceType: 'nucleotide', views: ['grid', 'table', 'insertions'], size: { width: '100%', height: '700px' }, headline: 'Mutations', }, parameters: withComponentDocs({ componentDocs: { tag: 'gs-mutations-component', opensShadowDom: true, expectsChildren: false, codeExample, }, }), tags: ['autodocs'], }"
647
+ "default": "{ title: 'Input/Mutation filter', component: 'gs-mutation-filter', parameters: withComponentDocs({ actions: { handles: ['gs-mutation-filter-changed', 'gs-mutation-filter-on-blur'], }, fetchMock: {}, componentDocs: { opensShadowDom: true, expectsChildren: false, codeExample, }, }), argTypes: { initialValue: { control: { type: 'object', }, }, width: { control: 'text' }, height: { control: 'text' }, }, decorators: [withActions], tags: ['autodocs'], }"
646
648
  },
647
649
  {
648
650
  "kind": "variable",
649
651
  "name": "Default",
650
652
  "type": {
651
- "text": "StoryObj<MutationsProps>"
653
+ "text": "StoryObj<MutationFilterProps>"
652
654
  },
653
- "default": "{ ...Template, parameters: { fetchMock: { mocks: [ { matcher: { name: 'nucleotideMutations', url: NUCLEOTIDE_MUTATIONS_ENDPOINT, body: { country: 'Switzerland', pangoLineage: 'B.1.1.7', dateTo: '2022-01-01', minProportion: 0, }, }, response: { status: 200, body: nucleotideMutations, }, }, { matcher: { name: 'nucleotideInsertions', url: NUCLEOTIDE_INSERTIONS_ENDPOINT, body: { country: 'Switzerland', pangoLineage: 'B.1.1.7', dateTo: '2022-01-01' }, }, response: { status: 200, body: nucleotideInsertions, }, }, ], }, }, }"
655
+ "default": "{ ...Template, args: { initialValue: ['A123T'], }, }"
654
656
  },
655
657
  {
656
658
  "kind": "variable",
657
- "name": "OnTableTab",
659
+ "name": "FiresFilterChangedEvent",
658
660
  "type": {
659
- "text": "StoryObj<MutationsProps>"
661
+ "text": "StoryObj<MutationFilterProps>"
660
662
  },
661
- "default": "{ ...Default, play: async ({ canvasElement }) => { const canvas = await withinShadowRoot(canvasElement, 'gs-mutations-component'); await waitFor(() => expect(canvas.getByRole('button', { name: 'Table' })).toBeInTheDocument()); await fireEvent.click(canvas.getByRole('button', { name: 'Table' })); }, }"
663
+ "default": "{ ...Template, play: async ({ canvasElement, step }) => { const canvas = await withinShadowRoot(canvasElement, 'gs-mutation-filter'); const inputField = () => canvas.getByPlaceholderText('Enter a mutation', { exact: false }); const submitButton = () => canvas.getByRole('button', { name: '+' }); const listenerMock = fn(); await step('Setup event listener mock', async () => { canvasElement.addEventListener('gs-mutation-filter-changed', listenerMock); }); await step('wait until data is loaded', async () => { await waitFor(() => { return expect(inputField()).toBeEnabled(); }); }); await step('Enter a valid mutation', async () => { await userEvent.type(inputField(), 'A123T'); await waitFor(() => submitButton().click()); await waitFor(() => expect(listenerMock).toHaveBeenCalledWith( expect.objectContaining({ detail: { nucleotideMutations: ['A123T'], aminoAcidMutations: [], nucleotideInsertions: [], aminoAcidInsertions: [], }, }), ), ); }); }, }"
662
664
  },
663
665
  {
664
666
  "kind": "variable",
665
- "name": "OnInsertionsTab",
667
+ "name": "FiresFilterOnBlurEvent",
666
668
  "type": {
667
- "text": "StoryObj<MutationsProps>"
669
+ "text": "StoryObj<MutationFilterProps>"
668
670
  },
669
- "default": "{ ...Default, play: async ({ canvasElement }) => { const canvas = await withinShadowRoot(canvasElement, 'gs-mutations-component'); await waitFor(() => expect(canvas.getByRole('button', { name: 'Insertions' })).toBeInTheDocument()); await fireEvent.click(canvas.getByRole('button', { name: 'Insertions' })); }, }"
671
+ "default": "{ ...Template, play: async ({ canvasElement, step }) => { const canvas = await withinShadowRoot(canvasElement, 'gs-mutation-filter'); const inputField = () => canvas.getByPlaceholderText('Enter a mutation', { exact: false }); const listenerMock = fn(); await step('Setup event listener mock', async () => { canvasElement.addEventListener('gs-mutation-filter-on-blur', listenerMock); }); await step('wait until data is loaded', async () => { await waitFor(() => { return expect(inputField()).toBeEnabled(); }); }); await step('Move outside of input', async () => { await userEvent.type(inputField(), 'A123T'); await userEvent.tab(); await expect(listenerMock).toHaveBeenCalled(); }); }, }"
670
672
  }
671
673
  ],
672
674
  "exports": [
@@ -675,7 +677,7 @@
675
677
  "name": "default",
676
678
  "declaration": {
677
679
  "name": "meta",
678
- "module": "src/web-components/display/mutations-component.stories.ts"
680
+ "module": "src/web-components/input/gs-mutation-filter.stories.ts"
679
681
  }
680
682
  },
681
683
  {
@@ -683,212 +685,166 @@
683
685
  "name": "Default",
684
686
  "declaration": {
685
687
  "name": "Default",
686
- "module": "src/web-components/display/mutations-component.stories.ts"
688
+ "module": "src/web-components/input/gs-mutation-filter.stories.ts"
687
689
  }
688
690
  },
689
691
  {
690
692
  "kind": "js",
691
- "name": "OnTableTab",
693
+ "name": "FiresFilterChangedEvent",
692
694
  "declaration": {
693
- "name": "OnTableTab",
694
- "module": "src/web-components/display/mutations-component.stories.ts"
695
+ "name": "FiresFilterChangedEvent",
696
+ "module": "src/web-components/input/gs-mutation-filter.stories.ts"
695
697
  }
696
698
  },
697
699
  {
698
700
  "kind": "js",
699
- "name": "OnInsertionsTab",
701
+ "name": "FiresFilterOnBlurEvent",
700
702
  "declaration": {
701
- "name": "OnInsertionsTab",
702
- "module": "src/web-components/display/mutations-component.stories.ts"
703
+ "name": "FiresFilterOnBlurEvent",
704
+ "module": "src/web-components/input/gs-mutation-filter.stories.ts"
703
705
  }
704
706
  }
705
707
  ]
706
708
  },
707
709
  {
708
710
  "kind": "javascript-module",
709
- "path": "src/web-components/display/mutations-component.tsx",
711
+ "path": "src/web-components/input/gs-mutation-filter.tsx",
710
712
  "declarations": [
711
713
  {
712
714
  "kind": "class",
713
- "description": "This component displays mutations (substitutions, deletions and insertions) for a given variant.\n\n## Views\n\n### Table View\n\nThe table view shows all substitutions and deletions for the given variant.\nIt shows the type (substitution or deletion), the total count of the mutation\nand the proportion of the mutation in the variant.\nThe proportion is relative to the total number of sequences matching\nthe specified sequence filters with non-ambiguous reads at that position.\n\nThe proportion interval filter can be used to filter the displayed mutations on client side.\n\n### Grid View\n\nThe grid view shows the proportion of each sequence symbol (nucleotide or amino acid) for each position that has a mutation.\nOnly positions with at least one mutation in the selected proportion interval are shown.\n\n### Insertions View\n\nThe insertions view shows the count of all insertions for the given variant.",
714
- "name": "MutationsComponent",
715
+ "description": "## Context\nThis component provides an input field to specify filters for nucleotide and amino acid mutations and insertions.\n\nInput values have to be provided one at a time and submitted by pressing the Enter key or by clicking the '+' button.\nAfter submission, the component validates the input and fires an event with the selected mutations.\nAll previously selected mutations are displayed at the input field and added to the event.\nUsers can remove a mutation by clicking the 'x' button next to the mutation.\n\n## Input Validation\n\nValidation of the input is performed according to the following rules:\n\n### Mutations\n\nMutations have to conform to the following format: `<gene/segment>:<symbol at reference><position><Substituted symbol/Deletion>`\n- Gene/segment: The gene or segment where the mutation occurs. Must be contained in the reference genome.\n (Optional for elements with only one gene/segment)\n- Symbol at reference: The symbol at the reference position. (Optional)\n- Position: The position of the mutation. (Required)\n- Substituted symbol/Deletion: The substituted symbol or '-' for a deletion. (Required)\n\nExamples: `S:614G`, `614G`, `614-`, `614G`\n\n### Insertions\n\nInsertions have to conform to the following format: `ins_<gene/segment>:<position>:<Inserted symbols>`\n- Gene/segment: The gene or segment where the insertion occurs. Must be contained in the reference genome.\n (Optional for elements with only one gene/segment)\n- Position: The position of the insertion. (Required)\n- Inserted symbols: The symbols that are inserted. (Required)\n\nExamples: `ins_S:614:G`, `ins_614:G`",
716
+ "name": "MutationFilterComponent",
715
717
  "members": [
716
718
  {
717
719
  "kind": "field",
718
- "name": "variant",
720
+ "name": "initialValue",
719
721
  "type": {
720
- "text": "Record<string, string | number | null | boolean>"
722
+ "text": "{\n nucleotideMutations: string[];\n aminoAcidMutations: string[];\n nucleotideInsertions: string[];\n aminoAcidInsertions: string[];\n }\n | string[]\n | undefined"
721
723
  },
722
- "default": "{ displayName: '' }",
723
- "description": "The `variant` will be sent as is to LAPIS to filter the mutation data.\nIt must be a valid LAPIS filter object.",
724
- "attribute": "variant"
724
+ "default": "undefined",
725
+ "description": "The initial value to use for this mutation filter.\nAll values provided must be valid mutations or insertions.\nInvalid values will be ignored.",
726
+ "attribute": "initialValue"
725
727
  },
726
728
  {
727
729
  "kind": "field",
728
- "name": "sequenceType",
730
+ "name": "width",
729
731
  "type": {
730
- "text": "'nucleotide' | 'amino acid'"
732
+ "text": "string"
731
733
  },
732
- "default": "'nucleotide'",
733
- "description": "The type of the sequence for which the mutations should be shown.",
734
- "attribute": "sequenceType"
734
+ "default": "'100%'",
735
+ "description": "The width of the component.\n\nVisit https://genspectrum.github.io/dashboards/?path=/docs/components-size-of-components--docs for more information.",
736
+ "attribute": "width"
735
737
  },
736
738
  {
737
739
  "kind": "field",
738
- "name": "views",
740
+ "name": "height",
739
741
  "type": {
740
- "text": "('table' | 'grid' | 'insertions')[]"
742
+ "text": "string"
741
743
  },
742
- "default": "['table', 'grid']",
743
- "description": "A list of tabs with views that this component should provide.",
744
- "attribute": "views"
745
- },
744
+ "default": "'6.5rem'",
745
+ "description": "The height of the component.\n\nVisit https://genspectrum.github.io/dashboards/?path=/docs/components-size-of-components--docs for more information.",
746
+ "attribute": "height"
747
+ }
748
+ ],
749
+ "events": [
746
750
  {
747
- "kind": "field",
748
- "name": "size",
749
751
  "type": {
750
- "text": "{ width?: string; height?: string } | undefined"
752
+ "text": "CustomEvent<{nucleotideMutations: string[],aminoAcidMutations: string[],nucleotideInsertions: string[],aminoAcidInsertions: string[]}>"
751
753
  },
752
- "default": "undefined",
753
- "description": "The size of the component.\n\nIf not set, the component will take the full width of its container with height 700px.\n\nThe width and height should be a string with a unit in css style, e.g. '100%', '500px' or '50vh'.\nIf the unit is %, the size will be relative to the container of the component.",
754
- "attribute": "size"
754
+ "description": "Fired when: - The user has submitted a valid mutation or insertion - The user has removed a mutation or insertion",
755
+ "name": "gs-mutation-filter-changed"
755
756
  },
756
757
  {
757
- "kind": "field",
758
- "name": "headline",
759
758
  "type": {
760
- "text": "string | undefined"
759
+ "text": "CustomEvent<{nucleotideMutations: string[],aminoAcidMutations: string[],nucleotideInsertions: string[],aminoAcidInsertions: string[]}>"
761
760
  },
762
- "default": "'Mutations'",
763
- "description": "The headline of the component. Set to an empty string to hide the headline.",
764
- "attribute": "headline"
761
+ "description": "Fired when: - the mutation filter has lost focus Contains the selected mutations in the format",
762
+ "name": "gs-mutation-filter-on-blur"
765
763
  }
766
764
  ],
767
765
  "attributes": [
768
766
  {
769
- "name": "variant",
770
- "type": {
771
- "text": "Record<string, string | number | null | boolean>"
772
- },
773
- "default": "{ displayName: '' }",
774
- "description": "The `variant` will be sent as is to LAPIS to filter the mutation data.\nIt must be a valid LAPIS filter object.",
775
- "fieldName": "variant"
776
- },
777
- {
778
- "name": "sequenceType",
779
- "type": {
780
- "text": "'nucleotide' | 'amino acid'"
781
- },
782
- "default": "'nucleotide'",
783
- "description": "The type of the sequence for which the mutations should be shown.",
784
- "fieldName": "sequenceType"
785
- },
786
- {
787
- "name": "views",
767
+ "name": "initialValue",
788
768
  "type": {
789
- "text": "('table' | 'grid' | 'insertions')[]"
769
+ "text": "{\n nucleotideMutations: string[];\n aminoAcidMutations: string[];\n nucleotideInsertions: string[];\n aminoAcidInsertions: string[];\n }\n | string[]\n | undefined"
790
770
  },
791
- "default": "['table', 'grid']",
792
- "description": "A list of tabs with views that this component should provide.",
793
- "fieldName": "views"
771
+ "default": "undefined",
772
+ "description": "The initial value to use for this mutation filter.\nAll values provided must be valid mutations or insertions.\nInvalid values will be ignored.",
773
+ "fieldName": "initialValue"
794
774
  },
795
775
  {
796
- "name": "size",
776
+ "name": "width",
797
777
  "type": {
798
- "text": "{ width?: string; height?: string } | undefined"
778
+ "text": "string"
799
779
  },
800
- "default": "undefined",
801
- "description": "The size of the component.\n\nIf not set, the component will take the full width of its container with height 700px.\n\nThe width and height should be a string with a unit in css style, e.g. '100%', '500px' or '50vh'.\nIf the unit is %, the size will be relative to the container of the component.",
802
- "fieldName": "size"
780
+ "default": "'100%'",
781
+ "description": "The width of the component.\n\nVisit https://genspectrum.github.io/dashboards/?path=/docs/components-size-of-components--docs for more information.",
782
+ "fieldName": "width"
803
783
  },
804
784
  {
805
- "name": "headline",
785
+ "name": "height",
806
786
  "type": {
807
- "text": "string | undefined"
787
+ "text": "string"
808
788
  },
809
- "default": "'Mutations'",
810
- "description": "The headline of the component. Set to an empty string to hide the headline.",
811
- "fieldName": "headline"
789
+ "default": "'6.5rem'",
790
+ "description": "The height of the component.\n\nVisit https://genspectrum.github.io/dashboards/?path=/docs/components-size-of-components--docs for more information.",
791
+ "fieldName": "height"
812
792
  }
813
793
  ],
814
794
  "superclass": {
815
- "name": "PreactLitAdapterWithGridJsStyles",
816
- "module": "/src/web-components/PreactLitAdapterWithGridJsStyles"
795
+ "name": "PreactLitAdapter",
796
+ "module": "/src/web-components/PreactLitAdapter"
817
797
  },
818
- "tagName": "gs-mutations-component",
798
+ "tagName": "gs-mutation-filter",
819
799
  "customElement": true
820
800
  }
821
801
  ],
822
802
  "exports": [
823
803
  {
824
804
  "kind": "js",
825
- "name": "MutationsComponent",
805
+ "name": "MutationFilterComponent",
826
806
  "declaration": {
827
- "name": "MutationsComponent",
828
- "module": "src/web-components/display/mutations-component.tsx"
807
+ "name": "MutationFilterComponent",
808
+ "module": "src/web-components/input/gs-mutation-filter.tsx"
829
809
  }
830
810
  },
831
811
  {
832
812
  "kind": "custom-element-definition",
833
- "name": "gs-mutations-component",
813
+ "name": "gs-mutation-filter",
834
814
  "declaration": {
835
- "name": "MutationsComponent",
836
- "module": "src/web-components/display/mutations-component.tsx"
815
+ "name": "MutationFilterComponent",
816
+ "module": "src/web-components/input/gs-mutation-filter.tsx"
837
817
  }
838
818
  }
839
819
  ]
840
820
  },
841
821
  {
842
822
  "kind": "javascript-module",
843
- "path": "src/web-components/display/prevalence-over-time-component.stories.ts",
823
+ "path": "src/web-components/input/gs-text-input.stories.ts",
844
824
  "declarations": [
845
825
  {
846
826
  "kind": "variable",
847
827
  "name": "meta",
848
828
  "type": {
849
- "text": "Meta<PrevalenceOverTimeProps>"
850
- },
851
- "default": "{ title: 'Visualization/Prevalence over time', component: 'gs-prevalence-over-time', argTypes: { numerator: { control: 'object' }, denominator: { control: 'object' }, granularity: { options: ['day', 'week', 'month', 'year'], control: { type: 'radio' }, }, smoothingWindow: { control: 'number' }, views: { options: ['bar', 'line', 'bubble', 'table'], control: { type: 'check' }, }, confidenceIntervalMethods: { options: ['wilson'], control: { type: 'check' }, }, size: [{ control: 'object' }], headline: { control: 'text' }, }, parameters: withComponentDocs({ componentDocs: { tag: 'gs-prevalence-over-time', opensShadowDom: true, expectsChildren: false, codeExample, }, }), tags: ['autodocs'], }"
852
- },
853
- {
854
- "kind": "variable",
855
- "name": "TwoVariants",
856
- "type": {
857
- "text": "StoryObj<PrevalenceOverTimeProps>"
858
- },
859
- "default": "{ ...Template, args: { numerator: [ { displayName: 'EG', country: 'USA', pangoLineage: 'EG*', dateFrom: '2023-01-01' }, { displayName: 'JN.1', country: 'USA', pangoLineage: 'JN.1*', dateFrom: '2023-01-01' }, ], denominator: { country: 'USA', dateFrom: '2023-01-01', displayName: 'All' }, granularity: 'month', smoothingWindow: 0, views: ['bar', 'line', 'bubble', 'table'], confidenceIntervalMethods: ['wilson'], size: { width: '100%', height: '700px' }, headline: 'Prevalence over time', }, parameters: { fetchMock: { mocks: [ { matcher: { name: 'numeratorEG', url: AGGREGATED_ENDPOINT, body: { country: 'USA', pangoLineage: 'EG*', dateFrom: '2023-01-01', fields: ['date'], }, }, response: { status: 200, body: numeratorEG, }, }, { matcher: { name: 'numeratorJN1', url: AGGREGATED_ENDPOINT, body: { country: 'USA', pangoLineage: 'JN.1*', dateFrom: '2023-01-01', fields: ['date'], }, }, response: { status: 200, body: numeratorJN1, }, }, { matcher: { name: 'denominator', url: AGGREGATED_ENDPOINT, body: { country: 'USA', dateFrom: '2023-01-01', fields: ['date'], }, }, response: { status: 200, body: denominator, }, }, ], }, }, }"
860
- },
861
- {
862
- "kind": "variable",
863
- "name": "OneVariant",
864
- "type": {
865
- "text": "StoryObj<PrevalenceOverTimeProps>"
866
- },
867
- "default": "{ ...Template, args: { numerator: { displayName: 'EG', country: 'USA', pangoLineage: 'BA.2.86*', dateFrom: '2023-10-01' }, denominator: { country: 'USA', dateFrom: '2023-10-01', displayName: 'All' }, granularity: 'day', smoothingWindow: 7, views: ['bar', 'line', 'bubble', 'table'], confidenceIntervalMethods: ['wilson'], size: { width: '100%', height: '700px' }, headline: 'Prevalence over time', }, parameters: { fetchMock: { mocks: [ { matcher: { name: 'numeratorOneVariant', url: AGGREGATED_ENDPOINT, body: { country: 'USA', pangoLineage: 'BA.2.86*', dateFrom: '2023-10-01', fields: ['date'], }, }, response: { status: 200, body: numeratorOneVariant, }, }, { matcher: { name: 'denominatorOneVariant', url: AGGREGATED_ENDPOINT, body: { country: 'USA', dateFrom: '2023-10-01', fields: ['date'], }, }, response: { status: 200, body: denominatorOneVariant, }, }, ], }, }, }"
868
- },
869
- {
870
- "kind": "variable",
871
- "name": "OneVariantOnLineTab",
872
- "type": {
873
- "text": "StoryObj<PrevalenceOverTimeProps>"
829
+ "text": "Meta<Required<TextInputProps>>"
874
830
  },
875
- "default": "{ ...OneVariant, play: async ({ canvasElement }) => { const canvas = await withinShadowRoot(canvasElement, 'gs-prevalence-over-time'); await waitFor(() => expect(canvas.getByRole('button', { name: 'Line' })).toBeInTheDocument()); await fireEvent.click(canvas.getByRole('button', { name: 'Line' })); }, }"
831
+ "default": "{ title: 'Input/Text input', component: 'gs-text-input', parameters: withComponentDocs({ actions: { handles: ['gs-text-input-changed'], }, fetchMock: { mocks: [ { matcher: { name: 'hosts', url: AGGREGATED_ENDPOINT, body: { fields: ['host'], }, }, response: { status: 200, body: data, }, }, ], }, componentDocs: { opensShadowDom: true, expectsChildren: false, codeExample, }, }), argTypes: { lapisField: { control: { type: 'text', }, }, placeholderText: { control: { type: 'text', }, }, initialValue: { control: { type: 'text', }, }, width: { control: { type: 'text', }, }, }, decorators: [withActions], tags: ['autodocs'], }"
876
832
  },
877
833
  {
878
834
  "kind": "variable",
879
- "name": "OneVariantOnBubbleTab",
835
+ "name": "Default",
880
836
  "type": {
881
- "text": "StoryObj<PrevalenceOverTimeProps>"
837
+ "text": "StoryObj<Required<TextInputProps>>"
882
838
  },
883
- "default": "{ ...OneVariant, play: async ({ canvasElement }) => { const canvas = await withinShadowRoot(canvasElement, 'gs-prevalence-over-time'); await waitFor(() => expect(canvas.getByRole('button', { name: 'Bubble' })).toBeInTheDocument()); await fireEvent.click(canvas.getByRole('button', { name: 'Bubble' })); }, }"
839
+ "default": "{ render: (args) => { return html` <gs-app lapis=\"${LAPIS_URL}\"> <div class=\"max-w-screen-lg\"> <gs-text-input .lapisField=${args.lapisField} .placeholderText=${args.placeholderText} .initialValue=${args.initialValue} .width=${args.width} ></gs-text-input> </div> </gs-app>`; }, args: { lapisField: 'host', placeholderText: 'Enter host name', initialValue: 'Homo sapiens', width: '100%', }, }"
884
840
  },
885
841
  {
886
842
  "kind": "variable",
887
- "name": "OneVariantOnTableTab",
843
+ "name": "FiresEvent",
888
844
  "type": {
889
- "text": "StoryObj<PrevalenceOverTimeProps>"
845
+ "text": "StoryObj<Required<TextInputProps>>"
890
846
  },
891
- "default": "{ ...OneVariant, play: async ({ canvasElement }) => { const canvas = await withinShadowRoot(canvasElement, 'gs-prevalence-over-time'); await waitFor(() => expect(canvas.getByRole('button', { name: 'Table' })).toBeInTheDocument()); await fireEvent.click(canvas.getByRole('button', { name: 'Table' })); }, }"
847
+ "default": "{ ...Default, play: async ({ canvasElement, step }) => { const canvas = await withinShadowRoot(canvasElement, 'gs-text-input'); const inputField = () => canvas.getByPlaceholderText('Enter host name'); const listenerMock = fn(); await step('Setup event listener mock', async () => { canvasElement.addEventListener('gs-text-input-changed', listenerMock); }); await step('wait until data is loaded', async () => { await waitFor(() => { return expect(inputField()).toBeEnabled(); }); }); await step('Enters an invalid host name', async () => { await userEvent.type(inputField(), 'notInList'); await expect(listenerMock).not.toHaveBeenCalled(); await userEvent.type(inputField(), '{backspace>9/}'); }); await step('Enter a valid host name', async () => { await userEvent.type(inputField(), 'Homo'); await expect(listenerMock).toHaveBeenCalledWith( expect.objectContaining({ detail: { host: 'Homo', }, }), ); }); }, args: { ...Default.args, initialValue: '', }, }"
892
848
  }
893
849
  ],
894
850
  "exports": [
@@ -897,261 +853,267 @@
897
853
  "name": "default",
898
854
  "declaration": {
899
855
  "name": "meta",
900
- "module": "src/web-components/display/prevalence-over-time-component.stories.ts"
901
- }
902
- },
903
- {
904
- "kind": "js",
905
- "name": "TwoVariants",
906
- "declaration": {
907
- "name": "TwoVariants",
908
- "module": "src/web-components/display/prevalence-over-time-component.stories.ts"
909
- }
910
- },
911
- {
912
- "kind": "js",
913
- "name": "OneVariant",
914
- "declaration": {
915
- "name": "OneVariant",
916
- "module": "src/web-components/display/prevalence-over-time-component.stories.ts"
856
+ "module": "src/web-components/input/gs-text-input.stories.ts"
917
857
  }
918
858
  },
919
859
  {
920
860
  "kind": "js",
921
- "name": "OneVariantOnLineTab",
922
- "declaration": {
923
- "name": "OneVariantOnLineTab",
924
- "module": "src/web-components/display/prevalence-over-time-component.stories.ts"
925
- }
926
- },
927
- {
928
- "kind": "js",
929
- "name": "OneVariantOnBubbleTab",
861
+ "name": "Default",
930
862
  "declaration": {
931
- "name": "OneVariantOnBubbleTab",
932
- "module": "src/web-components/display/prevalence-over-time-component.stories.ts"
863
+ "name": "Default",
864
+ "module": "src/web-components/input/gs-text-input.stories.ts"
933
865
  }
934
866
  },
935
867
  {
936
868
  "kind": "js",
937
- "name": "OneVariantOnTableTab",
869
+ "name": "FiresEvent",
938
870
  "declaration": {
939
- "name": "OneVariantOnTableTab",
940
- "module": "src/web-components/display/prevalence-over-time-component.stories.ts"
871
+ "name": "FiresEvent",
872
+ "module": "src/web-components/input/gs-text-input.stories.ts"
941
873
  }
942
874
  }
943
875
  ]
944
876
  },
945
877
  {
946
878
  "kind": "javascript-module",
947
- "path": "src/web-components/display/prevalence-over-time-component.tsx",
879
+ "path": "src/web-components/input/gs-text-input.tsx",
948
880
  "declarations": [
949
881
  {
950
882
  "kind": "class",
951
- "description": "This component displays the prevalence over time of one or more variants.\nThe prevalence is calculated as the ratio of the number of cases of each variant given as `numerator`\nto the number of cases of the variant given as `denominator`.\n\nIn the chart views,\n- the user can select whether to display a confidence interval (not available in the bubble chart).\n The confidence interval is calculated using [Wilson score interval](https://en.wikipedia.org/wiki/Binomial_proportion_confidence_interval),\n with a confidence level of 95%.\n- the x-axis shows time steps in the selected `granularity`.\n- the user can select the y-axis scale (linear, logistic, logit).\n\n## Views\n\n### Bar View\n\nDisplays the prevalence over time as a bar chart.\nShows a bar for each variant in the `numerator` on every time step.\n\n### Line View\n\nDisplays the prevalence over time as a line chart.\nEach data point is connected for better visibility.\nShows a line for each variant in the `numerator`.\n\n### Bubble View\n\nDisplays the prevalence over time as a bubble chart.\nThe size of the bubbles represents the number of cases of the `denominator` variant.\nThe height of the bubbles represents the prevalence of the `numerator` variants.\n\n### Table View\n\nDisplays the prevalence over time as a table with one row per time point.",
952
- "name": "PrevalenceOverTimeComponent",
883
+ "description": "\n## Context\n\nThis component provides a text input field to specify filters for arbitrary fields of this LAPIS instance.",
884
+ "name": "TextInputComponent",
953
885
  "members": [
954
886
  {
955
887
  "kind": "field",
956
- "name": "numerator",
888
+ "name": "initialValue",
957
889
  "type": {
958
- "text": "| (Record<string, string | number | null | boolean> & { displayName: string })\n | (Record<string, string | number | null | boolean> & {\n displayName: string;\n })[]"
890
+ "text": "string"
959
891
  },
960
- "default": "{ displayName: '' }",
961
- "description": "Either a single variant or an array of variants to compare.\nThis must be a valid LAPIS filter object with an additional `displayName` property\nwhich will be used as the label for the variant in the views,\nor an array of such objects.",
962
- "attribute": "numerator"
892
+ "default": "''",
893
+ "description": "The initial value to use for this text input.",
894
+ "attribute": "initialValue"
963
895
  },
964
896
  {
965
897
  "kind": "field",
966
- "name": "denominator",
898
+ "name": "lapisField",
967
899
  "type": {
968
- "text": "Record<string, string | number | null | boolean> & { displayName: string }"
900
+ "text": "string"
969
901
  },
970
- "default": "{ displayName: '' }",
971
- "description": "The variant that the variants in `numerator` are compared to.",
972
- "attribute": "denominator"
902
+ "default": "''",
903
+ "description": "Required.\n\nThe LAPIS field name to use for this text input.\nThe field must exist on this LAPIS instance.",
904
+ "attribute": "lapisField"
973
905
  },
974
906
  {
975
907
  "kind": "field",
976
- "name": "granularity",
908
+ "name": "placeholderText",
977
909
  "type": {
978
- "text": "'day' | 'week' | 'month' | 'year'"
910
+ "text": "string"
979
911
  },
980
- "default": "'day'",
981
- "description": "The granularity of the time axis.",
982
- "attribute": "granularity"
912
+ "default": "''",
913
+ "description": "The placeholder text to display in the input field.",
914
+ "attribute": "placeholderText"
983
915
  },
984
916
  {
985
917
  "kind": "field",
986
- "name": "smoothingWindow",
918
+ "name": "width",
987
919
  "type": {
988
- "text": "number"
920
+ "text": "string"
989
921
  },
990
- "default": "0",
991
- "description": "The number of time steps to use for smoothing the data.\n`0` means no smoothing.\nMust be a non-negative integer.\n\nFor a given time, the shown value is the mean of the neighbouring measured values.\nThe `smoothingWindow` value provides the number of neighbouring values to take into account.\nThe resulting time is computed via `Math.floor(smoothingWindow / 2)`.",
992
- "attribute": "smoothingWindow"
993
- },
922
+ "default": "'100%'",
923
+ "description": "The width of the component.\n\nVisit https://genspectrum.github.io/dashboards/?path=/docs/components-size-of-components--docs for more information.",
924
+ "attribute": "width"
925
+ }
926
+ ],
927
+ "events": [
994
928
  {
995
- "kind": "field",
996
- "name": "views",
997
929
  "type": {
998
- "text": "('bar' | 'line' | 'bubble' | 'table')[]"
930
+ "text": "CustomEvent<Record<string, string>>"
999
931
  },
1000
- "default": "['bar', 'line', 'bubble', 'table']",
1001
- "description": "A list of tabs with views that this component should provide.",
1002
- "attribute": "views"
1003
- },
932
+ "description": "Fired when the input field is changed. The `details` of this event contain an object with the `lapisField` as key and the input value as value. Example: ``` { \"host\": \"Homo sapiens\" } ```",
933
+ "name": "gs-text-input-changed"
934
+ }
935
+ ],
936
+ "attributes": [
1004
937
  {
1005
- "kind": "field",
1006
- "name": "confidenceIntervalMethods",
938
+ "name": "initialValue",
1007
939
  "type": {
1008
- "text": "('wilson' | 'none')[]"
940
+ "text": "string"
1009
941
  },
1010
- "default": "['wilson']",
1011
- "description": "A list of methods to calculate the confidence interval.\nThe option `none` is always available and disables confidence intervals.\nPass an empty array to disable the confidence interval selector.",
1012
- "attribute": "confidenceIntervalMethods"
1013
- },
1014
- {
1015
- "kind": "field",
1016
- "name": "headline",
1017
- "type": {
1018
- "text": "string | undefined"
1019
- },
1020
- "default": "'Prevalence over time'",
1021
- "description": "The headline of the component. Set to an empty string to hide the headline.",
1022
- "attribute": "headline"
1023
- },
1024
- {
1025
- "kind": "field",
1026
- "name": "size",
1027
- "type": {
1028
- "text": "{ width?: string; height?: string } | undefined"
1029
- },
1030
- "default": "undefined",
1031
- "description": "The size of the component.\n\nIf not set, the component will take the full width of its container with height 700px.\n\nThe width and height should be a string with a unit in css style, e.g. '100%', '500px' or '50vh'.\nIf the unit is %, the size will be relative to the container of the component.",
1032
- "attribute": "size"
1033
- }
1034
- ],
1035
- "attributes": [
1036
- {
1037
- "name": "numerator",
1038
- "type": {
1039
- "text": "| (Record<string, string | number | null | boolean> & { displayName: string })\n | (Record<string, string | number | null | boolean> & {\n displayName: string;\n })[]"
1040
- },
1041
- "default": "{ displayName: '' }",
1042
- "description": "Either a single variant or an array of variants to compare.\nThis must be a valid LAPIS filter object with an additional `displayName` property\nwhich will be used as the label for the variant in the views,\nor an array of such objects.",
1043
- "fieldName": "numerator"
1044
- },
1045
- {
1046
- "name": "denominator",
1047
- "type": {
1048
- "text": "Record<string, string | number | null | boolean> & { displayName: string }"
1049
- },
1050
- "default": "{ displayName: '' }",
1051
- "description": "The variant that the variants in `numerator` are compared to.",
1052
- "fieldName": "denominator"
1053
- },
1054
- {
1055
- "name": "granularity",
1056
- "type": {
1057
- "text": "'day' | 'week' | 'month' | 'year'"
1058
- },
1059
- "default": "'day'",
1060
- "description": "The granularity of the time axis.",
1061
- "fieldName": "granularity"
1062
- },
1063
- {
1064
- "name": "smoothingWindow",
1065
- "type": {
1066
- "text": "number"
1067
- },
1068
- "default": "0",
1069
- "description": "The number of time steps to use for smoothing the data.\n`0` means no smoothing.\nMust be a non-negative integer.\n\nFor a given time, the shown value is the mean of the neighbouring measured values.\nThe `smoothingWindow` value provides the number of neighbouring values to take into account.\nThe resulting time is computed via `Math.floor(smoothingWindow / 2)`.",
1070
- "fieldName": "smoothingWindow"
1071
- },
1072
- {
1073
- "name": "views",
1074
- "type": {
1075
- "text": "('bar' | 'line' | 'bubble' | 'table')[]"
1076
- },
1077
- "default": "['bar', 'line', 'bubble', 'table']",
1078
- "description": "A list of tabs with views that this component should provide.",
1079
- "fieldName": "views"
942
+ "default": "''",
943
+ "description": "The initial value to use for this text input.",
944
+ "fieldName": "initialValue"
1080
945
  },
1081
946
  {
1082
- "name": "confidenceIntervalMethods",
947
+ "name": "lapisField",
1083
948
  "type": {
1084
- "text": "('wilson' | 'none')[]"
949
+ "text": "string"
1085
950
  },
1086
- "default": "['wilson']",
1087
- "description": "A list of methods to calculate the confidence interval.\nThe option `none` is always available and disables confidence intervals.\nPass an empty array to disable the confidence interval selector.",
1088
- "fieldName": "confidenceIntervalMethods"
951
+ "default": "''",
952
+ "description": "Required.\n\nThe LAPIS field name to use for this text input.\nThe field must exist on this LAPIS instance.",
953
+ "fieldName": "lapisField"
1089
954
  },
1090
955
  {
1091
- "name": "headline",
956
+ "name": "placeholderText",
1092
957
  "type": {
1093
- "text": "string | undefined"
958
+ "text": "string"
1094
959
  },
1095
- "default": "'Prevalence over time'",
1096
- "description": "The headline of the component. Set to an empty string to hide the headline.",
1097
- "fieldName": "headline"
960
+ "default": "''",
961
+ "description": "The placeholder text to display in the input field.",
962
+ "fieldName": "placeholderText"
1098
963
  },
1099
964
  {
1100
- "name": "size",
965
+ "name": "width",
1101
966
  "type": {
1102
- "text": "{ width?: string; height?: string } | undefined"
967
+ "text": "string"
1103
968
  },
1104
- "default": "undefined",
1105
- "description": "The size of the component.\n\nIf not set, the component will take the full width of its container with height 700px.\n\nThe width and height should be a string with a unit in css style, e.g. '100%', '500px' or '50vh'.\nIf the unit is %, the size will be relative to the container of the component.",
1106
- "fieldName": "size"
969
+ "default": "'100%'",
970
+ "description": "The width of the component.\n\nVisit https://genspectrum.github.io/dashboards/?path=/docs/components-size-of-components--docs for more information.",
971
+ "fieldName": "width"
1107
972
  }
1108
973
  ],
1109
974
  "superclass": {
1110
- "name": "PreactLitAdapterWithGridJsStyles",
1111
- "module": "/src/web-components/PreactLitAdapterWithGridJsStyles"
975
+ "name": "PreactLitAdapter",
976
+ "module": "/src/web-components/PreactLitAdapter"
1112
977
  },
1113
- "tagName": "gs-prevalence-over-time",
978
+ "tagName": "gs-text-input",
1114
979
  "customElement": true
1115
980
  }
1116
981
  ],
1117
982
  "exports": [
1118
983
  {
1119
984
  "kind": "js",
1120
- "name": "PrevalenceOverTimeComponent",
985
+ "name": "TextInputComponent",
1121
986
  "declaration": {
1122
- "name": "PrevalenceOverTimeComponent",
1123
- "module": "src/web-components/display/prevalence-over-time-component.tsx"
987
+ "name": "TextInputComponent",
988
+ "module": "src/web-components/input/gs-text-input.tsx"
1124
989
  }
1125
990
  },
1126
991
  {
1127
992
  "kind": "custom-element-definition",
1128
- "name": "gs-prevalence-over-time",
993
+ "name": "gs-text-input",
1129
994
  "declaration": {
1130
- "name": "PrevalenceOverTimeComponent",
1131
- "module": "src/web-components/display/prevalence-over-time-component.tsx"
995
+ "name": "TextInputComponent",
996
+ "module": "src/web-components/input/gs-text-input.tsx"
997
+ }
998
+ }
999
+ ]
1000
+ },
1001
+ {
1002
+ "kind": "javascript-module",
1003
+ "path": "src/web-components/input/index.ts",
1004
+ "declarations": [],
1005
+ "exports": [
1006
+ {
1007
+ "kind": "js",
1008
+ "name": "DateRangeSelectorComponent",
1009
+ "declaration": {
1010
+ "name": "DateRangeSelectorComponent",
1011
+ "module": "./gs-date-range-selector"
1012
+ }
1013
+ },
1014
+ {
1015
+ "kind": "js",
1016
+ "name": "LocationFilterComponent",
1017
+ "declaration": {
1018
+ "name": "LocationFilterComponent",
1019
+ "module": "./gs-location-filter"
1020
+ }
1021
+ },
1022
+ {
1023
+ "kind": "js",
1024
+ "name": "TextInputComponent",
1025
+ "declaration": {
1026
+ "name": "TextInputComponent",
1027
+ "module": "./gs-text-input"
1028
+ }
1029
+ },
1030
+ {
1031
+ "kind": "js",
1032
+ "name": "MutationFilterComponent",
1033
+ "declaration": {
1034
+ "name": "MutationFilterComponent",
1035
+ "module": "./gs-mutation-filter"
1036
+ }
1037
+ }
1038
+ ]
1039
+ },
1040
+ {
1041
+ "kind": "javascript-module",
1042
+ "path": "src/web-components/input/introduction.mdx",
1043
+ "declarations": [],
1044
+ "exports": []
1045
+ },
1046
+ {
1047
+ "kind": "javascript-module",
1048
+ "path": "src/web-components/introduction.mdx",
1049
+ "declarations": [],
1050
+ "exports": []
1051
+ },
1052
+ {
1053
+ "kind": "javascript-module",
1054
+ "path": "src/web-components/lapis-context.ts",
1055
+ "declarations": [
1056
+ {
1057
+ "kind": "variable",
1058
+ "name": "lapisContext"
1059
+ }
1060
+ ],
1061
+ "exports": [
1062
+ {
1063
+ "kind": "js",
1064
+ "name": "lapisContext",
1065
+ "declaration": {
1066
+ "name": "lapisContext",
1067
+ "module": "src/web-components/lapis-context.ts"
1068
+ }
1069
+ }
1070
+ ]
1071
+ },
1072
+ {
1073
+ "kind": "javascript-module",
1074
+ "path": "src/web-components/reference-genome-context.ts",
1075
+ "declarations": [
1076
+ {
1077
+ "kind": "variable",
1078
+ "name": "referenceGenomeContext"
1079
+ }
1080
+ ],
1081
+ "exports": [
1082
+ {
1083
+ "kind": "js",
1084
+ "name": "referenceGenomeContext",
1085
+ "declaration": {
1086
+ "name": "referenceGenomeContext",
1087
+ "module": "src/web-components/reference-genome-context.ts"
1132
1088
  }
1133
1089
  }
1134
1090
  ]
1135
1091
  },
1136
1092
  {
1137
1093
  "kind": "javascript-module",
1138
- "path": "src/web-components/display/relative-growth-advantage-component.stories.ts",
1094
+ "path": "src/web-components/visualization/data_visualization_statistical_analysis.mdx",
1095
+ "declarations": [],
1096
+ "exports": []
1097
+ },
1098
+ {
1099
+ "kind": "javascript-module",
1100
+ "path": "src/web-components/visualization/gs-aggregate.stories.ts",
1139
1101
  "declarations": [
1140
1102
  {
1141
1103
  "kind": "variable",
1142
1104
  "name": "meta",
1143
1105
  "type": {
1144
- "text": "Meta<RelativeGrowthAdvantageProps>"
1106
+ "text": "Meta<Required<AggregateProps>>"
1145
1107
  },
1146
- "default": "{ title: 'Visualization/Relative growth advantage', component: 'gs-relative-growth-advantage', argTypes: { numerator: { control: 'object' }, denominator: { control: 'object' }, generationTime: { control: 'number' }, views: { options: ['line'], control: { type: 'check' }, }, size: [{ control: 'object' }], headline: { control: 'text' }, }, parameters: withComponentDocs({ componentDocs: { tag: 'gs-relative-growth-advantage', opensShadowDom: true, expectsChildren: false, codeExample, }, }), tags: ['autodocs'], }"
1108
+ "default": "{ title: 'Visualization/Aggregate', component: 'gs-aggregate', argTypes: { fields: [{ control: 'object' }], views: { options: ['table'], control: { type: 'check' }, }, width: { control: 'text' }, height: { control: 'text' }, headline: { control: 'text' }, }, parameters: withComponentDocs({ fetchMock: { mocks: [ { matcher: { name: 'aggregatedData', url: AGGREGATED_ENDPOINT, body: { fields: ['division', 'host'], country: 'USA', }, }, response: { status: 200, body: aggregatedData, }, }, ], }, componentDocs: { opensShadowDom: true, expectsChildren: false, codeExample, }, }), tags: ['autodocs'], }"
1147
1109
  },
1148
1110
  {
1149
1111
  "kind": "variable",
1150
- "name": "Default",
1112
+ "name": "Table",
1151
1113
  "type": {
1152
- "text": "StoryObj<RelativeGrowthAdvantageProps>"
1114
+ "text": "StoryObj<Required<AggregateProps>>"
1153
1115
  },
1154
- "default": "{ ...Template, args: { numerator: { country: 'Switzerland', pangoLineage: 'B.1.1.7', dateFrom: '2020-12-01', dateTo: '2021-03-01' }, denominator: { country: 'Switzerland', dateFrom: '2020-12-01', dateTo: '2021-03-01' }, generationTime: 7, views: ['line'], size: { width: '100%', height: '700px' }, headline: 'Relative growth advantage', }, parameters: { fetchMock: { mocks: [ { matcher: { name: 'numerator', url: AGGREGATED_ENDPOINT, body: { country: 'Switzerland', pangoLineage: 'B.1.1.7', dateFrom: '2020-12-01', dateTo: '2021-03-01', fields: ['date'], }, }, response: { status: 200, body: numerator, }, }, { matcher: { name: 'denominator', url: AGGREGATED_ENDPOINT, body: { country: 'Switzerland', dateFrom: '2020-12-01', dateTo: '2021-03-01', fields: ['date'], }, }, response: { status: 200, body: denominator, }, }, ], }, }, }"
1116
+ "default": "{ render: (args) => html` <gs-app lapis=\"${LAPIS_URL}\"> <gs-aggregate .fields=${args.fields} .filter=${args.filter} .views=${args.views} .width=${args.width} .height=${args.height} .headline=${args.headline} ></gs-aggregate> </gs-app> `, args: { fields: ['division', 'host'], views: ['table'], filter: { country: 'USA', }, width: '100%', height: '700px', headline: 'Aggregate', }, }"
1155
1117
  }
1156
1118
  ],
1157
1119
  "exports": [
@@ -1160,222 +1122,199 @@
1160
1122
  "name": "default",
1161
1123
  "declaration": {
1162
1124
  "name": "meta",
1163
- "module": "src/web-components/display/relative-growth-advantage-component.stories.ts"
1125
+ "module": "src/web-components/visualization/gs-aggregate.stories.ts"
1164
1126
  }
1165
1127
  },
1166
1128
  {
1167
1129
  "kind": "js",
1168
- "name": "Default",
1130
+ "name": "Table",
1169
1131
  "declaration": {
1170
- "name": "Default",
1171
- "module": "src/web-components/display/relative-growth-advantage-component.stories.ts"
1132
+ "name": "Table",
1133
+ "module": "src/web-components/visualization/gs-aggregate.stories.ts"
1172
1134
  }
1173
1135
  }
1174
1136
  ]
1175
1137
  },
1176
1138
  {
1177
1139
  "kind": "javascript-module",
1178
- "path": "src/web-components/display/relative-growth-advantage-component.tsx",
1140
+ "path": "src/web-components/visualization/gs-aggregate.tsx",
1179
1141
  "declarations": [
1180
1142
  {
1181
1143
  "kind": "class",
1182
- "description": "We assume a discrete time model, where new infections happen exactly every `generationTime` days.\nThis is what we call a \"generation\".\n\nThis component estimates the relative growth advantage of a variant by performing a logistic regression.\nBased on the inferred logistic growth rate, we derive the relative growth advantage (per generation).\n\nFor details on the scientific method, see:\nChen, Chaoran, et al. \"Quantification of the spread of SARS-CoV-2 variant B.1.1.7 in Switzerland.\" Epidemics (2021);\ndoi: [10.1016/j.epidem.2021.100480](https://doi.org/10.1016/j.epidem.2021.100480)\n\nThis component fetches aggregated data from LAPIS.\nThen the data is sent to `https://cov-spectrum.org/api/v2/computed/model/chen2021Fitness`\nwhich performs the logistic regression and calculates the relative growth advantage.\n\n## Views\n\n### Line View\n\nThe line view shows the relative growth advantage over time in a line chart.\nThe dots in the plot show the proportions of the focal variant (`numerator`) to the `denominator` variant\nfor every day as observed in the data.\nThe line shows a logistic curve fitted to the data points, including a 95% confidence interval.",
1183
- "name": "RelativeGrowthAdvantageComponent",
1144
+ "description": "## Context\n\nThis component displays aggregated data in a table, which can provide an overview of the underlying data.\n\nIt expects a list of `fields` to aggregate by and a `filter` to apply to the data.\n\n## Views\n\n### Table View\n\nIn the table view, the data is presented in a table format where each field is a column,\nalong with the aggregated value and its proportion.\nThe proportion represents the ratio of the aggregated value to the total count of the data\n(considering the applied filter).",
1145
+ "name": "AggregateComponent",
1184
1146
  "members": [
1185
1147
  {
1186
1148
  "kind": "field",
1187
- "name": "numerator",
1149
+ "name": "fields",
1188
1150
  "type": {
1189
- "text": "Record<string, string | number | null | boolean>"
1151
+ "text": "string[]"
1190
1152
  },
1191
- "default": "{}",
1192
- "description": "The LAPIS filter for the focal variant.",
1193
- "attribute": "numerator"
1153
+ "default": "[]",
1154
+ "description": "The fields to aggregate by.\nEvery field will be a table column.\nEvery field must exist in the backing LAPIS instance.\n\nIf left empty, the component will only show the absolute count of the provided `filter` and proportion `100%`.",
1155
+ "attribute": "fields"
1194
1156
  },
1195
1157
  {
1196
1158
  "kind": "field",
1197
- "name": "denominator",
1159
+ "name": "views",
1198
1160
  "type": {
1199
- "text": "Record<string, string | number | null | boolean>"
1161
+ "text": "View[]"
1200
1162
  },
1201
- "default": "{}",
1202
- "description": "The LAPIS filter for the variant that the focal variant (`numerator`) should be compared to.",
1203
- "attribute": "denominator"
1163
+ "default": "['table']",
1164
+ "description": "A list of tabs with views that this component should provide.",
1165
+ "attribute": "views"
1204
1166
  },
1205
1167
  {
1206
1168
  "kind": "field",
1207
- "name": "generationTime",
1169
+ "name": "filter",
1208
1170
  "type": {
1209
- "text": "number"
1171
+ "text": "LapisFilter"
1210
1172
  },
1211
- "default": "7",
1212
- "description": "The generation time represents the number of days over which the variant's relative growth advantage is measured.\nFor example, if we set a generation time of 7 days, then we estimate the growth advantage per week.",
1213
- "attribute": "generationTime"
1173
+ "default": "{}",
1174
+ "description": "The filter to apply to the data.\nIt must be a valid LAPIS filter object.",
1175
+ "attribute": "filter"
1214
1176
  },
1215
1177
  {
1216
1178
  "kind": "field",
1217
- "name": "views",
1179
+ "name": "width",
1218
1180
  "type": {
1219
- "text": "'line'[]"
1181
+ "text": "string"
1220
1182
  },
1221
- "default": "['line']",
1222
- "description": "A list of tabs with views that this component should provide.",
1223
- "attribute": "views"
1183
+ "default": "'100%'",
1184
+ "description": "The width of the component.\n\nVisit https://genspectrum.github.io/dashboards/?path=/docs/components-size-of-components--docs for more information.",
1185
+ "attribute": "width"
1224
1186
  },
1225
1187
  {
1226
1188
  "kind": "field",
1227
- "name": "headline",
1189
+ "name": "height",
1228
1190
  "type": {
1229
- "text": "string | undefined"
1191
+ "text": "string"
1230
1192
  },
1231
- "default": "'Relative growth advantage'",
1232
- "description": "The headline of the component. Set to an empty string to hide the headline.",
1233
- "attribute": "headline"
1193
+ "default": "'700px'",
1194
+ "description": "The height of the component.\n\nVisit https://genspectrum.github.io/dashboards/?path=/docs/components-size-of-components--docs for more information.",
1195
+ "attribute": "height"
1234
1196
  },
1235
1197
  {
1236
1198
  "kind": "field",
1237
- "name": "size",
1199
+ "name": "headline",
1238
1200
  "type": {
1239
- "text": "{ width?: string; height?: string } | undefined"
1201
+ "text": "string"
1240
1202
  },
1241
- "default": "undefined",
1242
- "description": "The size of the component.\n\nIf not set, the component will take the full width of its container with height 700px.\n\nThe width and height should be a string with a unit in css style, e.g. '100%', '500px' or '50vh'.\nIf the unit is %, the size will be relative to the container of the component.",
1243
- "attribute": "size"
1203
+ "default": "'Aggregate'",
1204
+ "description": "The headline of the component. Set to an empty string to hide the headline.",
1205
+ "attribute": "headline"
1244
1206
  }
1245
1207
  ],
1246
1208
  "attributes": [
1247
1209
  {
1248
- "name": "numerator",
1210
+ "name": "fields",
1249
1211
  "type": {
1250
- "text": "Record<string, string | number | null | boolean>"
1212
+ "text": "string[]"
1251
1213
  },
1252
- "default": "{}",
1253
- "description": "The LAPIS filter for the focal variant.",
1254
- "fieldName": "numerator"
1214
+ "default": "[]",
1215
+ "description": "The fields to aggregate by.\nEvery field will be a table column.\nEvery field must exist in the backing LAPIS instance.\n\nIf left empty, the component will only show the absolute count of the provided `filter` and proportion `100%`.",
1216
+ "fieldName": "fields"
1255
1217
  },
1256
1218
  {
1257
- "name": "denominator",
1219
+ "name": "views",
1258
1220
  "type": {
1259
- "text": "Record<string, string | number | null | boolean>"
1221
+ "text": "View[]"
1260
1222
  },
1261
- "default": "{}",
1262
- "description": "The LAPIS filter for the variant that the focal variant (`numerator`) should be compared to.",
1263
- "fieldName": "denominator"
1223
+ "default": "['table']",
1224
+ "description": "A list of tabs with views that this component should provide.",
1225
+ "fieldName": "views"
1264
1226
  },
1265
1227
  {
1266
- "name": "generationTime",
1228
+ "name": "filter",
1267
1229
  "type": {
1268
- "text": "number"
1230
+ "text": "LapisFilter"
1269
1231
  },
1270
- "default": "7",
1271
- "description": "The generation time represents the number of days over which the variant's relative growth advantage is measured.\nFor example, if we set a generation time of 7 days, then we estimate the growth advantage per week.",
1272
- "fieldName": "generationTime"
1273
- },
1232
+ "default": "{}",
1233
+ "description": "The filter to apply to the data.\nIt must be a valid LAPIS filter object.",
1234
+ "fieldName": "filter"
1235
+ },
1274
1236
  {
1275
- "name": "views",
1237
+ "name": "width",
1276
1238
  "type": {
1277
- "text": "'line'[]"
1239
+ "text": "string"
1278
1240
  },
1279
- "default": "['line']",
1280
- "description": "A list of tabs with views that this component should provide.",
1281
- "fieldName": "views"
1241
+ "default": "'100%'",
1242
+ "description": "The width of the component.\n\nVisit https://genspectrum.github.io/dashboards/?path=/docs/components-size-of-components--docs for more information.",
1243
+ "fieldName": "width"
1282
1244
  },
1283
1245
  {
1284
- "name": "headline",
1246
+ "name": "height",
1285
1247
  "type": {
1286
- "text": "string | undefined"
1248
+ "text": "string"
1287
1249
  },
1288
- "default": "'Relative growth advantage'",
1289
- "description": "The headline of the component. Set to an empty string to hide the headline.",
1290
- "fieldName": "headline"
1250
+ "default": "'700px'",
1251
+ "description": "The height of the component.\n\nVisit https://genspectrum.github.io/dashboards/?path=/docs/components-size-of-components--docs for more information.",
1252
+ "fieldName": "height"
1291
1253
  },
1292
1254
  {
1293
- "name": "size",
1255
+ "name": "headline",
1294
1256
  "type": {
1295
- "text": "{ width?: string; height?: string } | undefined"
1257
+ "text": "string"
1296
1258
  },
1297
- "default": "undefined",
1298
- "description": "The size of the component.\n\nIf not set, the component will take the full width of its container with height 700px.\n\nThe width and height should be a string with a unit in css style, e.g. '100%', '500px' or '50vh'.\nIf the unit is %, the size will be relative to the container of the component.",
1299
- "fieldName": "size"
1259
+ "default": "'Aggregate'",
1260
+ "description": "The headline of the component. Set to an empty string to hide the headline.",
1261
+ "fieldName": "headline"
1300
1262
  }
1301
1263
  ],
1302
1264
  "superclass": {
1303
- "name": "PreactLitAdapter",
1304
- "module": "/src/web-components/PreactLitAdapter"
1265
+ "name": "PreactLitAdapterWithGridJsStyles",
1266
+ "module": "/src/web-components/PreactLitAdapterWithGridJsStyles"
1305
1267
  },
1306
- "tagName": "gs-relative-growth-advantage",
1268
+ "tagName": "gs-aggregate",
1307
1269
  "customElement": true
1308
1270
  }
1309
1271
  ],
1310
1272
  "exports": [
1311
1273
  {
1312
1274
  "kind": "js",
1313
- "name": "RelativeGrowthAdvantageComponent",
1275
+ "name": "AggregateComponent",
1314
1276
  "declaration": {
1315
- "name": "RelativeGrowthAdvantageComponent",
1316
- "module": "src/web-components/display/relative-growth-advantage-component.tsx"
1277
+ "name": "AggregateComponent",
1278
+ "module": "src/web-components/visualization/gs-aggregate.tsx"
1317
1279
  }
1318
1280
  },
1319
1281
  {
1320
1282
  "kind": "custom-element-definition",
1321
- "name": "gs-relative-growth-advantage",
1322
- "declaration": {
1323
- "name": "RelativeGrowthAdvantageComponent",
1324
- "module": "src/web-components/display/relative-growth-advantage-component.tsx"
1325
- }
1326
- }
1327
- ]
1328
- },
1329
- {
1330
- "kind": "javascript-module",
1331
- "path": "src/web-components/index.ts",
1332
- "declarations": [],
1333
- "exports": [
1334
- {
1335
- "kind": "js",
1336
- "name": "App",
1337
- "declaration": {
1338
- "name": "App",
1339
- "module": "./app.js"
1340
- }
1341
- },
1342
- {
1343
- "kind": "js",
1344
- "name": "*",
1345
- "declaration": {
1346
- "name": "*",
1347
- "package": "./display"
1348
- }
1349
- },
1350
- {
1351
- "kind": "js",
1352
- "name": "*",
1283
+ "name": "gs-aggregate",
1353
1284
  "declaration": {
1354
- "name": "*",
1355
- "package": "./input"
1285
+ "name": "AggregateComponent",
1286
+ "module": "src/web-components/visualization/gs-aggregate.tsx"
1356
1287
  }
1357
1288
  }
1358
1289
  ]
1359
1290
  },
1360
1291
  {
1361
1292
  "kind": "javascript-module",
1362
- "path": "src/web-components/input/date-range-selector-component.stories.ts",
1293
+ "path": "src/web-components/visualization/gs-mutation-comparison.stories.ts",
1363
1294
  "declarations": [
1364
1295
  {
1365
1296
  "kind": "variable",
1366
1297
  "name": "meta",
1367
1298
  "type": {
1368
- "text": "Meta<DateRangeSelectorProps<'CustomDateRange'>>"
1299
+ "text": "Meta<Required<MutationComparisonProps>>"
1369
1300
  },
1370
- "default": "{ title: 'Input/DateRangeSelector', component: 'gs-date-range-selector', parameters: withComponentDocs({ actions: { handles: ['gs-date-range-changed'], }, fetchMock: {}, componentDocs: { tag: 'gs-date-range-selector', opensShadowDom: true, expectsChildren: false, codeExample, }, }), argTypes: { initialValue: { control: { type: 'select', }, options: [ PRESET_VALUE_CUSTOM, PRESET_VALUE_ALL_TIMES, PRESET_VALUE_LAST_2_WEEKS, PRESET_VALUE_LAST_MONTH, PRESET_VALUE_LAST_2_MONTHS, PRESET_VALUE_LAST_3_MONTHS, PRESET_VALUE_LAST_6_MONTHS, 'CustomDateRange', ], }, }, args: { customSelectOptions: [{ label: 'CustomDateRange', dateFrom: '2021-01-01', dateTo: '2021-12-31' }], earliestDate: '1970-01-01', initialValue: PRESET_VALUE_LAST_6_MONTHS, }, decorators: [withActions], tags: ['autodocs'], }"
1301
+ "default": "{ title: 'Visualization/Mutation comparison', component: 'gs-mutation-comparison', argTypes: { variants: { control: 'object' }, sequenceType: { options: ['nucleotide', 'amino acid'], control: { type: 'radio' }, }, views: { options: ['table', 'venn'], control: { type: 'check' }, }, width: { control: 'text' }, height: { control: 'text' }, headline: { control: 'text' }, }, parameters: withComponentDocs({ componentDocs: { opensShadowDom: true, expectsChildren: false, codeExample, }, }), tags: ['autodocs'], }"
1371
1302
  },
1372
1303
  {
1373
1304
  "kind": "variable",
1374
- "name": "DateRangeSelectorStory",
1305
+ "name": "Default",
1375
1306
  "type": {
1376
- "text": "StoryObj<DateRangeSelectorProps<'CustomDateRange'>>"
1307
+ "text": "StoryObj<Required<MutationComparisonProps>>"
1377
1308
  },
1378
- "default": "{ render: (args) => html` <gs-app lapis=\"${LAPIS_URL}\"> <div class=\"max-w-screen-lg\"> <gs-date-range-selector .customSelectOptions=${args.customSelectOptions} .earliestDate=${args.earliestDate} .initialValue=${args.initialValue} ></gs-date-range-selector> </div> </gs-app>`, play: async ({ canvasElement, step }) => { const canvas = await withinShadowRoot(canvasElement, 'gs-date-range-selector'); const dateTo = () => canvas.getByPlaceholderText('Date to'); await step('Expect last 6 months to be selected', async () => { await expect(canvas.getByRole('combobox')).toHaveValue('last6Months'); await waitFor(() => { expect(dateTo()).toHaveValue(toYYYYMMDD(new Date())); }); }); }, }"
1309
+ "default": "{ ...Template, args: { variants: [ { displayName: 'Some variant', lapisFilter: { country: 'Switzerland', pangoLineage: 'B.1.1.7', dateTo }, }, { displayName: 'Other variant', lapisFilter: { country: 'Switzerland', pangoLineage: 'B.1.1.7', dateFrom, dateTo, }, }, ], sequenceType: 'nucleotide', views: ['table', 'venn'], width: '100%', height: '700px', headline: 'Mutation comparison', }, parameters: { fetchMock: { mocks: [ { matcher: { name: 'nucleotideMutationsSomeVariant', url: NUCLEOTIDE_MUTATIONS_ENDPOINT, body: { country: 'Switzerland', pangoLineage: 'B.1.1.7', dateTo, minProportion: 0, }, }, response: { status: 200, body: nucleotideMutationsSomeVariant, }, }, { matcher: { name: 'nucleotideMutationsOtherVariant', url: NUCLEOTIDE_MUTATIONS_ENDPOINT, body: { country: 'Switzerland', pangoLineage: 'B.1.1.7', dateFrom, dateTo, minProportion: 0, }, }, response: { status: 200, body: nucleotideMutationsOtherVariant, }, }, ], }, }, play: async ({ canvasElement, step }) => { const canvas = await withinShadowRoot(canvasElement, 'gs-mutation-comparison'); await step('Min and max proportions should be 50% and 100%', async () => { const minInput = () => canvas.getAllByLabelText('%')[0]; const maxInput = () => canvas.getAllByLabelText('%')[1]; await waitFor(() => expect(minInput()).toHaveValue(50)); await waitFor(() => expect(maxInput()).toHaveValue(100)); }); }, }"
1310
+ },
1311
+ {
1312
+ "kind": "variable",
1313
+ "name": "VennDiagram",
1314
+ "type": {
1315
+ "text": "StoryObj<Required<MutationComparisonProps>>"
1316
+ },
1317
+ "default": "{ ...Default, play: async ({ canvasElement, step }) => { const canvas = await withinShadowRoot(canvasElement, 'gs-mutation-comparison'); await step('Switch to Venn diagram view', async () => { await waitFor(() => expect(canvas.getByRole('button', { name: 'Venn' })).toBeInTheDocument()); await fireEvent.click(canvas.getByRole('button', { name: 'Venn' })); await waitFor(() => expect( canvas.getByText('You have no elements selected. Click in the venn diagram to select.'), ).toBeVisible(), ); }); }, }"
1379
1318
  }
1380
1319
  ],
1381
1320
  "exports": [
@@ -1384,206 +1323,215 @@
1384
1323
  "name": "default",
1385
1324
  "declaration": {
1386
1325
  "name": "meta",
1387
- "module": "src/web-components/input/date-range-selector-component.stories.ts"
1326
+ "module": "src/web-components/visualization/gs-mutation-comparison.stories.ts"
1388
1327
  }
1389
1328
  },
1390
1329
  {
1391
1330
  "kind": "js",
1392
- "name": "DateRangeSelectorStory",
1331
+ "name": "Default",
1393
1332
  "declaration": {
1394
- "name": "DateRangeSelectorStory",
1395
- "module": "src/web-components/input/date-range-selector-component.stories.ts"
1333
+ "name": "Default",
1334
+ "module": "src/web-components/visualization/gs-mutation-comparison.stories.ts"
1335
+ }
1336
+ },
1337
+ {
1338
+ "kind": "js",
1339
+ "name": "VennDiagram",
1340
+ "declaration": {
1341
+ "name": "VennDiagram",
1342
+ "module": "src/web-components/visualization/gs-mutation-comparison.stories.ts"
1396
1343
  }
1397
1344
  }
1398
1345
  ]
1399
1346
  },
1400
1347
  {
1401
1348
  "kind": "javascript-module",
1402
- "path": "src/web-components/input/date-range-selector-component.tsx",
1349
+ "path": "src/web-components/visualization/gs-mutation-comparison.tsx",
1403
1350
  "declarations": [
1404
1351
  {
1405
1352
  "kind": "class",
1406
- "description": "## Context\nThis component is a group of input fields designed to specify a date range. It consists of 3 fields:\n\n- a select field to choose a predefined date range,\n- an input field with an attached date picker for the start date,\n- an input field with an attached date picker for the end date.\n\nSetting a value in the select field will overwrite the previous values of the start and end date.\nSetting a value in either of the date pickers will set the select field to \"custom\",\nwhich represents an arbitrary date range.",
1407
- "name": "DateRangeSelectorComponent",
1353
+ "description": "## Context\n\nThis component allows to compare mutations between different variants.\nA variant is defined by its LAPIS filter.\n\nIt only shows substitutions and deletions, it does not show insertions.\n\n## Views\n\n### Table View\n\nThe table view shows mutations\nand the proportions with which the mutation occurs in the respective variant.\nIt only shows mutations that are present in at least one of the variants\nand where the proportion is within the selected proportion interval for at least one variant.\n\n### Venn View\n\nThe Venn view shows the overlap of mutations between the variants in a Venn diagram.\nA variant is considered to have a certain mutation,\nif the proportion of the mutation in the variant is within the selected proportion interval.\nThus, changing the proportion interval may change a mutations from being \"common\" between variant\nto being \"for one variant only\".",
1354
+ "name": "MutationComparisonComponent",
1408
1355
  "members": [
1409
1356
  {
1410
1357
  "kind": "field",
1411
- "name": "customSelectOptions",
1358
+ "name": "variants",
1412
1359
  "type": {
1413
- "text": "{ label: string; dateFrom: string; dateTo: string }[]"
1360
+ "text": "{\n lapisFilter: Record<string, string | number | null | boolean>;\n displayName: string;\n }[]"
1414
1361
  },
1415
1362
  "default": "[]",
1416
- "description": "An array of custom options that the select field should provide,\nin addition to the predefined options.\nThe `label` will be shown to the user, and it will be available as `initialValue`.\nThe dates must be in the format `YYYY-MM-DD`.",
1417
- "attribute": "customSelectOptions"
1363
+ "description": "Required.\n\nAn array of variants to compare.\n\nThe `lapisFilter` will be sent as is to LAPIS to filter the mutation data.\nIt must be a valid LAPIS filter object.\n\nThe `displayName` will be used as the label for the variant in the views.\nIt should be human-readable.",
1364
+ "attribute": "variants"
1418
1365
  },
1419
1366
  {
1420
1367
  "kind": "field",
1421
- "name": "earliestDate",
1368
+ "name": "sequenceType",
1422
1369
  "type": {
1423
- "text": "string | undefined"
1370
+ "text": "'nucleotide' | 'amino acid'"
1424
1371
  },
1425
- "default": "'1900-01-01'",
1426
- "description": "The `dateFrom` value to use in the `allTimes` preset in the format `YYYY-MM-DD`.",
1427
- "attribute": "earliestDate"
1372
+ "default": "'nucleotide'",
1373
+ "description": "The type of the sequence for which the mutations should be shown.",
1374
+ "attribute": "sequenceType"
1428
1375
  },
1429
1376
  {
1430
1377
  "kind": "field",
1431
- "name": "initialValue",
1378
+ "name": "views",
1432
1379
  "type": {
1433
- "text": "'custom'\n | 'allTimes'\n | 'last2Weeks'\n | 'lastMonth'\n | 'last2Months'\n | 'last3Months'\n | 'last6Months'\n | string\n | undefined"
1380
+ "text": "('table' | 'venn')[]"
1434
1381
  },
1435
- "default": "'last6Months'",
1436
- "description": "The initial value to use for this date range selector.\nMust be a valid label from the preset labels or a `label` given in the `customSelectOptions`.\n\nIf the value is invalid, the component will default to `'last6Months'`.",
1437
- "attribute": "initialValue"
1438
- }
1439
- ],
1440
- "events": [
1382
+ "default": "['table']",
1383
+ "description": "A list of tabs with views that this component should provide.",
1384
+ "attribute": "views"
1385
+ },
1441
1386
  {
1387
+ "kind": "field",
1388
+ "name": "width",
1442
1389
  "type": {
1443
- "text": "CustomEvent<{ dateFrom: string; dateTo: string; }>"
1390
+ "text": "string"
1444
1391
  },
1445
- "description": "Fired when: - The select field is changed, - A date is selected in either of the date pickers, - A date was typed into either of the date input fields, and the input field loses focus (\"on blur\"). Contains the dates in the format `YYYY-MM-DD`.",
1446
- "name": "gs-date-range-changed"
1392
+ "default": "'100%'",
1393
+ "description": "The width of the component.\n\nVisit https://genspectrum.github.io/dashboards/?path=/docs/components-size-of-components--docs for more information.",
1394
+ "attribute": "width"
1395
+ },
1396
+ {
1397
+ "kind": "field",
1398
+ "name": "height",
1399
+ "type": {
1400
+ "text": "string"
1401
+ },
1402
+ "default": "'700px'",
1403
+ "description": "The height of the component.\n\nVisit https://genspectrum.github.io/dashboards/?path=/docs/components-size-of-components--docs for more information.",
1404
+ "attribute": "height"
1405
+ },
1406
+ {
1407
+ "kind": "field",
1408
+ "name": "headline",
1409
+ "type": {
1410
+ "text": "string"
1411
+ },
1412
+ "default": "'Mutation comparison'",
1413
+ "description": "The headline of the component. Set to an empty string to hide the headline.",
1414
+ "attribute": "headline"
1447
1415
  }
1448
1416
  ],
1449
1417
  "attributes": [
1450
1418
  {
1451
- "name": "customSelectOptions",
1419
+ "name": "variants",
1452
1420
  "type": {
1453
- "text": "{ label: string; dateFrom: string; dateTo: string }[]"
1421
+ "text": "{\n lapisFilter: Record<string, string | number | null | boolean>;\n displayName: string;\n }[]"
1454
1422
  },
1455
1423
  "default": "[]",
1456
- "description": "An array of custom options that the select field should provide,\nin addition to the predefined options.\nThe `label` will be shown to the user, and it will be available as `initialValue`.\nThe dates must be in the format `YYYY-MM-DD`.",
1457
- "fieldName": "customSelectOptions"
1424
+ "description": "Required.\n\nAn array of variants to compare.\n\nThe `lapisFilter` will be sent as is to LAPIS to filter the mutation data.\nIt must be a valid LAPIS filter object.\n\nThe `displayName` will be used as the label for the variant in the views.\nIt should be human-readable.",
1425
+ "fieldName": "variants"
1458
1426
  },
1459
1427
  {
1460
- "name": "earliestDate",
1428
+ "name": "sequenceType",
1461
1429
  "type": {
1462
- "text": "string | undefined"
1430
+ "text": "'nucleotide' | 'amino acid'"
1463
1431
  },
1464
- "default": "'1900-01-01'",
1465
- "description": "The `dateFrom` value to use in the `allTimes` preset in the format `YYYY-MM-DD`.",
1466
- "fieldName": "earliestDate"
1432
+ "default": "'nucleotide'",
1433
+ "description": "The type of the sequence for which the mutations should be shown.",
1434
+ "fieldName": "sequenceType"
1467
1435
  },
1468
1436
  {
1469
- "name": "initialValue",
1437
+ "name": "views",
1470
1438
  "type": {
1471
- "text": "'custom'\n | 'allTimes'\n | 'last2Weeks'\n | 'lastMonth'\n | 'last2Months'\n | 'last3Months'\n | 'last6Months'\n | string\n | undefined"
1439
+ "text": "('table' | 'venn')[]"
1472
1440
  },
1473
- "default": "'last6Months'",
1474
- "description": "The initial value to use for this date range selector.\nMust be a valid label from the preset labels or a `label` given in the `customSelectOptions`.\n\nIf the value is invalid, the component will default to `'last6Months'`.",
1475
- "fieldName": "initialValue"
1441
+ "default": "['table']",
1442
+ "description": "A list of tabs with views that this component should provide.",
1443
+ "fieldName": "views"
1444
+ },
1445
+ {
1446
+ "name": "width",
1447
+ "type": {
1448
+ "text": "string"
1449
+ },
1450
+ "default": "'100%'",
1451
+ "description": "The width of the component.\n\nVisit https://genspectrum.github.io/dashboards/?path=/docs/components-size-of-components--docs for more information.",
1452
+ "fieldName": "width"
1453
+ },
1454
+ {
1455
+ "name": "height",
1456
+ "type": {
1457
+ "text": "string"
1458
+ },
1459
+ "default": "'700px'",
1460
+ "description": "The height of the component.\n\nVisit https://genspectrum.github.io/dashboards/?path=/docs/components-size-of-components--docs for more information.",
1461
+ "fieldName": "height"
1462
+ },
1463
+ {
1464
+ "name": "headline",
1465
+ "type": {
1466
+ "text": "string"
1467
+ },
1468
+ "default": "'Mutation comparison'",
1469
+ "description": "The headline of the component. Set to an empty string to hide the headline.",
1470
+ "fieldName": "headline"
1476
1471
  }
1477
1472
  ],
1478
1473
  "superclass": {
1479
- "name": "PreactLitAdapter",
1480
- "module": "/src/web-components/PreactLitAdapter"
1474
+ "name": "PreactLitAdapterWithGridJsStyles",
1475
+ "module": "/src/web-components/PreactLitAdapterWithGridJsStyles"
1481
1476
  },
1482
- "tagName": "gs-date-range-selector",
1477
+ "tagName": "gs-mutation-comparison",
1483
1478
  "customElement": true
1484
1479
  }
1485
1480
  ],
1486
1481
  "exports": [
1487
1482
  {
1488
1483
  "kind": "js",
1489
- "name": "DateRangeSelectorComponent",
1484
+ "name": "MutationComparisonComponent",
1490
1485
  "declaration": {
1491
- "name": "DateRangeSelectorComponent",
1492
- "module": "src/web-components/input/date-range-selector-component.tsx"
1486
+ "name": "MutationComparisonComponent",
1487
+ "module": "src/web-components/visualization/gs-mutation-comparison.tsx"
1493
1488
  }
1494
1489
  },
1495
1490
  {
1496
1491
  "kind": "custom-element-definition",
1497
- "name": "gs-date-range-selector",
1498
- "declaration": {
1499
- "name": "DateRangeSelectorComponent",
1500
- "module": "src/web-components/input/date-range-selector-component.tsx"
1501
- }
1502
- }
1503
- ]
1504
- },
1505
- {
1506
- "kind": "javascript-module",
1507
- "path": "src/web-components/input/index.ts",
1508
- "declarations": [],
1509
- "exports": [
1510
- {
1511
- "kind": "js",
1512
- "name": "DateRangeSelectorComponent",
1513
- "declaration": {
1514
- "name": "DateRangeSelectorComponent",
1515
- "module": "./date-range-selector-component"
1516
- }
1517
- },
1518
- {
1519
- "kind": "js",
1520
- "name": "LocationFilterComponent",
1521
- "declaration": {
1522
- "name": "LocationFilterComponent",
1523
- "module": "./location-filter-component"
1524
- }
1525
- },
1526
- {
1527
- "kind": "js",
1528
- "name": "TextInputComponent",
1529
- "declaration": {
1530
- "name": "TextInputComponent",
1531
- "module": "./text-input-component"
1532
- }
1533
- },
1534
- {
1535
- "kind": "js",
1536
- "name": "MutationFilterComponent",
1492
+ "name": "gs-mutation-comparison",
1537
1493
  "declaration": {
1538
- "name": "MutationFilterComponent",
1539
- "module": "./mutation-filter-component"
1494
+ "name": "MutationComparisonComponent",
1495
+ "module": "src/web-components/visualization/gs-mutation-comparison.tsx"
1540
1496
  }
1541
1497
  }
1542
1498
  ]
1543
1499
  },
1544
1500
  {
1545
1501
  "kind": "javascript-module",
1546
- "path": "src/web-components/input/location-filter-component.stories.ts",
1502
+ "path": "src/web-components/visualization/gs-mutations.stories.ts",
1547
1503
  "declarations": [
1548
1504
  {
1549
1505
  "kind": "variable",
1550
1506
  "name": "meta",
1551
1507
  "type": {
1552
- "text": "Meta"
1553
- },
1554
- "default": "{ title: 'Input/Location filter', component: 'gs-location-filter', parameters: withComponentDocs({ actions: { handles: ['gs-location-changed'], }, componentDocs: { tag: 'gs-location-filter', opensShadowDom: true, expectsChildren: false, codeExample: `<gs-location-filter fields=\"['continent', 'country']\" value='Europe / Switzerland'></gs-location-filter>`, }, }), decorators: [withActions], tags: ['autodocs'], }"
1555
- },
1556
- {
1557
- "kind": "variable",
1558
- "name": "LocationFilter",
1559
- "type": {
1560
- "text": "StoryObj<LocationFilterProps>"
1508
+ "text": "Meta<Required<MutationsProps>>"
1561
1509
  },
1562
- "default": "{ ...Template, parameters: { fetchMock: { mocks: [ { matcher: aggregatedEndpointMatcher, response: { status: 200, body: data, }, }, ], }, }, play: async ({ canvasElement }) => { const canvas = await withinShadowRoot(canvasElement, 'gs-location-filter'); await waitFor(() => { return expect(canvas.getByRole('combobox')).toBeEnabled(); }); }, }"
1510
+ "default": "{ title: 'Visualization/Mutations', component: 'gs-mutations', argTypes: { variant: { control: 'object' }, sequenceType: { options: ['nucleotide', 'amino acid'], control: { type: 'radio' }, }, views: { options: ['table', 'grid', 'insertions'], control: { type: 'check' }, }, width: { control: 'text' }, height: { control: 'text' }, headline: { control: 'text' }, }, args: { variant: { country: 'Switzerland', pangoLineage: 'B.1.1.7', dateTo: '2022-01-01' }, sequenceType: 'nucleotide', views: ['grid', 'table', 'insertions'], width: '100%', height: '700px', headline: 'Mutations', }, parameters: withComponentDocs({ componentDocs: { opensShadowDom: true, expectsChildren: false, codeExample, }, }), tags: ['autodocs'], }"
1563
1511
  },
1564
1512
  {
1565
1513
  "kind": "variable",
1566
- "name": "DelayToShowLoadingState",
1514
+ "name": "Default",
1567
1515
  "type": {
1568
- "text": "StoryObj<LocationFilterProps>"
1516
+ "text": "StoryObj<Required<MutationsProps>>"
1569
1517
  },
1570
- "default": "{ ...Template, parameters: { fetchMock: { mocks: [ { matcher: aggregatedEndpointMatcher, response: { status: 200, body: data, }, options: { delay: 5000, }, }, ], }, }, }"
1518
+ "default": "{ ...Template, parameters: { fetchMock: { mocks: [ { matcher: { name: 'nucleotideMutations', url: NUCLEOTIDE_MUTATIONS_ENDPOINT, body: { country: 'Switzerland', pangoLineage: 'B.1.1.7', dateTo: '2022-01-01', minProportion: 0, }, }, response: { status: 200, body: nucleotideMutations, }, }, { matcher: { name: 'nucleotideInsertions', url: NUCLEOTIDE_INSERTIONS_ENDPOINT, body: { country: 'Switzerland', pangoLineage: 'B.1.1.7', dateTo: '2022-01-01' }, }, response: { status: 200, body: nucleotideInsertions, }, }, ], }, }, }"
1571
1519
  },
1572
1520
  {
1573
1521
  "kind": "variable",
1574
- "name": "FetchingLocationsFails",
1522
+ "name": "OnTableTab",
1575
1523
  "type": {
1576
- "text": "StoryObj<LocationFilterProps>"
1524
+ "text": "StoryObj<Required<MutationsProps>>"
1577
1525
  },
1578
- "default": "{ ...Template, parameters: { fetchMock: { mocks: [ { matcher: aggregatedEndpointMatcher, response: { status: 400, body: { error: 'no data' }, }, }, ], }, }, play: async ({ canvasElement }) => { const canvas = await withinShadowRoot(canvasElement, 'gs-location-filter'); await waitFor(() => expect(canvas.getByText('Bad Request: {\"error\":\"no data\"} ', { exact: false })).toBeInTheDocument(), ); }, }"
1526
+ "default": "{ ...Default, play: async ({ canvasElement }) => { const canvas = await withinShadowRoot(canvasElement, 'gs-mutations'); await waitFor(() => expect(canvas.getByRole('button', { name: 'Table' })).toBeInTheDocument()); await fireEvent.click(canvas.getByRole('button', { name: 'Table' })); }, }"
1579
1527
  },
1580
1528
  {
1581
1529
  "kind": "variable",
1582
- "name": "FiresEvent",
1530
+ "name": "OnInsertionsTab",
1583
1531
  "type": {
1584
- "text": "StoryObj<LocationFilterProps>"
1532
+ "text": "StoryObj<Required<MutationsProps>>"
1585
1533
  },
1586
- "default": "{ ...Template, parameters: { fetchMock: { mocks: [ { matcher: aggregatedEndpointMatcher, response: { status: 200, body: data, }, }, ], }, }, play: async ({ canvasElement, step }) => { const canvas = await withinShadowRoot(canvasElement, 'gs-location-filter'); const submitButton = () => canvas.getByRole('button', { name: 'Submit' }); const inputField = () => canvas.getByRole('combobox'); const listenerMock = fn(); await step('Setup event listener mock', async () => { canvasElement.addEventListener('gs-location-changed', listenerMock); }); await step('wait until data is loaded', async () => { await waitFor(() => { return expect(inputField()).toBeEnabled(); }); }); await step('Input invalid location', async () => { await userEvent.type(inputField(), 'Not / A / Location'); await userEvent.click(submitButton()); await expect(listenerMock).not.toHaveBeenCalled(); await userEvent.type(inputField(), '{backspace>18/}'); }); await step('Select Asia', async () => { await userEvent.type(inputField(), 'Asia'); await userEvent.click(submitButton()); await expect(listenerMock).toHaveBeenCalledWith( expect.objectContaining({ detail: { region: 'Asia', }, }), ); }); await step('Select Asia / Bangladesh / Rajshahi / Chapainawabgonj', async () => { await userEvent.type(inputField(), ' / Bangladesh / Rajshahi / Chapainawabgonj'); await userEvent.click(submitButton()); await expect(listenerMock).toHaveBeenCalledWith( expect.objectContaining({ detail: { region: 'Asia', country: 'Bangladesh', division: 'Rajshahi', location: 'Chapainawabgonj', }, }), ); }); }, }"
1534
+ "default": "{ ...Default, play: async ({ canvasElement }) => { const canvas = await withinShadowRoot(canvasElement, 'gs-mutations'); await waitFor(() => expect(canvas.getByRole('button', { name: 'Insertions' })).toBeInTheDocument()); await fireEvent.click(canvas.getByRole('button', { name: 'Insertions' })); }, }"
1587
1535
  }
1588
1536
  ],
1589
1537
  "exports": [
@@ -1592,164 +1540,239 @@
1592
1540
  "name": "default",
1593
1541
  "declaration": {
1594
1542
  "name": "meta",
1595
- "module": "src/web-components/input/location-filter-component.stories.ts"
1543
+ "module": "src/web-components/visualization/gs-mutations.stories.ts"
1596
1544
  }
1597
1545
  },
1598
1546
  {
1599
1547
  "kind": "js",
1600
- "name": "LocationFilter",
1548
+ "name": "Default",
1601
1549
  "declaration": {
1602
- "name": "LocationFilter",
1603
- "module": "src/web-components/input/location-filter-component.stories.ts"
1550
+ "name": "Default",
1551
+ "module": "src/web-components/visualization/gs-mutations.stories.ts"
1604
1552
  }
1605
1553
  },
1606
1554
  {
1607
1555
  "kind": "js",
1608
- "name": "DelayToShowLoadingState",
1556
+ "name": "OnTableTab",
1609
1557
  "declaration": {
1610
- "name": "DelayToShowLoadingState",
1611
- "module": "src/web-components/input/location-filter-component.stories.ts"
1558
+ "name": "OnTableTab",
1559
+ "module": "src/web-components/visualization/gs-mutations.stories.ts"
1612
1560
  }
1613
1561
  },
1614
1562
  {
1615
1563
  "kind": "js",
1616
- "name": "FetchingLocationsFails",
1564
+ "name": "OnInsertionsTab",
1617
1565
  "declaration": {
1618
- "name": "FetchingLocationsFails",
1619
- "module": "src/web-components/input/location-filter-component.stories.ts"
1620
- }
1621
- },
1622
- {
1623
- "kind": "js",
1624
- "name": "FiresEvent",
1625
- "declaration": {
1626
- "name": "FiresEvent",
1627
- "module": "src/web-components/input/location-filter-component.stories.ts"
1566
+ "name": "OnInsertionsTab",
1567
+ "module": "src/web-components/visualization/gs-mutations.stories.ts"
1628
1568
  }
1629
1569
  }
1630
1570
  ]
1631
1571
  },
1632
1572
  {
1633
1573
  "kind": "javascript-module",
1634
- "path": "src/web-components/input/location-filter-component.tsx",
1574
+ "path": "src/web-components/visualization/gs-mutations.tsx",
1635
1575
  "declarations": [
1636
1576
  {
1637
1577
  "kind": "class",
1638
- "description": "## Context\n\nThis component provides an input field to specify filters for locations.\n\nIt expects a list of fields that form a strict hierarchical order, such as continent, country, and city.\nThe component retrieves a list of all possible values for these fields from the Lapis instance.\nThis list is then utilized to display autocomplete suggestions and to validate the input.\n\nGiven `fields` are `['field1', 'field2', ..., 'fieldN']`,\nthen valid values for the location filter must be in the form `valueForField1 / valueForField2 / ... / valueForFieldK`,\nwhere `1 <= K <= N`.\nValues for the fields `i > K` are considered `undefined`.",
1639
- "name": "LocationFilterComponent",
1578
+ "description": "## Context\n\nThis component displays mutations (substitutions, deletions and insertions) for a given variant.\n\n## Views\n\n### Table View\n\nThe table view shows all substitutions and deletions for the given variant.\nIt shows the type (substitution or deletion), the total count of the mutation\nand the proportion of the mutation in the variant.\nThe proportion is relative to the total number of sequences matching\nthe specified sequence filters with non-ambiguous reads at that position.\n\nThe proportion interval filter can be used to filter the displayed mutations on client side.\n\n### Grid View\n\nThe grid view shows the proportion of each sequence symbol (nucleotide or amino acid) for each position that has a mutation.\nOnly positions with at least one mutation in the selected proportion interval are shown.\n\n### Insertions View\n\nThe insertions view shows the count of all insertions for the given variant.",
1579
+ "name": "MutationsComponent",
1640
1580
  "members": [
1641
1581
  {
1642
1582
  "kind": "field",
1643
- "name": "initialValue",
1583
+ "name": "variant",
1584
+ "type": {
1585
+ "text": "Record<string, string | number | null | boolean>"
1586
+ },
1587
+ "default": "{}",
1588
+ "description": "Required.\n\nThe `variant` will be sent as is to LAPIS to filter the mutation data.\nIt must be a valid LAPIS filter object.",
1589
+ "attribute": "variant"
1590
+ },
1591
+ {
1592
+ "kind": "field",
1593
+ "name": "sequenceType",
1594
+ "type": {
1595
+ "text": "'nucleotide' | 'amino acid'"
1596
+ },
1597
+ "default": "'nucleotide'",
1598
+ "description": "The type of the sequence for which the mutations should be shown.",
1599
+ "attribute": "sequenceType"
1600
+ },
1601
+ {
1602
+ "kind": "field",
1603
+ "name": "views",
1604
+ "type": {
1605
+ "text": "('table' | 'grid' | 'insertions')[]"
1606
+ },
1607
+ "default": "['table', 'grid']",
1608
+ "description": "A list of tabs with views that this component should provide.",
1609
+ "attribute": "views"
1610
+ },
1611
+ {
1612
+ "kind": "field",
1613
+ "name": "width",
1644
1614
  "type": {
1645
1615
  "text": "string"
1646
1616
  },
1647
- "default": "''",
1648
- "description": "The initial value to use for this location filter.\nMust be of the form `valueForField1 / valueForField2 / ... / valueForFieldN`.",
1649
- "attribute": "initialValue"
1617
+ "default": "'100%'",
1618
+ "description": "The width of the component.\n\nVisit https://genspectrum.github.io/dashboards/?path=/docs/components-size-of-components--docs for more information.",
1619
+ "attribute": "width"
1650
1620
  },
1651
1621
  {
1652
1622
  "kind": "field",
1653
- "name": "fields",
1623
+ "name": "height",
1654
1624
  "type": {
1655
- "text": "string[]"
1625
+ "text": "string"
1656
1626
  },
1657
- "default": "[]",
1658
- "description": "The fields to display in the location filter, in hierarchical order.\nThe top-level field should be the first entry in the array.\nThis component assumes that the values for each field form a strict hierarchy\n(e.g., `fields = ['continent', 'country', 'city']`).",
1659
- "attribute": "fields"
1660
- }
1661
- ],
1662
- "events": [
1627
+ "default": "'700px'",
1628
+ "description": "The height of the component.\n\nVisit https://genspectrum.github.io/dashboards/?path=/docs/components-size-of-components--docs for more information.",
1629
+ "attribute": "height"
1630
+ },
1663
1631
  {
1632
+ "kind": "field",
1633
+ "name": "headline",
1664
1634
  "type": {
1665
- "text": "CustomEvent<Record<string, string>>"
1635
+ "text": "string"
1666
1636
  },
1667
- "description": "Fired when the field is submitted with a valid location value. The `details` of this event contain an object with all `fields` as keys and the corresponding values as values, if they are not `undefined`. Example: ``` { continent: \"Asia\", country: \"China\", city: \"Beijing\" } ```",
1668
- "name": "gs-location-changed"
1637
+ "default": "'Mutations'",
1638
+ "description": "The headline of the component. Set to an empty string to hide the headline.",
1639
+ "attribute": "headline"
1669
1640
  }
1670
1641
  ],
1671
1642
  "attributes": [
1672
1643
  {
1673
- "name": "initialValue",
1644
+ "name": "variant",
1645
+ "type": {
1646
+ "text": "Record<string, string | number | null | boolean>"
1647
+ },
1648
+ "default": "{}",
1649
+ "description": "Required.\n\nThe `variant` will be sent as is to LAPIS to filter the mutation data.\nIt must be a valid LAPIS filter object.",
1650
+ "fieldName": "variant"
1651
+ },
1652
+ {
1653
+ "name": "sequenceType",
1654
+ "type": {
1655
+ "text": "'nucleotide' | 'amino acid'"
1656
+ },
1657
+ "default": "'nucleotide'",
1658
+ "description": "The type of the sequence for which the mutations should be shown.",
1659
+ "fieldName": "sequenceType"
1660
+ },
1661
+ {
1662
+ "name": "views",
1663
+ "type": {
1664
+ "text": "('table' | 'grid' | 'insertions')[]"
1665
+ },
1666
+ "default": "['table', 'grid']",
1667
+ "description": "A list of tabs with views that this component should provide.",
1668
+ "fieldName": "views"
1669
+ },
1670
+ {
1671
+ "name": "width",
1674
1672
  "type": {
1675
1673
  "text": "string"
1676
1674
  },
1677
- "default": "''",
1678
- "description": "The initial value to use for this location filter.\nMust be of the form `valueForField1 / valueForField2 / ... / valueForFieldN`.",
1679
- "fieldName": "initialValue"
1675
+ "default": "'100%'",
1676
+ "description": "The width of the component.\n\nVisit https://genspectrum.github.io/dashboards/?path=/docs/components-size-of-components--docs for more information.",
1677
+ "fieldName": "width"
1680
1678
  },
1681
1679
  {
1682
- "name": "fields",
1680
+ "name": "height",
1683
1681
  "type": {
1684
- "text": "string[]"
1682
+ "text": "string"
1685
1683
  },
1686
- "default": "[]",
1687
- "description": "The fields to display in the location filter, in hierarchical order.\nThe top-level field should be the first entry in the array.\nThis component assumes that the values for each field form a strict hierarchy\n(e.g., `fields = ['continent', 'country', 'city']`).",
1688
- "fieldName": "fields"
1684
+ "default": "'700px'",
1685
+ "description": "The height of the component.\n\nVisit https://genspectrum.github.io/dashboards/?path=/docs/components-size-of-components--docs for more information.",
1686
+ "fieldName": "height"
1687
+ },
1688
+ {
1689
+ "name": "headline",
1690
+ "type": {
1691
+ "text": "string"
1692
+ },
1693
+ "default": "'Mutations'",
1694
+ "description": "The headline of the component. Set to an empty string to hide the headline.",
1695
+ "fieldName": "headline"
1689
1696
  }
1690
1697
  ],
1691
1698
  "superclass": {
1692
- "name": "PreactLitAdapter",
1693
- "module": "/src/web-components/PreactLitAdapter"
1699
+ "name": "PreactLitAdapterWithGridJsStyles",
1700
+ "module": "/src/web-components/PreactLitAdapterWithGridJsStyles"
1694
1701
  },
1695
- "tagName": "gs-location-filter",
1702
+ "tagName": "gs-mutations",
1696
1703
  "customElement": true
1697
1704
  }
1698
1705
  ],
1699
1706
  "exports": [
1700
1707
  {
1701
1708
  "kind": "js",
1702
- "name": "LocationFilterComponent",
1709
+ "name": "MutationsComponent",
1703
1710
  "declaration": {
1704
- "name": "LocationFilterComponent",
1705
- "module": "src/web-components/input/location-filter-component.tsx"
1711
+ "name": "MutationsComponent",
1712
+ "module": "src/web-components/visualization/gs-mutations.tsx"
1706
1713
  }
1707
1714
  },
1708
1715
  {
1709
1716
  "kind": "custom-element-definition",
1710
- "name": "gs-location-filter",
1717
+ "name": "gs-mutations",
1711
1718
  "declaration": {
1712
- "name": "LocationFilterComponent",
1713
- "module": "src/web-components/input/location-filter-component.tsx"
1719
+ "name": "MutationsComponent",
1720
+ "module": "src/web-components/visualization/gs-mutations.tsx"
1714
1721
  }
1715
1722
  }
1716
1723
  ]
1717
1724
  },
1718
1725
  {
1719
1726
  "kind": "javascript-module",
1720
- "path": "src/web-components/input/mutation-filter-component.stories.ts",
1727
+ "path": "src/web-components/visualization/gs-prevalence-over-time.stories.ts",
1721
1728
  "declarations": [
1722
1729
  {
1723
1730
  "kind": "variable",
1724
1731
  "name": "meta",
1725
1732
  "type": {
1726
- "text": "Meta<MutationFilterProps>"
1733
+ "text": "Meta<Required<PrevalenceOverTimeProps>>"
1727
1734
  },
1728
- "default": "{ title: 'Input/Mutation filter', component: 'gs-mutation-filter', parameters: withComponentDocs({ actions: { handles: ['gs-mutation-filter-changed', 'gs-mutation-filter-on-blur'], }, fetchMock: {}, componentDocs: { tag: 'gs-mutation-filter', opensShadowDom: true, expectsChildren: false, codeExample, }, }), decorators: [withActions], tags: ['autodocs'], }"
1735
+ "default": "{ title: 'Visualization/Prevalence over time', component: 'gs-prevalence-over-time', argTypes: { numerator: { control: 'object' }, denominator: { control: 'object' }, granularity: { options: ['day', 'week', 'month', 'year'], control: { type: 'radio' }, }, smoothingWindow: { control: 'number' }, views: { options: ['bar', 'line', 'bubble', 'table'], control: { type: 'check' }, }, confidenceIntervalMethods: { options: ['wilson'], control: { type: 'check' }, }, width: { control: 'text' }, height: { control: 'text' }, headline: { control: 'text' }, }, parameters: withComponentDocs({ componentDocs: { opensShadowDom: true, expectsChildren: false, codeExample, }, }), tags: ['autodocs'], }"
1729
1736
  },
1730
1737
  {
1731
1738
  "kind": "variable",
1732
- "name": "Default",
1739
+ "name": "TwoVariants",
1733
1740
  "type": {
1734
- "text": "StoryObj<MutationFilterProps>"
1741
+ "text": "StoryObj<Required<PrevalenceOverTimeProps>>"
1735
1742
  },
1736
- "default": "{ ...Template, args: { initialValue: ['A123T'], }, }"
1743
+ "default": "{ ...Template, args: { numerator: [ { displayName: 'EG', lapisFilter: { country: 'USA', pangoLineage: 'EG*', dateFrom: '2023-01-01' } }, { displayName: 'JN.1', lapisFilter: { country: 'USA', pangoLineage: 'JN.1*', dateFrom: '2023-01-01' } }, ], denominator: { country: 'USA', dateFrom: '2023-01-01' }, granularity: 'month', smoothingWindow: 0, views: ['bar', 'line', 'bubble', 'table'], confidenceIntervalMethods: ['wilson'], width: '100%', height: '700px', headline: 'Prevalence over time', }, parameters: { fetchMock: { mocks: [ { matcher: { name: 'numeratorEG', url: AGGREGATED_ENDPOINT, body: { country: 'USA', pangoLineage: 'EG*', dateFrom: '2023-01-01', fields: ['date'], }, }, response: { status: 200, body: numeratorEG, }, }, { matcher: { name: 'numeratorJN1', url: AGGREGATED_ENDPOINT, body: { country: 'USA', pangoLineage: 'JN.1*', dateFrom: '2023-01-01', fields: ['date'], }, }, response: { status: 200, body: numeratorJN1, }, }, { matcher: { name: 'denominator', url: AGGREGATED_ENDPOINT, body: { country: 'USA', dateFrom: '2023-01-01', fields: ['date'], }, }, response: { status: 200, body: denominator, }, }, ], }, }, }"
1737
1744
  },
1738
1745
  {
1739
1746
  "kind": "variable",
1740
- "name": "FiresFilterChangedEvent",
1747
+ "name": "OneVariant",
1741
1748
  "type": {
1742
- "text": "StoryObj<MutationFilterProps>"
1749
+ "text": "StoryObj<Required<PrevalenceOverTimeProps>>"
1743
1750
  },
1744
- "default": "{ ...Template, play: async ({ canvasElement, step }) => { const canvas = await withinShadowRoot(canvasElement, 'gs-mutation-filter'); const inputField = () => canvas.getByPlaceholderText('Enter a mutation', { exact: false }); const submitButton = () => canvas.getByRole('button', { name: '+' }); const listenerMock = fn(); await step('Setup event listener mock', async () => { canvasElement.addEventListener('gs-mutation-filter-changed', listenerMock); }); await step('wait until data is loaded', async () => { await waitFor(() => { return expect(inputField()).toBeEnabled(); }); }); await step('Enter a valid mutation', async () => { await userEvent.type(inputField(), 'A123T'); await waitFor(() => submitButton().click()); await waitFor(() => expect(listenerMock).toHaveBeenCalledWith( expect.objectContaining({ detail: { nucleotideMutations: ['A123T'], aminoAcidMutations: [], nucleotideInsertions: [], aminoAcidInsertions: [], }, }), ), ); }); }, }"
1751
+ "default": "{ ...Template, args: { numerator: { displayName: 'EG', lapisFilter: { country: 'USA', pangoLineage: 'BA.2.86*', dateFrom: '2023-10-01' }, }, denominator: { country: 'USA', dateFrom: '2023-10-01' }, granularity: 'day', smoothingWindow: 7, views: ['bar', 'line', 'bubble', 'table'], confidenceIntervalMethods: ['wilson'], width: '100%', height: '700px', headline: 'Prevalence over time', }, parameters: { fetchMock: { mocks: [ { matcher: { name: 'numeratorOneVariant', url: AGGREGATED_ENDPOINT, body: { country: 'USA', pangoLineage: 'BA.2.86*', dateFrom: '2023-10-01', fields: ['date'], }, }, response: { status: 200, body: numeratorOneVariant, }, }, { matcher: { name: 'denominatorOneVariant', url: AGGREGATED_ENDPOINT, body: { country: 'USA', dateFrom: '2023-10-01', fields: ['date'], }, }, response: { status: 200, body: denominatorOneVariant, }, }, ], }, }, }"
1745
1752
  },
1746
1753
  {
1747
1754
  "kind": "variable",
1748
- "name": "FiresFilterOnBlurEvent",
1755
+ "name": "OneVariantOnLineTab",
1749
1756
  "type": {
1750
- "text": "StoryObj<MutationFilterProps>"
1757
+ "text": "StoryObj<Required<PrevalenceOverTimeProps>>"
1751
1758
  },
1752
- "default": "{ ...Template, play: async ({ canvasElement, step }) => { const canvas = await withinShadowRoot(canvasElement, 'gs-mutation-filter'); const inputField = () => canvas.getByPlaceholderText('Enter a mutation', { exact: false }); const listenerMock = fn(); await step('Setup event listener mock', async () => { canvasElement.addEventListener('gs-mutation-filter-on-blur', listenerMock); }); await step('wait until data is loaded', async () => { await waitFor(() => { return expect(inputField()).toBeEnabled(); }); }); await step('Move outside of input', async () => { await userEvent.type(inputField(), 'A123T'); await userEvent.tab(); await expect(listenerMock).toHaveBeenCalled(); }); }, }"
1759
+ "default": "{ ...OneVariant, play: async ({ canvasElement }) => { const canvas = await withinShadowRoot(canvasElement, 'gs-prevalence-over-time'); await waitFor(() => expect(canvas.getByRole('button', { name: 'Line' })).toBeInTheDocument()); await fireEvent.click(canvas.getByRole('button', { name: 'Line' })); }, }"
1760
+ },
1761
+ {
1762
+ "kind": "variable",
1763
+ "name": "OneVariantOnBubbleTab",
1764
+ "type": {
1765
+ "text": "StoryObj<Required<PrevalenceOverTimeProps>>"
1766
+ },
1767
+ "default": "{ ...OneVariant, play: async ({ canvasElement }) => { const canvas = await withinShadowRoot(canvasElement, 'gs-prevalence-over-time'); await waitFor(() => expect(canvas.getByRole('button', { name: 'Bubble' })).toBeInTheDocument()); await fireEvent.click(canvas.getByRole('button', { name: 'Bubble' })); }, }"
1768
+ },
1769
+ {
1770
+ "kind": "variable",
1771
+ "name": "OneVariantOnTableTab",
1772
+ "type": {
1773
+ "text": "StoryObj<Required<PrevalenceOverTimeProps>>"
1774
+ },
1775
+ "default": "{ ...OneVariant, play: async ({ canvasElement }) => { const canvas = await withinShadowRoot(canvasElement, 'gs-prevalence-over-time'); await waitFor(() => expect(canvas.getByRole('button', { name: 'Table' })).toBeInTheDocument()); await fireEvent.click(canvas.getByRole('button', { name: 'Table' })); }, }"
1753
1776
  }
1754
1777
  ],
1755
1778
  "exports": [
@@ -1758,136 +1781,280 @@
1758
1781
  "name": "default",
1759
1782
  "declaration": {
1760
1783
  "name": "meta",
1761
- "module": "src/web-components/input/mutation-filter-component.stories.ts"
1784
+ "module": "src/web-components/visualization/gs-prevalence-over-time.stories.ts"
1762
1785
  }
1763
1786
  },
1764
1787
  {
1765
1788
  "kind": "js",
1766
- "name": "Default",
1789
+ "name": "TwoVariants",
1767
1790
  "declaration": {
1768
- "name": "Default",
1769
- "module": "src/web-components/input/mutation-filter-component.stories.ts"
1791
+ "name": "TwoVariants",
1792
+ "module": "src/web-components/visualization/gs-prevalence-over-time.stories.ts"
1770
1793
  }
1771
1794
  },
1772
1795
  {
1773
1796
  "kind": "js",
1774
- "name": "FiresFilterChangedEvent",
1797
+ "name": "OneVariant",
1775
1798
  "declaration": {
1776
- "name": "FiresFilterChangedEvent",
1777
- "module": "src/web-components/input/mutation-filter-component.stories.ts"
1799
+ "name": "OneVariant",
1800
+ "module": "src/web-components/visualization/gs-prevalence-over-time.stories.ts"
1778
1801
  }
1779
1802
  },
1780
1803
  {
1781
1804
  "kind": "js",
1782
- "name": "FiresFilterOnBlurEvent",
1805
+ "name": "OneVariantOnLineTab",
1783
1806
  "declaration": {
1784
- "name": "FiresFilterOnBlurEvent",
1785
- "module": "src/web-components/input/mutation-filter-component.stories.ts"
1807
+ "name": "OneVariantOnLineTab",
1808
+ "module": "src/web-components/visualization/gs-prevalence-over-time.stories.ts"
1809
+ }
1810
+ },
1811
+ {
1812
+ "kind": "js",
1813
+ "name": "OneVariantOnBubbleTab",
1814
+ "declaration": {
1815
+ "name": "OneVariantOnBubbleTab",
1816
+ "module": "src/web-components/visualization/gs-prevalence-over-time.stories.ts"
1817
+ }
1818
+ },
1819
+ {
1820
+ "kind": "js",
1821
+ "name": "OneVariantOnTableTab",
1822
+ "declaration": {
1823
+ "name": "OneVariantOnTableTab",
1824
+ "module": "src/web-components/visualization/gs-prevalence-over-time.stories.ts"
1786
1825
  }
1787
1826
  }
1788
1827
  ]
1789
1828
  },
1790
1829
  {
1791
1830
  "kind": "javascript-module",
1792
- "path": "src/web-components/input/mutation-filter-component.tsx",
1831
+ "path": "src/web-components/visualization/gs-prevalence-over-time.tsx",
1793
1832
  "declarations": [
1794
1833
  {
1795
1834
  "kind": "class",
1796
- "description": "## Context\nThis component provides an input field to specify filters for nucleotide and amino acid mutations and insertions.\n\nInput values have to be provided one at a time and submitted by pressing the Enter key or by clicking the '+' button.\nAfter submission, the component validates the input and fires an event with the selected mutations.\nAll previously selected mutations are displayed at the input field and added to the event.\nUsers can remove a mutation by clicking the 'x' button next to the mutation.\n\nValidation of the input is performed according to the following rules:\n\nMutations have to conform to the following format: `<gene/segment>:<symbol at reference><position><Substituted symbol/Deletion>`\n - Gene/segment: The gene or segment where the mutation occurs. Must be contained in the reference genome\n (Optional for elements with only one gene/segment)\n - Symbol at reference: The symbol at the reference position. (Optional)\n - Position: The position of the mutation. (Required)\n - Substituted symbol/Deletion: The substituted symbol or '-' for a deletion. (Required)\n Example: S:614G, 614G, 614- or 614G\n\nInsertions have to conform to the following format: `ins_<gene/segment>:<position>:<Inserted symbols>`\n - Gene/segment: The gene or segment where the insertion occurs. Must be contained in the reference genome\n (Optional for elements with only one gene/segment)\n - Position: The position of the insertion. (Required)\n - Inserted symbols: The symbols that are inserted. (Required)\n Example: ins_S:614:G, ins_614:G",
1797
- "name": "MutationFilterComponent",
1835
+ "description": "## Context\n\nThis component displays the prevalence over time of one or more variants.\nThe prevalence is calculated as the ratio of the number of cases of each variant given as `numerator`\nto the number of cases of the variant given as `denominator`.\n\nIn the chart views,\n- the user can select whether to display a confidence interval (not available in the bubble chart).\n The confidence interval is calculated using [Wilson score interval](https://en.wikipedia.org/wiki/Binomial_proportion_confidence_interval),\n with a confidence level of 95%.\n- the x-axis shows time steps in the selected `granularity`.\n- the user can select the y-axis scale (linear, logistic, logit).\n\n## Views\n\n### Bar View\n\nDisplays the prevalence over time as a bar chart.\nShows a bar for each variant in the `numerator` on every time step.\n\n### Line View\n\nDisplays the prevalence over time as a line chart.\nEach data point is connected for better visibility.\nShows a line for each variant in the `numerator`.\n\n### Bubble View\n\nDisplays the prevalence over time as a bubble chart.\nThe size of the bubbles represents the number of cases of the `denominator` variant.\nThe height of the bubbles represents the prevalence of the `numerator` variants.\n\n### Table View\n\nDisplays the prevalence over time as a table with one row per time point.",
1836
+ "name": "PrevalenceOverTimeComponent",
1798
1837
  "members": [
1799
1838
  {
1800
- "kind": "field",
1801
- "name": "initialValue",
1839
+ "kind": "field",
1840
+ "name": "numerator",
1841
+ "type": {
1842
+ "text": "{\n lapisFilter: Record<string, string | number | null | boolean>;\n displayName: string;\n }\n | {\n lapisFilter: Record<string, string | number | null | boolean>;\n displayName: string;\n }[]"
1843
+ },
1844
+ "default": "{ displayName: '', lapisFilter: {} }",
1845
+ "description": "Required.\n\nEither a single variant or an array of variants to compare.\nThis must be a valid LAPIS filter object with an additional `displayName` property\nwhich will be used as the label for the variant in the views,\nor an array of such objects.",
1846
+ "attribute": "numerator"
1847
+ },
1848
+ {
1849
+ "kind": "field",
1850
+ "name": "denominator",
1851
+ "type": {
1852
+ "text": "Record<string, string | number | null | boolean>"
1853
+ },
1854
+ "default": "{}",
1855
+ "description": "Required.\n\nThe variant that the variants in `numerator` are compared to.",
1856
+ "attribute": "denominator"
1857
+ },
1858
+ {
1859
+ "kind": "field",
1860
+ "name": "granularity",
1861
+ "type": {
1862
+ "text": "'day' | 'week' | 'month' | 'year'"
1863
+ },
1864
+ "default": "'day'",
1865
+ "description": "The granularity of the time axis.",
1866
+ "attribute": "granularity"
1867
+ },
1868
+ {
1869
+ "kind": "field",
1870
+ "name": "smoothingWindow",
1871
+ "type": {
1872
+ "text": "number"
1873
+ },
1874
+ "default": "0",
1875
+ "description": "The number of time steps to use for smoothing the data.\n`0` means no smoothing.\nMust be a non-negative integer.\n\nFor a given time, the shown value is the mean of the neighbouring measured values.\nThe `smoothingWindow` value provides the number of neighbouring values to take into account.\nThe resulting time is computed via `Math.floor(smoothingWindow / 2)`.",
1876
+ "attribute": "smoothingWindow"
1877
+ },
1878
+ {
1879
+ "kind": "field",
1880
+ "name": "views",
1881
+ "type": {
1882
+ "text": "('bar' | 'line' | 'bubble' | 'table')[]"
1883
+ },
1884
+ "default": "['bar', 'line', 'bubble', 'table']",
1885
+ "description": "A list of tabs with views that this component should provide.",
1886
+ "attribute": "views"
1887
+ },
1888
+ {
1889
+ "kind": "field",
1890
+ "name": "confidenceIntervalMethods",
1891
+ "type": {
1892
+ "text": "('wilson' | 'none')[]"
1893
+ },
1894
+ "default": "['wilson']",
1895
+ "description": "A list of methods to calculate the confidence interval.\nThe option `none` is always available and disables confidence intervals.\nPass an empty array to disable the confidence interval selector.",
1896
+ "attribute": "confidenceIntervalMethods"
1897
+ },
1898
+ {
1899
+ "kind": "field",
1900
+ "name": "headline",
1901
+ "type": {
1902
+ "text": "string"
1903
+ },
1904
+ "default": "'Prevalence over time'",
1905
+ "description": "The headline of the component. Set to an empty string to hide the headline.",
1906
+ "attribute": "headline"
1907
+ },
1908
+ {
1909
+ "kind": "field",
1910
+ "name": "width",
1911
+ "type": {
1912
+ "text": "string"
1913
+ },
1914
+ "default": "'100%'",
1915
+ "description": "The width of the component.\n\nVisit https://genspectrum.github.io/dashboards/?path=/docs/components-size-of-components--docs for more information.",
1916
+ "attribute": "width"
1917
+ },
1918
+ {
1919
+ "kind": "field",
1920
+ "name": "height",
1921
+ "type": {
1922
+ "text": "string"
1923
+ },
1924
+ "default": "'700px'",
1925
+ "description": "The height of the component.\n\nVisit https://genspectrum.github.io/dashboards/?path=/docs/components-size-of-components--docs for more information.",
1926
+ "attribute": "height"
1927
+ }
1928
+ ],
1929
+ "attributes": [
1930
+ {
1931
+ "name": "numerator",
1932
+ "type": {
1933
+ "text": "{\n lapisFilter: Record<string, string | number | null | boolean>;\n displayName: string;\n }\n | {\n lapisFilter: Record<string, string | number | null | boolean>;\n displayName: string;\n }[]"
1934
+ },
1935
+ "default": "{ displayName: '', lapisFilter: {} }",
1936
+ "description": "Required.\n\nEither a single variant or an array of variants to compare.\nThis must be a valid LAPIS filter object with an additional `displayName` property\nwhich will be used as the label for the variant in the views,\nor an array of such objects.",
1937
+ "fieldName": "numerator"
1938
+ },
1939
+ {
1940
+ "name": "denominator",
1941
+ "type": {
1942
+ "text": "Record<string, string | number | null | boolean>"
1943
+ },
1944
+ "default": "{}",
1945
+ "description": "Required.\n\nThe variant that the variants in `numerator` are compared to.",
1946
+ "fieldName": "denominator"
1947
+ },
1948
+ {
1949
+ "name": "granularity",
1950
+ "type": {
1951
+ "text": "'day' | 'week' | 'month' | 'year'"
1952
+ },
1953
+ "default": "'day'",
1954
+ "description": "The granularity of the time axis.",
1955
+ "fieldName": "granularity"
1956
+ },
1957
+ {
1958
+ "name": "smoothingWindow",
1802
1959
  "type": {
1803
- "text": "SelectedMutationFilterStrings | string[] | undefined"
1960
+ "text": "number"
1804
1961
  },
1805
- "default": "undefined",
1806
- "description": "The initial value to use for this mutation filter.\nMust be either\n- an array of strings of valid mutations.\n- an object with the keys `nucleotideMutations`, `aminoAcidMutations`, `nucleotideInsertions` and `aminoAcidInsertions` and corresponding string arrays.",
1807
- "attribute": "initialValue"
1808
- }
1809
- ],
1810
- "events": [
1962
+ "default": "0",
1963
+ "description": "The number of time steps to use for smoothing the data.\n`0` means no smoothing.\nMust be a non-negative integer.\n\nFor a given time, the shown value is the mean of the neighbouring measured values.\nThe `smoothingWindow` value provides the number of neighbouring values to take into account.\nThe resulting time is computed via `Math.floor(smoothingWindow / 2)`.",
1964
+ "fieldName": "smoothingWindow"
1965
+ },
1811
1966
  {
1967
+ "name": "views",
1812
1968
  "type": {
1813
- "text": "CustomEvent<{nucleotideMutations: string[],aminoAcidMutations: string[],nucleotideInsertions: string[],aminoAcidInsertions: string[]}>"
1969
+ "text": "('bar' | 'line' | 'bubble' | 'table')[]"
1814
1970
  },
1815
- "description": "Fired when: - The user has submitted a valid mutation or insertion - The user has removed a mutation or insertion",
1816
- "name": "gs-mutation-filter-changed"
1971
+ "default": "['bar', 'line', 'bubble', 'table']",
1972
+ "description": "A list of tabs with views that this component should provide.",
1973
+ "fieldName": "views"
1817
1974
  },
1818
1975
  {
1976
+ "name": "confidenceIntervalMethods",
1819
1977
  "type": {
1820
- "text": "CustomEvent<{nucleotideMutations: string[],aminoAcidMutations: string[],nucleotideInsertions: string[],aminoAcidInsertions: string[]}>"
1978
+ "text": "('wilson' | 'none')[]"
1821
1979
  },
1822
- "description": "Fired when: - the mutation filter has lost focus Contains the selected mutations in the format",
1823
- "name": "gs-mutation-filter-on-blur"
1824
- }
1825
- ],
1826
- "attributes": [
1980
+ "default": "['wilson']",
1981
+ "description": "A list of methods to calculate the confidence interval.\nThe option `none` is always available and disables confidence intervals.\nPass an empty array to disable the confidence interval selector.",
1982
+ "fieldName": "confidenceIntervalMethods"
1983
+ },
1827
1984
  {
1828
- "name": "initialValue",
1985
+ "name": "headline",
1829
1986
  "type": {
1830
- "text": "SelectedMutationFilterStrings | string[] | undefined"
1987
+ "text": "string"
1831
1988
  },
1832
- "default": "undefined",
1833
- "description": "The initial value to use for this mutation filter.\nMust be either\n- an array of strings of valid mutations.\n- an object with the keys `nucleotideMutations`, `aminoAcidMutations`, `nucleotideInsertions` and `aminoAcidInsertions` and corresponding string arrays.",
1834
- "fieldName": "initialValue"
1989
+ "default": "'Prevalence over time'",
1990
+ "description": "The headline of the component. Set to an empty string to hide the headline.",
1991
+ "fieldName": "headline"
1992
+ },
1993
+ {
1994
+ "name": "width",
1995
+ "type": {
1996
+ "text": "string"
1997
+ },
1998
+ "default": "'100%'",
1999
+ "description": "The width of the component.\n\nVisit https://genspectrum.github.io/dashboards/?path=/docs/components-size-of-components--docs for more information.",
2000
+ "fieldName": "width"
2001
+ },
2002
+ {
2003
+ "name": "height",
2004
+ "type": {
2005
+ "text": "string"
2006
+ },
2007
+ "default": "'700px'",
2008
+ "description": "The height of the component.\n\nVisit https://genspectrum.github.io/dashboards/?path=/docs/components-size-of-components--docs for more information.",
2009
+ "fieldName": "height"
1835
2010
  }
1836
2011
  ],
1837
2012
  "superclass": {
1838
- "name": "PreactLitAdapter",
1839
- "module": "/src/web-components/PreactLitAdapter"
2013
+ "name": "PreactLitAdapterWithGridJsStyles",
2014
+ "module": "/src/web-components/PreactLitAdapterWithGridJsStyles"
1840
2015
  },
1841
- "tagName": "gs-mutation-filter",
2016
+ "tagName": "gs-prevalence-over-time",
1842
2017
  "customElement": true
1843
2018
  }
1844
2019
  ],
1845
2020
  "exports": [
1846
2021
  {
1847
2022
  "kind": "js",
1848
- "name": "MutationFilterComponent",
2023
+ "name": "PrevalenceOverTimeComponent",
1849
2024
  "declaration": {
1850
- "name": "MutationFilterComponent",
1851
- "module": "src/web-components/input/mutation-filter-component.tsx"
2025
+ "name": "PrevalenceOverTimeComponent",
2026
+ "module": "src/web-components/visualization/gs-prevalence-over-time.tsx"
1852
2027
  }
1853
2028
  },
1854
2029
  {
1855
2030
  "kind": "custom-element-definition",
1856
- "name": "gs-mutation-filter",
2031
+ "name": "gs-prevalence-over-time",
1857
2032
  "declaration": {
1858
- "name": "MutationFilterComponent",
1859
- "module": "src/web-components/input/mutation-filter-component.tsx"
2033
+ "name": "PrevalenceOverTimeComponent",
2034
+ "module": "src/web-components/visualization/gs-prevalence-over-time.tsx"
1860
2035
  }
1861
2036
  }
1862
2037
  ]
1863
2038
  },
1864
2039
  {
1865
2040
  "kind": "javascript-module",
1866
- "path": "src/web-components/input/text-input-component.stories.ts",
2041
+ "path": "src/web-components/visualization/gs-relative-growth-advantage.stories.ts",
1867
2042
  "declarations": [
1868
2043
  {
1869
2044
  "kind": "variable",
1870
2045
  "name": "meta",
1871
2046
  "type": {
1872
- "text": "Meta<TextInputProps>"
2047
+ "text": "Meta<RelativeGrowthAdvantageProps>"
1873
2048
  },
1874
- "default": "{ title: 'Input/Text input', component: 'gs-text-input', parameters: withComponentDocs({ actions: { handles: ['gs-text-input-changed'], }, fetchMock: { mocks: [ { matcher: { name: 'hosts', url: AGGREGATED_ENDPOINT, body: { fields: ['host'], }, }, response: { status: 200, body: data, }, }, ], }, componentDocs: { tag: 'gs-text-input', opensShadowDom: true, expectsChildren: false, codeExample, }, }), decorators: [withActions], tags: ['autodocs'], }"
2049
+ "default": "{ title: 'Visualization/Relative growth advantage', component: 'gs-relative-growth-advantage', argTypes: { numerator: { control: 'object' }, denominator: { control: 'object' }, generationTime: { control: 'number' }, views: { options: ['line'], control: { type: 'check' }, }, width: { control: 'text' }, height: { control: 'text' }, headline: { control: 'text' }, }, parameters: withComponentDocs({ componentDocs: { opensShadowDom: true, expectsChildren: false, codeExample, }, }), tags: ['autodocs'], }"
1875
2050
  },
1876
2051
  {
1877
2052
  "kind": "variable",
1878
2053
  "name": "Default",
1879
2054
  "type": {
1880
- "text": "StoryObj<TextInputProps>"
1881
- },
1882
- "default": "{ render: (args) => { return html` <gs-app lapis=\"${LAPIS_URL}\"> <div class=\"max-w-screen-lg\"> <gs-text-input .lapisField=${args.lapisField} .placeholderText=${args.placeholderText} .initialValue=${args.initialValue} ></gs-text-input> </div> </gs-app>`; }, args: { lapisField: 'host', placeholderText: 'Enter host name', initialValue: 'Homo sapiens', }, }"
1883
- },
1884
- {
1885
- "kind": "variable",
1886
- "name": "FiresEvent",
1887
- "type": {
1888
- "text": "StoryObj<TextInputProps>"
2055
+ "text": "StoryObj<Required<RelativeGrowthAdvantageProps>>"
1889
2056
  },
1890
- "default": "{ ...Default, play: async ({ canvasElement, step }) => { const canvas = await withinShadowRoot(canvasElement, 'gs-text-input'); const inputField = () => canvas.getByPlaceholderText('Enter host name'); const listenerMock = fn(); await step('Setup event listener mock', async () => { canvasElement.addEventListener('gs-text-input-changed', listenerMock); }); await step('wait until data is loaded', async () => { await waitFor(() => { return expect(inputField()).toBeEnabled(); }); }); await step('Enters an invalid host name', async () => { await userEvent.type(inputField(), 'notInList'); await expect(listenerMock).not.toHaveBeenCalled(); await userEvent.type(inputField(), '{backspace>9/}'); }); await step('Enter a valid host name', async () => { await userEvent.type(inputField(), 'Homo'); await expect(listenerMock).toHaveBeenCalledWith( expect.objectContaining({ detail: { host: 'Homo', }, }), ); }); }, args: { ...Default.args, initialValue: '', }, }"
2057
+ "default": "{ ...Template, args: { numerator: { country: 'Switzerland', pangoLineage: 'B.1.1.7', dateFrom: '2020-12-01', dateTo: '2021-03-01' }, denominator: { country: 'Switzerland', dateFrom: '2020-12-01', dateTo: '2021-03-01' }, generationTime: 7, views: ['line'], width: '100%', height: '700px', headline: 'Relative growth advantage', }, parameters: { fetchMock: { mocks: [ { matcher: { name: 'numerator', url: AGGREGATED_ENDPOINT, body: { country: 'Switzerland', pangoLineage: 'B.1.1.7', dateFrom: '2020-12-01', dateTo: '2021-03-01', fields: ['date'], }, }, response: { status: 200, body: numerator, }, }, { matcher: { name: 'denominator', url: AGGREGATED_ENDPOINT, body: { country: 'Switzerland', dateFrom: '2020-12-01', dateTo: '2021-03-01', fields: ['date'], }, }, response: { status: 200, body: denominator, }, }, ], }, }, }"
1891
2058
  }
1892
2059
  ],
1893
2060
  "exports": [
@@ -1896,7 +2063,7 @@
1896
2063
  "name": "default",
1897
2064
  "declaration": {
1898
2065
  "name": "meta",
1899
- "module": "src/web-components/input/text-input-component.stories.ts"
2066
+ "module": "src/web-components/visualization/gs-relative-growth-advantage.stories.ts"
1900
2067
  }
1901
2068
  },
1902
2069
  {
@@ -1904,160 +2071,226 @@
1904
2071
  "name": "Default",
1905
2072
  "declaration": {
1906
2073
  "name": "Default",
1907
- "module": "src/web-components/input/text-input-component.stories.ts"
1908
- }
1909
- },
1910
- {
1911
- "kind": "js",
1912
- "name": "FiresEvent",
1913
- "declaration": {
1914
- "name": "FiresEvent",
1915
- "module": "src/web-components/input/text-input-component.stories.ts"
2074
+ "module": "src/web-components/visualization/gs-relative-growth-advantage.stories.ts"
1916
2075
  }
1917
2076
  }
1918
2077
  ]
1919
2078
  },
1920
2079
  {
1921
2080
  "kind": "javascript-module",
1922
- "path": "src/web-components/input/text-input-component.tsx",
2081
+ "path": "src/web-components/visualization/gs-relative-growth-advantage.tsx",
1923
2082
  "declarations": [
1924
2083
  {
1925
2084
  "kind": "class",
1926
- "description": "\n## Context\n\nThis component provides a text input field to specify filters for arbitrary fields of this Lapis instance.",
1927
- "name": "TextInputComponent",
2085
+ "description": "## Context\n\nFor this component, we assume a discrete time model, where new infections happen exactly every `generationTime` days.\nThis is what we call a \"generation\".\n\nThis component estimates the relative growth advantage of a variant by performing a logistic regression.\nBased on the inferred logistic growth rate, we derive the relative growth advantage (per generation).\n\nFor details on the scientific method, see:\nChen, Chaoran, et al. \"Quantification of the spread of SARS-CoV-2 variant B.1.1.7 in Switzerland.\" Epidemics (2021);\ndoi: [10.1016/j.epidem.2021.100480](https://doi.org/10.1016/j.epidem.2021.100480)\n\nThis component fetches aggregated data from LAPIS.\nThen the data is sent to `https://cov-spectrum.org/api/v2/computed/model/chen2021Fitness`\nwhich performs the logistic regression and calculates the relative growth advantage.\n\n## Views\n\n### Line View\n\nThe line view shows the relative growth advantage over time in a line chart.\nThe dots in the plot show the proportions of the focal variant (`numerator`) to the `denominator` variant\nfor every day as observed in the data.\nThe line shows a logistic curve fitted to the data points, including a 95% confidence interval.",
2086
+ "name": "RelativeGrowthAdvantageComponent",
1928
2087
  "members": [
1929
2088
  {
1930
2089
  "kind": "field",
1931
- "name": "initialValue",
2090
+ "name": "numerator",
1932
2091
  "type": {
1933
- "text": "string | undefined"
2092
+ "text": "Record<string, string | number | null | boolean>"
1934
2093
  },
1935
- "default": "''",
1936
- "description": "The initial value to use for this text input.",
1937
- "attribute": "initialValue"
2094
+ "default": "{}",
2095
+ "description": "Required.\n\nThe LAPIS filter for the focal variant.",
2096
+ "attribute": "numerator"
1938
2097
  },
1939
2098
  {
1940
2099
  "kind": "field",
1941
- "name": "lapisField",
2100
+ "name": "denominator",
2101
+ "type": {
2102
+ "text": "Record<string, string | number | null | boolean>"
2103
+ },
2104
+ "default": "{}",
2105
+ "description": "Required.\n\nThe LAPIS filter for the variant that the focal variant (`numerator`) should be compared to.",
2106
+ "attribute": "denominator"
2107
+ },
2108
+ {
2109
+ "kind": "field",
2110
+ "name": "generationTime",
2111
+ "type": {
2112
+ "text": "number"
2113
+ },
2114
+ "default": "7",
2115
+ "description": "The generation time represents the number of days over which the variant's relative growth advantage is measured.\nFor example, if we set a generation time of 7 days, then we estimate the growth advantage per week.",
2116
+ "attribute": "generationTime"
2117
+ },
2118
+ {
2119
+ "kind": "field",
2120
+ "name": "views",
2121
+ "type": {
2122
+ "text": "'line'[]"
2123
+ },
2124
+ "default": "['line']",
2125
+ "description": "A list of tabs with views that this component should provide.",
2126
+ "attribute": "views"
2127
+ },
2128
+ {
2129
+ "kind": "field",
2130
+ "name": "headline",
1942
2131
  "type": {
1943
2132
  "text": "string"
1944
2133
  },
1945
- "default": "''",
1946
- "description": "The Lapis field name to use for this text input.",
1947
- "attribute": "lapisField"
2134
+ "default": "'Relative growth advantage'",
2135
+ "description": "The headline of the component. Set to an empty string to hide the headline.",
2136
+ "attribute": "headline"
1948
2137
  },
1949
2138
  {
1950
2139
  "kind": "field",
1951
- "name": "placeholderText",
2140
+ "name": "width",
1952
2141
  "type": {
1953
- "text": "string | undefined"
2142
+ "text": "string"
1954
2143
  },
1955
- "default": "''",
1956
- "description": "The placeholder text to display in the input field.",
1957
- "attribute": "placeholderText"
1958
- }
1959
- ],
1960
- "events": [
2144
+ "default": "'100%'",
2145
+ "description": "The width of the component.\n\nVisit https://genspectrum.github.io/dashboards/?path=/docs/components-size-of-components--docs for more information.",
2146
+ "attribute": "width"
2147
+ },
1961
2148
  {
2149
+ "kind": "field",
2150
+ "name": "height",
1962
2151
  "type": {
1963
- "text": "CustomEvent<Record<string, string>>"
2152
+ "text": "string"
1964
2153
  },
1965
- "description": "Fired when the input field is changed. The `details` of this event contain an object with the `lapisField` as key and the input value as value. Example: ``` { \"host\": \"Homo sapiens\" } ```",
1966
- "name": "gs-text-input-changed"
2154
+ "default": "'700px'",
2155
+ "description": "The height of the component.\n\nVisit https://genspectrum.github.io/dashboards/?path=/docs/components-size-of-components--docs for more information.",
2156
+ "attribute": "height"
1967
2157
  }
1968
2158
  ],
1969
2159
  "attributes": [
1970
2160
  {
1971
- "name": "initialValue",
2161
+ "name": "numerator",
1972
2162
  "type": {
1973
- "text": "string | undefined"
2163
+ "text": "Record<string, string | number | null | boolean>"
1974
2164
  },
1975
- "default": "''",
1976
- "description": "The initial value to use for this text input.",
1977
- "fieldName": "initialValue"
2165
+ "default": "{}",
2166
+ "description": "Required.\n\nThe LAPIS filter for the focal variant.",
2167
+ "fieldName": "numerator"
1978
2168
  },
1979
2169
  {
1980
- "name": "lapisField",
2170
+ "name": "denominator",
2171
+ "type": {
2172
+ "text": "Record<string, string | number | null | boolean>"
2173
+ },
2174
+ "default": "{}",
2175
+ "description": "Required.\n\nThe LAPIS filter for the variant that the focal variant (`numerator`) should be compared to.",
2176
+ "fieldName": "denominator"
2177
+ },
2178
+ {
2179
+ "name": "generationTime",
2180
+ "type": {
2181
+ "text": "number"
2182
+ },
2183
+ "default": "7",
2184
+ "description": "The generation time represents the number of days over which the variant's relative growth advantage is measured.\nFor example, if we set a generation time of 7 days, then we estimate the growth advantage per week.",
2185
+ "fieldName": "generationTime"
2186
+ },
2187
+ {
2188
+ "name": "views",
2189
+ "type": {
2190
+ "text": "'line'[]"
2191
+ },
2192
+ "default": "['line']",
2193
+ "description": "A list of tabs with views that this component should provide.",
2194
+ "fieldName": "views"
2195
+ },
2196
+ {
2197
+ "name": "headline",
1981
2198
  "type": {
1982
2199
  "text": "string"
1983
2200
  },
1984
- "default": "''",
1985
- "description": "The Lapis field name to use for this text input.",
1986
- "fieldName": "lapisField"
2201
+ "default": "'Relative growth advantage'",
2202
+ "description": "The headline of the component. Set to an empty string to hide the headline.",
2203
+ "fieldName": "headline"
1987
2204
  },
1988
2205
  {
1989
- "name": "placeholderText",
2206
+ "name": "width",
1990
2207
  "type": {
1991
- "text": "string | undefined"
2208
+ "text": "string"
1992
2209
  },
1993
- "default": "''",
1994
- "description": "The placeholder text to display in the input field.",
1995
- "fieldName": "placeholderText"
2210
+ "default": "'100%'",
2211
+ "description": "The width of the component.\n\nVisit https://genspectrum.github.io/dashboards/?path=/docs/components-size-of-components--docs for more information.",
2212
+ "fieldName": "width"
2213
+ },
2214
+ {
2215
+ "name": "height",
2216
+ "type": {
2217
+ "text": "string"
2218
+ },
2219
+ "default": "'700px'",
2220
+ "description": "The height of the component.\n\nVisit https://genspectrum.github.io/dashboards/?path=/docs/components-size-of-components--docs for more information.",
2221
+ "fieldName": "height"
1996
2222
  }
1997
2223
  ],
1998
2224
  "superclass": {
1999
2225
  "name": "PreactLitAdapter",
2000
2226
  "module": "/src/web-components/PreactLitAdapter"
2001
2227
  },
2002
- "tagName": "gs-text-input",
2228
+ "tagName": "gs-relative-growth-advantage",
2003
2229
  "customElement": true
2004
2230
  }
2005
2231
  ],
2006
2232
  "exports": [
2007
2233
  {
2008
2234
  "kind": "js",
2009
- "name": "TextInputComponent",
2235
+ "name": "RelativeGrowthAdvantageComponent",
2010
2236
  "declaration": {
2011
- "name": "TextInputComponent",
2012
- "module": "src/web-components/input/text-input-component.tsx"
2237
+ "name": "RelativeGrowthAdvantageComponent",
2238
+ "module": "src/web-components/visualization/gs-relative-growth-advantage.tsx"
2013
2239
  }
2014
2240
  },
2015
2241
  {
2016
2242
  "kind": "custom-element-definition",
2017
- "name": "gs-text-input",
2243
+ "name": "gs-relative-growth-advantage",
2018
2244
  "declaration": {
2019
- "name": "TextInputComponent",
2020
- "module": "src/web-components/input/text-input-component.tsx"
2245
+ "name": "RelativeGrowthAdvantageComponent",
2246
+ "module": "src/web-components/visualization/gs-relative-growth-advantage.tsx"
2021
2247
  }
2022
2248
  }
2023
2249
  ]
2024
2250
  },
2025
2251
  {
2026
2252
  "kind": "javascript-module",
2027
- "path": "src/web-components/lapis-context.ts",
2028
- "declarations": [
2029
- {
2030
- "kind": "variable",
2031
- "name": "lapisContext"
2032
- }
2033
- ],
2253
+ "path": "src/web-components/visualization/index.ts",
2254
+ "declarations": [],
2034
2255
  "exports": [
2035
2256
  {
2036
2257
  "kind": "js",
2037
- "name": "lapisContext",
2258
+ "name": "MutationComparisonComponent",
2038
2259
  "declaration": {
2039
- "name": "lapisContext",
2040
- "module": "src/web-components/lapis-context.ts"
2260
+ "name": "MutationComparisonComponent",
2261
+ "module": "./gs-mutation-comparison"
2041
2262
  }
2042
- }
2043
- ]
2044
- },
2045
- {
2046
- "kind": "javascript-module",
2047
- "path": "src/web-components/reference-genome-context.ts",
2048
- "declarations": [
2263
+ },
2049
2264
  {
2050
- "kind": "variable",
2051
- "name": "referenceGenomeContext"
2052
- }
2053
- ],
2054
- "exports": [
2265
+ "kind": "js",
2266
+ "name": "MutationsComponent",
2267
+ "declaration": {
2268
+ "name": "MutationsComponent",
2269
+ "module": "./gs-mutations"
2270
+ }
2271
+ },
2055
2272
  {
2056
2273
  "kind": "js",
2057
- "name": "referenceGenomeContext",
2274
+ "name": "PrevalenceOverTimeComponent",
2058
2275
  "declaration": {
2059
- "name": "referenceGenomeContext",
2060
- "module": "src/web-components/reference-genome-context.ts"
2276
+ "name": "PrevalenceOverTimeComponent",
2277
+ "module": "./gs-prevalence-over-time"
2278
+ }
2279
+ },
2280
+ {
2281
+ "kind": "js",
2282
+ "name": "RelativeGrowthAdvantageComponent",
2283
+ "declaration": {
2284
+ "name": "RelativeGrowthAdvantageComponent",
2285
+ "module": "./gs-relative-growth-advantage"
2286
+ }
2287
+ },
2288
+ {
2289
+ "kind": "js",
2290
+ "name": "AggregateComponent",
2291
+ "declaration": {
2292
+ "name": "AggregateComponent",
2293
+ "module": "./gs-aggregate"
2061
2294
  }
2062
2295
  }
2063
2296
  ]