@genspectrum/dashboard-components 0.1.4 → 0.1.5

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 (38) hide show
  1. package/custom-elements.json +213 -78
  2. package/dist/dashboard-components.js +303 -53
  3. package/dist/dashboard-components.js.map +1 -1
  4. package/dist/genspectrum-components.d.ts +288 -69
  5. package/dist/style.css +142 -15
  6. package/package.json +3 -3
  7. package/src/preact/aggregatedData/aggregate.stories.tsx +2 -0
  8. package/src/preact/aggregatedData/aggregate.tsx +9 -4
  9. package/src/preact/components/headline.stories.tsx +19 -1
  10. package/src/preact/components/headline.tsx +9 -1
  11. package/src/preact/components/info.stories.tsx +24 -3
  12. package/src/preact/components/info.tsx +49 -5
  13. package/src/preact/dateRangeSelector/date-range-selector.tsx +10 -10
  14. package/src/preact/mutationComparison/mutation-comparison.stories.tsx +3 -0
  15. package/src/preact/mutationComparison/mutation-comparison.tsx +3 -3
  16. package/src/preact/mutationFilter/mutation-filter.tsx +1 -1
  17. package/src/preact/mutations/mutations.stories.tsx +3 -0
  18. package/src/preact/mutations/mutations.tsx +9 -3
  19. package/src/preact/prevalenceOverTime/prevalence-over-time.stories.tsx +4 -0
  20. package/src/preact/prevalenceOverTime/prevalence-over-time.tsx +14 -4
  21. package/src/preact/relativeGrowthAdvantage/relative-growth-advantage.stories.tsx +3 -0
  22. package/src/preact/relativeGrowthAdvantage/relative-growth-advantage.tsx +49 -4
  23. package/src/web-components/display/aggregate-component.stories.ts +3 -0
  24. package/src/web-components/display/aggregate-component.tsx +15 -1
  25. package/src/web-components/display/mutation-comparison-component.stories.ts +3 -0
  26. package/src/web-components/display/mutation-comparison-component.tsx +7 -0
  27. package/src/web-components/display/mutations-component.stories.ts +27 -7
  28. package/src/web-components/display/mutations-component.tsx +58 -4
  29. package/src/web-components/display/prevalence-over-time-component.stories.ts +24 -0
  30. package/src/web-components/display/prevalence-over-time-component.tsx +93 -5
  31. package/src/web-components/display/relative-growth-advantage-component.stories.ts +21 -0
  32. package/src/web-components/display/relative-growth-advantage-component.tsx +54 -3
  33. package/src/web-components/input/date-range-selector-component.stories.ts +17 -2
  34. package/src/web-components/input/date-range-selector-component.tsx +57 -5
  35. package/src/web-components/input/mutation-filter-component.stories.ts +13 -3
  36. package/src/web-components/input/mutation-filter-component.tsx +50 -2
  37. package/src/web-components/input/text-input-component.stories.ts +14 -3
  38. package/src/web-components/input/text-input-component.tsx +23 -1
@@ -64,7 +64,7 @@
64
64
  "type": {
65
65
  "text": "Meta"
66
66
  },
67
- "default": "{\n title: 'Wrapper/App',\n component: 'gs-app',\n parameters: withComponentDocs({\n fetchMock: {},\n componentDocs: {\n tag: 'gs-app',\n opensShadowDom: false,\n expectsChildren: true,\n codeExample,\n },\n }),\n decorators: [withActions],\n tags: ['autodocs'],\n}"
67
+ "default": "{ title: 'Wrapper/App', component: 'gs-app', parameters: withComponentDocs({ fetchMock: {}, componentDocs: { tag: 'gs-app', opensShadowDom: false, expectsChildren: true, codeExample, }, }), decorators: [withActions], tags: ['autodocs'], }"
68
68
  },
69
69
  {
70
70
  "kind": "variable",
@@ -72,7 +72,7 @@
72
72
  "type": {
73
73
  "text": "StoryObj<{ lapis: string }>"
74
74
  },
75
- "default": "{\n ...Template,\n play: async ({ canvasElement }) => {\n const canvas = within(canvasElement);\n\n await waitFor(() => {\n expect(canvas.getByText(LAPIS_URL)).toBeVisible();\n expect(canvas.getByText('\"name\": \"ORF1a\",', { exact: false })).toBeVisible();\n });\n },\n}"
75
+ "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(); }); }, }"
76
76
  },
77
77
  {
78
78
  "kind": "variable",
@@ -80,7 +80,7 @@
80
80
  "type": {
81
81
  "text": "StoryObj<{ lapis: string }>"
82
82
  },
83
- "default": "{\n ...Template,\n parameters: {\n fetchMock: {\n mocks: [\n {\n matcher: {\n name: 'referenceGenome',\n url: REFERENCE_GENOME_ENDPOINT,\n },\n response: {\n status: 200,\n body: referenceGenome,\n },\n options: {\n delay: 5000,\n },\n },\n ],\n },\n },\n}"
83
+ "default": "{ ...Template, parameters: { fetchMock: { mocks: [ { matcher: { name: 'referenceGenome', url: REFERENCE_GENOME_ENDPOINT, }, response: { status: 200, body: referenceGenome, }, options: { delay: 5000, }, }, ], }, }, }"
84
84
  },
85
85
  {
86
86
  "kind": "variable",
@@ -88,7 +88,7 @@
88
88
  "type": {
89
89
  "text": "StoryObj<{ lapis: string }>"
90
90
  },
91
- "default": "{\n ...Template,\n args: {\n lapis: 'definitely-not-a-valid-url',\n },\n play: async ({ canvasElement }) => {\n const canvas = within(canvasElement);\n\n await waitFor(() => {\n expect(canvas.getByText('Error')).toBeVisible();\n });\n },\n}"
91
+ "default": "{ ...Template, args: { lapis: 'definitely-not-a-valid-url', }, play: async ({ canvasElement }) => { const canvas = within(canvasElement); await waitFor(() => { expect(canvas.getByText('Error')).toBeVisible(); }); }, }"
92
92
  },
93
93
  {
94
94
  "kind": "class",
@@ -109,7 +109,7 @@
109
109
  "type": {
110
110
  "text": "ReferenceGenome"
111
111
  },
112
- "default": "{\n nucleotideSequences: [],\n genes: [],\n }"
112
+ "default": "{ nucleotideSequences: [], genes: [], }"
113
113
  }
114
114
  ],
115
115
  "superclass": {
@@ -231,7 +231,7 @@
231
231
  "type": {
232
232
  "text": "Meta<AggregateProps>"
233
233
  },
234
- "default": "{\n title: 'Visualization/Aggregate',\n component: 'gs-aggregate-component',\n argTypes: {\n fields: [{ control: 'object' }],\n views: {\n options: ['table'],\n control: { type: 'check' },\n },\n size: [{ control: 'object' }],\n },\n parameters: withComponentDocs({\n fetchMock: {\n mocks: [\n {\n matcher: {\n name: 'aggregatedData',\n url: AGGREGATED_ENDPOINT,\n body: {\n fields: ['division', 'host'],\n country: 'USA',\n },\n },\n response: {\n status: 200,\n body: aggregatedData,\n },\n },\n ],\n },\n componentDocs: {\n tag: 'gs-aggregate-component',\n opensShadowDom: true,\n expectsChildren: false,\n codeExample: `<gs-aggregate-component fields='[\"division\", \"host\"]' filter='{\"country\": \"USA\"}' views='[\"table\"]'></gs-aggregate-component>`,\n },\n }),\n tags: ['autodocs'],\n}"
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'], }"
235
235
  },
