@genspectrum/dashboard-components 0.14.1 → 0.15.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 (74) hide show
  1. package/custom-elements.json +90 -58
  2. package/dist/{LineageFilterChangedEvent-C9dXOxt6.js → LineageFilterChangedEvent-COWV-Y0k.js} +6 -6
  3. package/dist/LineageFilterChangedEvent-COWV-Y0k.js.map +1 -0
  4. package/dist/assets/mutationOverTimeWorker-BL50C-yi.js.map +1 -0
  5. package/dist/components.d.ts +52 -56
  6. package/dist/components.js +79 -58
  7. package/dist/components.js.map +1 -1
  8. package/dist/util.d.ts +49 -49
  9. package/dist/util.js +2 -2
  10. package/package.json +2 -2
  11. package/src/lapisApi/lapisApi.ts +1 -1
  12. package/src/operator/FillMissingOperator.spec.ts +1 -1
  13. package/src/operator/GroupByAndSumOperator.spec.ts +1 -1
  14. package/src/operator/GroupByOperator.spec.ts +2 -2
  15. package/src/operator/MapOperator.spec.ts +1 -1
  16. package/src/operator/MockOperator.spec.ts +1 -1
  17. package/src/operator/MockOperator.ts +6 -4
  18. package/src/operator/SortOperator.spec.ts +1 -1
  19. package/src/preact/aggregatedData/aggregate.stories.tsx +1 -1
  20. package/src/preact/components/csv-download-button.stories.tsx +2 -2
  21. package/src/preact/components/csv-download-button.tsx +1 -1
  22. package/src/preact/components/error-boundary.stories.tsx +5 -5
  23. package/src/preact/components/error-boundary.tsx +14 -3
  24. package/src/preact/components/error-display.stories.tsx +9 -9
  25. package/src/preact/components/fullscreen.tsx +3 -3
  26. package/src/preact/components/info.tsx +1 -1
  27. package/src/preact/components/mutation-type-selector.stories.tsx +1 -1
  28. package/src/preact/components/table.stories.tsx +3 -3
  29. package/src/preact/components/table.tsx +1 -1
  30. package/src/preact/{dateRangeSelector/date-range-selector.stories.tsx → dateRangeFilter/date-range-filter.stories.tsx} +18 -21
  31. package/src/preact/{dateRangeSelector/date-range-selector.tsx → dateRangeFilter/date-range-filter.tsx} +11 -11
  32. package/src/preact/{dateRangeSelector → dateRangeFilter}/dateRangeOption.ts +2 -2
  33. package/src/preact/lineageFilter/lineage-filter.stories.tsx +6 -6
  34. package/src/preact/locationFilter/fetchAutocompletionList.ts +1 -1
  35. package/src/preact/locationFilter/location-filter.stories.tsx +6 -6
  36. package/src/preact/map/sequences-by-location.stories.tsx +1 -1
  37. package/src/preact/mutationFilter/mutation-filter.stories.tsx +2 -2
  38. package/src/preact/mutations/getMutationsGridData.ts +1 -1
  39. package/src/preact/mutationsOverTime/mutations-over-time-grid.tsx +3 -3
  40. package/src/preact/mutationsOverTime/mutations-over-time.stories.tsx +1 -1
  41. package/src/preact/mutationsOverTime/mutations-over-time.tsx +1 -0
  42. package/src/preact/numberSequencesOverTime/number-sequences-over-time.stories.tsx +1 -1
  43. package/src/preact/prevalenceOverTime/prevalence-over-time-bubble-chart.tsx +4 -4
  44. package/src/preact/prevalenceOverTime/prevalence-over-time.stories.tsx +1 -1
  45. package/src/preact/relativeGrowthAdvantage/relative-growth-advantage.stories.tsx +1 -1
  46. package/src/preact/shared/floating-ui/hooks.ts +1 -1
  47. package/src/preact/{textInput/TextInputChangedEvent.ts → textFilter/TextFilterChangedEvent.ts} +2 -2
  48. package/src/preact/{textInput/text-input.stories.tsx → textFilter/text-filter.stories.tsx} +14 -14
  49. package/src/preact/{textInput/text-input.tsx → textFilter/text-filter.tsx} +10 -10
  50. package/src/utilEntrypoint.ts +2 -2
  51. package/src/utils/map2d.ts +1 -0
  52. package/src/web-components/gs-app.stories.ts +7 -7
  53. package/src/web-components/input/{gs-date-range-selector.stories.ts → gs-date-range-filter.stories.ts} +65 -20
  54. package/src/web-components/input/{gs-date-range-selector.tsx → gs-date-range-filter.tsx} +28 -13
  55. package/src/web-components/input/gs-lineage-filter.stories.ts +1 -1
  56. package/src/web-components/input/gs-location-filter.stories.ts +1 -1
  57. package/src/web-components/input/gs-mutation-filter.stories.ts +7 -7
  58. package/src/web-components/input/{gs-text-input.stories.ts → gs-text-filter.stories.ts} +18 -18
  59. package/src/web-components/input/{gs-text-input.tsx → gs-text-filter.tsx} +16 -16
  60. package/src/web-components/input/index.ts +2 -2
  61. package/src/web-components/visualization/gs-aggregate.tsx +2 -2
  62. package/standalone-bundle/assets/mutationOverTimeWorker-CFB5-Mdk.js.map +1 -0
  63. package/standalone-bundle/dashboard-components.js +2233 -2220
  64. package/standalone-bundle/dashboard-components.js.map +1 -1
  65. package/dist/LineageFilterChangedEvent-C9dXOxt6.js.map +0 -1
  66. package/dist/assets/mutationOverTimeWorker-Dxnxrfe0.js.map +0 -1
  67. package/standalone-bundle/assets/mutationOverTimeWorker-CmSrq4SZ.js.map +0 -1
  68. /package/src/preact/{dateRangeSelector → dateRangeFilter}/computeInitialValues.spec.ts +0 -0
  69. /package/src/preact/{dateRangeSelector → dateRangeFilter}/computeInitialValues.ts +0 -0
  70. /package/src/preact/{dateRangeSelector → dateRangeFilter}/dateConversion.ts +0 -0
  71. /package/src/preact/{dateRangeSelector → dateRangeFilter}/selectableOptions.ts +0 -0
  72. /package/src/preact/{textInput → textFilter}/__mockData__/aggregated_hosts.json +0 -0
  73. /package/src/preact/{textInput → textFilter}/fetchStringAutocompleteList.spec.ts +0 -0
  74. /package/src/preact/{textInput → textFilter}/fetchStringAutocompleteList.ts +0 -0
@@ -84,7 +84,7 @@
84
84
  "type": {
85
85
  "text": "StoryObj<{ lapis: string }>"
86
86
  },
