@genspectrum/dashboard-components 1.8.2 → 1.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/custom-elements.json +2 -2
- package/dist/components.d.ts +25 -25
- package/dist/components.js +165 -44
- package/dist/components.js.map +1 -1
- package/dist/util.d.ts +25 -25
- package/package.json +1 -1
- package/src/preact/components/downshift-combobox.tsx +2 -1
- package/src/preact/components/portal-tooltip.tsx +129 -0
- package/src/preact/components/tooltip.tsx +32 -16
- package/src/preact/lineageFilter/fetchLineageAutocompleteList.spec.ts +72 -0
- package/src/preact/lineageFilter/fetchLineageAutocompleteList.ts +81 -19
- package/src/preact/lineageFilter/lineage-filter.stories.tsx +57 -1
- package/src/preact/lineageFilter/lineage-filter.tsx +1 -1
- package/src/preact/locationFilter/location-filter.tsx +3 -1
- package/src/preact/mutationsOverTime/mutations-over-time-grid-tooltip.tsx +1 -1
- package/src/preact/mutationsOverTime/mutations-over-time-grid.tsx +13 -7
- package/src/preact/mutationsOverTime/mutations-over-time.tsx +13 -4
- package/src/preact/textFilter/text-filter.tsx +3 -1
- package/src/preact/wastewater/mutationsOverTime/wastewater-mutations-over-time.tsx +8 -4
- package/src/web-components/input/gs-lineage-filter.stories.ts +1 -1
- package/src/web-components/input/gs-text-filter.stories.ts +1 -1
- package/standalone-bundle/dashboard-components.js +4452 -4361
- package/standalone-bundle/dashboard-components.js.map +1 -1
package/custom-elements.json
CHANGED
|
@@ -638,7 +638,7 @@
|
|
|
638
638
|
"type": {
|
|
639
639
|
"text": "StoryObj<Required<LineageFilterProps>>"
|
|
640
640
|
},
|
|
641
|
-
"default": "{ ...LineageFilter, play: async ({ canvasElement, step }) => { const canvas = await withinShadowRoot(canvasElement, 'gs-lineage-filter'); const inputField = () => canvas.getByPlaceholderText('Enter a lineage'); const listenerMock = fn(); await step('Setup event listener mock', () => { canvasElement.addEventListener(gsEventNames.lineageFilterChanged, listenerMock); }); await step('wait until data is loaded', async () => { await waitFor(() => { return expect(inputField()).toBeEnabled(); }); }); await step('Enters an invalid lineage value', async () => { await userEvent.type(inputField(), 'notInList'); await expect(listenerMock).not.toHaveBeenCalled(); }); await step('Empty input', async () => { await userEvent.type(inputField(), '{backspace>9/}'); await userEvent.click(canvas.getByLabelText('toggle menu')); await waitFor(() => { return expect(listenerMock.mock.calls.at(-1)![0].detail).toStrictEqual({ pangoLineage: undefined, }); }); }); await step('Enter a valid lineage value', async () => { await userEvent.type(inputField(), 'B.1.1.7*'); await userEvent.click(canvas.getByRole('option', { name: 'B.1.1.7*(
|
|
641
|
+
"default": "{ ...LineageFilter, play: async ({ canvasElement, step }) => { const canvas = await withinShadowRoot(canvasElement, 'gs-lineage-filter'); const inputField = () => canvas.getByPlaceholderText('Enter a lineage'); const listenerMock = fn(); await step('Setup event listener mock', () => { canvasElement.addEventListener(gsEventNames.lineageFilterChanged, listenerMock); }); await step('wait until data is loaded', async () => { await waitFor(() => { return expect(inputField()).toBeEnabled(); }); }); await step('Enters an invalid lineage value', async () => { await userEvent.type(inputField(), 'notInList'); await expect(listenerMock).not.toHaveBeenCalled(); }); await step('Empty input', async () => { await userEvent.type(inputField(), '{backspace>9/}'); await userEvent.click(canvas.getByLabelText('toggle menu')); await waitFor(() => { return expect(listenerMock.mock.calls.at(-1)![0].detail).toStrictEqual({ pangoLineage: undefined, }); }); }); await step('Enter a valid lineage value', async () => { await userEvent.type(inputField(), 'B.1.1.7*'); await userEvent.click(canvas.getByRole('option', { name: 'B.1.1.7*(677,146)' })); await waitFor(() => { return expect(listenerMock.mock.calls.at(-1)![0].detail).toStrictEqual({ pangoLineage: 'B.1.1.7*', }); }); }); }, args: { ...LineageFilter.args, value: '', }, }"
|
|
642
642
|
}
|
|
643
643
|
],
|
|
644
644
|
"exports": [
|
|
@@ -1530,7 +1530,7 @@
|
|
|
1530
1530
|
"type": {
|
|
1531
1531
|
"text": "StoryObj<Required<TextFilterProps>>"
|
|
1532
1532
|
},
|
|
1533
|
-
"default": "{ ...Default, play: async ({ canvasElement, step }) => { const canvas = await withinShadowRoot(canvasElement, 'gs-text-filter'); const inputField = () => canvas.getByPlaceholderText('Enter host name'); const listenerMock = fn(); await step('Setup event listener mock', () => { canvasElement.addEventListener(gsEventNames.textFilterChanged, listenerMock); }); await step('wait until data is loaded', async () => { await waitFor(() => { return expect(inputField()).toBeEnabled(); }); }); await step('Enters an invalid host name', async () => { await userEvent.type(inputField(), 'notInList'); await expect(listenerMock).not.toHaveBeenCalled(); }); await step('Empty input', async () => { await userEvent.type(inputField(), '{backspace>9/}'); }); await step('Enter a valid host name', async () => { await userEvent.type(inputField(), 'Homo s'); const dropdownOption = await canvas.findByText('Homo sapiens'); await userEvent.click(dropdownOption); }); await step('Verify event is fired with correct detail', async () => { await waitFor(async () => { await expect(listenerMock).toHaveBeenCalledWith( expect.objectContaining({ detail: { host: 'Homo sapiens', }, }), ); }); }); await step('Remove initial value', async () => { await userEvent.click(canvas.getByRole('button', { name: 'clear selection' })); await expect(listenerMock).
|
|
1533
|
+
"default": "{ ...Default, play: async ({ canvasElement, step }) => { const canvas = await withinShadowRoot(canvasElement, 'gs-text-filter'); const inputField = () => canvas.getByPlaceholderText('Enter host name'); const listenerMock = fn(); await step('Setup event listener mock', () => { canvasElement.addEventListener(gsEventNames.textFilterChanged, listenerMock); }); await step('wait until data is loaded', async () => { await waitFor(() => { return expect(inputField()).toBeEnabled(); }); }); await step('Enters an invalid host name', async () => { await userEvent.type(inputField(), 'notInList'); await expect(listenerMock).not.toHaveBeenCalled(); }); await step('Empty input', async () => { await userEvent.type(inputField(), '{backspace>9/}'); }); await step('Enter a valid host name', async () => { await userEvent.type(inputField(), 'Homo s'); const dropdownOption = await canvas.findByText('Homo sapiens'); await userEvent.click(dropdownOption); }); await step('Verify event is fired with correct detail', async () => { await waitFor(async () => { await expect(listenerMock).toHaveBeenCalledWith( expect.objectContaining({ detail: { host: 'Homo sapiens', }, }), ); }); }); await step('Remove initial value', async () => { await userEvent.click(canvas.getByRole('button', { name: 'clear selection' })); await expect(listenerMock).toHaveBeenCalledWith( expect.objectContaining({ detail: { host: undefined, }, }), ); }); }, args: { ...Default.args, value: '', }, }"
|
|
1534
1534
|
}
|
|
1535
1535
|
],
|
|
1536
1536
|
"exports": [
|
package/dist/components.d.ts
CHANGED
|
@@ -1651,6 +1651,22 @@ declare global {
|
|
|
1651
1651
|
}
|
|
1652
1652
|
|
|
1653
1653
|
|
|
1654
|
+
declare global {
|
|
1655
|
+
interface HTMLElementTagNameMap {
|
|
1656
|
+
'gs-wastewater-mutations-over-time': WastewaterMutationsOverTimeComponent;
|
|
1657
|
+
}
|
|
1658
|
+
}
|
|
1659
|
+
|
|
1660
|
+
|
|
1661
|
+
declare global {
|
|
1662
|
+
namespace JSX {
|
|
1663
|
+
interface IntrinsicElements {
|
|
1664
|
+
'gs-wastewater-mutations-over-time': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
|
|
1665
|
+
}
|
|
1666
|
+
}
|
|
1667
|
+
}
|
|
1668
|
+
|
|
1669
|
+
|
|
1654
1670
|
declare global {
|
|
1655
1671
|
interface HTMLElementTagNameMap {
|
|
1656
1672
|
'gs-genome-data-viewer': GenomeDataViewerComponent;
|
|
@@ -1749,7 +1765,7 @@ declare global {
|
|
|
1749
1765
|
|
|
1750
1766
|
declare global {
|
|
1751
1767
|
interface HTMLElementTagNameMap {
|
|
1752
|
-
'gs-
|
|
1768
|
+
'gs-number-sequences-over-time': NumberSequencesOverTimeComponent;
|
|
1753
1769
|
}
|
|
1754
1770
|
}
|
|
1755
1771
|
|
|
@@ -1757,7 +1773,7 @@ declare global {
|
|
|
1757
1773
|
declare global {
|
|
1758
1774
|
namespace JSX {
|
|
1759
1775
|
interface IntrinsicElements {
|
|
1760
|
-
'gs-
|
|
1776
|
+
'gs-number-sequences-over-time': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
|
|
1761
1777
|
}
|
|
1762
1778
|
}
|
|
1763
1779
|
}
|
|
@@ -1765,7 +1781,7 @@ declare global {
|
|
|
1765
1781
|
|
|
1766
1782
|
declare global {
|
|
1767
1783
|
interface HTMLElementTagNameMap {
|
|
1768
|
-
'gs-
|
|
1784
|
+
'gs-mutations-over-time': MutationsOverTimeComponent;
|
|
1769
1785
|
}
|
|
1770
1786
|
}
|
|
1771
1787
|
|
|
@@ -1773,7 +1789,7 @@ declare global {
|
|
|
1773
1789
|
declare global {
|
|
1774
1790
|
namespace JSX {
|
|
1775
1791
|
interface IntrinsicElements {
|
|
1776
|
-
'gs-
|
|
1792
|
+
'gs-mutations-over-time': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
|
|
1777
1793
|
}
|
|
1778
1794
|
}
|
|
1779
1795
|
}
|
|
@@ -1813,7 +1829,10 @@ declare global {
|
|
|
1813
1829
|
|
|
1814
1830
|
declare global {
|
|
1815
1831
|
interface HTMLElementTagNameMap {
|
|
1816
|
-
'gs-
|
|
1832
|
+
'gs-location-filter': LocationFilterComponent;
|
|
1833
|
+
}
|
|
1834
|
+
interface HTMLElementEventMap {
|
|
1835
|
+
[gsEventNames.locationChanged]: LocationChangedEvent;
|
|
1817
1836
|
}
|
|
1818
1837
|
}
|
|
1819
1838
|
|
|
@@ -1821,7 +1840,7 @@ declare global {
|
|
|
1821
1840
|
declare global {
|
|
1822
1841
|
namespace JSX {
|
|
1823
1842
|
interface IntrinsicElements {
|
|
1824
|
-
'gs-
|
|
1843
|
+
'gs-location-filter': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
|
|
1825
1844
|
}
|
|
1826
1845
|
}
|
|
1827
1846
|
}
|
|
@@ -1847,25 +1866,6 @@ declare global {
|
|
|
1847
1866
|
}
|
|
1848
1867
|
|
|
1849
1868
|
|
|
1850
|
-
declare global {
|
|
1851
|
-
interface HTMLElementTagNameMap {
|
|
1852
|
-
'gs-location-filter': LocationFilterComponent;
|
|
1853
|
-
}
|
|
1854
|
-
interface HTMLElementEventMap {
|
|
1855
|
-
[gsEventNames.locationChanged]: LocationChangedEvent;
|
|
1856
|
-
}
|
|
1857
|
-
}
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
declare global {
|
|
1861
|
-
namespace JSX {
|
|
1862
|
-
interface IntrinsicElements {
|
|
1863
|
-
'gs-location-filter': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
|
|
1864
|
-
}
|
|
1865
|
-
}
|
|
1866
|
-
}
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
1869
|
declare global {
|
|
1870
1870
|
interface HTMLElementTagNameMap {
|
|
1871
1871
|
'gs-text-filter': TextFilterComponent;
|
package/dist/components.js
CHANGED
|
@@ -1585,6 +1585,20 @@ const MinMaxRangeSlider = ({
|
|
|
1585
1585
|
function getGradientBoundary(x2, lowerBound, upperBound) {
|
|
1586
1586
|
return (x2 - lowerBound) / (upperBound - lowerBound) * 100;
|
|
1587
1587
|
}
|
|
1588
|
+
const TOOLTIP_BASE_STYLES = "z-10 w-max bg-white p-4 border border-gray-200 rounded-md";
|
|
1589
|
+
const Tooltip = ({ children, content, position = "bottom", tooltipStyle }) => {
|
|
1590
|
+
return /* @__PURE__ */ u$1("div", { className: `relative group`, children: [
|
|
1591
|
+
/* @__PURE__ */ u$1("div", { children }),
|
|
1592
|
+
/* @__PURE__ */ u$1(
|
|
1593
|
+
"div",
|
|
1594
|
+
{
|
|
1595
|
+
className: `absolute ${TOOLTIP_BASE_STYLES} invisible group-hover:visible ${getPositionCss(position)}`,
|
|
1596
|
+
style: tooltipStyle,
|
|
1597
|
+
children: content
|
|
1598
|
+
}
|
|
1599
|
+
)
|
|
1600
|
+
] });
|
|
1601
|
+
};
|
|
1588
1602
|
function getPositionCss(position) {
|
|
1589
1603
|
switch (position) {
|
|
1590
1604
|
case "top":
|
|
@@ -1607,19 +1621,6 @@ function getPositionCss(position) {
|
|
|
1607
1621
|
return "";
|
|
1608
1622
|
}
|
|
1609
1623
|
}
|
|
1610
|
-
const Tooltip = ({ children, content, position = "bottom", tooltipStyle }) => {
|
|
1611
|
-
return /* @__PURE__ */ u$1("div", { className: `relative group`, children: [
|
|
1612
|
-
/* @__PURE__ */ u$1("div", { children }),
|
|
1613
|
-
/* @__PURE__ */ u$1(
|
|
1614
|
-
"div",
|
|
1615
|
-
{
|
|
1616
|
-
className: `absolute z-10 w-max bg-white p-4 border border-gray-200 rounded-md invisible group-hover:visible ${getPositionCss(position)}`,
|
|
1617
|
-
style: tooltipStyle,
|
|
1618
|
-
children: content
|
|
1619
|
-
}
|
|
1620
|
-
)
|
|
1621
|
-
] });
|
|
1622
|
-
};
|
|
1623
1624
|
const ColorsRGB = {
|
|
1624
1625
|
indigo: [51, 34, 136],
|
|
1625
1626
|
green: [17, 119, 51],
|
|
@@ -7422,7 +7423,7 @@ const MutationsOverTimeGridTooltip = ({
|
|
|
7422
7423
|
value
|
|
7423
7424
|
}) => {
|
|
7424
7425
|
const dateClass = toTemporalClass(date);
|
|
7425
|
-
return /* @__PURE__ */ u$1("div", { children: [
|
|
7426
|
+
return /* @__PURE__ */ u$1("div", { className: "text-center", children: [
|
|
7426
7427
|
/* @__PURE__ */ u$1("p", { children: /* @__PURE__ */ u$1("span", { className: "font-bold", children: dateClass.englishName() }) }),
|
|
7427
7428
|
/* @__PURE__ */ u$1("p", { children: [
|
|
7428
7429
|
"(",
|
|
@@ -7549,6 +7550,79 @@ const getTextColorForScale = (value, colorScale) => {
|
|
|
7549
7550
|
const alpha = (value - colorScale.min) / colorRange;
|
|
7550
7551
|
return alpha <= 0.5 ? "black" : "white";
|
|
7551
7552
|
};
|
|
7553
|
+
const PortalTooltip = ({
|
|
7554
|
+
children,
|
|
7555
|
+
content,
|
|
7556
|
+
position = "bottom",
|
|
7557
|
+
tooltipStyle,
|
|
7558
|
+
portalTarget
|
|
7559
|
+
}) => {
|
|
7560
|
+
const [isHovered, setIsHovered] = d(false);
|
|
7561
|
+
const [tooltipPosition, setTooltipPosition] = d({ top: 0, left: 0 });
|
|
7562
|
+
const triggerRef = A$1(null);
|
|
7563
|
+
const tooltipRef = A$1(null);
|
|
7564
|
+
_(() => {
|
|
7565
|
+
if (isHovered && triggerRef.current !== null && tooltipRef.current !== null) {
|
|
7566
|
+
const triggerRect = triggerRef.current.getBoundingClientRect();
|
|
7567
|
+
const tooltipRect = tooltipRef.current.getBoundingClientRect();
|
|
7568
|
+
const newPosition = calculateTooltipPosition(triggerRect, tooltipRect, position);
|
|
7569
|
+
setTooltipPosition(newPosition);
|
|
7570
|
+
}
|
|
7571
|
+
}, [isHovered, position]);
|
|
7572
|
+
const tooltipContent = /* @__PURE__ */ u$1(
|
|
7573
|
+
"div",
|
|
7574
|
+
{
|
|
7575
|
+
ref: tooltipRef,
|
|
7576
|
+
className: `fixed ${TOOLTIP_BASE_STYLES} ${isHovered ? "visible" : "invisible"}`,
|
|
7577
|
+
style: Object.assign({}, tooltipStyle, { top: tooltipPosition.top, left: tooltipPosition.left }),
|
|
7578
|
+
children: content
|
|
7579
|
+
}
|
|
7580
|
+
);
|
|
7581
|
+
return /* @__PURE__ */ u$1(Fragment, { children: [
|
|
7582
|
+
/* @__PURE__ */ u$1("div", { ref: triggerRef, onMouseEnter: () => setIsHovered(true), onMouseLeave: () => setIsHovered(false), children }),
|
|
7583
|
+
portalTarget !== null && $(tooltipContent, portalTarget)
|
|
7584
|
+
] });
|
|
7585
|
+
};
|
|
7586
|
+
function calculateTooltipPosition(triggerRect, tooltipRect, position) {
|
|
7587
|
+
const gap = 4;
|
|
7588
|
+
let top;
|
|
7589
|
+
let left;
|
|
7590
|
+
switch (position) {
|
|
7591
|
+
case "top":
|
|
7592
|
+
top = triggerRect.top - tooltipRect.height - gap;
|
|
7593
|
+
left = triggerRect.left + triggerRect.width / 2 - tooltipRect.width / 2;
|
|
7594
|
+
break;
|
|
7595
|
+
case "top-start":
|
|
7596
|
+
top = triggerRect.top - tooltipRect.height - gap;
|
|
7597
|
+
left = triggerRect.left;
|
|
7598
|
+
break;
|
|
7599
|
+
case "top-end":
|
|
7600
|
+
top = triggerRect.top - tooltipRect.height - gap;
|
|
7601
|
+
left = triggerRect.right - tooltipRect.width;
|
|
7602
|
+
break;
|
|
7603
|
+
case "bottom":
|
|
7604
|
+
top = triggerRect.bottom + gap;
|
|
7605
|
+
left = triggerRect.left + triggerRect.width / 2 - tooltipRect.width / 2;
|
|
7606
|
+
break;
|
|
7607
|
+
case "bottom-start":
|
|
7608
|
+
top = triggerRect.bottom + gap;
|
|
7609
|
+
left = triggerRect.left;
|
|
7610
|
+
break;
|
|
7611
|
+
case "bottom-end":
|
|
7612
|
+
top = triggerRect.bottom + gap;
|
|
7613
|
+
left = triggerRect.right - tooltipRect.width;
|
|
7614
|
+
break;
|
|
7615
|
+
case "left":
|
|
7616
|
+
top = triggerRect.top + triggerRect.height / 2 - tooltipRect.height / 2;
|
|
7617
|
+
left = triggerRect.left - tooltipRect.width - gap;
|
|
7618
|
+
break;
|
|
7619
|
+
case "right":
|
|
7620
|
+
top = triggerRect.top + triggerRect.height / 2 - tooltipRect.height / 2;
|
|
7621
|
+
left = triggerRect.right + gap;
|
|
7622
|
+
break;
|
|
7623
|
+
}
|
|
7624
|
+
return { top, left };
|
|
7625
|
+
}
|
|
7552
7626
|
const pageSizeContext = createContext$1({
|
|
7553
7627
|
pageSize: -1,
|
|
7554
7628
|
setPageSize: () => {
|
|
@@ -7731,7 +7805,8 @@ const MutationsOverTimeGrid = ({
|
|
|
7731
7805
|
data,
|
|
7732
7806
|
colorScale,
|
|
7733
7807
|
sequenceType,
|
|
7734
|
-
pageSizes
|
|
7808
|
+
pageSizes,
|
|
7809
|
+
tooltipPortalTarget
|
|
7735
7810
|
}) => {
|
|
7736
7811
|
const tableData = T$1(() => {
|
|
7737
7812
|
const allMutations = data.getFirstAxisKeys();
|
|
@@ -7772,14 +7847,15 @@ const MutationsOverTimeGrid = ({
|
|
|
7772
7847
|
columnIndex,
|
|
7773
7848
|
numberOfColumns
|
|
7774
7849
|
),
|
|
7775
|
-
colorScale
|
|
7850
|
+
colorScale,
|
|
7851
|
+
tooltipPortalTarget
|
|
7776
7852
|
}
|
|
7777
7853
|
) });
|
|
7778
7854
|
}
|
|
7779
7855
|
});
|
|
7780
7856
|
});
|
|
7781
7857
|
return [mutationHeader, ...dateHeaders];
|
|
7782
|
-
}, [colorScale, data, sequenceType]);
|
|
7858
|
+
}, [colorScale, data, sequenceType, tooltipPortalTarget]);
|
|
7783
7859
|
const { pageSize } = usePageSizeContext();
|
|
7784
7860
|
const table = usePreactTable({
|
|
7785
7861
|
data: tableData,
|
|
@@ -7818,13 +7894,14 @@ function getTooltipPosition(rowIndex, rows, columnIndex, columns) {
|
|
|
7818
7894
|
const tooltipY = columnIndex < columns / 2 ? "start" : "end";
|
|
7819
7895
|
return `${tooltipX}-${tooltipY}`;
|
|
7820
7896
|
}
|
|
7821
|
-
const ProportionCell = ({ value, mutation, date, tooltipPosition, colorScale }) => {
|
|
7897
|
+
const ProportionCell = ({ value, mutation, date, tooltipPosition, colorScale, tooltipPortalTarget }) => {
|
|
7822
7898
|
const proportion = (value == null ? void 0 : value.type) === "belowThreshold" ? void 0 : value == null ? void 0 : value.proportion;
|
|
7823
7899
|
return /* @__PURE__ */ u$1("div", { className: "py-1 w-full h-full", children: /* @__PURE__ */ u$1(
|
|
7824
|
-
|
|
7900
|
+
PortalTooltip,
|
|
7825
7901
|
{
|
|
7826
7902
|
content: /* @__PURE__ */ u$1(MutationsOverTimeGridTooltip, { mutation, date, value }),
|
|
7827
7903
|
position: tooltipPosition,
|
|
7904
|
+
portalTarget: tooltipPortalTarget,
|
|
7828
7905
|
children: /* @__PURE__ */ u$1(
|
|
7829
7906
|
"div",
|
|
7830
7907
|
{
|
|
@@ -8053,6 +8130,11 @@ const MutationsOverTimeTabs$1 = ({
|
|
|
8053
8130
|
overallMutationData
|
|
8054
8131
|
}) => {
|
|
8055
8132
|
const tabsRef = useDispatchFinishedLoadingEvent();
|
|
8133
|
+
const tooltipPortalTargetRef = A$1(null);
|
|
8134
|
+
const [tooltipPortalTarget, setTooltipPortalTarget] = d(null);
|
|
8135
|
+
_(() => {
|
|
8136
|
+
setTooltipPortalTarget(tooltipPortalTargetRef.current);
|
|
8137
|
+
}, []);
|
|
8056
8138
|
const [mutationFilterValue, setMutationFilterValue] = d({
|
|
8057
8139
|
textFilter: "",
|
|
8058
8140
|
annotationNameFilter: /* @__PURE__ */ new Set()
|
|
@@ -8102,7 +8184,8 @@ const MutationsOverTimeTabs$1 = ({
|
|
|
8102
8184
|
data: filteredData,
|
|
8103
8185
|
colorScale,
|
|
8104
8186
|
sequenceType: originalComponentProps.sequenceType,
|
|
8105
|
-
pageSizes: originalComponentProps.pageSizes
|
|
8187
|
+
pageSizes: originalComponentProps.pageSizes,
|
|
8188
|
+
tooltipPortalTarget
|
|
8106
8189
|
}
|
|
8107
8190
|
)
|
|
8108
8191
|
};
|
|
@@ -8129,7 +8212,7 @@ const MutationsOverTimeTabs$1 = ({
|
|
|
8129
8212
|
mutationFilterValue
|
|
8130
8213
|
}
|
|
8131
8214
|
);
|
|
8132
|
-
return /* @__PURE__ */ u$1(PageSizeContextProvider, { pageSizes: originalComponentProps.pageSizes, children: /* @__PURE__ */ u$1(Tabs, { ref: tabsRef, tabs, toolbar }) });
|
|
8215
|
+
return /* @__PURE__ */ u$1("div", { ref: tooltipPortalTargetRef, children: /* @__PURE__ */ u$1(PageSizeContextProvider, { pageSizes: originalComponentProps.pageSizes, children: /* @__PURE__ */ u$1(Tabs, { ref: tabsRef, tabs, toolbar }) }) });
|
|
8133
8216
|
};
|
|
8134
8217
|
const Toolbar$2 = ({
|
|
8135
8218
|
activeTab,
|
|
@@ -9718,6 +9801,7 @@ const MutationsOverTimeTabs = ({
|
|
|
9718
9801
|
originalComponentProps
|
|
9719
9802
|
}) => {
|
|
9720
9803
|
const tabsRef = useDispatchFinishedLoadingEvent();
|
|
9804
|
+
const tooltipPortalTargetRef = A$1(null);
|
|
9721
9805
|
const [mutationFilterValue, setMutationFilterValue] = d({
|
|
9722
9806
|
textFilter: "",
|
|
9723
9807
|
annotationNameFilter: /* @__PURE__ */ new Set()
|
|
@@ -9740,7 +9824,8 @@ const MutationsOverTimeTabs = ({
|
|
|
9740
9824
|
}),
|
|
9741
9825
|
colorScale,
|
|
9742
9826
|
pageSizes: originalComponentProps.pageSizes,
|
|
9743
|
-
sequenceType: originalComponentProps.sequenceType
|
|
9827
|
+
sequenceType: originalComponentProps.sequenceType,
|
|
9828
|
+
tooltipPortalTarget: tooltipPortalTargetRef.current
|
|
9744
9829
|
}
|
|
9745
9830
|
)
|
|
9746
9831
|
})),
|
|
@@ -9767,7 +9852,7 @@ const MutationsOverTimeTabs = ({
|
|
|
9767
9852
|
mutationFilterValue
|
|
9768
9853
|
}
|
|
9769
9854
|
);
|
|
9770
|
-
return /* @__PURE__ */ u$1(PageSizeContextProvider, { pageSizes: originalComponentProps.pageSizes, children: /* @__PURE__ */ u$1(Tabs, { ref: tabsRef, tabs, toolbar }) });
|
|
9855
|
+
return /* @__PURE__ */ u$1("div", { ref: tooltipPortalTargetRef, children: /* @__PURE__ */ u$1(PageSizeContextProvider, { pageSizes: originalComponentProps.pageSizes, children: /* @__PURE__ */ u$1(Tabs, { ref: tabsRef, tabs, toolbar }) }) });
|
|
9771
9856
|
};
|
|
9772
9857
|
const Toolbar = ({
|
|
9773
9858
|
colorScale,
|
|
@@ -14106,7 +14191,8 @@ function DownshiftCombobox({
|
|
|
14106
14191
|
highlightedIndex,
|
|
14107
14192
|
getItemProps,
|
|
14108
14193
|
inputValue,
|
|
14109
|
-
closeMenu
|
|
14194
|
+
closeMenu,
|
|
14195
|
+
reset
|
|
14110
14196
|
} = useCombobox({
|
|
14111
14197
|
onInputValueChange({ inputValue: inputValue2 }) {
|
|
14112
14198
|
setInputIsInvalid(false);
|
|
@@ -14136,7 +14222,7 @@ function DownshiftCombobox({
|
|
|
14136
14222
|
setInputIsInvalid(true);
|
|
14137
14223
|
};
|
|
14138
14224
|
const clearInput = () => {
|
|
14139
|
-
|
|
14225
|
+
reset();
|
|
14140
14226
|
};
|
|
14141
14227
|
const buttonRef = A$1(null);
|
|
14142
14228
|
return /* @__PURE__ */ u$1("div", { ref: divRef, className: "relative w-full", children: [
|
|
@@ -14273,7 +14359,7 @@ const LocationSelector = ({
|
|
|
14273
14359
|
/* @__PURE__ */ u$1("span", { children: item.label }),
|
|
14274
14360
|
!hideCounts && /* @__PURE__ */ u$1("span", { className: "ml-2 text-gray-500", children: [
|
|
14275
14361
|
"(",
|
|
14276
|
-
item.count,
|
|
14362
|
+
item.count.toLocaleString("en-US"),
|
|
14277
14363
|
")"
|
|
14278
14364
|
] })
|
|
14279
14365
|
] }),
|
|
@@ -14445,7 +14531,7 @@ const TextSelector = ({
|
|
|
14445
14531
|
/* @__PURE__ */ u$1("span", { children: item.value }),
|
|
14446
14532
|
!hideCounts && /* @__PURE__ */ u$1("span", { className: "ml-2 text-gray-500", children: [
|
|
14447
14533
|
"(",
|
|
14448
|
-
item.count,
|
|
14534
|
+
item.count.toLocaleString("en-US"),
|
|
14449
14535
|
")"
|
|
14450
14536
|
] })
|
|
14451
14537
|
] });
|
|
@@ -15310,27 +15396,35 @@ async function fetchLineageAutocompleteList({
|
|
|
15310
15396
|
lapisFilter,
|
|
15311
15397
|
signal
|
|
15312
15398
|
}) {
|
|
15313
|
-
const [countsByLineage, lineageTree] = await Promise.all([
|
|
15399
|
+
const [countsByLineage, { lineageTree, aliasMapping }] = await Promise.all([
|
|
15314
15400
|
getCountsByLineage({
|
|
15315
15401
|
lapisUrl,
|
|
15316
15402
|
lapisField,
|
|
15317
15403
|
lapisFilter,
|
|
15318
15404
|
signal
|
|
15319
15405
|
}),
|
|
15320
|
-
|
|
15406
|
+
getLineageTreeAndAliases({ lapisUrl, lapisField, signal })
|
|
15321
15407
|
]);
|
|
15322
|
-
|
|
15323
|
-
|
|
15324
|
-
|
|
15325
|
-
|
|
15326
|
-
|
|
15327
|
-
|
|
15328
|
-
|
|
15329
|
-
|
|
15330
|
-
|
|
15331
|
-
|
|
15332
|
-
|
|
15333
|
-
|
|
15408
|
+
const prefixToLineage = findMissingPrefixMappings(lineageTree, aliasMapping);
|
|
15409
|
+
const actualLineageItems = Array.from(lineageTree.keys()).flatMap((lineage2) => [
|
|
15410
|
+
{
|
|
15411
|
+
lineage: lineage2,
|
|
15412
|
+
count: countsByLineage.get(lineage2) ?? 0
|
|
15413
|
+
},
|
|
15414
|
+
{
|
|
15415
|
+
lineage: `${lineage2}*`,
|
|
15416
|
+
count: getCountsIncludingSublineages(lineage2, lineageTree, countsByLineage)
|
|
15417
|
+
}
|
|
15418
|
+
]);
|
|
15419
|
+
const prefixAliasItems = Array.from(prefixToLineage.entries()).map(([prefix, actualLineage]) => ({
|
|
15420
|
+
lineage: `${prefix}*`,
|
|
15421
|
+
count: getCountsIncludingSublineages(actualLineage, lineageTree, countsByLineage)
|
|
15422
|
+
}));
|
|
15423
|
+
return [...actualLineageItems, ...prefixAliasItems].sort((a2, b2) => {
|
|
15424
|
+
const aKey = a2.lineage.replace(/\*/g, " ");
|
|
15425
|
+
const bKey = b2.lineage.replace(/\*/g, " ");
|
|
15426
|
+
return aKey.localeCompare(bKey);
|
|
15427
|
+
});
|
|
15334
15428
|
}
|
|
15335
15429
|
async function getCountsByLineage({
|
|
15336
15430
|
lapisUrl,
|
|
@@ -15344,18 +15438,22 @@ async function getCountsByLineage({
|
|
|
15344
15438
|
const countsByLineageArray = (await fetchAggregatedOperator.evaluate(lapisUrl, signal)).content;
|
|
15345
15439
|
return new Map(countsByLineageArray.map((value) => [value[lapisField], value.count]));
|
|
15346
15440
|
}
|
|
15347
|
-
async function
|
|
15441
|
+
async function getLineageTreeAndAliases({
|
|
15348
15442
|
lapisUrl,
|
|
15349
15443
|
lapisField,
|
|
15350
15444
|
signal
|
|
15351
15445
|
}) {
|
|
15352
15446
|
const lineageDefinitions = await fetchLineageDefinition({ lapisUrl, lapisField, signal });
|
|
15353
15447
|
const lineageTree = /* @__PURE__ */ new Map();
|
|
15448
|
+
const aliasMapping = /* @__PURE__ */ new Map();
|
|
15354
15449
|
Object.entries(lineageDefinitions).forEach(([lineage2, definition]) => {
|
|
15355
15450
|
var _a;
|
|
15356
15451
|
if (!lineageTree.has(lineage2)) {
|
|
15357
15452
|
lineageTree.set(lineage2, { children: [] });
|
|
15358
15453
|
}
|
|
15454
|
+
if (definition.aliases && definition.aliases.length > 0) {
|
|
15455
|
+
aliasMapping.set(lineage2, definition.aliases);
|
|
15456
|
+
}
|
|
15359
15457
|
(_a = definition.parents) == null ? void 0 : _a.forEach((parent) => {
|
|
15360
15458
|
var _a2;
|
|
15361
15459
|
const parentChildren = (_a2 = lineageTree.get(parent)) == null ? void 0 : _a2.children;
|
|
@@ -15363,7 +15461,7 @@ async function getLineageTree({
|
|
|
15363
15461
|
lineageTree.set(parent, { children: newParentChildren });
|
|
15364
15462
|
});
|
|
15365
15463
|
});
|
|
15366
|
-
return lineageTree;
|
|
15464
|
+
return { lineageTree, aliasMapping };
|
|
15367
15465
|
}
|
|
15368
15466
|
function getCountsIncludingSublineages(lineage2, lineageTree, countsByLineage) {
|
|
15369
15467
|
const descendants = getAllDescendants(lineage2, lineageTree);
|
|
@@ -15381,6 +15479,29 @@ function getAllDescendants(lineage2, lineageTree) {
|
|
|
15381
15479
|
});
|
|
15382
15480
|
return /* @__PURE__ */ new Set([...children, ...childrenOfChildren.flatMap((child) => Array.from(child))]);
|
|
15383
15481
|
}
|
|
15482
|
+
function findMissingPrefixMappings(lineageTree, aliasMapping) {
|
|
15483
|
+
const lineages = Array.from(lineageTree.keys());
|
|
15484
|
+
const lineagesSet = new Set(lineages);
|
|
15485
|
+
const allPrefixes = lineages.flatMap((lineage2) => {
|
|
15486
|
+
const parts = lineage2.split(".");
|
|
15487
|
+
return parts.map((_2, i2) => parts.slice(0, i2 + 1).join("."));
|
|
15488
|
+
});
|
|
15489
|
+
const missingPrefixes = new Set(allPrefixes.filter((prefix) => !lineagesSet.has(prefix)));
|
|
15490
|
+
const reverseAliasMapping = /* @__PURE__ */ new Map();
|
|
15491
|
+
aliasMapping.forEach((aliases, lineage2) => {
|
|
15492
|
+
aliases.forEach((alias) => {
|
|
15493
|
+
reverseAliasMapping.set(alias, lineage2);
|
|
15494
|
+
});
|
|
15495
|
+
});
|
|
15496
|
+
const prefixToLineage = /* @__PURE__ */ new Map();
|
|
15497
|
+
missingPrefixes.forEach((prefix) => {
|
|
15498
|
+
const actualLineage = reverseAliasMapping.get(prefix);
|
|
15499
|
+
if (actualLineage) {
|
|
15500
|
+
prefixToLineage.set(prefix, actualLineage);
|
|
15501
|
+
}
|
|
15502
|
+
});
|
|
15503
|
+
return prefixToLineage;
|
|
15504
|
+
}
|
|
15384
15505
|
const lineageSelectorPropsSchema = z$2.object({
|
|
15385
15506
|
lapisField: z$2.string().min(1),
|
|
15386
15507
|
placeholderText: z$2.string().optional(),
|
|
@@ -15450,7 +15571,7 @@ const LineageSelector = ({
|
|
|
15450
15571
|
/* @__PURE__ */ u$1("span", { children: item.lineage }),
|
|
15451
15572
|
!hideCounts && /* @__PURE__ */ u$1("span", { className: "ml-2 text-gray-500", children: [
|
|
15452
15573
|
"(",
|
|
15453
|
-
item.count,
|
|
15574
|
+
item.count.toLocaleString("en-US"),
|
|
15454
15575
|
")"
|
|
15455
15576
|
] })
|
|
15456
15577
|
] })
|