236
236
  {
237
237
  "kind": "variable",
@@ -239,7 +239,7 @@
239
239
  "type": {
240
240
  "text": "StoryObj<AggregateProps>"
241
241
  },
242
- "default": "{\n render: (args) => html`\n <gs-app lapis=\"${LAPIS_URL}\">\n <gs-aggregate-component\n .fields=${args.fields}\n .filter=${args.filter}\n .views=${args.views}\n .size=${args.size}\n ></gs-aggregate-component>\n </gs-app>\n `,\n args: {\n fields: ['division', 'host'],\n views: ['table'],\n filter: {\n country: 'USA',\n },\n size: { width: '100%', height: '700px' },\n },\n}"
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', }, }"
243
243
  }
244
244
  ],
245
245
  "exports": [
@@ -309,6 +309,16 @@
309
309
  "default": "undefined",
310
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
311
  "attribute": "size"
312
+ },
313
+ {
314
+ "kind": "field",
315
+ "name": "headline",
316
+ "type": {
317
+ "text": "string | undefined"
318
+ },
319
+ "default": "'Aggregate'",
320
+ "description": "The headline of the component. Set to an empty string to hide the headline.",
321
+ "attribute": "headline"
312
322
  }
313
323
  ],
314
324
  "attributes": [
@@ -347,6 +357,15 @@
347
357
  "default": "undefined",
348
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.",
349
359
  "fieldName": "size"
360
+ },
361
+ {
362
+ "name": "headline",
363
+ "type": {
364
+ "text": "string | undefined"
365
+ },
366
+ "default": "'Aggregate'",
367
+ "description": "The headline of the component. Set to an empty string to hide the headline.",
368
+ "fieldName": "headline"
350
369
  }
351
370
  ],
352
371
  "superclass": {
@@ -433,7 +452,7 @@
433
452
  "type": {
434
453
  "text": "Meta<MutationComparisonProps>"
435
454
  },
436
- "default": "{\n title: 'Visualization/Mutation comparison',\n component: 'gs-mutation-comparison-component',\n argTypes: {\n variants: { control: 'object' },\n sequenceType: {\n options: ['nucleotide', 'amino acid'],\n control: { type: 'radio' },\n },\n views: {\n options: ['table', 'venn'],\n control: { type: 'check' },\n },\n size: { control: 'object' },\n },\n parameters: withComponentDocs({\n componentDocs: {\n tag: 'gs-mutation-comparison-component',\n opensShadowDom: true,\n expectsChildren: false,\n codeExample,\n },\n }),\n tags: ['autodocs'],\n}"
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'], }"
437
456
  },
438
457
  {
439
458
  "kind": "variable",
@@ -441,7 +460,7 @@
441
460
  "type": {
442
461
  "text": "StoryObj<MutationComparisonProps>"
443
462
  },
444
- "default": "{\n ...Template,\n args: {\n variants: [\n {\n displayName: 'Some variant',\n lapisFilter: { country: 'Switzerland', pangoLineage: 'B.1.1.7', dateTo },\n },\n {\n displayName: 'Other variant',\n lapisFilter: {\n country: 'Switzerland',\n pangoLineage: 'B.1.1.7',\n dateFrom,\n dateTo,\n },\n },\n ],\n sequenceType: 'nucleotide',\n views: ['table', 'venn'],\n size: { width: '100%', height: '700px' },\n },\n parameters: {\n fetchMock: {\n mocks: [\n {\n matcher: {\n name: 'nucleotideMutationsSomeVariant',\n url: NUCLEOTIDE_MUTATIONS_ENDPOINT,\n body: {\n country: 'Switzerland',\n pangoLineage: 'B.1.1.7',\n dateTo,\n minProportion: 0,\n },\n },\n response: {\n status: 200,\n body: nucleotideMutationsSomeVariant,\n },\n },\n {\n matcher: {\n name: 'nucleotideMutationsOtherVariant',\n url: NUCLEOTIDE_MUTATIONS_ENDPOINT,\n body: {\n country: 'Switzerland',\n pangoLineage: 'B.1.1.7',\n dateFrom,\n dateTo,\n minProportion: 0,\n },\n },\n response: {\n status: 200,\n body: nucleotideMutationsOtherVariant,\n },\n },\n ],\n },\n },\n play: async ({ canvasElement, step }) => {\n const canvas = await withinShadowRoot(canvasElement, 'gs-mutation-comparison-component');\n\n await step('Min and max proportions should be 50% and 100%', async () => {\n const minInput = () => canvas.getAllByLabelText('%')[0];\n const maxInput = () => canvas.getAllByLabelText('%')[1];\n\n await waitFor(() => expect(minInput()).toHaveValue(50));\n await waitFor(() => expect(maxInput()).toHaveValue(100));\n });\n },\n}"
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)); }); }, }"
445
464
  },
446
465
  {
447
466
  "kind": "variable",
@@ -449,7 +468,7 @@
449
468
  "type": {
450
469
  "text": "StoryObj<MutationComparisonProps>"
451
470
  },
452
- "default": "{\n ...Default,\n play: async ({ canvasElement, step }) => {\n const canvas = await withinShadowRoot(canvasElement, 'gs-mutation-comparison-component');\n\n await step('Switch to Venn diagram view', async () => {\n await waitFor(() => expect(canvas.getByRole('button', { name: 'Venn' })).toBeInTheDocument());\n\n await fireEvent.click(canvas.getByRole('button', { name: 'Venn' }));\n\n await waitFor(() =>\n expect(\n canvas.getByText('You have no elements selected. Click in the venn diagram to select.'),\n ).toBeVisible(),\n );\n });\n },\n}"
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(), ); }); }, }"
453
472
  }
454
473
  ],
455
474
  "exports": [
@@ -527,6 +546,16 @@
527
546
  "default": "undefined",
528
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.",
529
548
  "attribute": "size"
549
+ },
550
+ {
551
+ "kind": "field",
552
+ "name": "headline",
553
+ "type": {
554
+ "text": "string | undefined"
555
+ },
556
+ "default": "'Mutation comparison'",
557
+ "description": "The headline of the component. Set to an empty string to hide the headline.",
558
+ "attribute": "headline"
530
559
  }
531
560
  ],
532
561
  "attributes": [
@@ -565,6 +594,15 @@
565
594
  "default": "undefined",
566
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.",
567
596
  "fieldName": "size"
597
+ },
598
+ {
599
+ "name": "headline",
600
+ "type": {
601
+ "text": "string | undefined"
602
+ },
603
+ "default": "'Mutation comparison'",
604
+ "description": "The headline of the component. Set to an empty string to hide the headline.",
605
+ "fieldName": "headline"
568
606
  }
569
607
  ],
570
608
  "superclass": {
@@ -604,7 +642,7 @@
604
642
  "type": {
605
643
  "text": "Meta<MutationsProps>"
606
644
  },
607
- "default": "{\n title: 'Visualization/Mutations',\n component: 'gs-mutations',\n argTypes: {\n variant: { control: 'object' },\n sequenceType: {\n options: ['nucleotide', 'amino acid'],\n control: { type: 'radio' },\n },\n views: {\n options: ['table', 'grid', 'insertions'],\n control: { type: 'check' },\n },\n size: { control: 'object' },\n },\n}"
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'], }"
608
646
  },