87
- "default": "{ ...Template, play: async ({ canvasElement }) => { const canvas = within(canvasElement); await waitFor(() => { expect(canvas.getByText(LAPIS_URL)).toBeVisible(); expect(canvas.getByText('\"name\": \"ORF1a\",', { exact: false })).toBeVisible(); }); }, }"
87
+ "default": "{ ...Template, play: async ({ canvasElement }) => { const canvas = within(canvasElement); await waitFor(async () => { await expect(canvas.getByText(LAPIS_URL)).toBeVisible(); await expect(canvas.getByText('\"name\": \"ORF1a\",', { exact: false })).toBeVisible(); }); }, }"
88
88
  },
89
89
  {
90
90
  "kind": "variable",
@@ -92,7 +92,7 @@
92
92
  "type": {
93
93
  "text": "StoryObj<{ lapis: string }>"
94
94
  },
95
- "default": "{ ...Default, args: { ...Default.args, lapis: 'notAValidUrl', }, play: async ({ canvasElement }) => { const canvas = within(canvasElement); await waitFor(() => { expect(canvas.getByText(\"Error: Invalid LAPIS URL: 'notAValidUrl'\", { exact: false })).toBeVisible(); }); }, }"
95
+ "default": "{ ...Default, args: { ...Default.args, lapis: 'notAValidUrl', }, play: async ({ canvasElement }) => { const canvas = within(canvasElement); await waitFor(async () => { await expect(canvas.getByText(\"Error: Invalid LAPIS URL: 'notAValidUrl'\", { exact: false })).toBeVisible(); }); }, }"
96
96
  },
97
97
  {
98
98
  "kind": "variable",
@@ -108,7 +108,7 @@
108
108
  "type": {
109
109
  "text": "StoryObj<{ lapis: string }>"
110
110
  },
111
- "default": "{ ...Template, args: { lapis: 'https://url.to.lapis-definitely-not-a-valid-url', }, play: async ({ canvasElement }) => { const canvas = within(canvasElement); await waitFor(() => { expect(canvas.getByText('Error: Cannot fetch reference genome.', { exact: false })).toBeVisible(); }); }, }"
111
+ "default": "{ ...Template, args: { lapis: 'https://url.to.lapis-definitely-not-a-valid-url', }, play: async ({ canvasElement }) => { const canvas = within(canvasElement); await waitFor(async () => { await expect(canvas.getByText('Error: Cannot fetch reference genome.', { exact: false })).toBeVisible(); }); }, }"
112
112
  },
113
113
  {
114
114
  "kind": "class",
@@ -290,31 +290,47 @@
290
290
  },
