@genspectrum/dashboard-components 1.9.2 → 1.10.1

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.
@@ -596,6 +596,12 @@
596
596
  }
597
597
  ]
598
598
  },
599
+ {
600
+ "kind": "javascript-module",
601
+ "path": "src/web-components/input/gs-lineage-filter.spec.ts",
602
+ "declarations": [],
603
+ "exports": []
604
+ },
599
605
  {
600
606
  "kind": "javascript-module",
601
607
  "path": "src/web-components/input/gs-lineage-filter.stories.ts",
@@ -606,7 +612,7 @@
606
612
  "type": {
607
613
  "text": "Meta<Required<LineageFilterProps>>"
608
614
  },
609
- "default": "{ title: 'Input/Lineage filter', component: 'gs-lineage-filter', parameters: withComponentDocs({ actions: { handles: [gsEventNames.lineageFilterChanged, ...previewHandles], }, fetchMock: { mocks: [ { matcher: { name: 'pangoLineage', url: AGGREGATED_ENDPOINT, body: { fields: ['pangoLineage'], country: 'Germany', }, }, response: { status: 200, body: aggregatedData, }, }, { matcher: { name: 'lineageDefinition', url: lineageDefinitionEndpoint(LAPIS_URL, 'pangoLineage'), }, response: { status: 200, body: lineageDefinition, }, }, ], }, componentDocs: { opensShadowDom: true, expectsChildren: false, codeExample, }, }), tags: ['autodocs'], argTypes: { lapisField: { control: { type: 'select', }, options: ['host'], }, placeholderText: { control: { type: 'text', }, }, value: { control: { type: 'text', }, }, width: { control: { type: 'text', }, }, lapisFilter: { control: { type: 'object', }, }, hideCounts: { control: { type: 'boolean', }, }, }, }"
615
+ "default": "{ title: 'Input/Lineage filter', component: 'gs-lineage-filter', parameters: withComponentDocs({ actions: { handles: [gsEventNames.lineageFilterChanged, gsEventNames.lineageFilterMultiChanged, ...previewHandles], }, fetchMock: { mocks: [ { matcher: { name: 'pangoLineage', url: AGGREGATED_ENDPOINT, body: { fields: ['pangoLineage'], country: 'Germany', }, }, response: { status: 200, body: aggregatedData, }, }, { matcher: { name: 'lineageDefinition', url: lineageDefinitionEndpoint(LAPIS_URL, 'pangoLineage'), }, response: { status: 200, body: lineageDefinition, }, }, ], }, componentDocs: { opensShadowDom: true, expectsChildren: false, codeExample, }, }), tags: ['autodocs'], argTypes: { lapisField: { control: { type: 'select', }, options: ['host'], }, placeholderText: { control: { type: 'text', }, }, value: { control: { type: 'object', }, }, width: { control: { type: 'text', }, }, lapisFilter: { control: { type: 'object', }, }, hideCounts: { control: { type: 'boolean', }, }, multiSelect: { control: { type: 'boolean', }, }, }, }"
610
616
  },
611
617
  {
612
618
  "kind": "variable",
@@ -616,6 +622,22 @@
616
622
  },
617
623
  "default": "{ ...Template, play: async ({ canvasElement }) => { const canvas = await withinShadowRoot(canvasElement, 'gs-lineage-filter'); await waitFor(() => { return expect(canvas.getByPlaceholderText('Enter a lineage')).toBeVisible(); }); }, }"
618
624
  },