609
647
  {
610
648
  "kind": "variable",
@@ -612,7 +650,7 @@
612
650
  "type": {
613
651
  "text": "StoryObj<MutationsProps>"
614
652
  },
615
- "default": "{\n ...Template,\n args: {\n variant: { country: 'Switzerland', pangoLineage: 'B.1.1.7', dateTo: '2022-01-01' },\n sequenceType: 'nucleotide',\n views: ['grid', 'table', 'insertions'],\n size: { width: '100%', height: '700px' },\n },\n parameters: {\n fetchMock: {\n mocks: [\n {\n matcher: {\n name: 'nucleotideMutations',\n url: NUCLEOTIDE_MUTATIONS_ENDPOINT,\n body: {\n country: 'Switzerland',\n pangoLineage: 'B.1.1.7',\n dateTo: '2022-01-01',\n minProportion: 0,\n },\n },\n response: {\n status: 200,\n body: nucleotideMutations,\n },\n },\n {\n matcher: {\n name: 'nucleotideInsertions',\n url: NUCLEOTIDE_INSERTIONS_ENDPOINT,\n body: { country: 'Switzerland', pangoLineage: 'B.1.1.7', dateTo: '2022-01-01' },\n },\n response: {\n status: 200,\n body: nucleotideInsertions,\n },\n },\n ],\n },\n },\n}"
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, }, }, ], }, }, }"
616
654
  },
617
655
  {
618
656
  "kind": "variable",
@@ -620,7 +658,7 @@
620
658
  "type": {
621
659
  "text": "StoryObj<MutationsProps>"
622
660
  },
623
- "default": "{\n ...Default,\n play: async ({ canvasElement }) => {\n const canvas = await withinShadowRoot(canvasElement, 'gs-mutations-component');\n\n await waitFor(() => expect(canvas.getByRole('button', { name: 'Table' })).toBeInTheDocument());\n\n await fireEvent.click(canvas.getByRole('button', { name: 'Table' }));\n },\n}"
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' })); }, }"
624
662
  },
625
663
  {
626
664
  "kind": "variable",
@@ -628,7 +666,7 @@
628
666
  "type": {
629
667
  "text": "StoryObj<MutationsProps>"
630
668
  },
631
- "default": "{\n ...Default,\n play: async ({ canvasElement }) => {\n const canvas = await withinShadowRoot(canvasElement, 'gs-mutations-component');\n\n await waitFor(() => expect(canvas.getByRole('button', { name: 'Insertions' })).toBeInTheDocument());\n\n await fireEvent.click(canvas.getByRole('button', { name: 'Insertions' }));\n },\n}"
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' })); }, }"
632
670
  }
633
671
  ],
634
672
  "exports": [
@@ -672,34 +710,37 @@
672
710
  "declarations": [
673
711
  {
674
712
  "kind": "class",
675
- "description": "",
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.",
676
714
  "name": "MutationsComponent",
677
715
  "members": [
678
716
  {
679
717
  "kind": "field",
680
718
  "name": "variant",
681
719
  "type": {
682
- "text": "LapisFilter"
720
+ "text": "Record<string, string | number | null | boolean>"
683
721
  },
684
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.",
685
724
  "attribute": "variant"
686
725
  },
687
726
  {
688
727
  "kind": "field",
689
728
  "name": "sequenceType",
690
729
  "type": {
691
- "text": "SequenceType"
730
+ "text": "'nucleotide' | 'amino acid'"
692
731
  },
693
732
  "default": "'nucleotide'",
733
+ "description": "The type of the sequence for which the mutations should be shown.",
694
734
  "attribute": "sequenceType"
695
735
  },
696
736
  {
697
737
  "kind": "field",
698
738
  "name": "views",
699
739
  "type": {
700
- "text": "View[]"
740
+ "text": "('table' | 'grid' | 'insertions')[]"
701
741
  },
702
742
  "default": "['table', 'grid']",
743
+ "description": "A list of tabs with views that this component should provide.",
703
744
  "attribute": "views"
704
745
  },
705
746
  {
@@ -711,31 +752,44 @@
711
752
  "default": "undefined",
712
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.",
713
754
  "attribute": "size"
755
+ },
756
+ {
757
+ "kind": "field",
758
+ "name": "headline",
759
+ "type": {
760
+ "text": "string | undefined"
761
+ },
762
+ "default": "'Mutations'",
763
+ "description": "The headline of the component. Set to an empty string to hide the headline.",
764
+ "attribute": "headline"
714
765
  }
715
766
  ],
716
767
  "attributes": [
717
768
  {
718
769
  "name": "variant",
719
770
  "type": {
720
- "text": "LapisFilter"
771
+ "text": "Record<string, string | number | null | boolean>"
721
772
  },
722
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.",
723
775
  "fieldName": "variant"
724
776
  },
725
777
  {
726
778
  "name": "sequenceType",
727
779
  "type": {
728
- "text": "SequenceType"
780
+ "text": "'nucleotide' | 'amino acid'"
729
781
  },
730
782
  "default": "'nucleotide'",
783
+ "description": "The type of the sequence for which the mutations should be shown.",
731
784
  "fieldName": "sequenceType"
732
785
  },
733
786
  {
734
787
  "name": "views",
735
788
  "type": {
736
- "text": "View[]"
789
+ "text": "('table' | 'grid' | 'insertions')[]"
737
790
  },
738
791
  "default": "['table', 'grid']",
792
+ "description": "A list of tabs with views that this component should provide.",
739
793
  "fieldName": "views"
740
794
  },
741
795
  {
@@ -746,6 +800,15 @@
746
800
  "default": "undefined",
747
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.",
748
802
  "fieldName": "size"
803
+ },
804
+ {
805
+ "name": "headline",
806
+ "type": {
807
+ "text": "string | undefined"
808
+ },
809
+ "default": "'Mutations'",
810
+ "description": "The headline of the component. Set to an empty string to hide the headline.",
811
+ "fieldName": "headline"
749
812
  }
750
813
  ],
751
814
  "superclass": {
@@ -785,7 +848,7 @@
785
848
  "type": {
786
849
  "text": "Meta<PrevalenceOverTimeProps>"
787
850
  },
788
- "default": "{\n title: 'Visualization/Prevalence over time',\n component: 'gs-prevalence-over-time',\n argTypes: {\n numerator: { control: 'object' },\n denominator: { control: 'object' },\n granularity: {\n options: ['day', 'week', 'month', 'year'],\n control: { type: 'radio' },\n },\n smoothingWindow: { control: 'number' },\n views: {\n options: ['bar', 'line', 'bubble', 'table'],\n control: { type: 'check' },\n },\n confidenceIntervalMethods: {\n options: ['wilson'],\n control: { type: 'check' },\n },\n size: [{ control: 'object' }],\n },\n}"
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'], }"
789
852
  },
790
853
  {
791
854
  "kind": "variable",
@@ -793,7 +856,7 @@
793
856
  "type": {
794
857
  "text": "StoryObj<PrevalenceOverTimeProps>"
795
858
  },
796
- "default": "{\n ...Template,\n args: {\n numerator: [\n { displayName: 'EG', country: 'USA', pangoLineage: 'EG*', dateFrom: '2023-01-01' },\n { displayName: 'JN.1', country: 'USA', pangoLineage: 'JN.1*', dateFrom: '2023-01-01' },\n ],\n denominator: { country: 'USA', dateFrom: '2023-01-01', displayName: 'All' },\n granularity: 'month',\n smoothingWindow: 0,\n views: ['bar', 'line', 'bubble', 'table'],\n confidenceIntervalMethods: ['wilson'],\n size: { width: '100%', height: '700px' },\n },\n parameters: {\n fetchMock: {\n mocks: [\n {\n matcher: {\n name: 'numeratorEG',\n url: AGGREGATED_ENDPOINT,\n body: {\n country: 'USA',\n pangoLineage: 'EG*',\n dateFrom: '2023-01-01',\n fields: ['date'],\n },\n },\n response: {\n status: 200,\n body: numeratorEG,\n },\n },\n {\n matcher: {\n name: 'numeratorJN1',\n url: AGGREGATED_ENDPOINT,\n body: {\n country: 'USA',\n pangoLineage: 'JN.1*',\n dateFrom: '2023-01-01',\n fields: ['date'],\n },\n },\n response: {\n status: 200,\n body: numeratorJN1,\n },\n },\n {\n matcher: {\n name: 'denominator',\n url: AGGREGATED_ENDPOINT,\n body: {\n country: 'USA',\n dateFrom: '2023-01-01',\n fields: ['date'],\n },\n },\n response: {\n status: 200,\n body: denominator,\n },\n },\n ],\n },\n },\n}"
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, }, }, ], }, }, }"
797
860
  },