291
291
  {
292
292
  "kind": "javascript-module",
293
- "path": "src/web-components/input/gs-date-range-selector.stories.ts",
293
+ "path": "src/web-components/input/gs-date-range-filter.stories.ts",
294
294
  "declarations": [
295
295
  {
296
296
  "kind": "variable",
297
297
  "name": "meta",
298
298
  "type": {
299
- "text": "Meta<Required<DateRangeSelectorProps>>"
299
+ "text": "Meta<Required<DateRangeFilterProps>>"
300
300
  },
301
- "default": "{ title: 'Input/DateRangeSelector', component: 'gs-date-range-selector', parameters: withComponentDocs({ actions: { handles: ['gs-date-range-filter-changed', 'gs-date-range-option-changed', ...previewHandles], }, fetchMock: {}, componentDocs: { opensShadowDom: true, expectsChildren: false, codeExample, }, }), argTypes: { value: { control: { type: 'object', }, }, lapisDateField: { control: { type: 'text' } }, dateRangeOptions: { control: { type: 'object', }, }, earliestDate: { control: { type: 'text', }, }, width: { control: { type: 'text', }, }, }, args: { dateRangeOptions: [ dateRangeOptionPresets.lastMonth, dateRangeOptionPresets.last3Months, dateRangeOptionPresets.allTimes, customDateRange, ], earliestDate: '1970-01-01', value: dateRangeOptionPresets.lastMonth.label, lapisDateField: 'aDateColumn', width: '100%', }, tags: ['autodocs'], }"
301
+ "default": "{ title: 'Input/DateRangeFilter', component: 'gs-date-range-filter', parameters: withComponentDocs({ actions: { handles: ['gs-date-range-filter-changed', 'gs-date-range-option-changed', ...previewHandles], }, fetchMock: {}, componentDocs: { opensShadowDom: true, expectsChildren: false, codeExample, }, }), argTypes: { value: { control: { type: 'object', }, }, lapisDateField: { control: { type: 'text' } }, dateRangeOptions: { control: { type: 'object', }, }, earliestDate: { control: { type: 'text', }, }, width: { control: { type: 'text', }, }, }, args: { dateRangeOptions: [ dateRangeOptionPresets.lastMonth, dateRangeOptionPresets.last3Months, dateRangeOptionPresets.allTimes, { label: '2021', dateFrom: '2021-01-01', dateTo: '2021-12-31' }, customDateRange, ], earliestDate: '1970-01-01', value: dateRangeOptionPresets.lastMonth.label, lapisDateField: 'aDateColumn', width: '100%', }, tags: ['autodocs'], }"
302
302
  },
303
303
  {
304
304
  "kind": "variable",
305
305
  "name": "Default",
306
306
  "type": {
307
- "text": "StoryObj<Required<DateRangeSelectorProps>>"
307
+ "text": "StoryObj<Required<DateRangeFilterProps>>"
308
308
  },
309
- "default": "{ render: (args) => html` <gs-app lapis=\"${LAPIS_URL}\"> <div class=\"max-w-screen-lg\"> <gs-date-range-selector .dateRangeOptions=${args.dateRangeOptions} .earliestDate=${args.earliestDate} .value=${args.value} .width=${args.width} .lapisDateField=${args.lapisDateField} ></gs-date-range-selector> </div> </gs-app>`, }"
309
+ "default": "{ render: (args) => html` <gs-app lapis=\"${LAPIS_URL}\"> <div class=\"max-w-screen-lg\"> <gs-date-range-filter .dateRangeOptions=${args.dateRangeOptions} .earliestDate=${args.earliestDate} .value=${args.value} .width=${args.width} .lapisDateField=${args.lapisDateField} ></gs-date-range-filter> </div> </gs-app>`, }"
310
+ },
311
+ {
312
+ "kind": "variable",
313
+ "name": "TestRenderAttributesInHtmlInsteadOfUsingPropertyExpression",
314
+ "type": {
315
+ "text": "StoryObj<Required<DateRangeFilterProps>>"
316
+ },
317
+ "default": "{ render: (args) => html` <gs-app lapis=\"${LAPIS_URL}\"> <div class=\"max-w-screen-lg\"> <gs-date-range-filter .dateRangeOptions=${args.dateRangeOptions} earliestDate=\"${args.earliestDate}\" value=\"${args.value}\" width=\"${args.width}\" lapisDateField=\"${args.lapisDateField}\" ></gs-date-range-filter> </div> </gs-app>`, play: async ({ canvasElement }) => { const canvas = await withinShadowRoot(canvasElement, 'gs-date-range-filter'); await waitFor(async () => { await expect(selectField(canvas)).toHaveValue('Last month'); }); }, argTypes: { value: { control: { type: 'text', }, }, }, }"
318
+ },
319
+ {
320
+ "kind": "variable",
321
+ "name": "TestSettingANumericValueIsTreatedAsString",
322
+ "type": {
323
+ "text": "StoryObj<Required<DateRangeFilterProps>>"
324
+ },
325
+ "default": "{ ...TestRenderAttributesInHtmlInsteadOfUsingPropertyExpression, args: { ...TestRenderAttributesInHtmlInsteadOfUsingPropertyExpression.args, value: '2021', }, play: async ({ canvasElement }) => { const canvas = await withinShadowRoot(canvasElement, 'gs-date-range-filter'); await waitFor(async () => { await expect(selectField(canvas)).toHaveValue('2021'); }); }, }"
310
326
  },
311
327
  {
312
328
  "kind": "variable",
313
329
  "name": "FiresEvents",
314
330
  "type": {
315
- "text": "StoryObj<Required<DateRangeSelectorProps>>"
331
+ "text": "StoryObj<Required<DateRangeFilterProps>>"
316
332
  },
317
- "default": "{ ...Default, play: async ({ canvasElement, step }) => { const canvas = await withinShadowRoot(canvasElement, 'gs-date-range-selector'); const filterChangedListenerMock = fn(); const optionChangedListenerMock = fn(); await step('Setup event listener mock', async () => { canvasElement.addEventListener('gs-date-range-filter-changed', filterChangedListenerMock); canvasElement.addEventListener('gs-date-range-option-changed', optionChangedListenerMock); }); await step('Expect last 6 months to be selected', async () => { await expect(selectField(canvas)).toHaveValue('Last month'); await waitFor(() => { expect(dateToPicker(canvas)).toHaveValue(toYYYYMMDD(new Date())); }); }); await step('Expect event to be fired when selecting a different value', async () => { await userEvent.selectOptions(selectField(canvas), 'CustomDateRange'); await expect(dateToPicker(canvas)).toHaveValue(customDateRange.dateTo); await expect(filterChangedListenerMock).toHaveBeenCalledWith( expect.objectContaining({ detail: { aDateColumnFrom: customDateRange.dateFrom, aDateColumnTo: customDateRange.dateTo, }, }), ); await expect(optionChangedListenerMock).toHaveBeenCalledWith( expect.objectContaining({ detail: customDateRange.label, }), ); }); }, }"
333
+ "default": "{ ...Default, play: async ({ canvasElement, step }) => { const canvas = await withinShadowRoot(canvasElement, 'gs-date-range-filter'); const filterChangedListenerMock = fn(); const optionChangedListenerMock = fn(); await step('Setup event listener mock', () => { canvasElement.addEventListener('gs-date-range-filter-changed', filterChangedListenerMock); canvasElement.addEventListener('gs-date-range-option-changed', optionChangedListenerMock); }); await step('Expect last 6 months to be selected', async () => { await expect(selectField(canvas)).toHaveValue('Last month'); await waitFor(async () => { await expect(dateToPicker(canvas)).toHaveValue(toYYYYMMDD(new Date())); }); }); await step('Expect event to be fired when selecting a different value', async () => { await userEvent.selectOptions(selectField(canvas), 'CustomDateRange'); await expect(dateToPicker(canvas)).toHaveValue(customDateRange.dateTo); await expect(filterChangedListenerMock).toHaveBeenCalledWith( expect.objectContaining({ detail: { aDateColumnFrom: customDateRange.dateFrom, aDateColumnTo: customDateRange.dateTo, }, }), ); await expect(optionChangedListenerMock).toHaveBeenCalledWith( expect.objectContaining({ detail: customDateRange.label, }), ); }); }, }"
318
334
  }
319
335
  ],
320
336
  "exports": [
@@ -323,7 +339,7 @@
323
339
  "name": "default",
324
340
  "declaration": {
325
341
  "name": "meta",
326
- "module": "src/web-components/input/gs-date-range-selector.stories.ts"
342
+ "module": "src/web-components/input/gs-date-range-filter.stories.ts"
327
343
  }
328
344
  },
329
345
  {
@@ -331,7 +347,23 @@
331
347
  "name": "Default",
332
348
  "declaration": {
333
349
  "name": "Default",
334
- "module": "src/web-components/input/gs-date-range-selector.stories.ts"
350
+ "module": "src/web-components/input/gs-date-range-filter.stories.ts"
351
+ }
352
+ },
353
+ {
354
+ "kind": "js",
355
+ "name": "TestRenderAttributesInHtmlInsteadOfUsingPropertyExpression",
356
+ "declaration": {
357
+ "name": "TestRenderAttributesInHtmlInsteadOfUsingPropertyExpression",
358
+ "module": "src/web-components/input/gs-date-range-filter.stories.ts"
359
+ }
360
+ },
361
+ {
362
+ "kind": "js",
363
+ "name": "TestSettingANumericValueIsTreatedAsString",
364
+ "declaration": {
365
+ "name": "TestSettingANumericValueIsTreatedAsString",
366
+ "module": "src/web-components/input/gs-date-range-filter.stories.ts"
335
367
  }
336
368
  },
337
369
  {
@@ -339,19 +371,19 @@
339
371
  "name": "FiresEvents",
340
372
  "declaration": {
341
373
  "name": "FiresEvents",
342
- "module": "src/web-components/input/gs-date-range-selector.stories.ts"
374
+ "module": "src/web-components/input/gs-date-range-filter.stories.ts"
343
375
  }
344
376
  }
345
377
  ]
346
378
  },