625
+ {
626
+ "kind": "variable",
627
+ "name": "LineageFilterStringValue",
628
+ "type": {
629
+ "text": "StoryObj<Required<LineageFilterProps>>"
630
+ },
631
+ "default": "{ render: (args) => { return html` <gs-app lapis=\"${LAPIS_URL}\"> <div class=\"max-w-(--breakpoint-lg)\"> <gs-lineage-filter lapisField=\"pangoLineage\" placeholderText=\"Enter a lineage\" value=\"B.1.1.7\" .multiSelect=${args.multiSelect} ></gs-lineage-filter> </div> </gs-app>`; }, args: { multiSelect: false, }, play: async ({ canvasElement }) => { const canvas = await withinShadowRoot(canvasElement, 'gs-lineage-filter'); await waitFor(() => { return expect(canvas.getByPlaceholderText('Enter a lineage')).toBeVisible(); }); }, }"
632
+ },
633
+ {
634
+ "kind": "variable",
635
+ "name": "LineageFilterArrayValue",
636
+ "type": {
637
+ "text": "StoryObj<Required<LineageFilterProps>>"
638
+ },
639
+ "default": "{ render: (args) => { return html` <gs-app lapis=\"${LAPIS_URL}\"> <div class=\"max-w-(--breakpoint-lg)\"> <gs-lineage-filter lapisField=\"pangoLineage\" placeholderText=\"Enter a lineage\" value='[\"B.1.1.7\", \"B.1.1.10\"]' .multiSelect=${args.multiSelect} ></gs-lineage-filter> </div> </gs-app>`; }, args: { multiSelect: true, }, play: async ({ canvasElement }) => { const canvas = await withinShadowRoot(canvasElement, 'gs-lineage-filter'); await waitFor(() => { return expect(canvas.getByPlaceholderText('Enter a lineage')).toBeVisible(); }); }, }"
640
+ },
619
641
  {
620
642
  "kind": "variable",
621
643
  "name": "DelayToShowLoadingState",
@@ -639,6 +661,14 @@
639
661
  "text": "StoryObj<Required<LineageFilterProps>>"
640
662
  },
641
663
  "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(gsEventNames.lineageFilterChanged, 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*(677,146)' })); await waitFor(() => { return expect(listenerMock.mock.calls.at(-1)![0].detail).toStrictEqual({ pangoLineage: 'B.1.1.7*', }); }); }); }, args: { ...LineageFilter.args, value: '', }, }"
664
+ },
665
+ {
666
+ "kind": "variable",
667
+ "name": "MultiSelectMode",
668
+ "type": {
669
+ "text": "StoryObj<Required<LineageFilterProps>>"
670
+ },
671
+ "default": "{ ...Template, args: { ...Template.args, multiSelect: true, value: ['B.1.1.7', 'BA.5'], placeholderText: 'Select lineages', }, play: async ({ canvasElement }) => { const canvas = await withinShadowRoot(canvasElement, 'gs-lineage-filter'); await waitFor(() => { return expect(canvas.getByPlaceholderText('Select lineages')).toBeVisible(); }); }, }"
642
672
  }
643
673
  ],
644
674
  "exports": [
@@ -658,6 +688,22 @@
658
688
  "module": "src/web-components/input/gs-lineage-filter.stories.ts"
659
689
  }
660
690
  },
691
+ {
692
+ "kind": "js",
693
+ "name": "LineageFilterStringValue",
694
+ "declaration": {
695
+ "name": "LineageFilterStringValue",
696
+ "module": "src/web-components/input/gs-lineage-filter.stories.ts"
697
+ }
698
+ },
699
+ {
700
+ "kind": "js",
701
+ "name": "LineageFilterArrayValue",
702
+ "declaration": {
703
+ "name": "LineageFilterArrayValue",
704
+ "module": "src/web-components/input/gs-lineage-filter.stories.ts"
705
+ }
706
+ },
661
707
  {
662
708
  "kind": "js",
663
709
  "name": "DelayToShowLoadingState",
@@ -681,6 +727,14 @@
681
727
  "name": "FiresEvent",
682
728
  "module": "src/web-components/input/gs-lineage-filter.stories.ts"
683
729
  }
730
+ },
731
+ {
732
+ "kind": "js",
733
+ "name": "MultiSelectMode",
734
+ "declaration": {
735
+ "name": "MultiSelectMode",
736
+ "module": "src/web-components/input/gs-lineage-filter.stories.ts"
737
+ }
684
738
  }
