@genspectrum/dashboard-components 0.13.4 → 0.13.6

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.
@@ -805,7 +805,7 @@
805
805
  "type": {
806
806
  "text": "StoryObj<LocationFilterProps>"
807
807
  },
808
- "default": "{ ...Template, parameters: { fetchMock: { mocks: [ { matcher: aggregatedEndpointMatcher, response: { status: 200, body: data, }, }, ], }, }, play: async ({ canvasElement, step }) => { const canvas = await withinShadowRoot(canvasElement, 'gs-location-filter'); const inputField = () => canvas.getByRole('combobox'); const listenerMock = fn(); await step('Setup event listener mock', async () => { canvasElement.addEventListener('gs-location-changed', listenerMock); }); await step('wait until data is loaded', async () => { await waitFor(() => { return expect(inputField()).toBeEnabled(); }); }); await step('Input invalid location', async () => { await userEvent.type(inputField(), 'Not / A / Location'); await expect(listenerMock).not.toHaveBeenCalled(); }); await step('Empty input', async () => { await userEvent.type(inputField(), '{backspace>18/}'); await userEvent.click(canvas.getByLabelText('toggle menu')); await waitFor(() => { return expect(listenerMock.mock.calls.at(-1)![0].detail).toStrictEqual({ region: undefined, country: undefined, division: undefined, location: undefined, }); }); }); await step('Select Asia', async () => { await userEvent.type(inputField(), 'Asia'); await userEvent.click(canvas.getByRole('option', { name: 'Asia Asia' })); await waitFor(() => { return expect(listenerMock.mock.calls.at(-1)![0].detail).toStrictEqual({ region: 'Asia', country: undefined, division: undefined, location: undefined, }); }); }); await step('Select Asia / Bangladesh / Rajshahi / Chapainawabgonj', async () => { await userEvent.type(inputField(), ' / Bangladesh / Rajshahi / Chapainawabgonj'); await userEvent.click(canvas.getByText('Asia / Bangladesh / Rajshahi / Chapainawabgonj')); await waitFor(() => { return expect(listenerMock.mock.calls.at(-1)![0].detail).toStrictEqual({ region: 'Asia', country: 'Bangladesh', division: 'Rajshahi', location: 'Chapainawabgonj', }); }); }); }, }"
808
+ "default": "{ ...Template, parameters: { fetchMock: { mocks: [ { matcher: aggregatedEndpointMatcher, response: { status: 200, body: data, }, }, ], }, }, play: async ({ canvasElement, step }) => { const canvas = await withinShadowRoot(canvasElement, 'gs-location-filter'); const inputField = () => canvas.getByRole('combobox'); const listenerMock = fn(); await step('Setup event listener mock', async () => { canvasElement.addEventListener('gs-location-changed', listenerMock); }); await step('wait until data is loaded', async () => { await waitFor(() => { return expect(inputField()).toBeEnabled(); }); }); await step('Input invalid location', async () => { await userEvent.type(inputField(), 'Not / A / Location'); await expect(listenerMock).not.toHaveBeenCalled(); }); await step('Empty input', async () => { await userEvent.type(inputField(), '{backspace>18/}'); await userEvent.click(canvas.getByLabelText('toggle menu')); await waitFor(() => { return expect(listenerMock.mock.calls.at(-1)![0].detail).toStrictEqual({ region: undefined, country: undefined, division: undefined, location: undefined, }); }); }); await step('Select Asia', async () => { await userEvent.type(inputField(), 'Asia'); await userEvent.click(canvas.getByRole('option', { name: /^Asia.*Asia$/ })); await waitFor(() => { return expect(listenerMock.mock.calls.at(-1)![0].detail).toStrictEqual({ region: 'Asia', country: undefined, division: undefined, location: undefined, }); }); }); await step('Select Asia / Bangladesh / Rajshahi / Chapainawabgonj', async () => { await userEvent.type(inputField(), ' / Bangladesh / Rajshahi / Chapainawabgonj'); await userEvent.click(canvas.getByText('Asia / Bangladesh / Rajshahi / Chapainawabgonj')); await waitFor(() => { return expect(listenerMock.mock.calls.at(-1)![0].detail).toStrictEqual({ region: 'Asia', country: 'Bangladesh', division: 'Rajshahi', location: 'Chapainawabgonj', }); }); }); }, }"
809
809
  }
810
810
  ],
811
811
  "exports": [
@@ -4047,6 +4047,16 @@
4047
4047
  "default": "'700px'",
4048
4048
  "description": "The height of the component.\n\nVisit https://genspectrum.github.io/dashboard-components/?path=/docs/components-size-of-components--docs for more information.",
4049
4049
  "attribute": "height"
4050
+ },
4051
+ {
4052
+ "kind": "field",
4053
+ "name": "maxNumberOfGridRows",
4054
+ "type": {
4055
+ "text": "number"
4056
+ },
4057
+ "default": "100",
4058
+ "description": "The maximum number of grid rows to display.\n\nVisit https://genspectrum.github.io/dashboard-components/?path=/docs/components-size-of-components--docs for more information.",
4059
+ "attribute": "maxNumberOfGridRows"
4050
4060
  }