347
379
  {
348
380
  "kind": "javascript-module",
349
- "path": "src/web-components/input/gs-date-range-selector.tsx",
381
+ "path": "src/web-components/input/gs-date-range-filter.tsx",
350
382
  "declarations": [
351
383
  {
352
384
  "kind": "class",
353
385
  "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.",
354
- "name": "DateRangeSelectorComponent",
386
+ "name": "DateRangeFilterComponent",
355
387
  "members": [
356
388
  {
357
389
  "kind": "field",
@@ -471,25 +503,25 @@
471
503
  "name": "PreactLitAdapter",
472
504
  "module": "/src/web-components/PreactLitAdapter"
473
505
  },
474
- "tagName": "gs-date-range-selector",
506
+ "tagName": "gs-date-range-filter",
475
507
  "customElement": true
476
508
  }
477
509
  ],
478
510
  "exports": [
479
511
  {
480
512
  "kind": "js",
481
- "name": "DateRangeSelectorComponent",
513
+ "name": "DateRangeFilterComponent",
482
514
  "declaration": {
483
- "name": "DateRangeSelectorComponent",
484
- "module": "src/web-components/input/gs-date-range-selector.tsx"
515
+ "name": "DateRangeFilterComponent",
516
+ "module": "src/web-components/input/gs-date-range-filter.tsx"
485
517
  }
486
518
  },
487
519
  {
488
520
  "kind": "custom-element-definition",
489
- "name": "gs-date-range-selector",
521
+ "name": "gs-date-range-filter",
490
522
  "declaration": {
491
- "name": "DateRangeSelectorComponent",
492
- "module": "src/web-components/input/gs-date-range-selector.tsx"
523
+ "name": "DateRangeFilterComponent",
524
+ "module": "src/web-components/input/gs-date-range-filter.tsx"
493
525
  }
494
526
  }
495
527
  ]
@@ -536,7 +568,7 @@
536
568
  "type": {
537
569
  "text": "StoryObj<Required<LineageFilterProps>>"
538
570
  },
539
- "default": "{ ...LineageFilter, play: async ({ canvasElement, step }) => { const canvas = await withinShadowRoot(canvasElement, 'gs-lineage-filter'); const inputField = () => canvas.getByPlaceholderText('Enter a lineage'); const listenerMock = fn(); await step('Setup event listener mock', async () => { canvasElement.addEventListener('gs-lineage-filter-changed', listenerMock); }); await step('wait until data is loaded', async () => { await waitFor(() => { return expect(inputField()).toBeEnabled(); }); }); await step('Enters an invalid lineage value', async () => { await userEvent.type(inputField(), 'notInList'); await expect(listenerMock).not.toHaveBeenCalled(); }); await step('Empty input', async () => { await userEvent.type(inputField(), '{backspace>9/}'); await userEvent.click(canvas.getByLabelText('toggle menu')); await waitFor(() => { return expect(listenerMock.mock.calls.at(-1)![0].detail).toStrictEqual({ pangoLineage: undefined, }); }); }); await step('Enter a valid lineage value', async () => { await userEvent.type(inputField(), 'B.1.1.7*'); await userEvent.click(canvas.getByRole('option', { name: 'B.1.1.7*' })); await waitFor(() => { return expect(listenerMock.mock.calls.at(-1)![0].detail).toStrictEqual({ pangoLineage: 'B.1.1.7*', }); }); }); }, args: { ...LineageFilter.args, value: '', }, }"
571
+ "default": "{ ...LineageFilter, play: async ({ canvasElement, step }) => { const canvas = await withinShadowRoot(canvasElement, 'gs-lineage-filter'); const inputField = () => canvas.getByPlaceholderText('Enter a lineage'); const listenerMock = fn(); await step('Setup event listener mock', () => { canvasElement.addEventListener('gs-lineage-filter-changed', listenerMock); }); await step('wait until data is loaded', async () => { await waitFor(() => { return expect(inputField()).toBeEnabled(); }); }); await step('Enters an invalid lineage value', async () => { await userEvent.type(inputField(), 'notInList'); await expect(listenerMock).not.toHaveBeenCalled(); }); await step('Empty input', async () => { await userEvent.type(inputField(), '{backspace>9/}'); await userEvent.click(canvas.getByLabelText('toggle menu')); await waitFor(() => { return expect(listenerMock.mock.calls.at(-1)![0].detail).toStrictEqual({ pangoLineage: undefined, }); }); }); await step('Enter a valid lineage value', async () => { await userEvent.type(inputField(), 'B.1.1.7*'); await userEvent.click(canvas.getByRole('option', { name: 'B.1.1.7*' })); await waitFor(() => { return expect(listenerMock.mock.calls.at(-1)![0].detail).toStrictEqual({ pangoLineage: 'B.1.1.7*', }); }); }); }, args: { ...LineageFilter.args, value: '', }, }"
540
572
  }
541
573
  ],
542
574
  "exports": [
@@ -767,7 +799,7 @@
767
799
  "type": {
768
800
  "text": "StoryObj<LocationFilterProps>"
769
801
  },
770
- "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 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 expect(listenerMock).not.toHaveBeenCalled(); }); await step('Empty input', async () => { await userEvent.type(inputField(), '{backspace>18/}'); await userEvent.click(canvas.getByLabelText('toggle menu')); await waitFor(() => { return expect(listenerMock.mock.calls.at(-1)![0].detail).toStrictEqual({ region: undefined, country: undefined, division: undefined, location: undefined, }); }); }); await step('Select Asia', async () => { await userEvent.type(inputField(), 'Asia'); await userEvent.click(canvas.getByRole('option', { name: /^Asia.*Asia$/ })); await waitFor(() => { return expect(listenerMock.mock.calls.at(-1)![0].detail).toStrictEqual({ region: 'Asia', country: undefined, division: undefined, location: undefined, }); }); }); await step('Select Asia / Bangladesh / Rajshahi / Chapainawabgonj', async () => { await userEvent.type(inputField(), ' / Bangladesh / Rajshahi / Chapainawabgonj'); await userEvent.click(canvas.getByText('Asia / Bangladesh / Rajshahi / Chapainawabgonj')); await waitFor(() => { return expect(listenerMock.mock.calls.at(-1)![0].detail).toStrictEqual({ region: 'Asia', country: 'Bangladesh', division: 'Rajshahi', location: 'Chapainawabgonj', }); }); }); }, }"
802
+ "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 inputField = () => canvas.getByRole('combobox'); const listenerMock = fn(); await step('Setup event listener mock', () => { 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 expect(listenerMock).not.toHaveBeenCalled(); }); await step('Empty input', async () => { await userEvent.type(inputField(), '{backspace>18/}'); await userEvent.click(canvas.getByLabelText('toggle menu')); await waitFor(() => { return expect(listenerMock.mock.calls.at(-1)![0].detail).toStrictEqual({ region: undefined, country: undefined, division: undefined, location: undefined, }); }); }); await step('Select Asia', async () => { await userEvent.type(inputField(), 'Asia'); await userEvent.click(canvas.getByRole('option', { name: /^Asia.*Asia$/ })); await waitFor(() => { return expect(listenerMock.mock.calls.at(-1)![0].detail).toStrictEqual({ region: 'Asia', country: undefined, division: undefined, location: undefined, }); }); }); await step('Select Asia / Bangladesh / Rajshahi / Chapainawabgonj', async () => { await userEvent.type(inputField(), ' / Bangladesh / Rajshahi / Chapainawabgonj'); await userEvent.click(canvas.getByText('Asia / Bangladesh / Rajshahi / Chapainawabgonj')); await waitFor(() => { return expect(listenerMock.mock.calls.at(-1)![0].detail).toStrictEqual({ region: 'Asia', country: 'Bangladesh', division: 'Rajshahi', location: 'Chapainawabgonj', }); }); }); }, }"
771
803
  }