685
739
  ]
686
740
  },
@@ -690,17 +744,17 @@
690
744
  "declarations": [
691
745
  {
692
746
  "kind": "class",
693
- "description": "\n## Context\n\nThis component provides a text input field to filter by lineages.\nCurrently, it is designed to work well with Pango Lineages,\nbut it may also be used for other lineage types, if suitable.\n\nIt fetches all available values of the `lapisField` from the LAPIS instance within the given `lapisFilter`\nand provides an autocomplete list with the available values of the lineage and sublineage queries\n(a `*` appended to the lineage value).",
747
+ "description": "## Context\n\nThis component provides a text input field to filter by lineages.\nCurrently, it is designed to work well with Pango Lineages,\nbut it may also be used for other lineage types, if suitable.\n\nIt fetches all available values of the `lapisField` from the LAPIS instance within the given `lapisFilter`\nand provides an autocomplete list with the available values of the lineage and sublineage queries\n(a `*` appended to the lineage value).\n\nWhen `multiSelect` is true, it allows selecting multiple lineages and displays them as removable chips.",
694
748
  "name": "LineageFilterComponent",
695
749
  "members": [
696
750
  {
697
751
  "kind": "field",
698
752
  "name": "value",
699
753
  "type": {
700
- "text": "string"
754
+ "text": "string | string[]"
701
755
  },
702
756
  "default": "''",
703
- "description": "The initial value to use for this lineage filter.",
757
+ "description": "The initial value to use for this lineage filter.\nCan be a string for single select mode or an array of strings (or comma-separated string) for multi-select mode.\nExamples: \"B.1.1.7\" or [\"B.1.1.7\", \"BA.5\"] or \"B.1.1.7,BA.5\"",
704
758
  "attribute": "value"
705
759
  },
706
760
  {
@@ -713,6 +767,16 @@
713
767
  "description": "Required.\n\nThe LAPIS field name to use for this lineage filter.\nThe field must exist on this LAPIS instance.",
714
768
  "attribute": "lapisField"
715
769
  },
770
+ {
771
+ "kind": "field",
772
+ "name": "multiSelect",
773
+ "type": {
774
+ "text": "boolean | undefined"
775
+ },
776
+ "default": "false",
777
+ "description": "Whether to enable multi-select mode.\nWhen true, allows selecting multiple lineages displayed as removable chips.\nDefaults to false.",
778
+ "attribute": "multiSelect"
779
+ },
716
780
  {
717
781
  "kind": "field",
718
782
  "name": "lapisFilter",
@@ -759,18 +823,25 @@
759
823
  "type": {
760
824
  "text": "CustomEvent<Record<string, string | undefined>>"
761
825
  },
762
- "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: ``` { \"pangoLineage\": \"B.1.1.7\" } ```",
826
+ "description": "Fired when the selection changes in single-select mode. The `details` of this event contain an object with the `lapisField` as key and the selected value as value. Example: ``` { \"pangoLineage\": \"B.1.1.7\" } ```",
763
827
  "name": "gs-lineage-filter-changed"
828
+ },
829
+ {
830
+ "type": {
831
+ "text": "CustomEvent<Record<string, string[] | undefined>>"
832
+ },
833
+ "description": "Fired when the selection changes in multi-select mode. The `details` of this event contain an object with the `lapisField` as key and an array of selected values. Example: ``` { \"pangoLineage\": [\"B.1.1.7\", \"BA.5\"] } ```",
834
+ "name": "gs-lineage-filter-multi-changed"
764
835
  }
765
836
  ],
766
837
  "attributes": [
767
838
  {
768
839
  "name": "value",
769
840
  "type": {
770
- "text": "string"
841
+ "text": "string | string[]"
771
842
  },
772
843
  "default": "''",
773
- "description": "The initial value to use for this lineage filter.",
844
+ "description": "The initial value to use for this lineage filter.\nCan be a string for single select mode or an array of strings (or comma-separated string) for multi-select mode.\nExamples: \"B.1.1.7\" or [\"B.1.1.7\", \"BA.5\"] or \"B.1.1.7,BA.5\"",
774
845
  "fieldName": "value"
775
846
  },
776
847
  {
@@ -782,6 +853,15 @@
782
853
  "description": "Required.\n\nThe LAPIS field name to use for this lineage filter.\nThe field must exist on this LAPIS instance.",
783
854
  "fieldName": "lapisField"
784
855
  },
856
+ {
857
+ "name": "multiSelect",
858
+ "type": {
859
+ "text": "boolean | undefined"
860
+ },
861
+ "default": "false",
862
+ "description": "Whether to enable multi-select mode.\nWhen true, allows selecting multiple lineages displayed as removable chips.\nDefaults to false.",
863
+ "fieldName": "multiSelect"
864
+ },
785
865
  {
786
866
  "name": "lapisFilter",
787
867
  "type": {
@@ -1530,7 +1610,7 @@
1530
1610
  "type": {
1531
1611
  "text": "StoryObj<Required<TextFilterProps>>"
1532
1612
  },
1533
- "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(gsEventNames.textFilterChanged, 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 userEvent.click(canvas.getByRole('button', { name: 'clear selection' })); await expect(listenerMock).toHaveBeenCalledWith( expect.objectContaining({ detail: { host: undefined, }, }), ); }); }, args: { ...Default.args, value: '', }, }"
1613
+ "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(gsEventNames.textFilterChanged, 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 userEvent.click(canvas.getByRole('button', { name: 'clear selection' })); await waitFor(async () => { await expect(listenerMock).toHaveBeenCalledWith( expect.objectContaining({ detail: { host: undefined, }, }), ); }); }); }, args: { ...Default.args, value: '', }, }"
1534
1614
  }
1535
1615
  ],
1536
1616
  "exports": [
@@ -6,6 +6,7 @@ const gsEventNames = {
6
6
  dateRangeOptionChanged: "gs-date-range-option-changed",
7
7
  mutationFilterChanged: "gs-mutation-filter-changed",
8
8
  lineageFilterChanged: "gs-lineage-filter-changed",
9
+ lineageFilterMultiChanged: "gs-lineage-filter-multi-changed",
9
10
  locationChanged: "gs-location-changed",
10
11
  textFilterChanged: "gs-text-filter-changed",
11
12
  numberRangeFilterChanged: "gs-number-range-filter-changed",
@@ -159,6 +160,15 @@ class LineageFilterChangedEvent extends CustomEvent {
159
160
  });
160
161
  }
161
162
  }
163
+ class LineageMultiFilterChangedEvent extends CustomEvent {
164
+ constructor(detail) {
165
+ super(gsEventNames.lineageFilterMultiChanged, {
166
+ detail,
167
+ bubbles: true,
168
+ composed: true
169
+ });
170
+ }
171
+ }
162
172
  const numberRangeSchema = z.object({
163
173
  min: z.number().optional(),
164
174
  max: z.number().optional()
@@ -196,12 +206,13 @@ export {
196
206
  lapisLocationFilterSchema as h,
197
207
  mutationTypeSchema as i,
198
208
  mutationsFilterSchema as j,
199
- numberRangeSchema as k,
209
+ LineageMultiFilterChangedEvent as k,
200
210
  lapisFilterSchema as l,
201
211
  mutationType as m,
202
212
  namedLapisFilterSchema as n,
213
+ numberRangeSchema as o,
203
214
  sequenceTypeSchema as s,
204
215
  temporalGranularitySchema as t,
205
216
  views as v
206
217
  };
207
- //# sourceMappingURL=NumberRangeFilterChangedEvent-BnPI-Asz.js.map
218
+ //# sourceMappingURL=NumberRangeFilterChangedEvent-Cdtcp9YL.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NumberRangeFilterChangedEvent-Cdtcp9YL.js","sources":["../src/utils/gsEventNames.ts","../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","../src/preact/numberRangeFilter/NumberRangeFilterChangedEvent.ts"],"sourcesContent":["export const gsEventNames = {\n error: 'gs-error',\n componentFinishedLoading: 'gs-component-finished-loading',\n dateRangeFilterChanged: 'gs-date-range-filter-changed',\n dateRangeOptionChanged: 'gs-date-range-option-changed',\n mutationFilterChanged: 'gs-mutation-filter-changed',\n lineageFilterChanged: 'gs-lineage-filter-changed',\n lineageFilterMultiChanged: 'gs-lineage-filter-multi-changed',\n locationChanged: 'gs-location-changed',\n textFilterChanged: 'gs-text-filter-changed',\n numberRangeFilterChanged: 'gs-number-range-filter-changed',\n numberRangeValueChanged: 'gs-number-range-value-changed',\n} as const;\n","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(), z.null()]));\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 SubstitutionOrDeletionOrInsertion = 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\nexport const mutationType = {\n nucleotideMutations: 'nucleotideMutations',\n nucleotideInsertions: 'nucleotideInsertions',\n aminoAcidMutations: 'aminoAcidMutations',\n aminoAcidInsertions: 'aminoAcidInsertions',\n} as const;\n\nexport const mutationTypeSchema = z.enum([\n mutationType.nucleotideMutations,\n mutationType.nucleotideInsertions,\n mutationType.aminoAcidMutations,\n mutationType.aminoAcidInsertions,\n]);\n\nexport type MutationType = z.infer<typeof mutationTypeSchema>;\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';\nimport { gsEventNames } from '../../utils/gsEventNames';\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 */\n dateFrom: z.string().date().optional(),\n /**\n * The end date of the date range in the format `YYYY-MM-DD`.\n */\n dateTo: z.string().date().optional(),\n});\n\nexport type DateRangeOption = z.infer<typeof dateRangeOptionSchema>;\n\nexport const dateRangeValueSchema = z\n .union([\n z.string(),\n z.object({\n dateFrom: z.string().date().optional(),\n dateTo: z.string().date().optional(),\n }),\n ])\n .nullable();\n\nexport type DateRangeValue = z.infer<typeof dateRangeValueSchema>;\n\nexport class DateRangeOptionChangedEvent extends CustomEvent<DateRangeValue> {\n constructor(detail: DateRangeValue) {\n super(gsEventNames.dateRangeOptionChanged, {\n detail,\n bubbles: true,\n composed: true,\n });\n }\n}\n\ntype DateRangeOptionPresets = {\n last2Weeks: DateRangeOption;\n lastMonth: DateRangeOption;\n last2Months: DateRangeOption;\n last3Months: DateRangeOption;\n last6Months: DateRangeOption;\n lastYear: DateRangeOption;\n};\n\nlet dateRangeOptionsPresetsCacheDate: string | null = null;\nlet dateRangeOptionPresetsCache: DateRangeOptionPresets | null = null;\n\n/**\n * Presets for the `gs-date-range-filter` component that can be used as `dateRangeOptions`.\n */\nexport const dateRangeOptionPresets = (): DateRangeOptionPresets => {\n const today = new Date();\n const todayString = new Date().toISOString().slice(0, 10);\n\n if (\n dateRangeOptionPresetsCache === null ||\n dateRangeOptionsPresetsCacheDate === null ||\n dateRangeOptionsPresetsCacheDate !== todayString\n ) {\n dateRangeOptionsPresetsCacheDate = todayString;\n\n const twoWeeksAgo = new Date();\n twoWeeksAgo.setDate(today.getDate() - 14);\n\n const lastMonth = new Date(today);\n lastMonth.setMonth(today.getMonth() - 1);\n\n const last2Months = new Date(today);\n last2Months.setMonth(today.getMonth() - 2);\n\n const last3Months = new Date(today);\n last3Months.setMonth(today.getMonth() - 3);\n\n const last6Months = new Date(today);\n last6Months.setMonth(today.getMonth() - 6);\n\n const lastYear = new Date(today);\n lastYear.setFullYear(today.getFullYear() - 1);\n\n dateRangeOptionPresetsCache = {\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 };\n }\n\n return dateRangeOptionPresetsCache;\n};\n","import { type LapisLocationFilter } from '../../types';\nimport { gsEventNames } from '../../utils/gsEventNames';\n\nexport class LocationChangedEvent extends CustomEvent<LapisLocationFilter> {\n constructor(detail: LapisLocationFilter) {\n super(gsEventNames.locationChanged, {\n detail,\n bubbles: true,\n composed: true,\n });\n }\n}\n","import { gsEventNames } from '../../utils/gsEventNames';\n\ntype LapisTextFilter = Record<string, string | undefined>;\n\nexport class TextFilterChangedEvent extends CustomEvent<LapisTextFilter> {\n constructor(detail: LapisTextFilter) {\n super(gsEventNames.textFilterChanged, {\n detail,\n bubbles: true,\n composed: true,\n });\n }\n}\n","import { gsEventNames } from '../../utils/gsEventNames';\n\ntype LapisLineageFilter = Record<string, string | undefined>;\ntype LapisLineageMultiFilter = Record<string, string[] | undefined>;\n\nexport class LineageFilterChangedEvent extends CustomEvent<LapisLineageFilter> {\n constructor(detail: LapisLineageFilter) {\n super(gsEventNames.lineageFilterChanged, {\n detail,\n bubbles: true,\n composed: true,\n });\n }\n}\n\nexport class LineageMultiFilterChangedEvent extends CustomEvent<LapisLineageMultiFilter> {\n constructor(detail: LapisLineageMultiFilter) {\n super(gsEventNames.lineageFilterMultiChanged, {\n detail,\n bubbles: true,\n composed: true,\n });\n }\n}\n","import z from 'zod';\n\nimport { gsEventNames } from '../../utils/gsEventNames';\n\nexport type LapisNumberFilter = Record<string, number | undefined>;\n\nexport const numberRangeSchema = z.object({\n min: z.number().optional(),\n max: z.number().optional(),\n});\nexport type NumberRange = z.infer<typeof numberRangeSchema>;\n\nexport class NumberRangeValueChangedEvent extends CustomEvent<NumberRange> {\n constructor(detail: NumberRange) {\n super(gsEventNames.numberRangeValueChanged, {\n detail,\n bubbles: true,\n composed: true,\n });\n }\n}\n\nexport class NumberRangeFilterChangedEvent extends CustomEvent<LapisNumberFilter> {\n constructor(detail: LapisNumberFilter) {\n super(gsEventNames.numberRangeFilterChanged, {\n detail,\n bubbles: true,\n composed: true,\n });\n }\n}\n"],"names":[],"mappings":"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"}
@@ -275,7 +275,6 @@ export declare class GenomeDataViewerComponent extends PreactLitAdapter {
275
275
  }
276
276
 
277
277
  /**
278
- *
279
278
  * ## Context
280
279
  *
281
280
  * This component provides a text input field to filter by lineages.
@@ -286,21 +285,35 @@ export declare class GenomeDataViewerComponent extends PreactLitAdapter {
286
285
  * and provides an autocomplete list with the available values of the lineage and sublineage queries
287
286
  * (a `*` appended to the lineage value).
288
287
  *
288
+ * When `multiSelect` is true, it allows selecting multiple lineages and displays them as removable chips.
289
+ *
289
290
  * @fires {CustomEvent<Record<string, string | undefined>>} gs-lineage-filter-changed
290
- * Fired when the input field is changed.
291
- * The `details` of this event contain an object with the `lapisField` as key and the input value as value.
291
+ * Fired when the selection changes in single-select mode.
292
+ * The `details` of this event contain an object with the `lapisField` as key and the selected value as value.
292
293
  * Example:
293
294
  * ```
294
295
  * {
295
296
  * "pangoLineage": "B.1.1.7"
296
297
  * }
297
- * ```
298
+ * ```
299
+ *
300
+ * @fires {CustomEvent<Record<string, string[] | undefined>>} gs-lineage-filter-multi-changed
301
+ * Fired when the selection changes in multi-select mode.
302
+ * The `details` of this event contain an object with the `lapisField` as key and an array of selected values.
303
+ * Example:
304
+ * ```
305
+ * {
306
+ * "pangoLineage": ["B.1.1.7", "BA.5"]
307
+ * }
308
+ * ```
298
309
  */
299
310
  export declare class LineageFilterComponent extends PreactLitAdapter {
300
311
  /**
301
312
  * The initial value to use for this lineage filter.
313
+ * Can be a string for single select mode or an array of strings (or comma-separated string) for multi-select mode.
314
+ * Examples: "B.1.1.7" or ["B.1.1.7", "BA.5"] or "B.1.1.7,BA.5"
302
315
  */
303
- value: string;
316
+ value: string | string[];
304
317
  /**
305
318
  * Required.
306
319
  *
@@ -308,6 +321,12 @@ export declare class LineageFilterComponent extends PreactLitAdapter {
308
321
  * The field must exist on this LAPIS instance.
309
322
  */
310
323
  lapisField: string;
324
+ /**
325
+ * Whether to enable multi-select mode.
326
+ * When true, allows selecting multiple lineages displayed as removable chips.
327
+ * Defaults to false.
328
+ */
329
+ multiSelect: boolean | undefined;
311
330
  /**
312
331
  * The filter that is used to fetch the available the autocomplete options.
313
332
  * If not set it fetches all available options.
@@ -334,6 +353,7 @@ export declare class LineageFilterComponent extends PreactLitAdapter {
334
353
  * Defaults to false.
335
354
  */
336
355
  hideCounts: boolean | undefined;
356
+ updated(changedProps: Map<string, unknown>): void;
337
357
  render(): JSX_2.Element;
338
358
  }
339
359
 
@@ -1651,22 +1671,6 @@ declare global {
1651
1671
  }
1652
1672
 
1653
1673
 
1654
- declare global {
1655
- interface HTMLElementTagNameMap {
1656
- 'gs-genome-data-viewer': GenomeDataViewerComponent;
1657
- }
1658
- }
1659
-
1660
-
1661
- declare global {
1662
- namespace JSX {
1663
- interface IntrinsicElements {
1664
- 'gs-genome-data-viewer': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1665
- }
1666
- }
1667
- }
1668
-
1669
-
1670
1674
  declare global {
1671
1675
  interface HTMLElementTagNameMap {
1672
1676
  'gs-mutation-comparison-component': MutationComparisonComponent;
@@ -1765,7 +1769,7 @@ declare global {
1765
1769
 
1766
1770
  declare global {
1767
1771
  interface HTMLElementTagNameMap {
1768
- 'gs-mutations-over-time': MutationsOverTimeComponent;
1772
+ 'gs-sequences-by-location': SequencesByLocationComponent;
1769
1773
  }
1770
1774
  }
1771
1775
 
@@ -1773,7 +1777,7 @@ declare global {
1773
1777
  declare global {
1774
1778
  namespace JSX {
1775
1779
  interface IntrinsicElements {
1776
- 'gs-mutations-over-time': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1780
+ 'gs-sequences-by-location': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1777
1781
  }
1778
1782
  }
1779
1783
  }
@@ -1781,7 +1785,7 @@ declare global {
1781
1785
 
1782
1786
  declare global {
1783
1787
  interface HTMLElementTagNameMap {
1784
- 'gs-sequences-by-location': SequencesByLocationComponent;
1788
+ 'gs-mutations-over-time': MutationsOverTimeComponent;
1785
1789
  }
1786
1790
  }
1787
1791
 
@@ -1789,7 +1793,7 @@ declare global {
1789
1793
  declare global {
1790
1794
  namespace JSX {
1791
1795
  interface IntrinsicElements {
1792
- 'gs-sequences-by-location': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1796
+ 'gs-mutations-over-time': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1793
1797
  }
1794
1798
  }
1795
1799
  }
@@ -1797,7 +1801,7 @@ declare global {
1797
1801
 
1798
1802
  declare global {
1799
1803
  interface HTMLElementTagNameMap {
1800
- 'gs-statistics': StatisticsComponent;
1804
+ 'gs-genome-data-viewer': GenomeDataViewerComponent;
1801
1805
  }
1802
1806
  }
1803
1807
 
@@ -1805,7 +1809,7 @@ declare global {
1805
1809
  declare global {
1806
1810
  namespace JSX {
1807
1811
  interface IntrinsicElements {
1808
- 'gs-statistics': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1812
+ 'gs-genome-data-viewer': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1809
1813
  }
1810
1814
  }
1811
1815
  }
@@ -1813,7 +1817,7 @@ declare global {
1813
1817
 
1814
1818
  declare global {
1815
1819
  interface HTMLElementTagNameMap {
1816
- 'gs-wastewater-mutations-over-time': WastewaterMutationsOverTimeComponent;
1820
+ 'gs-statistics': StatisticsComponent;
1817
1821
  }
1818
1822
  }
1819
1823
 
@@ -1821,7 +1825,7 @@ declare global {
1821
1825
  declare global {
1822
1826
  namespace JSX {
1823
1827
  interface IntrinsicElements {
1824
- 'gs-wastewater-mutations-over-time': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1828
+ 'gs-statistics': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1825
1829
  }
1826
1830
  }
1827
1831
  }
@@ -1904,12 +1908,33 @@ declare global {
1904
1908
  }
1905
1909
 
1906
1910
 
1911
+ declare global {
1912
+ interface HTMLElementTagNameMap {
1913
+ 'gs-number-range-filter': NumberRangeFilterComponent;
1914
+ }
1915
+ interface HTMLElementEventMap {
1916
+ [gsEventNames.numberRangeFilterChanged]: NumberRangeFilterChangedEvent;
1917
+ [gsEventNames.numberRangeValueChanged]: NumberRangeValueChangedEvent;
1918
+ }
1919
+ }
1920
+
1921
+
1922
+ declare global {
1923
+ namespace JSX {
1924
+ interface IntrinsicElements {
1925
+ 'gs-number-range-filter': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1926
+ }
1927
+ }
1928
+ }
1929
+
1930
+
1907
1931
  declare global {
1908
1932
  interface HTMLElementTagNameMap {
1909
1933
  'gs-lineage-filter': LineageFilterComponent;
1910
1934
  }
1911
1935
  interface HTMLElementEventMap {
1912
1936
  [gsEventNames.lineageFilterChanged]: LineageFilterChangedEvent;
1937
+ [gsEventNames.lineageFilterMultiChanged]: LineageMultiFilterChangedEvent;
1913
1938
  }
1914
1939
  }
1915
1940
 
@@ -1925,11 +1950,7 @@ declare global {
1925
1950
 
1926
1951
  declare global {
1927
1952
  interface HTMLElementTagNameMap {
1928
- 'gs-number-range-filter': NumberRangeFilterComponent;
1929
- }
1930
- interface HTMLElementEventMap {
1931
- [gsEventNames.numberRangeFilterChanged]: NumberRangeFilterChangedEvent;
1932
- [gsEventNames.numberRangeValueChanged]: NumberRangeValueChangedEvent;
1953
+ 'gs-wastewater-mutations-over-time': WastewaterMutationsOverTimeComponent;
1933
1954
  }
1934
1955
  }
1935
1956
 
@@ -1937,7 +1958,7 @@ declare global {
1937
1958
  declare global {
1938
1959
  namespace JSX {
1939
1960
  interface IntrinsicElements {
1940
- 'gs-number-range-filter': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1961
+ 'gs-wastewater-mutations-over-time': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1941
1962
  }
1942
1963
  }
1943
1964
  }