798
861
  {
799
862
  "kind": "variable",
@@ -801,7 +864,7 @@
801
864
  "type": {
802
865
  "text": "StoryObj<PrevalenceOverTimeProps>"
803
866
  },
804
- "default": "{\n ...Template,\n args: {\n numerator: { displayName: 'EG', country: 'USA', pangoLineage: 'BA.2.86*', dateFrom: '2023-10-01' },\n denominator: { country: 'USA', dateFrom: '2023-10-01', displayName: 'All' },\n granularity: 'day',\n smoothingWindow: 7,\n views: ['bar', 'line', 'bubble', 'table'],\n confidenceIntervalMethods: ['wilson'],\n size: { width: '100%', height: '700px' },\n },\n parameters: {\n fetchMock: {\n mocks: [\n {\n matcher: {\n name: 'numeratorOneVariant',\n url: AGGREGATED_ENDPOINT,\n body: {\n country: 'USA',\n pangoLineage: 'BA.2.86*',\n dateFrom: '2023-10-01',\n fields: ['date'],\n },\n },\n response: {\n status: 200,\n body: numeratorOneVariant,\n },\n },\n {\n matcher: {\n name: 'denominatorOneVariant',\n url: AGGREGATED_ENDPOINT,\n body: {\n country: 'USA',\n dateFrom: '2023-10-01',\n fields: ['date'],\n },\n },\n response: {\n status: 200,\n body: denominatorOneVariant,\n },\n },\n ],\n },\n },\n}"
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, }, }, ], }, }, }"
805
868
  },
806
869
  {
807
870
  "kind": "variable",
@@ -809,7 +872,7 @@
809
872
  "type": {
810
873
  "text": "StoryObj<PrevalenceOverTimeProps>"
811
874
  },
812
- "default": "{\n ...OneVariant,\n play: async ({ canvasElement }) => {\n const canvas = await withinShadowRoot(canvasElement, 'gs-prevalence-over-time');\n\n await waitFor(() => expect(canvas.getByRole('button', { name: 'Line' })).toBeInTheDocument());\n\n await fireEvent.click(canvas.getByRole('button', { name: 'Line' }));\n },\n}"
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' })); }, }"
813
876
  },
814
877
  {
815
878
  "kind": "variable",
@@ -817,7 +880,7 @@
817
880
  "type": {
818
881
  "text": "StoryObj<PrevalenceOverTimeProps>"
819
882
  },
820
- "default": "{\n ...OneVariant,\n play: async ({ canvasElement }) => {\n const canvas = await withinShadowRoot(canvasElement, 'gs-prevalence-over-time');\n\n await waitFor(() => expect(canvas.getByRole('button', { name: 'Bubble' })).toBeInTheDocument());\n\n await fireEvent.click(canvas.getByRole('button', { name: 'Bubble' }));\n },\n}"
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' })); }, }"
821
884
  },
822
885
  {
823
886
  "kind": "variable",
@@ -825,7 +888,7 @@
825
888
  "type": {
826
889
  "text": "StoryObj<PrevalenceOverTimeProps>"
827
890
  },
828
- "default": "{\n ...OneVariant,\n play: async ({ canvasElement }) => {\n const canvas = await withinShadowRoot(canvasElement, 'gs-prevalence-over-time');\n\n await waitFor(() => expect(canvas.getByRole('button', { name: 'Table' })).toBeInTheDocument());\n\n await fireEvent.click(canvas.getByRole('button', { name: 'Table' }));\n },\n}"
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' })); }, }"
829
892
  }
830
893
  ],