772
804
  ],
773
805
  "exports": [
@@ -982,7 +1014,7 @@
982
1014
  "type": {
983
1015
  "text": "StoryObj<MutationFilterProps>"
984
1016
  },
985
- "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-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'); const option = await canvas.findByRole('option'); await userEvent.click(option); await waitFor(() => expect(listenerMock).toHaveBeenCalledWith( expect.objectContaining({ detail: { nucleotideMutations: ['A123T'], aminoAcidMutations: [], nucleotideInsertions: [], aminoAcidInsertions: [], }, }), ), ); }); }, }"
1017
+ "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', () => { 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'); const option = await canvas.findByRole('option'); await userEvent.click(option); await waitFor(() => expect(listenerMock).toHaveBeenCalledWith( expect.objectContaining({ detail: { nucleotideMutations: ['A123T'], aminoAcidMutations: [], nucleotideInsertions: [], aminoAcidInsertions: [], }, }), ), ); }); }, }"
986
1018
  },
987
1019
  {
988
1020
  "kind": "variable",
@@ -990,7 +1022,7 @@
990
1022
  "type": {
991
1023
  "text": "StoryObj<MutationFilterProps>"
992
1024
  },
993
- "default": "{ ...Template, args: { ...Template.args, initialValue: ['seg1:123T', 'gene2:56', 'ins_seg2:78:AAA'], }, parameters: { fetchMock: { mocks: [ { matcher: { name: 'referenceGenome', url: REFERENCE_GENOME_ENDPOINT, }, response: { status: 200, body: { nucleotideSequences: [ { name: 'seg1', sequence: 'dummy', }, { name: 'seg2', sequence: 'dummy', }, ], genes: [ { name: 'gene1', sequence: 'dummy', }, { name: 'gene2', sequence: 'dummy', }, ], }, }, options: { overwriteRoutes: false, }, }, ], }, }, play: async ({ canvasElement }) => { const canvas = await withinShadowRoot(canvasElement, 'gs-mutation-filter'); const inputField = () => canvas.getByPlaceholderText('Enter a mutation', { exact: false }); await waitFor(() => { const placeholderText = inputField().getAttribute('placeholder'); expect(placeholderText).toEqual( 'Enter a mutation (e.g. seg1:23T, ins_seg1:10462:A, gene1:57Q, ins_gene1:31:N)', ); }); await waitFor(() => { expect(canvas.getByText('seg1:123T')).toBeVisible(); expect(canvas.getByText('gene2:56')).toBeVisible(); return expect(canvas.getByText('ins_seg2:78:AAA')).toBeVisible(); }); }, }"
1025
+ "default": "{ ...Template, args: { ...Template.args, initialValue: ['seg1:123T', 'gene2:56', 'ins_seg2:78:AAA'], }, parameters: { fetchMock: { mocks: [ { matcher: { name: 'referenceGenome', url: REFERENCE_GENOME_ENDPOINT, }, response: { status: 200, body: { nucleotideSequences: [ { name: 'seg1', sequence: 'dummy', }, { name: 'seg2', sequence: 'dummy', }, ], genes: [ { name: 'gene1', sequence: 'dummy', }, { name: 'gene2', sequence: 'dummy', }, ], }, }, options: { overwriteRoutes: false, }, }, ], }, }, play: async ({ canvasElement }) => { const canvas = await withinShadowRoot(canvasElement, 'gs-mutation-filter'); const inputField = () => canvas.getByPlaceholderText('Enter a mutation', { exact: false }); await waitFor(async () => { const placeholderText = inputField().getAttribute('placeholder'); await expect(placeholderText).toEqual( 'Enter a mutation (e.g. seg1:23T, ins_seg1:10462:A, gene1:57Q, ins_gene1:31:N)', ); }); await waitFor(async () => { await expect(canvas.getByText('seg1:123T')).toBeVisible(); await expect(canvas.getByText('gene2:56')).toBeVisible(); await expect(canvas.getByText('ins_seg2:78:AAA')).toBeVisible(); }); }, }"
994
1026
  }
995
1027
  ],
996
1028
  "exports": [
@@ -1116,31 +1148,31 @@
1116
1148
  },