4051
4061
  ],
4052
4062
  "attributes": [
@@ -4085,6 +4095,15 @@
4085
4095
  "default": "'700px'",
4086
4096
  "description": "The height of the component.\n\nVisit https://genspectrum.github.io/dashboard-components/?path=/docs/components-size-of-components--docs for more information.",
4087
4097
  "fieldName": "height"
4098
+ },
4099
+ {
4100
+ "name": "maxNumberOfGridRows",
4101
+ "type": {
4102
+ "text": "number"
4103
+ },
4104
+ "default": "100",
4105
+ "description": "The maximum number of grid rows to display.\n\nVisit https://genspectrum.github.io/dashboard-components/?path=/docs/components-size-of-components--docs for more information.",
4106
+ "fieldName": "maxNumberOfGridRows"
4088
4107
  }
4089
4108
  ],
4090
4109
  "superclass": {
@@ -1317,6 +1317,12 @@ export declare class WastewaterMutationsOverTimeComponent extends PreactLitAdapt
1317
1317
  * Visit https://genspectrum.github.io/dashboard-components/?path=/docs/components-size-of-components--docs for more information.
1318
1318
  */
1319
1319
  height: string;
1320
+ /**
1321
+ * The maximum number of grid rows to display.
1322
+ *
1323
+ * Visit https://genspectrum.github.io/dashboard-components/?path=/docs/components-size-of-components--docs for more information.
1324
+ */
1325
+ maxNumberOfGridRows: number;
1320
1326
  render(): JSX_2.Element;
1321
1327
  }
1322
1328
 