831
894
  "exports": [
@@ -885,34 +948,37 @@
885
948
  "declarations": [
886
949
  {
887
950
  "kind": "class",
888
- "description": "",
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.",
889
952
  "name": "PrevalenceOverTimeComponent",
890
953
  "members": [
891
954
  {
892
955
  "kind": "field",
893
956
  "name": "numerator",
894
957
  "type": {
895
- "text": "NamedLapisFilter | NamedLapisFilter[]"
958
+ "text": "| (Record<string, string | number | null | boolean> & { displayName: string })\n | (Record<string, string | number | null | boolean> & {\n displayName: string;\n })[]"
896
959
  },
897
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.",
898
962
  "attribute": "numerator"
899
963
  },
900
964
  {
901
965
  "kind": "field",
902
966
  "name": "denominator",
903
967
  "type": {
904
- "text": "NamedLapisFilter"
968
+ "text": "Record<string, string | number | null | boolean> & { displayName: string }"
905
969
  },
906
970
  "default": "{ displayName: '' }",
971
+ "description": "The variant that the variants in `numerator` are compared to.",
907
972
  "attribute": "denominator"
908
973
  },
909
974
  {
910
975
  "kind": "field",
911
976
  "name": "granularity",
912
977
  "type": {
913
- "text": "TemporalGranularity"
978
+ "text": "'day' | 'week' | 'month' | 'year'"
914
979
  },
915
980
  "default": "'day'",
981
+ "description": "The granularity of the time axis.",
916
982
  "attribute": "granularity"
917
983
  },
918
984
  {
@@ -922,26 +988,39 @@
922
988
  "text": "number"
923
989
  },
924
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)`.",
925
992
  "attribute": "smoothingWindow"
926
993
  },
927
994
  {
928
995
  "kind": "field",
929
996
  "name": "views",
930
997
  "type": {
931
- "text": "View[]"
998
+ "text": "('bar' | 'line' | 'bubble' | 'table')[]"
932
999
  },
933
1000
  "default": "['bar', 'line', 'bubble', 'table']",
1001
+ "description": "A list of tabs with views that this component should provide.",
934
1002
  "attribute": "views"
935
1003
  },
936
1004
  {
937
1005
  "kind": "field",
938
1006
  "name": "confidenceIntervalMethods",
939
1007
  "type": {
940
- "text": "ConfidenceIntervalMethod[]"
1008
+ "text": "('wilson' | 'none')[]"
941
1009
  },
942
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.",
943
1012
  "attribute": "confidenceIntervalMethods"
944
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
+ },
945
1024
  {
946
1025
  "kind": "field",
947
1026
  "name": "size",
@@ -957,25 +1036,28 @@
957
1036
  {
958
1037
  "name": "numerator",
959
1038
  "type": {
960
- "text": "NamedLapisFilter | NamedLapisFilter[]"
1039
+ "text": "| (Record<string, string | number | null | boolean> & { displayName: string })\n | (Record<string, string | number | null | boolean> & {\n displayName: string;\n })[]"
961
1040
  },
962
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.",
963
1043
  "fieldName": "numerator"
964
1044
  },
965
1045
  {
966
1046
  "name": "denominator",
967
1047
  "type": {
968
- "text": "NamedLapisFilter"
1048
+ "text": "Record<string, string | number | null | boolean> & { displayName: string }"
969
1049
  },
970
1050
  "default": "{ displayName: '' }",
1051
+ "description": "The variant that the variants in `numerator` are compared to.",
971
1052
  "fieldName": "denominator"
972
1053
  },
973
1054
  {
974
1055
  "name": "granularity",
975
1056
  "type": {
976
- "text": "TemporalGranularity"
1057
+ "text": "'day' | 'week' | 'month' | 'year'"
977
1058
  },
978
1059
  "default": "'day'",
1060
+ "description": "The granularity of the time axis.",
979
1061
  "fieldName": "granularity"
980
1062
  },
981
1063
  {
@@ -984,24 +1066,36 @@
984
1066
  "text": "number"
985
1067
  },
986
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)`.",
987
1070
  "fieldName": "smoothingWindow"
988
1071
  },
989
1072
  {
990
1073
  "name": "views",
991
1074
  "type": {
992
- "text": "View[]"
1075
+ "text": "('bar' | 'line' | 'bubble' | 'table')[]"
993
1076
  },
994
1077
  "default": "['bar', 'line', 'bubble', 'table']",
1078
+ "description": "A list of tabs with views that this component should provide.",
995
1079
  "fieldName": "views"
996
1080
  },
997
1081
  {
998
1082
  "name": "confidenceIntervalMethods",
999
1083
  "type": {
1000
- "text": "ConfidenceIntervalMethod[]"
1084
+ "text": "('wilson' | 'none')[]"
1001
1085
  },
1002
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.",
1003
1088
  "fieldName": "confidenceIntervalMethods"
1004
1089
  },
1090
+ {
1091
+ "name": "headline",
1092
+ "type": {
1093
+ "text": "string | undefined"
1094
+ },
1095
+ "default": "'Prevalence over time'",
1096
+ "description": "The headline of the component. Set to an empty string to hide the headline.",
1097
+ "fieldName": "headline"
1098
+ },
1005
1099
  {
1006
1100
  "name": "size",
1007
1101
  "type": {
@@ -1049,7 +1143,7 @@
1049
1143
  "type": {
1050
1144
  "text": "Meta<RelativeGrowthAdvantageProps>"
1051
1145
  },
1052
- "default": "{\n title: 'Visualization/Relative growth advantage',\n component: 'gs-relative-growth-advantage',\n argTypes: {\n numerator: { control: 'object' },\n denominator: { control: 'object' },\n generationTime: { control: 'number' },\n views: {\n options: ['line'],\n control: { type: 'check' },\n },\n size: [{ control: 'object' }],\n },\n}"
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'], }"
1053
1147
  },
1054
1148
  {
1055
1149
  "kind": "variable",
@@ -1057,7 +1151,7 @@
1057
1151
  "type": {
1058
1152
  "text": "StoryObj<RelativeGrowthAdvantageProps>"
1059
1153
  },
1060
- "default": "{\n ...Template,\n args: {\n numerator: { country: 'Switzerland', pangoLineage: 'B.1.1.7', dateFrom: '2020-12-01', dateTo: '2021-03-01' },\n denominator: { country: 'Switzerland', dateFrom: '2020-12-01', dateTo: '2021-03-01' },\n generationTime: 7,\n views: ['line'],\n size: { width: '100%', height: '700px' },\n },\n parameters: {\n fetchMock: {\n mocks: [\n {\n matcher: {\n name: 'numerator',\n url: AGGREGATED_ENDPOINT,\n body: {\n country: 'Switzerland',\n pangoLineage: 'B.1.1.7',\n dateFrom: '2020-12-01',\n dateTo: '2021-03-01',\n fields: ['date'],\n },\n },\n response: {\n status: 200,\n body: numerator,\n },\n },\n {\n matcher: {\n name: 'denominator',\n url: AGGREGATED_ENDPOINT,\n body: {\n country: 'Switzerland',\n dateFrom: '2020-12-01',\n dateTo: '2021-03-01',\n fields: ['date'],\n },\n },\n response: {\n status: 200,\n body: denominator,\n },\n },\n ],\n },\n },\n}"
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, }, }, ], }, }, }"
1061
1155
  }
1062
1156
  ],
1063
1157
  "exports": [
@@ -1085,25 +1179,27 @@
1085
1179
  "declarations": [
1086
1180
  {
1087
1181
  "kind": "class",
1088
- "description": "",
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.",
1089
1183
  "name": "RelativeGrowthAdvantageComponent",
1090
1184
  "members": [
1091
1185
  {
1092
1186
  "kind": "field",
1093
1187
  "name": "numerator",
1094
1188
  "type": {
1095
- "text": "LapisFilter"
1189
+ "text": "Record<string, string | number | null | boolean>"
1096
1190
  },
1097
1191
  "default": "{}",
1192
+ "description": "The LAPIS filter for the focal variant.",
1098
1193
  "attribute": "numerator"
1099
1194
  },
1100
1195
  {
1101
1196
  "kind": "field",
1102
1197
  "name": "denominator",
1103
1198
  "type": {
1104
- "text": "LapisFilter"
1199
+ "text": "Record<string, string | number | null | boolean>"
1105
1200
  },
1106
1201
  "default": "{}",
1202
+ "description": "The LAPIS filter for the variant that the focal variant (`numerator`) should be compared to.",
1107
1203
  "attribute": "denominator"
1108
1204
  },
1109
1205
  {
@@ -1113,17 +1209,29 @@
1113
1209
  "text": "number"
1114
1210
  },
1115
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.",
1116
1213
  "attribute": "generationTime"
1117
1214
  },
1118
1215
  {
1119
1216
  "kind": "field",
1120
1217
  "name": "views",
1121
1218
  "type": {
1122
- "text": "View[]"
1219
+ "text": "'line'[]"
1123
1220
  },
1124
1221
  "default": "['line']",
1222
+ "description": "A list of tabs with views that this component should provide.",
1125
1223
  "attribute": "views"
1126
1224
  },
1225
+ {
1226
+ "kind": "field",
1227
+ "name": "headline",
1228
+ "type": {
1229
+ "text": "string | undefined"
1230
+ },
1231
+ "default": "'Relative growth advantage'",
1232
+ "description": "The headline of the component. Set to an empty string to hide the headline.",
1233
+ "attribute": "headline"
1234
+ },
1127
1235
  {
1128
1236
  "kind": "field",
1129
1237
  "name": "size",
@@ -1139,17 +1247,19 @@
1139
1247
  {
1140
1248
  "name": "numerator",
1141
1249
  "type": {
1142
- "text": "LapisFilter"
1250
+ "text": "Record<string, string | number | null | boolean>"
1143
1251
  },
1144
1252
  "default": "{}",
1253
+ "description": "The LAPIS filter for the focal variant.",
1145
1254
  "fieldName": "numerator"
1146
1255
  },
1147
1256
  {
1148
1257
  "name": "denominator",
1149
1258
  "type": {
1150
- "text": "LapisFilter"
1259
+ "text": "Record<string, string | number | null | boolean>"
1151
1260
  },
1152
1261
  "default": "{}",
1262
+ "description": "The LAPIS filter for the variant that the focal variant (`numerator`) should be compared to.",
1153
1263
  "fieldName": "denominator"
1154
1264
  },
1155
1265
  {
@@ -1158,16 +1268,27 @@
1158
1268
  "text": "number"
1159
1269
  },
1160
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.",
1161
1272
  "fieldName": "generationTime"
1162
1273
  },
1163
1274
  {
1164
1275
  "name": "views",
1165
1276
  "type": {
1166
- "text": "View[]"
1277
+ "text": "'line'[]"
1167
1278
  },
1168
1279
  "default": "['line']",
1280
+ "description": "A list of tabs with views that this component should provide.",
1169
1281
  "fieldName": "views"
1170
1282
  },
1283
+ {
1284
+ "name": "headline",
1285
+ "type": {
1286
+ "text": "string | undefined"
1287
+ },
1288
+ "default": "'Relative growth advantage'",
1289
+ "description": "The headline of the component. Set to an empty string to hide the headline.",
1290
+ "fieldName": "headline"
1291
+ },
1171
1292
  {
1172
1293
  "name": "size",
1173
1294
  "type": {
@@ -1246,7 +1367,7 @@
1246
1367
  "type": {
1247
1368
  "text": "Meta<DateRangeSelectorProps<'CustomDateRange'>>"
1248
1369
  },
1249
- "default": "{\n title: 'Input/DateRangeSelector',\n component: 'gs-date-range-selector',\n parameters: {\n actions: {\n handles: ['gs-date-range-changed'],\n },\n fetchMock: {},\n },\n argTypes: {\n initialValue: {\n control: {\n type: 'select',\n },\n options: [\n PRESET_VALUE_CUSTOM,\n PRESET_VALUE_ALL_TIMES,\n PRESET_VALUE_LAST_2_WEEKS,\n PRESET_VALUE_LAST_MONTH,\n PRESET_VALUE_LAST_2_MONTHS,\n PRESET_VALUE_LAST_3_MONTHS,\n PRESET_VALUE_LAST_6_MONTHS,\n 'CustomDateRange',\n ],\n },\n },\n args: {\n customSelectOptions: [{ label: 'CustomDateRange', dateFrom: '2021-01-01', dateTo: '2021-12-31' }],\n earliestDate: '1970-01-01',\n initialValue: PRESET_VALUE_LAST_6_MONTHS,\n },\n decorators: [withActions],\n}"
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'], }"
1250
1371
  },
1251
1372
  {
1252
1373
  "kind": "variable",
@@ -1254,7 +1375,7 @@
1254
1375
  "type": {
1255
1376
  "text": "StoryObj<DateRangeSelectorProps<'CustomDateRange'>>"
1256
1377
  },
1257
- "default": "{\n render: (args) =>\n html` <gs-app lapis=\"${LAPIS_URL}\">\n <div class=\"max-w-screen-lg\">\n <gs-date-range-selector\n .customSelectOptions=${args.customSelectOptions}\n .earliestDate=${args.earliestDate}\n .initialValue=${args.initialValue}\n ></gs-date-range-selector>\n </div>\n </gs-app>`,\n play: async ({ canvasElement, step }) => {\n const canvas = await withinShadowRoot(canvasElement, 'gs-date-range-selector');\n const dateTo = () => canvas.getByPlaceholderText('Date to');\n\n await step('Expect last 6 months to be selected', async () => {\n await expect(canvas.getByRole('combobox')).toHaveValue('last6Months');\n await waitFor(() => {\n expect(dateTo()).toHaveValue(toYYYYMMDD(new Date()));\n });\n });\n },\n}"
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())); }); }); }, }"
1258
1379
  }