1117
1149
  {
1118
1150
  "kind": "javascript-module",
1119
- "path": "src/web-components/input/gs-text-input.stories.ts",
1151
+ "path": "src/web-components/input/gs-text-filter.stories.ts",
1120
1152
  "declarations": [
1121
1153
  {
1122
1154
  "kind": "variable",
1123
1155
  "name": "meta",
1124
1156
  "type": {
1125
- "text": "Meta<Required<TextInputProps>>"
1157
+ "text": "Meta<Required<TextFilterProps>>"
1126
1158
  },
1127
- "default": "{ title: 'Input/Text input', component: 'gs-text-input', parameters: withComponentDocs({ actions: { handles: ['gs-text-input-changed', ...previewHandles], }, fetchMock: { mocks: [ { matcher: { name: 'hosts', url: AGGREGATED_ENDPOINT, body: { fields: ['host'], country: 'Germany', }, }, response: { status: 200, body: data, }, }, ], }, componentDocs: { opensShadowDom: true, expectsChildren: false, codeExample, }, }), argTypes: { lapisField: { control: { type: 'text', }, }, placeholderText: { control: { type: 'text', }, }, value: { control: { type: 'text', }, }, width: { control: { type: 'text', }, }, lapisFilter: { control: { type: 'object', }, }, }, tags: ['autodocs'], }"
1159
+ "default": "{ title: 'Input/Text filter', component: 'gs-text-filter', parameters: withComponentDocs({ actions: { handles: ['gs-text-filter-changed', ...previewHandles], }, fetchMock: { mocks: [ { matcher: { name: 'hosts', url: AGGREGATED_ENDPOINT, body: { fields: ['host'], country: 'Germany', }, }, response: { status: 200, body: data, }, }, ], }, componentDocs: { opensShadowDom: true, expectsChildren: false, codeExample, }, }), argTypes: { lapisField: { control: { type: 'text', }, }, placeholderText: { control: { type: 'text', }, }, value: { control: { type: 'text', }, }, width: { control: { type: 'text', }, }, lapisFilter: { control: { type: 'object', }, }, }, tags: ['autodocs'], }"
1128
1160
  },
1129
1161
  {
1130
1162
  "kind": "variable",
1131
1163
  "name": "Default",
1132
1164
  "type": {
1133
- "text": "StoryObj<Required<TextInputProps>>"
1165
+ "text": "StoryObj<Required<TextFilterProps>>"
1134
1166
  },
1135
- "default": "{ render: (args) => { return html` <gs-app lapis=\"${LAPIS_URL}\"> <div class=\"max-w-screen-lg\"> <gs-text-input .lapisField=${args.lapisField} .lapisFilter=${args.lapisFilter} .placeholderText=${args.placeholderText} .value=${args.value} .width=${args.width} ></gs-text-input> </div> </gs-app>`; }, args: { lapisField: 'host', lapisFilter: { country: 'Germany' }, placeholderText: 'Enter host name', value: 'Homo sapiens', width: '100%', }, }"
1167
+ "default": "{ render: (args) => { return html` <gs-app lapis=\"${LAPIS_URL}\"> <div class=\"max-w-screen-lg\"> <gs-text-filter .lapisField=${args.lapisField} .lapisFilter=${args.lapisFilter} .placeholderText=${args.placeholderText} .value=${args.value} .width=${args.width} ></gs-text-filter> </div> </gs-app>`; }, args: { lapisField: 'host', lapisFilter: { country: 'Germany' }, placeholderText: 'Enter host name', value: 'Homo sapiens', width: '100%', }, }"
1136
1168
  },
1137
1169
  {
1138
1170
  "kind": "variable",
1139
1171
  "name": "FiresEvents",
1140
1172
  "type": {
1141
- "text": "StoryObj<Required<TextInputProps>>"
1173
+ "text": "StoryObj<Required<TextFilterProps>>"
1142
1174
  },
1143
- "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 step('Empty input', async () => { await userEvent.type(inputField(), '{backspace>9/}'); }); await step('Enter a valid host name', async () => { await userEvent.type(inputField(), 'Homo s'); const dropdownOption = await canvas.findByText('Homo sapiens'); await userEvent.click(dropdownOption); }); await step('Verify event is fired with correct detail', async () => { await waitFor(() => { expect(listenerMock).toHaveBeenCalledWith( expect.objectContaining({ detail: { host: 'Homo sapiens', }, }), ); }); }); await step('Remove initial value', async () => { await fireEvent.click(canvas.getByRole('button', { name: 'clear selection' })); await expect(listenerMock).toHaveBeenCalledWith( expect.objectContaining({ detail: { host: undefined, }, }), ); }); await step('Empty input', async () => { inputField().blur(); await expect(listenerMock.mock.calls.at(-1)![0].detail).toStrictEqual({ host: undefined, }); }); }, args: { ...Default.args, value: '', }, }"
1175
+ "default": "{ ...Default, play: async ({ canvasElement, step }) => { const canvas = await withinShadowRoot(canvasElement, 'gs-text-filter'); const inputField = () => canvas.getByPlaceholderText('Enter host name'); const listenerMock = fn(); await step('Setup event listener mock', () => { canvasElement.addEventListener('gs-text-filter-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 step('Empty input', async () => { await userEvent.type(inputField(), '{backspace>9/}'); }); await step('Enter a valid host name', async () => { await userEvent.type(inputField(), 'Homo s'); const dropdownOption = await canvas.findByText('Homo sapiens'); await userEvent.click(dropdownOption); }); await step('Verify event is fired with correct detail', async () => { await waitFor(async () => { await expect(listenerMock).toHaveBeenCalledWith( expect.objectContaining({ detail: { host: 'Homo sapiens', }, }), ); }); }); await step('Remove initial value', async () => { await fireEvent.click(canvas.getByRole('button', { name: 'clear selection' })); await expect(listenerMock).toHaveBeenCalledWith( expect.objectContaining({ detail: { host: undefined, }, }), ); }); await step('Empty input', async () => { inputField().blur(); await expect(listenerMock.mock.calls.at(-1)![0].detail).toStrictEqual({ host: undefined, }); }); }, args: { ...Default.args, value: '', }, }"
1144
1176
  }
1145
1177
  ],
1146
1178
  "exports": [
@@ -1149,7 +1181,7 @@
1149
1181
  "name": "default",
1150
1182
  "declaration": {
1151
1183
  "name": "meta",
1152
- "module": "src/web-components/input/gs-text-input.stories.ts"
1184
+ "module": "src/web-components/input/gs-text-filter.stories.ts"
1153
1185
  }
1154
1186
  },
1155
1187
  {
@@ -1157,7 +1189,7 @@
1157
1189
  "name": "Default",
1158
1190
  "declaration": {
1159
1191
  "name": "Default",
1160
- "module": "src/web-components/input/gs-text-input.stories.ts"
1192
+ "module": "src/web-components/input/gs-text-filter.stories.ts"
1161
1193
  }
1162
1194
  },
1163
1195
  {
@@ -1165,19 +1197,19 @@
1165
1197
  "name": "FiresEvents",
1166
1198
  "declaration": {
1167
1199
  "name": "FiresEvents",
1168
- "module": "src/web-components/input/gs-text-input.stories.ts"
1200
+ "module": "src/web-components/input/gs-text-filter.stories.ts"
1169
1201
  }
1170
1202
  }
1171
1203
  ]
1172
1204
  },
1173
1205
  {
1174
1206
  "kind": "javascript-module",
1175
- "path": "src/web-components/input/gs-text-input.tsx",
1207
+ "path": "src/web-components/input/gs-text-filter.tsx",
1176
1208
  "declarations": [
1177
1209
  {
1178
1210
  "kind": "class",
1179
1211
  "description": "\n## Context\n\nThis component provides a text input field to specify filters for arbitrary fields of this LAPIS instance.",
1180
- "name": "TextInputComponent",
1212
+ "name": "TextFilterComponent",
1181
1213
  "members": [
1182
1214
  {
1183
1215
  "kind": "field",
@@ -1186,7 +1218,7 @@
1186
1218
  "text": "string | undefined"
1187
1219
  },
1188
1220
  "default": "undefined",
1189
- "description": "The initial value to use for this text input.",
1221
+ "description": "The initial value to use for this text filter.",
1190
1222
  "attribute": "value"
1191
1223
  },
1192
1224
  {
@@ -1196,7 +1228,7 @@
1196
1228
  "text": "string"
1197
1229
  },
1198
1230
  "default": "''",
1199
- "description": "Required.\n\nThe LAPIS field name to use for this text input.\nThe field must exist on this LAPIS instance.",
1231
+ "description": "Required.\n\nThe LAPIS field name to use for this text filter.\nThe field must exist on this LAPIS instance.",
1200
1232
  "attribute": "lapisField"
1201
1233
  },
1202
1234
  {
@@ -1236,7 +1268,7 @@
1236
1268
  "text": "CustomEvent<Record<string, string | undefined>>"
1237
1269
  },
1238
1270
  "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\" } ```",
1239
- "name": "gs-text-input-changed"
1271
+ "name": "gs-text-filter-changed"
1240
1272
  }
1241
1273
  ],
