@genspectrum/dashboard-components 0.1.5 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/custom-elements.json +1161 -928
- package/dist/dashboard-components.js +663 -237
- package/dist/dashboard-components.js.map +1 -1
- package/dist/genspectrum-components.d.ts +177 -140
- package/dist/style.css +247 -50
- package/package.json +2 -3
- package/src/constants.ts +1 -1
- package/src/lapisApi/lapisApi.ts +46 -2
- package/src/lapisApi/lapisTypes.ts +14 -0
- package/src/preact/aggregatedData/aggregate.stories.tsx +4 -2
- package/src/preact/aggregatedData/aggregate.tsx +31 -29
- package/src/preact/components/error-boundary.stories.tsx +54 -0
- package/src/preact/components/error-boundary.tsx +22 -0
- package/src/preact/components/error-display.stories.tsx +32 -4
- package/src/preact/components/error-display.tsx +48 -1
- package/src/preact/components/loading-display.stories.tsx +6 -6
- package/src/preact/components/loading-display.tsx +1 -1
- package/src/preact/components/no-data-display.tsx +5 -1
- package/src/preact/components/resize-container.tsx +5 -14
- package/src/preact/dateRangeSelector/date-range-selector.stories.tsx +19 -0
- package/src/preact/dateRangeSelector/date-range-selector.tsx +38 -7
- package/src/preact/locationFilter/fetchAutocompletionList.ts +15 -1
- package/src/preact/locationFilter/location-filter.stories.tsx +23 -6
- package/src/preact/locationFilter/location-filter.tsx +28 -18
- package/src/preact/mutationComparison/mutation-comparison.stories.tsx +6 -3
- package/src/preact/mutationComparison/mutation-comparison.tsx +33 -32
- package/src/preact/mutationComparison/queryMutationData.ts +2 -3
- package/src/preact/mutationFilter/mutation-filter.stories.tsx +18 -3
- package/src/preact/mutationFilter/mutation-filter.tsx +26 -7
- package/src/preact/mutations/mutations.stories.tsx +6 -3
- package/src/preact/mutations/mutations.tsx +28 -26
- package/src/preact/prevalenceOverTime/prevalence-over-time.stories.tsx +14 -7
- package/src/preact/prevalenceOverTime/prevalence-over-time.tsx +50 -32
- package/src/preact/relativeGrowthAdvantage/relative-growth-advantage.stories.tsx +6 -3
- package/src/preact/relativeGrowthAdvantage/relative-growth-advantage.tsx +46 -32
- package/src/preact/textInput/text-input.stories.tsx +26 -0
- package/src/preact/textInput/text-input.tsx +25 -3
- package/src/query/queryPrevalenceOverTime.ts +4 -10
- package/src/types.ts +4 -1
- package/src/web-components/ResizeContainer.mdx +13 -0
- package/src/web-components/app.stories.ts +1 -2
- package/src/web-components/app.ts +7 -3
- package/src/web-components/index.ts +1 -1
- package/src/web-components/input/{date-range-selector-component.stories.ts → gs-date-range-selector.stories.ts} +29 -4
- package/src/web-components/input/{date-range-selector-component.tsx → gs-date-range-selector.tsx} +32 -10
- package/src/web-components/input/{location-filter-component.stories.ts → gs-location-filter.stories.ts} +32 -5
- package/src/web-components/input/{location-filter-component.tsx → gs-location-filter.tsx} +11 -1
- package/src/web-components/input/{mutation-filter-component.stories.ts → gs-mutation-filter.stories.ts} +23 -4
- package/src/web-components/input/gs-mutation-filter.tsx +126 -0
- package/src/web-components/input/{text-input-component.stories.ts → gs-text-input.stories.ts} +34 -6
- package/src/web-components/input/{text-input-component.tsx → gs-text-input.tsx} +16 -4
- package/src/web-components/input/index.ts +4 -4
- package/src/web-components/input/introduction.mdx +11 -0
- package/src/web-components/introduction.mdx +15 -0
- package/src/web-components/visualization/data_visualization_statistical_analysis.mdx +26 -0
- package/src/web-components/{display/aggregate-component.stories.ts → visualization/gs-aggregate.stories.ts} +23 -11
- package/src/web-components/visualization/gs-aggregate.tsx +88 -0
- package/src/web-components/{display/mutation-comparison-component.stories.ts → visualization/gs-mutation-comparison.stories.ts} +21 -16
- package/src/web-components/{display/mutation-comparison-component.tsx → visualization/gs-mutation-comparison.tsx} +27 -18
- package/src/web-components/{display/mutations-component.stories.ts → visualization/gs-mutations.stories.ts} +20 -15
- package/src/web-components/{display/mutations-component.tsx → visualization/gs-mutations.tsx} +20 -10
- package/src/web-components/{display/prevalence-over-time-component.stories.ts → visualization/gs-prevalence-over-time.stories.ts} +29 -20
- package/src/web-components/{display/prevalence-over-time-component.tsx → visualization/gs-prevalence-over-time.tsx} +47 -22
- package/src/web-components/{display/relative-growth-advantage-component.stories.ts → visualization/gs-relative-growth-advantage.stories.ts} +12 -7
- package/src/web-components/{display/relative-growth-advantage-component.tsx → visualization/gs-relative-growth-advantage.tsx} +21 -9
- package/src/web-components/visualization/index.ts +5 -0
- package/src/web-components/display/aggregate-component.tsx +0 -72
- package/src/web-components/display/index.ts +0 -5
- package/src/web-components/input/mutation-filter-component.tsx +0 -83
package/dist/style.css
CHANGED
|
@@ -975,37 +975,33 @@ html {
|
|
|
975
975
|
--tw-contain-paint: ;
|
|
976
976
|
--tw-contain-style: ;
|
|
977
977
|
}
|
|
978
|
-
.
|
|
978
|
+
.alert {
|
|
979
|
+
display: grid;
|
|
979
980
|
width: 100%;
|
|
981
|
+
grid-auto-flow: row;
|
|
982
|
+
align-content: flex-start;
|
|
983
|
+
align-items: center;
|
|
984
|
+
justify-items: center;
|
|
985
|
+
gap: 1rem;
|
|
986
|
+
text-align: center;
|
|
987
|
+
border-radius: var(--rounded-box, 1rem);
|
|
988
|
+
border-width: 1px;
|
|
989
|
+
--tw-border-opacity: 1;
|
|
990
|
+
border-color: var(--fallback-b2,oklch(var(--b2)/var(--tw-border-opacity)));
|
|
991
|
+
padding: 1rem;
|
|
992
|
+
--tw-text-opacity: 1;
|
|
993
|
+
color: var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity)));
|
|
994
|
+
--alert-bg: var(--fallback-b2,oklch(var(--b2)/1));
|
|
995
|
+
--alert-bg-mix: var(--fallback-b1,oklch(var(--b1)/1));
|
|
996
|
+
background-color: var(--alert-bg);
|
|
980
997
|
}
|
|
981
998
|
@media (min-width: 640px) {
|
|
982
999
|
|
|
983
|
-
.
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
.container {
|
|
990
|
-
max-width: 768px;
|
|
991
|
-
}
|
|
992
|
-
}
|
|
993
|
-
@media (min-width: 1024px) {
|
|
994
|
-
|
|
995
|
-
.container {
|
|
996
|
-
max-width: 1024px;
|
|
997
|
-
}
|
|
998
|
-
}
|
|
999
|
-
@media (min-width: 1280px) {
|
|
1000
|
-
|
|
1001
|
-
.container {
|
|
1002
|
-
max-width: 1280px;
|
|
1003
|
-
}
|
|
1004
|
-
}
|
|
1005
|
-
@media (min-width: 1536px) {
|
|
1006
|
-
|
|
1007
|
-
.container {
|
|
1008
|
-
max-width: 1536px;
|
|
1000
|
+
.alert {
|
|
1001
|
+
grid-auto-flow: column;
|
|
1002
|
+
grid-template-columns: auto minmax(auto,1fr);
|
|
1003
|
+
justify-items: start;
|
|
1004
|
+
text-align: start;
|
|
1009
1005
|
}
|
|
1010
1006
|
}
|
|
1011
1007
|
.avatar.placeholder > div {
|
|
@@ -1081,6 +1077,12 @@ html {
|
|
|
1081
1077
|
.btn:disabled {
|
|
1082
1078
|
pointer-events: none;
|
|
1083
1079
|
}
|
|
1080
|
+
.btn-circle {
|
|
1081
|
+
height: 3rem;
|
|
1082
|
+
width: 3rem;
|
|
1083
|
+
border-radius: 9999px;
|
|
1084
|
+
padding: 0px;
|
|
1085
|
+
}
|
|
1084
1086
|
:where(.btn:is(input[type="checkbox"])),
|
|
1085
1087
|
:where(.btn:is(input[type="radio"])) {
|
|
1086
1088
|
width: auto;
|
|
@@ -1233,6 +1235,17 @@ html {
|
|
|
1233
1235
|
--glass-border-opacity: 15%;
|
|
1234
1236
|
}
|
|
1235
1237
|
|
|
1238
|
+
.btn-ghost:hover {
|
|
1239
|
+
border-color: transparent;
|
|
1240
|
+
}
|
|
1241
|
+
|
|
1242
|
+
@supports (color: oklch(0% 0 0)) {
|
|
1243
|
+
|
|
1244
|
+
.btn-ghost:hover {
|
|
1245
|
+
background-color: var(--fallback-bc,oklch(var(--bc)/0.2));
|
|
1246
|
+
}
|
|
1247
|
+
}
|
|
1248
|
+
|
|
1236
1249
|
.btn-outline.btn-primary:hover {
|
|
1237
1250
|
--tw-text-opacity: 1;
|
|
1238
1251
|
color: var(--fallback-pc,oklch(var(--pc)/var(--tw-text-opacity)));
|
|
@@ -1455,6 +1468,69 @@ html {
|
|
|
1455
1468
|
:where(.menu li) .badge {
|
|
1456
1469
|
justify-self: end;
|
|
1457
1470
|
}
|
|
1471
|
+
.modal {
|
|
1472
|
+
pointer-events: none;
|
|
1473
|
+
position: fixed;
|
|
1474
|
+
inset: 0px;
|
|
1475
|
+
margin: 0px;
|
|
1476
|
+
display: grid;
|
|
1477
|
+
height: 100%;
|
|
1478
|
+
max-height: none;
|
|
1479
|
+
width: 100%;
|
|
1480
|
+
max-width: none;
|
|
1481
|
+
justify-items: center;
|
|
1482
|
+
padding: 0px;
|
|
1483
|
+
opacity: 0;
|
|
1484
|
+
overscroll-behavior: contain;
|
|
1485
|
+
z-index: 999;
|
|
1486
|
+
background-color: transparent;
|
|
1487
|
+
color: inherit;
|
|
1488
|
+
transition-duration: 200ms;
|
|
1489
|
+
transition-timing-function: cubic-bezier(0, 0, 0.2, 1);
|
|
1490
|
+
transition-property: transform, opacity, visibility;
|
|
1491
|
+
overflow-y: hidden;
|
|
1492
|
+
}
|
|
1493
|
+
:where(.modal) {
|
|
1494
|
+
align-items: center;
|
|
1495
|
+
}
|
|
1496
|
+
.modal-box {
|
|
1497
|
+
max-height: calc(100vh - 5em);
|
|
1498
|
+
grid-column-start: 1;
|
|
1499
|
+
grid-row-start: 1;
|
|
1500
|
+
width: 91.666667%;
|
|
1501
|
+
max-width: 32rem;
|
|
1502
|
+
--tw-scale-x: .9;
|
|
1503
|
+
--tw-scale-y: .9;
|
|
1504
|
+
transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
|
|
1505
|
+
border-bottom-right-radius: var(--rounded-box, 1rem);
|
|
1506
|
+
border-bottom-left-radius: var(--rounded-box, 1rem);
|
|
1507
|
+
border-top-left-radius: var(--rounded-box, 1rem);
|
|
1508
|
+
border-top-right-radius: var(--rounded-box, 1rem);
|
|
1509
|
+
--tw-bg-opacity: 1;
|
|
1510
|
+
background-color: var(--fallback-b1,oklch(var(--b1)/var(--tw-bg-opacity)));
|
|
1511
|
+
padding: 1.5rem;
|
|
1512
|
+
transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, -webkit-backdrop-filter;
|
|
1513
|
+
transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter;
|
|
1514
|
+
transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter, -webkit-backdrop-filter;
|
|
1515
|
+
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
|
1516
|
+
transition-timing-function: cubic-bezier(0, 0, 0.2, 1);
|
|
1517
|
+
transition-duration: 200ms;
|
|
1518
|
+
box-shadow: rgba(0, 0, 0, 0.25) 0px 25px 50px -12px;
|
|
1519
|
+
overflow-y: auto;
|
|
1520
|
+
overscroll-behavior: contain;
|
|
1521
|
+
}
|
|
1522
|
+
.modal-open,
|
|
1523
|
+
.modal:target,
|
|
1524
|
+
.modal-toggle:checked + .modal,
|
|
1525
|
+
.modal[open] {
|
|
1526
|
+
pointer-events: auto;
|
|
1527
|
+
visibility: visible;
|
|
1528
|
+
opacity: 1;
|
|
1529
|
+
}
|
|
1530
|
+
:root:has(:is(.modal-open, .modal:target, .modal-toggle:checked + .modal, .modal[open])) {
|
|
1531
|
+
overflow: hidden;
|
|
1532
|
+
scrollbar-gutter: stable;
|
|
1533
|
+
}
|
|
1458
1534
|
.radio {
|
|
1459
1535
|
flex-shrink: 0;
|
|
1460
1536
|
--chkbg: var(--bc);
|
|
@@ -1516,29 +1592,6 @@ html {
|
|
|
1516
1592
|
.select[multiple] {
|
|
1517
1593
|
height: auto;
|
|
1518
1594
|
}
|
|
1519
|
-
.stack {
|
|
1520
|
-
display: inline-grid;
|
|
1521
|
-
place-items: center;
|
|
1522
|
-
align-items: flex-end;
|
|
1523
|
-
}
|
|
1524
|
-
.stack > * {
|
|
1525
|
-
grid-column-start: 1;
|
|
1526
|
-
grid-row-start: 1;
|
|
1527
|
-
transform: translateY(10%) scale(0.9);
|
|
1528
|
-
z-index: 1;
|
|
1529
|
-
width: 100%;
|
|
1530
|
-
opacity: 0.6;
|
|
1531
|
-
}
|
|
1532
|
-
.stack > *:nth-child(2) {
|
|
1533
|
-
transform: translateY(5%) scale(0.95);
|
|
1534
|
-
z-index: 2;
|
|
1535
|
-
opacity: 0.8;
|
|
1536
|
-
}
|
|
1537
|
-
.stack > *:nth-child(1) {
|
|
1538
|
-
transform: translateY(0) scale(1);
|
|
1539
|
-
z-index: 3;
|
|
1540
|
-
opacity: 1;
|
|
1541
|
-
}
|
|
1542
1595
|
.steps {
|
|
1543
1596
|
display: inline-grid;
|
|
1544
1597
|
grid-auto-flow: column;
|
|
@@ -1638,6 +1691,13 @@ input.tab:checked + .tab-content,
|
|
|
1638
1691
|
--tw-bg-opacity: 1;
|
|
1639
1692
|
background-color: var(--fallback-b1,oklch(var(--b1)/var(--tw-bg-opacity)));
|
|
1640
1693
|
}
|
|
1694
|
+
.alert-error {
|
|
1695
|
+
border-color: var(--fallback-er,oklch(var(--er)/0.2));
|
|
1696
|
+
--tw-text-opacity: 1;
|
|
1697
|
+
color: var(--fallback-erc,oklch(var(--erc)/var(--tw-text-opacity)));
|
|
1698
|
+
--alert-bg: var(--fallback-er,oklch(var(--er)/1));
|
|
1699
|
+
--alert-bg-mix: var(--fallback-b1,oklch(var(--b1)/1));
|
|
1700
|
+
}
|
|
1641
1701
|
.btm-nav > *.disabled,
|
|
1642
1702
|
.btm-nav > *[disabled] {
|
|
1643
1703
|
pointer-events: none;
|
|
@@ -1706,6 +1766,20 @@ input.tab:checked + .tab-content,
|
|
|
1706
1766
|
--glass-opacity: 25%;
|
|
1707
1767
|
--glass-border-opacity: 15%;
|
|
1708
1768
|
}
|
|
1769
|
+
.btn-ghost {
|
|
1770
|
+
border-width: 1px;
|
|
1771
|
+
border-color: transparent;
|
|
1772
|
+
background-color: transparent;
|
|
1773
|
+
color: currentColor;
|
|
1774
|
+
--tw-shadow: 0 0 #0000;
|
|
1775
|
+
--tw-shadow-colored: 0 0 #0000;
|
|
1776
|
+
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
|
|
1777
|
+
outline-color: currentColor;
|
|
1778
|
+
}
|
|
1779
|
+
.btn-ghost.btn-active {
|
|
1780
|
+
border-color: transparent;
|
|
1781
|
+
background-color: var(--fallback-bc,oklch(var(--bc)/0.2));
|
|
1782
|
+
}
|
|
1709
1783
|
.btn-outline.btn-primary {
|
|
1710
1784
|
--tw-text-opacity: 1;
|
|
1711
1785
|
color: var(--fallback-p,oklch(var(--p)/var(--tw-text-opacity)));
|
|
@@ -2027,6 +2101,29 @@ input.tab:checked + .tab-content,
|
|
|
2027
2101
|
border-color: currentColor;
|
|
2028
2102
|
opacity: 0.6;
|
|
2029
2103
|
}
|
|
2104
|
+
.modal:not(dialog:not(.modal-open)),
|
|
2105
|
+
.modal::backdrop {
|
|
2106
|
+
background-color: #0006;
|
|
2107
|
+
animation: modal-pop 0.2s ease-out;
|
|
2108
|
+
}
|
|
2109
|
+
.modal-backdrop {
|
|
2110
|
+
z-index: -1;
|
|
2111
|
+
grid-column-start: 1;
|
|
2112
|
+
grid-row-start: 1;
|
|
2113
|
+
display: grid;
|
|
2114
|
+
align-self: stretch;
|
|
2115
|
+
justify-self: stretch;
|
|
2116
|
+
color: transparent;
|
|
2117
|
+
}
|
|
2118
|
+
.modal-open .modal-box,
|
|
2119
|
+
.modal-toggle:checked + .modal .modal-box,
|
|
2120
|
+
.modal:target .modal-box,
|
|
2121
|
+
.modal[open] .modal-box {
|
|
2122
|
+
--tw-translate-y: 0px;
|
|
2123
|
+
--tw-scale-x: 1;
|
|
2124
|
+
--tw-scale-y: 1;
|
|
2125
|
+
transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
|
|
2126
|
+
}
|
|
2030
2127
|
@keyframes modal-pop {
|
|
2031
2128
|
|
|
2032
2129
|
0% {
|
|
@@ -2186,6 +2283,30 @@ input.tab:checked + .tab-content,
|
|
|
2186
2283
|
background-position: calc(0% + 12px) calc(1px + 50%),
|
|
2187
2284
|
calc(0% + 16px) calc(1px + 50%);
|
|
2188
2285
|
}
|
|
2286
|
+
.skeleton {
|
|
2287
|
+
border-radius: var(--rounded-box, 1rem);
|
|
2288
|
+
--tw-bg-opacity: 1;
|
|
2289
|
+
background-color: var(--fallback-b3,oklch(var(--b3)/var(--tw-bg-opacity)));
|
|
2290
|
+
will-change: background-position;
|
|
2291
|
+
animation: skeleton 1.8s ease-in-out infinite;
|
|
2292
|
+
background-image: linear-gradient(
|
|
2293
|
+
105deg,
|
|
2294
|
+
transparent 0%,
|
|
2295
|
+
transparent 40%,
|
|
2296
|
+
var(--fallback-b1,oklch(var(--b1)/1)) 50%,
|
|
2297
|
+
transparent 60%,
|
|
2298
|
+
transparent 100%
|
|
2299
|
+
);
|
|
2300
|
+
background-size: 200% auto;
|
|
2301
|
+
background-repeat: no-repeat;
|
|
2302
|
+
background-position-x: -50%;
|
|
2303
|
+
}
|
|
2304
|
+
@media (prefers-reduced-motion) {
|
|
2305
|
+
|
|
2306
|
+
.skeleton {
|
|
2307
|
+
animation-duration: 15s;
|
|
2308
|
+
}
|
|
2309
|
+
}
|
|
2189
2310
|
@keyframes skeleton {
|
|
2190
2311
|
|
|
2191
2312
|
from {
|
|
@@ -2488,6 +2609,18 @@ input.tab:checked + .tab-content,
|
|
|
2488
2609
|
border-radius: 9999px;
|
|
2489
2610
|
padding: 0px;
|
|
2490
2611
|
}
|
|
2612
|
+
.btn-circle:where(.btn-md) {
|
|
2613
|
+
height: 3rem;
|
|
2614
|
+
width: 3rem;
|
|
2615
|
+
border-radius: 9999px;
|
|
2616
|
+
padding: 0px;
|
|
2617
|
+
}
|
|
2618
|
+
.btn-circle:where(.btn-lg) {
|
|
2619
|
+
height: 4rem;
|
|
2620
|
+
width: 4rem;
|
|
2621
|
+
border-radius: 9999px;
|
|
2622
|
+
padding: 0px;
|
|
2623
|
+
}
|
|
2491
2624
|
.join.join-vertical {
|
|
2492
2625
|
flex-direction: column;
|
|
2493
2626
|
}
|
|
@@ -2602,6 +2735,42 @@ input.tab:checked + .tab-content,
|
|
|
2602
2735
|
margin-bottom: 0px;
|
|
2603
2736
|
margin-inline-start: -1px;
|
|
2604
2737
|
}
|
|
2738
|
+
.modal-top :where(.modal-box) {
|
|
2739
|
+
width: 100%;
|
|
2740
|
+
max-width: none;
|
|
2741
|
+
--tw-translate-y: -2.5rem;
|
|
2742
|
+
--tw-scale-x: 1;
|
|
2743
|
+
--tw-scale-y: 1;
|
|
2744
|
+
transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
|
|
2745
|
+
border-bottom-right-radius: var(--rounded-box, 1rem);
|
|
2746
|
+
border-bottom-left-radius: var(--rounded-box, 1rem);
|
|
2747
|
+
border-top-left-radius: 0px;
|
|
2748
|
+
border-top-right-radius: 0px;
|
|
2749
|
+
}
|
|
2750
|
+
.modal-middle :where(.modal-box) {
|
|
2751
|
+
width: 91.666667%;
|
|
2752
|
+
max-width: 32rem;
|
|
2753
|
+
--tw-translate-y: 0px;
|
|
2754
|
+
--tw-scale-x: .9;
|
|
2755
|
+
--tw-scale-y: .9;
|
|
2756
|
+
transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
|
|
2757
|
+
border-top-left-radius: var(--rounded-box, 1rem);
|
|
2758
|
+
border-top-right-radius: var(--rounded-box, 1rem);
|
|
2759
|
+
border-bottom-right-radius: var(--rounded-box, 1rem);
|
|
2760
|
+
border-bottom-left-radius: var(--rounded-box, 1rem);
|
|
2761
|
+
}
|
|
2762
|
+
.modal-bottom :where(.modal-box) {
|
|
2763
|
+
width: 100%;
|
|
2764
|
+
max-width: none;
|
|
2765
|
+
--tw-translate-y: 2.5rem;
|
|
2766
|
+
--tw-scale-x: 1;
|
|
2767
|
+
--tw-scale-y: 1;
|
|
2768
|
+
transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
|
|
2769
|
+
border-top-left-radius: var(--rounded-box, 1rem);
|
|
2770
|
+
border-top-right-radius: var(--rounded-box, 1rem);
|
|
2771
|
+
border-bottom-right-radius: 0px;
|
|
2772
|
+
border-bottom-left-radius: 0px;
|
|
2773
|
+
}
|
|
2605
2774
|
.steps-horizontal .step {
|
|
2606
2775
|
grid-template-rows: 40px 1fr;
|
|
2607
2776
|
grid-template-columns: auto;
|
|
@@ -2727,9 +2896,15 @@ input.tab:checked + .tab-content,
|
|
|
2727
2896
|
.relative {
|
|
2728
2897
|
position: relative;
|
|
2729
2898
|
}
|
|
2899
|
+
.right-2 {
|
|
2900
|
+
right: 0.5rem;
|
|
2901
|
+
}
|
|
2730
2902
|
.right-6 {
|
|
2731
2903
|
right: 1.5rem;
|
|
2732
2904
|
}
|
|
2905
|
+
.top-2 {
|
|
2906
|
+
top: 0.5rem;
|
|
2907
|
+
}
|
|
2733
2908
|
.top-8 {
|
|
2734
2909
|
top: 2rem;
|
|
2735
2910
|
}
|
|
@@ -2811,6 +2986,10 @@ input.tab:checked + .tab-content,
|
|
|
2811
2986
|
.min-w-0 {
|
|
2812
2987
|
min-width: 0px;
|
|
2813
2988
|
}
|
|
2989
|
+
.min-w-max {
|
|
2990
|
+
min-width: -moz-max-content;
|
|
2991
|
+
min-width: max-content;
|
|
2992
|
+
}
|
|
2814
2993
|
.max-w-screen-lg {
|
|
2815
2994
|
max-width: 1024px;
|
|
2816
2995
|
}
|
|
@@ -2853,6 +3032,9 @@ input.tab:checked + .tab-content,
|
|
|
2853
3032
|
.overflow-auto {
|
|
2854
3033
|
overflow: auto;
|
|
2855
3034
|
}
|
|
3035
|
+
.overflow-scroll {
|
|
3036
|
+
overflow: scroll;
|
|
3037
|
+
}
|
|
2856
3038
|
.whitespace-nowrap {
|
|
2857
3039
|
white-space: nowrap;
|
|
2858
3040
|
}
|
|
@@ -2871,6 +3053,9 @@ input.tab:checked + .tab-content,
|
|
|
2871
3053
|
.rounded-lg {
|
|
2872
3054
|
border-radius: 0.5rem;
|
|
2873
3055
|
}
|
|
3056
|
+
.rounded-md {
|
|
3057
|
+
border-radius: 0.375rem;
|
|
3058
|
+
}
|
|
2874
3059
|
.rounded-none {
|
|
2875
3060
|
border-radius: 0px;
|
|
2876
3061
|
}
|
|
@@ -2956,6 +3141,10 @@ input.tab:checked + .tab-content,
|
|
|
2956
3141
|
padding-top: 0.5rem;
|
|
2957
3142
|
padding-bottom: 0.5rem;
|
|
2958
3143
|
}
|
|
3144
|
+
.py-4 {
|
|
3145
|
+
padding-top: 1rem;
|
|
3146
|
+
padding-bottom: 1rem;
|
|
3147
|
+
}
|
|
2959
3148
|
.text-justify {
|
|
2960
3149
|
text-align: justify;
|
|
2961
3150
|
}
|
|
@@ -2996,6 +3185,10 @@ input.tab:checked + .tab-content,
|
|
|
2996
3185
|
--tw-text-opacity: 1;
|
|
2997
3186
|
color: rgb(75 85 99 / var(--tw-text-opacity));
|
|
2998
3187
|
}
|
|
3188
|
+
.text-red-700 {
|
|
3189
|
+
--tw-text-opacity: 1;
|
|
3190
|
+
color: rgb(185 28 28 / var(--tw-text-opacity));
|
|
3191
|
+
}
|
|
2999
3192
|
.underline {
|
|
3000
3193
|
text-decoration-line: underline;
|
|
3001
3194
|
}
|
|
@@ -3032,6 +3225,10 @@ input.tab:checked + .tab-content,
|
|
|
3032
3225
|
--tw-text-opacity: 1;
|
|
3033
3226
|
color: rgb(30 64 175 / var(--tw-text-opacity));
|
|
3034
3227
|
}
|
|
3228
|
+
.hover\:text-gray-300:hover {
|
|
3229
|
+
--tw-text-opacity: 1;
|
|
3230
|
+
color: rgb(209 213 219 / var(--tw-text-opacity));
|
|
3231
|
+
}
|
|
3035
3232
|
.hover\:text-gray-700:hover {
|
|
3036
3233
|
--tw-text-opacity: 1;
|
|
3037
3234
|
color: rgb(55 65 81 / var(--tw-text-opacity));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@genspectrum/dashboard-components",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "GenSpectrum web components for building dashboards",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "AGPL-3.0-only",
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"lint:lit-analyzer": "lit-analyzer",
|
|
36
36
|
"generate-manifest": "npx custom-elements-manifest analyze --litelement --globs src/web-components/**",
|
|
37
37
|
"generate-manifest:watch": "npm run generate-manifest -- --watch",
|
|
38
|
-
"format": "prettier \"**/*.{
|
|
38
|
+
"format": "prettier \"**/*.{ts,tsx,json,md,mdx,mjs,cjs}\" --write",
|
|
39
39
|
"check-format": "prettier --check \"**/*.{ts,tsx,json,md,mdx,mjs,cjs}\"",
|
|
40
40
|
"check-types": "tsc --noEmit",
|
|
41
41
|
"check-dependencies": "depcheck",
|
|
@@ -101,7 +101,6 @@
|
|
|
101
101
|
"postcss": "^8.4.38",
|
|
102
102
|
"prettier": "^3.2.5",
|
|
103
103
|
"react": "^18.3.1",
|
|
104
|
-
"release-please": "^16.10.2",
|
|
105
104
|
"storybook": "^8.0.9",
|
|
106
105
|
"storybook-addon-fetch-mock": "^2.0.0",
|
|
107
106
|
"tailwindcss": "^3.4.3",
|
package/src/constants.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export const LAPIS_URL = 'https://
|
|
1
|
+
export const LAPIS_URL = 'https://lapis.cov-spectrum.org/open/v2/';
|
|
2
2
|
|
|
3
3
|
export const AGGREGATED_ENDPOINT = `${LAPIS_URL}/sample/aggregated`;
|
|
4
4
|
export const NUCLEOTIDE_MUTATIONS_ENDPOINT = `${LAPIS_URL}/sample/nucleotideMutations`;
|
package/src/lapisApi/lapisApi.ts
CHANGED
|
@@ -3,11 +3,35 @@ import {
|
|
|
3
3
|
aggregatedResponse,
|
|
4
4
|
insertionsResponse,
|
|
5
5
|
type LapisBaseRequest,
|
|
6
|
+
lapisError,
|
|
6
7
|
type MutationsRequest,
|
|
7
8
|
mutationsResponse,
|
|
9
|
+
problemDetail,
|
|
10
|
+
type ProblemDetail,
|
|
8
11
|
} from './lapisTypes';
|
|
9
12
|
import { type SequenceType } from '../types';
|
|
10
13
|
|
|
14
|
+
export class UnknownLapisError extends Error {
|
|
15
|
+
constructor(
|
|
16
|
+
message: string,
|
|
17
|
+
public readonly status: number,
|
|
18
|
+
) {
|
|
19
|
+
super(message);
|
|
20
|
+
this.name = 'UnknownLapisError';
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export class LapisError extends Error {
|
|
25
|
+
constructor(
|
|
26
|
+
message: string,
|
|
27
|
+
public readonly status: number,
|
|
28
|
+
public readonly problemDetail: ProblemDetail,
|
|
29
|
+
) {
|
|
30
|
+
super(message);
|
|
31
|
+
this.name = 'LapisError';
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
11
35
|
export async function fetchAggregated(lapisUrl: string, body: LapisBaseRequest, signal?: AbortSignal) {
|
|
12
36
|
const response = await fetch(aggregatedEndpoint(lapisUrl), {
|
|
13
37
|
method: 'POST',
|
|
@@ -79,9 +103,29 @@ export async function fetchReferenceGenome(lapisUrl: string, signal?: AbortSigna
|
|
|
79
103
|
const handleErrors = async (response: Response) => {
|
|
80
104
|
if (!response.ok) {
|
|
81
105
|
if (response.status >= 400 && response.status < 500) {
|
|
82
|
-
|
|
106
|
+
const json = await response.json();
|
|
107
|
+
|
|
108
|
+
const lapisErrorResult = lapisError.safeParse(json);
|
|
109
|
+
if (lapisErrorResult.success) {
|
|
110
|
+
throw new LapisError(
|
|
111
|
+
response.statusText + lapisErrorResult.data.error.detail,
|
|
112
|
+
response.status,
|
|
113
|
+
lapisErrorResult.data.error,
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const problemDetailResult = problemDetail.safeParse(json);
|
|
118
|
+
if (problemDetailResult.success) {
|
|
119
|
+
throw new LapisError(
|
|
120
|
+
response.statusText + problemDetailResult.data.detail,
|
|
121
|
+
response.status,
|
|
122
|
+
problemDetailResult.data,
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
throw new UnknownLapisError(`${response.statusText}: ${JSON.stringify(json)}`, response.status);
|
|
83
127
|
}
|
|
84
|
-
throw new
|
|
128
|
+
throw new UnknownLapisError(`${response.statusText}: ${response.status}`, response.status);
|
|
85
129
|
}
|
|
86
130
|
};
|
|
87
131
|
|
|
@@ -49,3 +49,17 @@ function makeLapisResponse<T extends ZodTypeAny>(data: T) {
|
|
|
49
49
|
data,
|
|
50
50
|
});
|
|
51
51
|
}
|
|
52
|
+
|
|
53
|
+
export const problemDetail = z.object({
|
|
54
|
+
title: z.string().optional(),
|
|
55
|
+
status: z.number(),
|
|
56
|
+
detail: z.string().optional(),
|
|
57
|
+
type: z.string(),
|
|
58
|
+
instance: z.string().optional(),
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
export type ProblemDetail = z.infer<typeof problemDetail>;
|
|
62
|
+
|
|
63
|
+
export const lapisError = z.object({
|
|
64
|
+
error: problemDetail,
|
|
65
|
+
});
|
|
@@ -10,7 +10,8 @@ const meta: Meta<AggregateProps> = {
|
|
|
10
10
|
component: Aggregate,
|
|
11
11
|
argTypes: {
|
|
12
12
|
fields: [{ control: 'object' }],
|
|
13
|
-
|
|
13
|
+
width: { control: 'text' },
|
|
14
|
+
height: { control: 'text' },
|
|
14
15
|
headline: { control: 'text' },
|
|
15
16
|
},
|
|
16
17
|
parameters: {
|
|
@@ -49,7 +50,8 @@ export const Default: StoryObj<AggregateProps> = {
|
|
|
49
50
|
filter: {
|
|
50
51
|
country: 'USA',
|
|
51
52
|
},
|
|
52
|
-
|
|
53
|
+
width: '100%',
|
|
54
|
+
height: '700px',
|
|
53
55
|
headline: 'Aggregate',
|
|
54
56
|
},
|
|
55
57
|
};
|
|
@@ -6,32 +6,52 @@ import { type AggregateData, queryAggregateData } from '../../query/queryAggrega
|
|
|
6
6
|
import { type LapisFilter } from '../../types';
|
|
7
7
|
import { LapisUrlContext } from '../LapisUrlContext';
|
|
8
8
|
import { CsvDownloadButton } from '../components/csv-download-button';
|
|
9
|
+
import { ErrorBoundary } from '../components/error-boundary';
|
|
9
10
|
import { ErrorDisplay } from '../components/error-display';
|
|
10
11
|
import Headline from '../components/headline';
|
|
11
12
|
import Info from '../components/info';
|
|
12
13
|
import { LoadingDisplay } from '../components/loading-display';
|
|
13
14
|
import { NoDataDisplay } from '../components/no-data-display';
|
|
14
|
-
import { ResizeContainer
|
|
15
|
+
import { ResizeContainer } from '../components/resize-container';
|
|
15
16
|
import Tabs from '../components/tabs';
|
|
16
17
|
import { useQuery } from '../useQuery';
|
|
17
18
|
|
|
18
19
|
export type View = 'table';
|
|
19
20
|
|
|
20
|
-
export
|
|
21
|
+
export type AggregateProps = {
|
|
22
|
+
width: string;
|
|
23
|
+
height: string;
|
|
24
|
+
headline?: string;
|
|
25
|
+
} & AggregateInnerProps;
|
|
26
|
+
|
|
27
|
+
export interface AggregateInnerProps {
|
|
21
28
|
filter: LapisFilter;
|
|
22
29
|
fields: string[];
|
|
23
30
|
views: View[];
|
|
24
|
-
size?: Size;
|
|
25
|
-
headline?: string;
|
|
26
31
|
}
|
|
27
32
|
|
|
28
33
|
export const Aggregate: FunctionComponent<AggregateProps> = ({
|
|
29
|
-
fields,
|
|
30
34
|
views,
|
|
35
|
+
width,
|
|
36
|
+
height,
|
|
37
|
+
headline = 'Mutations',
|
|
31
38
|
filter,
|
|
32
|
-
|
|
33
|
-
headline = 'Aggregate',
|
|
39
|
+
fields,
|
|
34
40
|
}) => {
|
|
41
|
+
const size = { height, width };
|
|
42
|
+
|
|
43
|
+
return (
|
|
44
|
+
<ErrorBoundary size={size} headline={headline}>
|
|
45
|
+
<ResizeContainer size={size}>
|
|
46
|
+
<Headline heading={headline}>
|
|
47
|
+
<AggregateInner fields={fields} filter={filter} views={views} />
|
|
48
|
+
</Headline>
|
|
49
|
+
</ResizeContainer>
|
|
50
|
+
</ErrorBoundary>
|
|
51
|
+
);
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
export const AggregateInner: FunctionComponent<AggregateInnerProps> = ({ fields, views, filter }) => {
|
|
35
55
|
const lapis = useContext(LapisUrlContext);
|
|
36
56
|
|
|
37
57
|
const { data, error, isLoading } = useQuery(async () => {
|
|
@@ -39,36 +59,18 @@ export const Aggregate: FunctionComponent<AggregateProps> = ({
|
|
|
39
59
|
}, [filter, fields, lapis]);
|
|
40
60
|
|
|
41
61
|
if (isLoading) {
|
|
42
|
-
return
|
|
43
|
-
<Headline heading={headline}>
|
|
44
|
-
<LoadingDisplay />
|
|
45
|
-
</Headline>
|
|
46
|
-
);
|
|
62
|
+
return <LoadingDisplay />;
|
|
47
63
|
}
|
|
48
64
|
|
|
49
65
|
if (error !== null) {
|
|
50
|
-
return
|
|
51
|
-
<Headline heading={headline}>
|
|
52
|
-
<ErrorDisplay error={error} />
|
|
53
|
-
</Headline>
|
|
54
|
-
);
|
|
66
|
+
return <ErrorDisplay error={error} />;
|
|
55
67
|
}
|
|
56
68
|
|
|
57
69
|
if (data === null) {
|
|
58
|
-
return
|
|
59
|
-
<Headline heading={headline}>
|
|
60
|
-
<NoDataDisplay />
|
|
61
|
-
</Headline>
|
|
62
|
-
);
|
|
70
|
+
return <NoDataDisplay />;
|
|
63
71
|
}
|
|
64
72
|
|
|
65
|
-
return
|
|
66
|
-
<ResizeContainer size={size} defaultSize={{ height: '700px', width: '100%' }}>
|
|
67
|
-
<Headline heading={headline}>
|
|
68
|
-
<AggregatedDataTabs data={data} views={views} fields={fields} />
|
|
69
|
-
</Headline>
|
|
70
|
-
</ResizeContainer>
|
|
71
|
-
);
|
|
73
|
+
return <AggregatedDataTabs data={data} views={views} fields={fields} />;
|
|
72
74
|
};
|
|
73
75
|
|
|
74
76
|
type AggregatedDataTabsProps = {
|