@@ -1380,7 +1386,7 @@ declare global {
1380
1386
 
1381
1387
  declare global {
1382
1388
  interface HTMLElementTagNameMap {
1383
- 'gs-relative-growth-advantage': RelativeGrowthAdvantageComponent;
1389
+ 'gs-prevalence-over-time': PrevalenceOverTimeComponent;
1384
1390
  }
1385
1391
  }
1386
1392
 
@@ -1388,7 +1394,7 @@ declare global {
1388
1394
  declare global {
1389
1395
  namespace JSX {
1390
1396
  interface IntrinsicElements {
1391
- 'gs-relative-growth-advantage': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1397
+ 'gs-prevalence-over-time': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1392
1398
  }
1393
1399
  }
1394
1400
  }
@@ -1396,7 +1402,7 @@ declare global {
1396
1402
 
1397
1403
  declare global {
1398
1404
  interface HTMLElementTagNameMap {
1399
- 'gs-aggregate': AggregateComponent;
1405
+ 'gs-relative-growth-advantage': RelativeGrowthAdvantageComponent;
1400
1406
  }
1401
1407
  }
1402
1408
 
@@ -1404,7 +1410,7 @@ declare global {
1404
1410
  declare global {
1405
1411
  namespace JSX {
1406
1412
  interface IntrinsicElements {
1407
- 'gs-aggregate': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1413
+ 'gs-relative-growth-advantage': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1408
1414
  }
1409
1415
  }
1410
1416
  }
@@ -1412,7 +1418,7 @@ declare global {
1412
1418
 
1413
1419
  declare global {
1414
1420
  interface HTMLElementTagNameMap {
1415
- 'gs-number-sequences-over-time': NumberSequencesOverTimeComponent;
1421
+ 'gs-aggregate': AggregateComponent;
1416
1422
  }
1417
1423
  }
1418
1424
 
@@ -1420,7 +1426,7 @@ declare global {
1420
1426
  declare global {
1421
1427
  namespace JSX {
1422
1428
  interface IntrinsicElements {
1423
- 'gs-number-sequences-over-time': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1429
+ 'gs-aggregate': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1424
1430
  }
1425
1431
  }
1426
1432
  }
@@ -1428,7 +1434,7 @@ declare global {
1428
1434
 
1429
1435
  declare global {
1430
1436
  interface HTMLElementTagNameMap {
1431
- 'gs-prevalence-over-time': PrevalenceOverTimeComponent;
1437
+ 'gs-mutations-over-time': MutationsOverTimeComponent;
1432
1438
  }
1433
1439
  }
1434
1440
 
@@ -1436,7 +1442,7 @@ declare global {
1436
1442
  declare global {
1437
1443
  namespace JSX {
1438
1444
  interface IntrinsicElements {
1439
- 'gs-prevalence-over-time': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1445
+ 'gs-mutations-over-time': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1440
1446
  }
1441
1447
  }
1442
1448
  }
@@ -1444,7 +1450,7 @@ declare global {
1444
1450
 
1445
1451
  declare global {
1446
1452
  interface HTMLElementTagNameMap {
1447
- 'gs-mutations-over-time': MutationsOverTimeComponent;
1453
+ 'gs-sequences-by-location': SequencesByLocationComponent;
1448
1454
  }
1449
1455
  }
1450
1456
 
@@ -1452,7 +1458,7 @@ declare global {
1452
1458
  declare global {
1453
1459
  namespace JSX {
1454
1460
  interface IntrinsicElements {
1455
- 'gs-mutations-over-time': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1461
+ 'gs-sequences-by-location': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1456
1462
  }
1457
1463
  }
1458
1464
  }
@@ -1460,7 +1466,7 @@ declare global {
1460
1466
 
1461
1467
  declare global {
1462
1468
  interface HTMLElementTagNameMap {
1463
- 'gs-sequences-by-location': SequencesByLocationComponent;
1469
+ 'gs-number-sequences-over-time': NumberSequencesOverTimeComponent;
1464
1470
  }
1465
1471
  }
1466
1472
 
@@ -1468,7 +1474,7 @@ declare global {
1468
1474
  declare global {
1469
1475
  namespace JSX {
1470
1476
  interface IntrinsicElements {
1471
- 'gs-sequences-by-location': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1477
+ 'gs-number-sequences-over-time': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1472
1478
  }
1473
1479
  }
1474
1480
  }
@@ -1528,10 +1534,10 @@ declare global {
1528
1534
 
1529
1535
  declare global {
1530
1536
  interface HTMLElementTagNameMap {
1531
- 'gs-text-input': TextInputComponent;
1537
+ 'gs-location-filter': LocationFilterComponent;
1532
1538
  }
1533
1539
  interface HTMLElementEventMap {
1534
- 'gs-text-input-changed': TextInputChangedEvent;
1540
+ 'gs-location-changed': LocationChangedEvent;
1535
1541
  }
1536
1542
  }
1537
1543
 
@@ -1539,7 +1545,7 @@ declare global {
1539
1545
  declare global {
1540
1546
  namespace JSX {
1541
1547
  interface IntrinsicElements {
1542
- 'gs-text-input': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1548
+ 'gs-location-filter': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1543
1549
  }
1544
1550
  }
1545
1551
  }
@@ -1547,10 +1553,10 @@ declare global {
1547
1553
 
1548
1554
  declare global {
1549
1555
  interface HTMLElementTagNameMap {
1550
- 'gs-mutation-filter': MutationFilterComponent;
1556
+ 'gs-text-input': TextInputComponent;
1551
1557
  }
1552
1558
  interface HTMLElementEventMap {
1553
- 'gs-mutation-filter-changed': CustomEvent<MutationsFilter>;
1559
+ 'gs-text-input-changed': TextInputChangedEvent;
1554
1560
  }
1555
1561
  }
1556
1562
 
@@ -1558,7 +1564,7 @@ declare global {
1558
1564
  declare global {
1559
1565
  namespace JSX {
1560
1566
  interface IntrinsicElements {
1561
- 'gs-mutation-filter': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1567
+ 'gs-text-input': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1562
1568
  }
1563
1569
  }
1564
1570
  }
@@ -1585,10 +1591,10 @@ declare global {
1585
1591
 
1586
1592
  declare global {
1587
1593
  interface HTMLElementTagNameMap {
1588
- 'gs-location-filter': LocationFilterComponent;
1594
+ 'gs-mutation-filter': MutationFilterComponent;
1589
1595
  }
1590
1596
  interface HTMLElementEventMap {
1591
- 'gs-location-changed': LocationChangedEvent;
1597
+ 'gs-mutation-filter-changed': CustomEvent<MutationsFilter>;
1592
1598
  }
1593
1599
  }
1594
1600
 
@@ -1596,7 +1602,7 @@ declare global {
1596
1602
  declare global {
1597
1603
  namespace JSX {
1598
1604
  interface IntrinsicElements {
1599
- 'gs-location-filter': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1605
+ 'gs-mutation-filter': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1600
1606
  }
1601
1607
  }
1602
1608
  }
@@ -1628,6 +1628,25 @@ const LoadingDisplay = () => {
1628
1628
  }
1629
1629
  );
1630
1630
  };
1631
+ const SubstitutionsLink = () => /* @__PURE__ */ u$1(InfoLink, { href: "https://www.genome.gov/genetics-glossary/Substitution", children: "substitutions" });
1632
+ const InsertionsLink = () => /* @__PURE__ */ u$1(InfoLink, { href: "https://www.genome.gov/genetics-glossary/Insertion", children: "insertions" });
1633
+ const DeletionsLink = () => /* @__PURE__ */ u$1(InfoLink, { href: "https://www.genome.gov/genetics-glossary/Deletion", children: "deletions" });
1634
+ const ProportionExplanation = () => /* @__PURE__ */ u$1(Fragment, { children: [
1635
+ /* @__PURE__ */ u$1(InfoHeadline2, { children: "Proportion calculation" }),
1636
+ /* @__PURE__ */ u$1(InfoParagraph, { children: "The proportion of a mutation is calculated by dividing the number of sequences with the mutation by the total number of sequences with a non-ambiguous symbol at the position." }),
1637
+ /* @__PURE__ */ u$1(InfoParagraph, { children: [
1638
+ /* @__PURE__ */ u$1("b", { children: "Example:" }),
1639
+ " Assume we look at nucleotide mutations at position 5 where the reference has a T and assume there are 10 sequences in total:",
1640
+ /* @__PURE__ */ u$1("ul", { className: "list-disc list-inside ml-2", children: [
1641
+ /* @__PURE__ */ u$1("li", { children: "3 sequences have a C," }),
1642
+ /* @__PURE__ */ u$1("li", { children: "2 sequences have a T," }),
1643
+ /* @__PURE__ */ u$1("li", { children: "1 sequence has a G," }),
1644
+ /* @__PURE__ */ u$1("li", { children: "3 sequences have an N," }),
1645
+ /* @__PURE__ */ u$1("li", { children: "1 sequence has a Y (which means T or C)," })
1646
+ ] }),
1647
+ "then the proportion of the T5C mutation is 50%. The 4 sequences that have an N or Y are excluded from the calculation."
1648
+ ] })
1649
+ ] });
1631
1650
  function useFloatingUi(referenceRef, floatingRef, middleware, placement) {
1632
1651
  const cleanupRef = A$1(null);
1633
1652
  y(() => {
@@ -2212,7 +2231,22 @@ const MutationComparisonInfo = ({ originalComponentProps }) => {
2212
2231
  const lapis = x$1(LapisUrlContext);
2213
2232
  return /* @__PURE__ */ u$1(Info, { children: [
2214
2233
  /* @__PURE__ */ u$1(InfoHeadline1, { children: "Info for mutation comparison" }),
2215
- /* @__PURE__ */ u$1(InfoParagraph, { children: "TODO: https://github.com/GenSpectrum/dashboard-components/issues/465" }),
2234
+ /* @__PURE__ */ u$1(InfoParagraph, { children: [
2235
+ "This displays ",
2236
+ /* @__PURE__ */ u$1(SubstitutionsLink, {}),
2237
+ " and ",
2238
+ /* @__PURE__ */ u$1(DeletionsLink, {}),
2239
+ " of several variants. It shows mutations where the proportion for any given variant falls within the range you can select in the component's toolbar."
2240
+ ] }),
2241
+ /* @__PURE__ */ u$1(ProportionExplanation, {}),
2242
+ originalComponentProps.views.includes(views.table) && /* @__PURE__ */ u$1(Fragment, { children: [
2243
+ /* @__PURE__ */ u$1(InfoHeadline2, { children: "Table View" }),
2244
+ /* @__PURE__ */ u$1(InfoParagraph, { children: "The table view displays the proportion of mutations that appear in any of the variants." })
2245
+ ] }),
2246
+ originalComponentProps.views.includes(views.venn) && /* @__PURE__ */ u$1(Fragment, { children: [
2247
+ /* @__PURE__ */ u$1(InfoHeadline2, { children: "Venn Diagram View" }),
2248
+ /* @__PURE__ */ u$1(InfoParagraph, { children: "The Venn diagram view illustrates which mutations overlap between the variants and which are exclusive to specific variants. Mutations overlap if their proportion falls within the selected range for two variants. If the proportion of a mutation is within the selected range for one variant but not for the other, the mutation is considered exclusive to that variant." })
2249
+ ] }),
2216
2250
  /* @__PURE__ */ u$1(InfoComponentCode, { componentName: "mutation-comparison", params: originalComponentProps, lapisUrl: lapis })
2217
2251
  ] });
2218
2252
  };
@@ -6075,31 +6109,16 @@ const MutationsInfo = ({ originalComponentProps }) => {
6075
6109
  return /* @__PURE__ */ u$1(Info, { children: [
6076
6110
  /* @__PURE__ */ u$1(InfoHeadline1, { children: "Mutations" }),
6077
6111
  /* @__PURE__ */ u$1(InfoParagraph, { children: [
6078
- "This shows mutations of a variant. There are three types of mutations:",
6079
- " ",
6080
- /* @__PURE__ */ u$1(InfoLink, { href: "https://www.genome.gov/genetics-glossary/Substitution", children: "substitutions" }),
6112
+ "This shows mutations of a variant. There are three types of mutations: ",
6113
+ /* @__PURE__ */ u$1(SubstitutionsLink, {}),
6081
6114
  ",",
6082
6115
  " ",
6083
- /* @__PURE__ */ u$1(InfoLink, { href: "https://www.genome.gov/genetics-glossary/Deletion", children: "deletions" }),
6084
- " and",
6085
- " ",
6086
- /* @__PURE__ */ u$1(InfoLink, { href: "https://www.genome.gov/genetics-glossary/Insertion", children: "insertions" }),
6116
+ /* @__PURE__ */ u$1(DeletionsLink, {}),
6117
+ " and ",
6118
+ /* @__PURE__ */ u$1(InsertionsLink, {}),
6087
6119
  "."
6088
6120
  ] }),
6089
- /* @__PURE__ */ u$1(InfoHeadline2, { children: "Proportion calculation" }),
6090
- /* @__PURE__ */ u$1(InfoParagraph, { children: "The proportion of a mutation is calculated by dividing the number of sequences with the mutation by the total number of sequences with a non-ambiguous symbol at the position." }),
6091
- /* @__PURE__ */ u$1(InfoParagraph, { children: [
6092
- /* @__PURE__ */ u$1("b", { children: "Example:" }),
6093
- " Assume we look at nucleotide mutations at position 5 where the reference has a T and assume there are 10 sequences in total:",
6094
- /* @__PURE__ */ u$1("ul", { className: "list-disc list-inside ml-2", children: [
6095
- /* @__PURE__ */ u$1("li", { children: "3 sequences have a C," }),
6096
- /* @__PURE__ */ u$1("li", { children: "2 sequences have a T," }),
6097
- /* @__PURE__ */ u$1("li", { children: "1 sequence has a G," }),
6098
- /* @__PURE__ */ u$1("li", { children: "3 sequences have an N," }),
6099
- /* @__PURE__ */ u$1("li", { children: "1 sequence has a Y (which means T or C)," })
6100
- ] }),
6101
- "then the proportion of the T5C mutation is 50%. The 4 sequences that have an N or Y are excluded from the calculation."
6102
- ] }),
6121
+ /* @__PURE__ */ u$1(ProportionExplanation, {}),
6103
6122
  /* @__PURE__ */ u$1(InfoComponentCode, { componentName: "mutations", params: originalComponentProps, lapisUrl: lapis })
6104
6123
  ] });
6105
6124
  };
@@ -9472,15 +9491,20 @@ const Tooltip = ({ children, content, position = "bottom" }) => {
9472
9491
  };
9473
9492
  const MAX_NUMBER_OF_GRID_ROWS = 100;
9474
9493
  const MUTATION_CELL_WIDTH_REM = 8;
9475
- const MutationsOverTimeGrid = ({ data, colorScale }) => {
9494
+ const MutationsOverTimeGrid = ({
9495
+ data,
9496
+ colorScale,
9497
+ maxNumberOfGridRows
9498
+ }) => {
9499
+ const currentMaxNumberOfGridRows = maxNumberOfGridRows ?? MAX_NUMBER_OF_GRID_ROWS;
9476
9500
  const allMutations = data.getFirstAxisKeys();
9477
- const shownMutations = allMutations.slice(0, MAX_NUMBER_OF_GRID_ROWS);
9501
+ const shownMutations = allMutations.slice(0, currentMaxNumberOfGridRows);
9478
9502
  const dates = data.getSecondAxisKeys();
9479
9503
  const gridRef = A$1(null);
9480
9504
  return /* @__PURE__ */ u$1(Fragment, { children: [
9481
- allMutations.length > MAX_NUMBER_OF_GRID_ROWS && /* @__PURE__ */ u$1("div", { className: "pl-2", children: [
9505
+ allMutations.length > currentMaxNumberOfGridRows && /* @__PURE__ */ u$1("div", { className: "pl-2", children: [
9482
9506
  "Showing ",
9483
- MAX_NUMBER_OF_GRID_ROWS,
9507
+ currentMaxNumberOfGridRows,
9484
9508
  " of ",
9485
9509
  allMutations.length,
9486
9510
  " mutations. You can narrow the filter to reduce the number of mutations."
@@ -11135,12 +11159,20 @@ async function queryWastewaterMutationsOverTime(lapis, lapisFilter, signal) {
11135
11159
  "aminoAcidMutationFrequency"
11136
11160
  ]);
11137
11161
  const data = (await fetchData.evaluate(lapis, signal)).content;
11138
- return data.map((row) => ({
11139
- location: row.location,
11140
- date: toTemporalClass(parseDateStringToTemporal(row.date, "day")),
11141
- nucleotideMutationFrequency: row.nucleotideMutationFrequency !== null ? transformMutations(JSON.parse(row.nucleotideMutationFrequency)) : [],
11142
- aminoAcidMutationFrequency: row.aminoAcidMutationFrequency !== null ? transformMutations(JSON.parse(row.aminoAcidMutationFrequency)) : []
11143
- }));
11162
+ return data.map((row) => {
11163
+ try {
11164
+ return {
11165
+ location: row.location,
11166
+ date: toTemporalClass(parseDateStringToTemporal(row.date, "day")),
11167
+ nucleotideMutationFrequency: row.nucleotideMutationFrequency !== null ? transformMutations(JSON.parse(row.nucleotideMutationFrequency)) : [],
11168
+ aminoAcidMutationFrequency: row.aminoAcidMutationFrequency !== null ? transformMutations(JSON.parse(row.aminoAcidMutationFrequency)) : []
11169
+ };
11170
+ } catch (e2) {
11171
+ throw new Error(
11172
+ `Failed to parse row of wastewater data: ${JSON.stringify(row)}: ${(e2 == null ? void 0 : e2.message) ?? "Unknown error"}`
11173
+ );
11174
+ }
11175
+ });
11144
11176
  }
11145
11177
  const mutationFrequencySchema = z$2.record(z$2.number().nullable());
11146
11178
  function transformMutations(input) {
@@ -11148,10 +11180,16 @@ function transformMutations(input) {
11148
11180
  if (!mutationFrequency.success) {
11149
11181
  throw new Error(`Failed to parse mutation frequency: ${mutationFrequency.error.message}`);
11150
11182
  }
11151
- return Object.entries(mutationFrequency.data).map(([key, value]) => ({
11152
- mutation: SubstitutionClass.parse(key),
11153
- proportion: value
11154
- }));
11183
+ return Object.entries(mutationFrequency.data).map(([key, value]) => {
11184
+ const mutation = SubstitutionClass.parse(key);
11185
+ if (mutation === null) {
11186
+ throw new Error(`Failed to parse mutation: "${key}"`);
11187
+ }
11188
+ return {
11189
+ mutation,
11190
+ proportion: value
11191
+ };
11192
+ });
11155
11193
  }
11156
11194
  async function computeWastewaterMutationsOverTimeDataPerLocation(lapis, lapisFilter, sequenceType, signal) {
11157
11195
  const data = await queryWastewaterMutationsOverTime(lapis, lapisFilter, signal);
@@ -11186,7 +11224,8 @@ const wastewaterMutationOverTimeSchema = z$2.object({
11186
11224
  lapisFilter: lapisFilterSchema,
11187
11225
  sequenceType: sequenceTypeSchema,
11188
11226
  width: z$2.string(),
11189
- height: z$2.string()
11227
+ height: z$2.string(),
11228
+ maxNumberOfGridRows: z$2.number().optional()
11190
11229
  });
11191
11230
  const WastewaterMutationsOverTime = (componentProps) => {
11192
11231
  const { width, height } = componentProps;
@@ -11220,18 +11259,20 @@ const WastewaterMutationsOverTimeInner = (componentProps) => {
11220
11259
  MutationsOverTimeTabs,
11221
11260
  {
11222
11261
  mutationOverTimeDataPerLocation,
11223
- originalComponentProps: componentProps
11262
+ originalComponentProps: componentProps,
11263
+ maxNumberOfGridRows: componentProps.maxNumberOfGridRows
11224
11264
  }
11225
11265
  );
11226
11266
  };
11227
11267
  const MutationsOverTimeTabs = ({
11228
11268
  mutationOverTimeDataPerLocation,
11229
- originalComponentProps
11269
+ originalComponentProps,
11270
+ maxNumberOfGridRows
11230
11271
  }) => {
11231
11272
  const [colorScale, setColorScale] = h({ min: 0, max: 1, color: "indigo" });
11232
11273
  const tabs = mutationOverTimeDataPerLocation.map(({ location, data }) => ({
11233
11274
  title: location,
11234
- content: /* @__PURE__ */ u$1(MutationsOverTimeGrid, { data, colorScale })
11275
+ content: /* @__PURE__ */ u$1(MutationsOverTimeGrid, { data, colorScale, maxNumberOfGridRows })
11235
11276
  }));
11236
11277
  const toolbar = /* @__PURE__ */ u$1(
11237
11278
  Toolbar,
@@ -11285,6 +11326,7 @@ let WastewaterMutationsOverTimeComponent = class extends PreactLitAdapterWithGri
11285
11326
  this.sequenceType = "nucleotide";
11286
11327
  this.width = "100%";
11287
11328
  this.height = "700px";
11329
+ this.maxNumberOfGridRows = 100;
11288
11330
  }
11289
11331
  render() {
11290
11332
  return /* @__PURE__ */ u$1(
@@ -11293,7 +11335,8 @@ let WastewaterMutationsOverTimeComponent = class extends PreactLitAdapterWithGri
11293
11335
  lapisFilter: this.lapisFilter,
11294
11336
  sequenceType: this.sequenceType,
11295
11337
  width: this.width,
11296
- height: this.height
11338
+ height: this.height,
11339
+ maxNumberOfGridRows: this.maxNumberOfGridRows
11297
11340
  }
11298
11341
  );
11299
11342
  }
@@ -11310,6 +11353,9 @@ __decorateClass$5([
11310
11353
  __decorateClass$5([
11311
11354
  n$1({ type: String })
11312
11355
  ], WastewaterMutationsOverTimeComponent.prototype, "height", 2);
11356
+ __decorateClass$5([
11357
+ n$1({ type: Number })
11358
+ ], WastewaterMutationsOverTimeComponent.prototype, "maxNumberOfGridRows", 2);
11313
11359
  WastewaterMutationsOverTimeComponent = __decorateClass$5([
11314
11360
  t$3("gs-wastewater-mutations-over-time")
11315
11361
  ], WastewaterMutationsOverTimeComponent);
@@ -11627,26 +11673,46 @@ async function fetchAutocompletionList({
11627
11673
  signal,
11628
11674
  lapisFilter
11629
11675
  }) {
11630
- const toAncestorInHierarchyOverwriteValues = Array(fields.length - 1).fill(0).map((_2, i2) => i2 + 1).map((i2) => fields.slice(i2).reduce((acc, field) => ({ ...acc, [field]: null }), {}));
11676
+ const helpersThatOverwriteAValueToItsAncestor = fields.map(
11677
+ (_2, i2) => fields.slice(i2 + 1).reduce((acc, field) => ({ ...acc, [field]: null }), {})
11678
+ );
11631
11679
  const fetchAggregatedOperator = new FetchAggregatedOperator(
11632
11680
  lapisFilter ?? {},
11633
11681
  fields
11634
11682
  );
11635
11683
  const data = (await fetchAggregatedOperator.evaluate(lapis, signal)).content;
11636
- const locationValues = data.map((entry) => fields.reduce((acc, field) => ({ ...acc, [field]: entry[field] }), {})).reduce((setOfAllHierarchies, entry) => {
11637
- setOfAllHierarchies.add(JSON.stringify(entry));
11638
- toAncestorInHierarchyOverwriteValues.forEach((overwriteValues) => {
11639
- setOfAllHierarchies.add(JSON.stringify({ ...entry, ...overwriteValues }));
11640
- });
11641
- return setOfAllHierarchies;
11642
- }, /* @__PURE__ */ new Set());
11643
- return [...locationValues].map((json) => JSON.parse(json)).sort(compareLocationEntries(fields)).map((entry) => fields.reduce((acc, field) => ({ ...acc, [field]: entry[field] ?? void 0 }), {}));
11684
+ const locationValues = data.map((entry) => ({
11685
+ value: fields.reduce((acc, field) => ({ ...acc, [field]: entry[field] }), {}),
11686
+ count: entry.count
11687
+ })).reduce((mapOfAllHierarchiesAndCounts, entry) => {
11688
+ return addValueAndAllAncestorsToMap(
11689
+ entry,
11690
+ helpersThatOverwriteAValueToItsAncestor,
11691
+ mapOfAllHierarchiesAndCounts
11692
+ );
11693
+ }, /* @__PURE__ */ new Map());
11694
+ return [...locationValues].map(([json, count]) => ({
11695
+ value: JSON.parse(json),
11696
+ count
11697
+ })).sort(compareLocationEntries(fields)).map(({ value, count }) => ({
11698
+ value: fields.reduce((acc, field) => ({ ...acc, [field]: value[field] ?? void 0 }), {}),
11699
+ count
11700
+ }));
11701
+ }
11702
+ function addValueAndAllAncestorsToMap({ value, count }, helpersThatOverwriteAValueToItsAncestor, mapOfAllHierarchiesAndCounts) {
11703
+ const keysOfAllHierarchyLevels = new Set(
11704
+ helpersThatOverwriteAValueToItsAncestor.map((overwriteValues) => ({ ...value, ...overwriteValues })).map((value2) => JSON.stringify(value2))
11705
+ );
11706
+ for (const key of keysOfAllHierarchyLevels) {
11707
+ mapOfAllHierarchiesAndCounts.set(key, (mapOfAllHierarchiesAndCounts.get(key) ?? 0) + count);
11708
+ }
11709
+ return mapOfAllHierarchiesAndCounts;
11644
11710
  }
11645
11711
  function compareLocationEntries(fields) {
11646
11712
  return (a2, b3) => {
11647
11713
  for (const field of fields) {
11648
- const valueA = a2[field];
11649
- const valueB = b3[field];
11714
+ const valueA = a2.value[field];
11715
+ const valueB = b3.value[field];
11650
11716
  if (valueA === valueB) {
11651
11717
  continue;
11652
11718
  }
@@ -14702,12 +14768,12 @@ function DownshiftCombobox({
14702
14768
  children: items.length > 0 ? items.map((item, index) => /* @__PURE__ */ u$1(
14703
14769
  "li",
14704
14770
  {
14705
- className: `${highlightedIndex === index ? "bg-blue-300" : ""} ${selectedItem !== null && itemToString2(selectedItem) === itemToString2(item) ? "font-bold" : ""} py-2 px-3 shadow-sm flex flex-col`,
14771
+ className: `${highlightedIndex === index ? "bg-blue-300" : ""} ${selectedItem !== null && itemToString2(selectedItem) === itemToString2(item) ? "font-bold" : ""} py-2 px-3 shadow-sm`,
14706
14772
  ...getItemProps({ item, index }),
14707
14773
  children: formatItemInList(item)
14708
14774
  },
14709
14775
  itemToString2(item)
14710
- )) : /* @__PURE__ */ u$1("li", { className: "py-2 px-3 shadow-sm flex flex-col", children: "No elements to select." })
14776
+ )) : /* @__PURE__ */ u$1("li", { className: "py-2 px-3 shadow-sm", children: "No elements to select." })
14711
14777
  }
14712
14778
  )
14713
14779
  ] });
@@ -14750,8 +14816,8 @@ const LocationSelector = ({
14750
14816
  return locationData.map((location) => toSelectItem(location, fields)).filter((item) => item !== void 0);
14751
14817
  }, [fields, locationData]);
14752
14818
  const selectedItem = T$1(() => {
14753
- return value !== void 0 ? toSelectItem(value, fields) : void 0;
14754
- }, [fields, value]);
14819
+ return value !== void 0 ? allItems.find((item) => item.description == concatenateLocation(value, fields)) : void 0;
14820
+ }, [fields, value, allItems]);
14755
14821
  return /* @__PURE__ */ u$1(
14756
14822
  DownshiftCombobox,
14757
14823
  {
@@ -14761,12 +14827,17 @@ const LocationSelector = ({
14761
14827
  createEvent: (item) => new LocationChangedEvent((item == null ? void 0 : item.lapisFilter) ?? emptyLocationFilter(fields)),
14762
14828
  itemToString: (item) => (item == null ? void 0 : item.label) ?? "",
14763
14829
  placeholderText,
14764
- formatItemInList: (item) => {
14765
- return /* @__PURE__ */ u$1(Fragment, { children: [
14830
+ formatItemInList: (item) => /* @__PURE__ */ u$1(Fragment, { children: [
14831
+ /* @__PURE__ */ u$1("p", { children: [
14766
14832
  /* @__PURE__ */ u$1("span", { children: item.label }),
14767
- /* @__PURE__ */ u$1("span", { className: "text-sm text-gray-500", children: item.description })
14768
- ] });
14769
- }
14833
+ /* @__PURE__ */ u$1("span", { className: "ml-2 text-gray-500", children: [
14834
+ "(",
14835
+ item.count,
14836
+ ")"
14837
+ ] })
14838
+ ] }),
14839
+ /* @__PURE__ */ u$1("span", { className: "text-sm text-gray-500", children: item.description })
14840
+ ] })
14770
14841
  }
14771
14842
  );
14772
14843
  };
@@ -14777,7 +14848,8 @@ function filterByInputValue$2(item, inputValue) {
14777
14848
  }
14778
14849
  return ((_a = item == null ? void 0 : item.label) == null ? void 0 : _a.toLowerCase().includes(inputValue.toLowerCase())) || (item == null ? void 0 : item.description.toLowerCase().includes(inputValue.toLowerCase()));
14779
14850
  }
14780
- function toSelectItem(locationFilter, fields) {
14851
+ function toSelectItem(locationEntry, fields) {
14852
+ const locationFilter = locationEntry.value;
14781
14853
  const concatenatedLocation = concatenateLocation(locationFilter, fields);
14782
14854
  const lastNonUndefinedField = [...fields].reverse().find((field) => locationFilter[field] !== void 0 && locationFilter[field] !== null);
14783
14855
  if (lastNonUndefinedField === void 0) {
@@ -14786,7 +14858,8 @@ function toSelectItem(locationFilter, fields) {
14786
14858
  return {
14787
14859
  lapisFilter: locationFilter,
14788
14860
  label: locationFilter[lastNonUndefinedField],
14789
- description: concatenatedLocation
14861
+ description: concatenatedLocation,
14862
+ count: locationEntry.count
14790
14863
  };
14791
14864
  }
14792
14865
  function concatenateLocation(locationFilter, fields) {