1242
1274
  "attributes": [
@@ -1246,7 +1278,7 @@
1246
1278
  "text": "string | undefined"
1247
1279
  },
1248
1280
  "default": "undefined",
1249
- "description": "The initial value to use for this text input.",
1281
+ "description": "The initial value to use for this text filter.",
1250
1282
  "fieldName": "value"
1251
1283
  },
1252
1284
  {
@@ -1255,7 +1287,7 @@
1255
1287
  "text": "string"
1256
1288
  },
1257
1289
  "default": "''",
1258
- "description": "Required.\n\nThe LAPIS field name to use for this text input.\nThe field must exist on this LAPIS instance.",
1290
+ "description": "Required.\n\nThe LAPIS field name to use for this text filter.\nThe field must exist on this LAPIS instance.",
1259
1291
  "fieldName": "lapisField"
1260
1292
  },
1261
1293
  {
@@ -1290,25 +1322,25 @@
1290
1322
  "name": "PreactLitAdapter",
1291
1323
  "module": "/src/web-components/PreactLitAdapter"
1292
1324
  },
1293
- "tagName": "gs-text-input",
1325
+ "tagName": "gs-text-filter",
1294
1326
  "customElement": true
1295
1327
  }
1296
1328
  ],
1297
1329
  "exports": [
1298
1330
  {
1299
1331
  "kind": "js",
1300
- "name": "TextInputComponent",
1332
+ "name": "TextFilterComponent",
1301
1333
  "declaration": {
1302
- "name": "TextInputComponent",
1303
- "module": "src/web-components/input/gs-text-input.tsx"
1334
+ "name": "TextFilterComponent",
1335
+ "module": "src/web-components/input/gs-text-filter.tsx"
1304
1336
  }
1305
1337
  },
1306
1338
  {
1307
1339
  "kind": "custom-element-definition",
1308
- "name": "gs-text-input",
1340
+ "name": "gs-text-filter",
1309
1341
  "declaration": {
1310
- "name": "TextInputComponent",
1311
- "module": "src/web-components/input/gs-text-input.tsx"
1342
+ "name": "TextFilterComponent",
1343
+ "module": "src/web-components/input/gs-text-filter.tsx"
1312
1344
  }
1313
1345
  }
1314
1346
  ]
@@ -1320,10 +1352,10 @@
1320
1352
  "exports": [
1321
1353
  {
1322
1354
  "kind": "js",
1323
- "name": "DateRangeSelectorComponent",
1355
+ "name": "DateRangeFilterComponent",
1324
1356
  "declaration": {
1325
- "name": "DateRangeSelectorComponent",
1326
- "module": "./gs-date-range-selector"
1357
+ "name": "DateRangeFilterComponent",
1358
+ "module": "./gs-date-range-filter"
1327
1359
  }
1328
1360
  },
1329
1361
  {
@@ -1336,10 +1368,10 @@
1336
1368
  },
1337
1369
  {
1338
1370
  "kind": "js",
1339
- "name": "TextInputComponent",
1371
+ "name": "TextFilterComponent",
1340
1372
  "declaration": {
1341
- "name": "TextInputComponent",
1342
- "module": "./gs-text-input"
1373
+ "name": "TextFilterComponent",
1374
+ "module": "./gs-text-filter"
1343
1375
  }
1344
1376
  },
1345
1377
  {
@@ -1513,7 +1545,7 @@
1513
1545
  "kind": "field",
1514
1546
  "name": "views",
1515
1547
  "type": {
1516
- "text": "AggregateView[]"
1548
+ "text": "('table' | 'bar')[]"
1517
1549
  },
1518
1550
  "default": "['table']",
1519
1551
  "description": "A list of tabs with views that this component should provide.",
@@ -1603,7 +1635,7 @@
1603
1635
  {
1604
1636
  "name": "views",
1605
1637
  "type": {
1606
- "text": "AggregateView[]"
1638
+ "text": "('table' | 'bar')[]"
1607
1639
  },
1608
1640
  "default": "['table']",
1609
1641
  "description": "A list of tabs with views that this component should provide.",
@@ -113,9 +113,9 @@ class LocationChangedEvent extends CustomEvent {
113
113
  });
114
114
  }
115
115
  }