1259
1380
  ],
1260
1381
  "exports": [
@@ -1282,16 +1403,17 @@
1282
1403
  "declarations": [
1283
1404
  {
1284
1405
  "kind": "class",
1285
- "description": "",
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.",
1286
1407
  "name": "DateRangeSelectorComponent",
1287
1408
  "members": [
1288
1409
  {
1289
1410
  "kind": "field",
1290
1411
  "name": "customSelectOptions",
1291
1412
  "type": {
1292
- "text": "CustomSelectOption<CustomLabel>[]"
1413
+ "text": "{ label: string; dateFrom: string; dateTo: string }[]"
1293
1414
  },
1294
1415
  "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`.",
1295
1417
  "attribute": "customSelectOptions"
1296
1418
  },
1297
1419
  {
@@ -1301,15 +1423,17 @@
1301
1423
  "text": "string | undefined"
1302
1424
  },
1303
1425
  "default": "'1900-01-01'",
1426
+ "description": "The `dateFrom` value to use in the `allTimes` preset in the format `YYYY-MM-DD`.",
1304
1427
  "attribute": "earliestDate"
1305
1428
  },
1306
1429
  {
1307
1430
  "kind": "field",
1308
1431
  "name": "initialValue",
1309
1432
  "type": {
1310
- "text": "PresetOptionValues | CustomLabel | string | undefined"
1433
+ "text": "'custom'\n | 'allTimes'\n | 'last2Weeks'\n | 'lastMonth'\n | 'last2Months'\n | 'last3Months'\n | 'last6Months'\n | string\n | undefined"
1311
1434
  },
1312
- "default": "''",
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'`.",
1313
1437
  "attribute": "initialValue"
1314
1438
  }
1315
1439
  ],
@@ -1318,7 +1442,7 @@
1318
1442
  "type": {
1319
1443
  "text": "CustomEvent<{ dateFrom: string; dateTo: string; }>"
1320
1444
  },
1321
- "description": "When the date range has changed",
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`.",
1322
1446
  "name": "gs-date-range-changed"
1323
1447
  }
1324
1448
  ],
@@ -1326,9 +1450,10 @@
1326
1450
  {
1327
1451
  "name": "customSelectOptions",
1328
1452
  "type": {
1329
- "text": "CustomSelectOption<CustomLabel>[]"
1453
+ "text": "{ label: string; dateFrom: string; dateTo: string }[]"
1330
1454
  },
1331
1455
  "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`.",
1332
1457
  "fieldName": "customSelectOptions"
1333
1458
  },
1334
1459
  {
@@ -1337,14 +1462,16 @@
1337
1462
  "text": "string | undefined"
1338
1463
  },
1339
1464
  "default": "'1900-01-01'",
1465
+ "description": "The `dateFrom` value to use in the `allTimes` preset in the format `YYYY-MM-DD`.",
1340
1466
  "fieldName": "earliestDate"
1341
1467
  },
1342
1468
  {
1343
1469
  "name": "initialValue",
1344
1470
  "type": {
1345
- "text": "PresetOptionValues | CustomLabel | string | undefined"
1471
+ "text": "'custom'\n | 'allTimes'\n | 'last2Weeks'\n | 'lastMonth'\n | 'last2Months'\n | 'last3Months'\n | 'last6Months'\n | string\n | undefined"
1346
1472
  },
1347
- "default": "''",
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'`.",
1348
1475
  "fieldName": "initialValue"
1349
1476
  }
1350
1477
  ],
@@ -1424,7 +1551,7 @@
1424
1551
  "type": {
1425
1552
  "text": "Meta"
1426
1553
  },
1427
- "default": "{\n title: 'Input/Location filter',\n component: 'gs-location-filter',\n parameters: withComponentDocs({\n actions: {\n handles: ['gs-location-changed'],\n },\n componentDocs: {\n tag: 'gs-location-filter',\n opensShadowDom: true,\n expectsChildren: false,\n codeExample: `<gs-location-filter fields=\"['continent', 'country']\" value='Europe / Switzerland'></gs-location-filter>`,\n },\n }),\n decorators: [withActions],\n tags: ['autodocs'],\n}"
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'], }"
1428
1555
  },
1429
1556
  {
1430
1557
  "kind": "variable",
@@ -1432,7 +1559,7 @@
1432
1559
  "type": {
1433
1560
  "text": "StoryObj<LocationFilterProps>"
1434
1561
  },
1435
- "default": "{\n ...Template,\n parameters: {\n fetchMock: {\n mocks: [\n {\n matcher: aggregatedEndpointMatcher,\n response: {\n status: 200,\n body: data,\n },\n },\n ],\n },\n },\n play: async ({ canvasElement }) => {\n const canvas = await withinShadowRoot(canvasElement, 'gs-location-filter');\n await waitFor(() => {\n return expect(canvas.getByRole('combobox')).toBeEnabled();\n });\n },\n}"
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(); }); }, }"
1436
1563
  },