116
- class TextInputChangedEvent extends CustomEvent {
116
+ class TextFilterChangedEvent extends CustomEvent {
117
117
  constructor(detail) {
118
- super("gs-text-input-changed", {
118
+ super("gs-text-filter-changed", {
119
119
  detail,
120
120
  bubbles: true,
121
121
  composed: true
@@ -134,10 +134,10 @@ class LineageFilterChangedEvent extends CustomEvent {
134
134
  export {
135
135
  DateRangeOptionChangedEvent as D,
136
136
  LocationChangedEvent as L,
137
- TextInputChangedEvent as T,
137
+ TextFilterChangedEvent as T,
138
138
  LineageFilterChangedEvent as a,
139
- dateRangeOptionSchema as b,
140
- dateRangeValueSchema as c,
139
+ dateRangeValueSchema as b,
140
+ dateRangeOptionSchema as c,
141
141
  dateRangeOptionPresets as d,
142
142
  toYYYYMMDD as e,
143
143
  lapisLocationFilterSchema as f,
@@ -148,4 +148,4 @@ export {
148
148
  temporalGranularitySchema as t,
149
149
  views as v
150
150
  };
151
- //# sourceMappingURL=LineageFilterChangedEvent-C9dXOxt6.js.map
151
+ //# sourceMappingURL=LineageFilterChangedEvent-COWV-Y0k.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LineageFilterChangedEvent-COWV-Y0k.js","sources":["../src/types.ts","../src/preact/dateRangeFilter/dateConversion.ts","../src/preact/dateRangeFilter/dateRangeOption.ts","../src/preact/locationFilter/LocationChangedEvent.ts","../src/preact/textFilter/TextFilterChangedEvent.ts","../src/preact/lineageFilter/LineageFilterChangedEvent.ts"],"sourcesContent":["import z from 'zod';\n\nimport {\n type Deletion,\n type DeletionClass,\n type Insertion,\n type InsertionClass,\n type Substitution,\n type SubstitutionClass,\n} from './utils/mutations';\n\nexport const mutationsFilterSchema = z.object({\n nucleotideMutations: z.array(z.string()),\n aminoAcidMutations: z.array(z.string()),\n nucleotideInsertions: z.array(z.string()),\n aminoAcidInsertions: z.array(z.string()),\n});\nexport type MutationsFilter = z.infer<typeof mutationsFilterSchema>;\n\nexport const lapisFilterSchema = z\n .record(z.union([z.string(), z.array(z.string()), z.number(), z.null(), z.boolean(), z.undefined()]))\n .and(mutationsFilterSchema.partial());\nexport type LapisFilter = z.infer<typeof lapisFilterSchema>;\n\nexport const namedLapisFilterSchema = z.object({\n lapisFilter: lapisFilterSchema,\n displayName: z.string(),\n});\nexport type NamedLapisFilter = z.infer<typeof namedLapisFilterSchema>;\n\nexport const lapisLocationFilterSchema = z.record(z.union([z.string(), z.undefined()]));\nexport type LapisLocationFilter = z.infer<typeof lapisLocationFilterSchema>;\n\nexport const temporalGranularitySchema = z.union([\n z.literal('day'),\n z.literal('week'),\n z.literal('month'),\n z.literal('year'),\n]);\nexport type TemporalGranularity = z.infer<typeof temporalGranularitySchema>;\n\nexport const sequenceTypeSchema = z.union([z.literal('nucleotide'), z.literal('amino acid')]);\nexport type SequenceType = z.infer<typeof sequenceTypeSchema>;\n\nexport type SubstitutionOrDeletion = 'substitution' | 'deletion';\n\nexport type MutationType = SubstitutionOrDeletion | 'insertion';\n\nexport type SubstitutionEntry<T extends Substitution = SubstitutionClass> = {\n type: 'substitution';\n mutation: T;\n count: number;\n proportion: number;\n};\n\nexport type DeletionEntry<T extends Deletion = DeletionClass> = {\n type: 'deletion';\n mutation: T;\n count: number;\n proportion: number;\n};\n\nexport type InsertionEntry<T extends Insertion = InsertionClass> = { type: 'insertion'; mutation: T; count: number };\n\nexport type SubstitutionOrDeletionEntry<\n S extends Substitution = SubstitutionClass,\n D extends Deletion = DeletionClass,\n> = SubstitutionEntry<S> | DeletionEntry<D>;\n\nexport type MutationEntry = SubstitutionEntry | DeletionEntry | InsertionEntry;\n\nexport const views = {\n table: 'table',\n venn: 'venn',\n grid: 'grid',\n insertions: 'insertions',\n bar: 'bar',\n line: 'line',\n bubble: 'bubble',\n map: 'map',\n} as const;\n","export const toYYYYMMDD = (date: Date) => {\n const options: Intl.DateTimeFormatOptions = { year: 'numeric', month: '2-digit', day: '2-digit' };\n return date.toLocaleDateString('en-CA', options);\n};\n","import z from 'zod';\n\nimport { toYYYYMMDD } from './dateConversion';\n\n/**\n * A date range option that can be used in the `gs-date-range-filter` component.\n */\nexport const dateRangeOptionSchema = z.object({\n /** The label of the date range option that will be shown to the user */\n label: z.string(),\n /**\n * The start date of the date range in the format `YYYY-MM-DD`.\n * If not set, the date range selector will default to the `earliestDate` property.\n */\n dateFrom: z.string().date().optional(),\n /**\n * The end date of the date range in the format `YYYY-MM-DD`.\n * If not set, the date range selector will default to the current date.\n */\n dateTo: z.string().date().optional(),\n});\n\nexport type DateRangeOption = z.infer<typeof dateRangeOptionSchema>;\n\nexport const dateRangeValueSchema = z.union([\n z.string(),\n z.object({\n dateFrom: z.string().date().optional(),\n dateTo: z.string().date().optional(),\n }),\n]);\n\nexport type DateRangeValue = z.infer<typeof dateRangeValueSchema>;\n\nexport type DateRangeSelectOption = Required<DateRangeValue>;\n\nexport class DateRangeOptionChangedEvent extends CustomEvent<DateRangeSelectOption> {\n constructor(detail: DateRangeSelectOption) {\n super('gs-date-range-option-changed', {\n detail,\n bubbles: true,\n composed: true,\n });\n }\n}\n\nconst today = new Date();\n\nconst twoWeeksAgo = new Date();\ntwoWeeksAgo.setDate(today.getDate() - 14);\n\nconst lastMonth = new Date(today);\nlastMonth.setMonth(today.getMonth() - 1);\n\nconst last2Months = new Date(today);\nlast2Months.setMonth(today.getMonth() - 2);\n\nconst last3Months = new Date(today);\nlast3Months.setMonth(today.getMonth() - 3);\n\nconst last6Months = new Date(today);\nlast6Months.setMonth(today.getMonth() - 6);\n\nconst lastYear = new Date(today);\nlastYear.setFullYear(today.getFullYear() - 1);\n\n/**\n * Presets for the `gs-date-range-filter` component that can be used as `dateRangeOptions`.\n */\nexport const dateRangeOptionPresets = {\n last2Weeks: {\n label: 'Last 2 weeks',\n dateFrom: toYYYYMMDD(twoWeeksAgo),\n },\n lastMonth: {\n label: 'Last month',\n dateFrom: toYYYYMMDD(lastMonth),\n },\n last2Months: {\n label: 'Last 2 months',\n dateFrom: toYYYYMMDD(last2Months),\n },\n last3Months: {\n label: 'Last 3 months',\n dateFrom: toYYYYMMDD(last3Months),\n },\n last6Months: {\n label: 'Last 6 months',\n dateFrom: toYYYYMMDD(last6Months),\n },\n lastYear: {\n label: 'Last year',\n dateFrom: toYYYYMMDD(lastYear),\n },\n allTimes: {\n label: 'All times',\n },\n} satisfies Record<string, DateRangeOption>;\n","import { type LapisLocationFilter } from '../../types';\n\nexport class LocationChangedEvent extends CustomEvent<LapisLocationFilter> {\n constructor(detail: LapisLocationFilter) {\n super('gs-location-changed', {\n detail,\n bubbles: true,\n composed: true,\n });\n }\n}\n","type LapisTextFilter = Record<string, string | undefined>;\n\nexport class TextFilterChangedEvent extends CustomEvent<LapisTextFilter> {\n constructor(detail: LapisTextFilter) {\n super('gs-text-filter-changed', {\n detail,\n bubbles: true,\n composed: true,\n });\n }\n}\n","type LapisLineageFilter = Record<string, string | undefined>;\n\nexport class LineageFilterChangedEvent extends CustomEvent<LapisLineageFilter> {\n constructor(detail: LapisLineageFilter) {\n super('gs-lineage-filter-changed', {\n detail,\n bubbles: true,\n composed: true,\n });\n }\n}\n"],"names":[],"mappings":"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"}