1437
1564
  {
1438
1565
  "kind": "variable",
@@ -1440,7 +1567,7 @@
1440
1567
  "type": {
1441
1568
  "text": "StoryObj<LocationFilterProps>"
1442
1569
  },
1443
- "default": "{\n ...Template,\n parameters: {\n fetchMock: {\n mocks: [\n {\n matcher: aggregatedEndpointMatcher,\n response: {\n status: 200,\n body: data,\n },\n options: {\n delay: 5000,\n },\n },\n ],\n },\n },\n}"
1570
+ "default": "{ ...Template, parameters: { fetchMock: { mocks: [ { matcher: aggregatedEndpointMatcher, response: { status: 200, body: data, }, options: { delay: 5000, }, }, ], }, }, }"
1444
1571
  },
1445
1572
  {
1446
1573
  "kind": "variable",
@@ -1448,7 +1575,7 @@
1448
1575
  "type": {
1449
1576
  "text": "StoryObj<LocationFilterProps>"
1450
1577
  },
1451
- "default": "{\n ...Template,\n parameters: {\n fetchMock: {\n mocks: [\n {\n matcher: aggregatedEndpointMatcher,\n response: {\n status: 400,\n body: { error: 'no data' },\n },\n },\n ],\n },\n },\n play: async ({ canvasElement }) => {\n const canvas = await withinShadowRoot(canvasElement, 'gs-location-filter');\n\n await waitFor(() =>\n expect(canvas.getByText('Bad Request: {\"error\":\"no data\"} ', { exact: false })).toBeInTheDocument(),\n );\n },\n}"
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(), ); }, }"
1452
1579
  },
1453
1580
  {
1454
1581
  "kind": "variable",
@@ -1456,7 +1583,7 @@
1456
1583
  "type": {
1457
1584
  "text": "StoryObj<LocationFilterProps>"
1458
1585
  },
1459
- "default": "{\n ...Template,\n parameters: {\n fetchMock: {\n mocks: [\n {\n matcher: aggregatedEndpointMatcher,\n response: {\n status: 200,\n body: data,\n },\n },\n ],\n },\n },\n play: async ({ canvasElement, step }) => {\n const canvas = await withinShadowRoot(canvasElement, 'gs-location-filter');\n\n const submitButton = () => canvas.getByRole('button', { name: 'Submit' });\n const inputField = () => canvas.getByRole('combobox');\n\n const listenerMock = fn();\n await step('Setup event listener mock', async () => {\n canvasElement.addEventListener('gs-location-changed', listenerMock);\n });\n\n await step('wait until data is loaded', async () => {\n await waitFor(() => {\n return expect(inputField()).toBeEnabled();\n });\n });\n\n await step('Input invalid location', async () => {\n await userEvent.type(inputField(), 'Not / A / Location');\n await userEvent.click(submitButton());\n await expect(listenerMock).not.toHaveBeenCalled();\n await userEvent.type(inputField(), '{backspace>18/}');\n });\n\n await step('Select Asia', async () => {\n await userEvent.type(inputField(), 'Asia');\n await userEvent.click(submitButton());\n await expect(listenerMock).toHaveBeenCalledWith(\n expect.objectContaining({\n detail: {\n region: 'Asia',\n },\n }),\n );\n });\n\n await step('Select Asia / Bangladesh / Rajshahi / Chapainawabgonj', async () => {\n await userEvent.type(inputField(), ' / Bangladesh / Rajshahi / Chapainawabgonj');\n await userEvent.click(submitButton());\n await expect(listenerMock).toHaveBeenCalledWith(\n expect.objectContaining({\n detail: {\n region: 'Asia',\n country: 'Bangladesh',\n division: 'Rajshahi',\n location: 'Chapainawabgonj',\n },\n }),\n );\n });\n },\n}"
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', }, }), ); }); }, }"
1460
1587
  }
1461
1588
  ],
1462
1589
  "exports": [
@@ -1596,9 +1723,9 @@
1596
1723
  "kind": "variable",
1597
1724
  "name": "meta",
1598
1725
  "type": {
1599
- "text": "Meta"
1726
+ "text": "Meta<MutationFilterProps>"
1600
1727
  },
1601
- "default": "{\n title: 'Input/Mutation filter',\n component: 'gs-mutation-filter',\n parameters: {\n actions: {\n handles: ['gs-mutation-filter-changed', 'gs-mutation-filter-on-blur'],\n },\n fetchMock: {},\n },\n decorators: [withActions],\n}"
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'], }"
1602
1729
  },
1603
1730
  {
1604
1731
  "kind": "variable",
@@ -1606,7 +1733,7 @@
1606
1733
  "type": {
1607
1734
  "text": "StoryObj<MutationFilterProps>"
1608
1735
  },
1609
- "default": "{\n ...Template,\n args: {\n initialValue: ['A123T'],\n },\n}"
1736
+ "default": "{ ...Template, args: { initialValue: ['A123T'], }, }"
1610
1737
  },
1611
1738
  {
1612
1739
  "kind": "variable",
@@ -1614,7 +1741,7 @@
1614
1741
  "type": {
1615
1742
  "text": "StoryObj<MutationFilterProps>"
1616
1743
  },
1617
- "default": "{\n ...Template,\n play: async ({ canvasElement, step }) => {\n const canvas = await withinShadowRoot(canvasElement, 'gs-mutation-filter');\n\n const inputField = () => canvas.getByPlaceholderText('Enter a mutation', { exact: false });\n const submitButton = () => canvas.getByRole('button', { name: '+' });\n const listenerMock = fn();\n await step('Setup event listener mock', async () => {\n canvasElement.addEventListener('gs-mutation-filter-changed', listenerMock);\n });\n\n await step('wait until data is loaded', async () => {\n await waitFor(() => {\n return expect(inputField()).toBeEnabled();\n });\n });\n\n await step('Enter a valid mutation', async () => {\n await userEvent.type(inputField(), 'A123T');\n await waitFor(() => submitButton().click());\n\n await waitFor(() =>\n expect(listenerMock).toHaveBeenCalledWith(\n expect.objectContaining({\n detail: {\n nucleotideMutations: ['A123T'],\n aminoAcidMutations: [],\n nucleotideInsertions: [],\n aminoAcidInsertions: [],\n },\n }),\n ),\n );\n });\n },\n}"
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: [], }, }), ), ); }); }, }"
1618
1745
  },
1619
1746
  {
1620
1747
  "kind": "variable",
@@ -1622,7 +1749,7 @@
1622
1749
  "type": {
1623
1750
  "text": "StoryObj<MutationFilterProps>"
1624
1751
  },
1625
- "default": "{\n ...Template,\n play: async ({ canvasElement, step }) => {\n const canvas = await withinShadowRoot(canvasElement, 'gs-mutation-filter');\n\n const inputField = () => canvas.getByPlaceholderText('Enter a mutation', { exact: false });\n const listenerMock = fn();\n await step('Setup event listener mock', async () => {\n canvasElement.addEventListener('gs-mutation-filter-on-blur', listenerMock);\n });\n\n await step('wait until data is loaded', async () => {\n await waitFor(() => {\n return expect(inputField()).toBeEnabled();\n });\n });\n\n await step('Move outside of input', async () => {\n await userEvent.type(inputField(), 'A123T');\n await userEvent.tab();\n\n await expect(listenerMock).toHaveBeenCalled();\n });\n },\n}"
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(); }); }, }"
1626
1753
  }
1627
1754
  ],
1628
1755
  "exports": [
@@ -1666,7 +1793,7 @@
1666
1793
  "declarations": [
1667
1794
  {
1668
1795
  "kind": "class",
1669
- "description": "",
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",
1670
1797
  "name": "MutationFilterComponent",
1671
1798
  "members": [
1672
1799
  {
@@ -1676,22 +1803,23 @@
1676
1803
  "text": "SelectedMutationFilterStrings | string[] | undefined"
1677
1804
  },
1678
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.",
1679
1807
  "attribute": "initialValue"
1680
1808
  }
1681
1809
  ],
1682
1810
  "events": [
1683
1811
  {
1684
1812
  "type": {
1685
- "text": "CustomEvent<SelectedMutationFilterStrings>"
1813
+ "text": "CustomEvent<{nucleotideMutations: string[],aminoAcidMutations: string[],nucleotideInsertions: string[],aminoAcidInsertions: string[]}>"
1686
1814
  },
1687
- "description": "When the mutation filter values have changed",
1815
+ "description": "Fired when: - The user has submitted a valid mutation or insertion - The user has removed a mutation or insertion",
1688
1816
  "name": "gs-mutation-filter-changed"
1689
1817
  },
1690
1818
  {
1691
1819
  "type": {
1692
- "text": "CustomEvent<SelectedMutationFilterStrings>"
1820
+ "text": "CustomEvent<{nucleotideMutations: string[],aminoAcidMutations: string[],nucleotideInsertions: string[],aminoAcidInsertions: string[]}>"
1693
1821
  },
1694
- "description": "When the mutation filter has lost focus",
1822
+ "description": "Fired when: - the mutation filter has lost focus Contains the selected mutations in the format",
1695
1823
  "name": "gs-mutation-filter-on-blur"
1696
1824
  }
1697
1825
  ],
@@ -1702,6 +1830,7 @@
1702
1830
  "text": "SelectedMutationFilterStrings | string[] | undefined"
1703
1831
  },
1704
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.",
1705
1834
  "fieldName": "initialValue"
1706
1835
  }
1707
1836
  ],
@@ -1740,9 +1869,9 @@
1740
1869
  "kind": "variable",
1741
1870
  "name": "meta",
1742
1871
  "type": {
1743
- "text": "Meta"
1872
+ "text": "Meta<TextInputProps>"
1744
1873
  },
1745
- "default": "{\n title: 'Input/Text input',\n component: 'gs-text-input',\n parameters: {\n actions: {\n handles: ['gs-text-input-changed'],\n },\n fetchMock: {\n mocks: [\n {\n matcher: {\n name: 'hosts',\n url: AGGREGATED_ENDPOINT,\n body: {\n fields: ['host'],\n },\n },\n response: {\n status: 200,\n body: data,\n },\n },\n ],\n },\n },\n decorators: [withActions],\n}"
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'], }"
1746
1875
  },
1747
1876
  {
1748
1877
  "kind": "variable",
@@ -1750,7 +1879,7 @@
1750
1879
  "type": {
1751
1880
  "text": "StoryObj<TextInputProps>"
1752
1881
  },
1753
- "default": "{\n render: (args) => {\n return html` <gs-app lapis=\"${LAPIS_URL}\">\n <div class=\"max-w-screen-lg\">\n <gs-text-input\n .lapisField=${args.lapisField}\n .placeholderText=${args.placeholderText}\n .initialValue=${args.initialValue}\n ></gs-text-input>\n </div>\n </gs-app>`;\n },\n args: {\n lapisField: 'host',\n placeholderText: 'Enter host name',\n initialValue: 'Homo sapiens',\n },\n}"
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', }, }"
1754
1883
  },
1755
1884
  {
1756
1885
  "kind": "variable",
@@ -1758,7 +1887,7 @@
1758
1887
  "type": {
1759
1888
  "text": "StoryObj<TextInputProps>"
1760
1889
  },
1761
- "default": "{\n ...Default,\n play: async ({ canvasElement, step }) => {\n const canvas = await withinShadowRoot(canvasElement, 'gs-text-input');\n\n const inputField = () => canvas.getByPlaceholderText('Enter host name');\n const listenerMock = fn();\n await step('Setup event listener mock', async () => {\n canvasElement.addEventListener('gs-text-input-changed', listenerMock);\n });\n\n await step('wait until data is loaded', async () => {\n await waitFor(() => {\n return expect(inputField()).toBeEnabled();\n });\n });\n\n await step('Enters an invalid host name', async () => {\n await userEvent.type(inputField(), 'notInList');\n await expect(listenerMock).not.toHaveBeenCalled();\n await userEvent.type(inputField(), '{backspace>9/}');\n });\n\n await step('Enter a valid host name', async () => {\n await userEvent.type(inputField(), 'Homo');\n\n await expect(listenerMock).toHaveBeenCalledWith(\n expect.objectContaining({\n detail: {\n host: 'Homo',\n },\n }),\n );\n });\n },\n args: {\n ...Default.args,\n initialValue: '',\n },\n}"
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: '', }, }"
1762
1891
  }
1763
1892
  ],
1764
1893
  "exports": [
@@ -1794,7 +1923,7 @@
1794
1923
  "declarations": [
1795
1924
  {
1796
1925
  "kind": "class",
1797
- "description": "",
1926
+ "description": "\n## Context\n\nThis component provides a text input field to specify filters for arbitrary fields of this Lapis instance.",
1798
1927
  "name": "TextInputComponent",
1799
1928
  "members": [
1800
1929
  {
@@ -1804,6 +1933,7 @@
1804
1933
  "text": "string | undefined"
1805
1934
  },
1806
1935
  "default": "''",
1936
+ "description": "The initial value to use for this text input.",
1807
1937
  "attribute": "initialValue"
1808
1938
  },
1809
1939
  {
@@ -1813,6 +1943,7 @@
1813
1943
  "text": "string"
1814
1944
  },
1815
1945
  "default": "''",
1946
+ "description": "The Lapis field name to use for this text input.",
1816
1947
  "attribute": "lapisField"
1817
1948
  },
1818
1949
  {
@@ -1822,6 +1953,7 @@
1822
1953
  "text": "string | undefined"
1823
1954
  },
1824
1955
  "default": "''",
1956
+ "description": "The placeholder text to display in the input field.",
1825
1957
  "attribute": "placeholderText"
1826
1958
  }
1827
1959
  ],
@@ -1830,7 +1962,7 @@
1830
1962
  "type": {
1831
1963
  "text": "CustomEvent<Record<string, string>>"
1832
1964
  },
1833
- "description": "When the text input has changed",
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\" } ```",
1834
1966
  "name": "gs-text-input-changed"
1835
1967
  }
1836
1968
  ],
@@ -1841,6 +1973,7 @@
1841
1973
  "text": "string | undefined"
1842
1974
  },
1843
1975
  "default": "''",
1976
+ "description": "The initial value to use for this text input.",
1844
1977
  "fieldName": "initialValue"
1845
1978
  },
1846
1979
  {
@@ -1849,6 +1982,7 @@
1849
1982
  "text": "string"
1850
1983
  },
1851
1984
  "default": "''",
1985
+ "description": "The Lapis field name to use for this text input.",
1852
1986
  "fieldName": "lapisField"
1853
1987
  },
1854
1988
  {
@@ -1857,6 +1991,7 @@
1857
1991
  "text": "string | undefined"
1858
1992
  },
1859
1993
  "default": "''",
1994
+ "description": "The placeholder text to display in the input field.",
1860
1995
  "fieldName": "placeholderText"
1861
1996
  }
1862
1997
  ],