@genspectrum/dashboard-components 0.1.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.
Files changed (186) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +109 -0
  3. package/custom-elements.json +1587 -0
  4. package/dist/dashboard-components.js +7322 -0
  5. package/dist/dashboard-components.js.map +1 -0
  6. package/dist/genspectrum-components.d.ts +298 -0
  7. package/dist/style.css +2930 -0
  8. package/package.json +109 -0
  9. package/src/constants.ts +6 -0
  10. package/src/index.ts +1 -0
  11. package/src/lapisApi/ReferenceGenome.ts +30 -0
  12. package/src/lapisApi/__mockData__/referenceGenome.json +58 -0
  13. package/src/lapisApi/lapisApi.ts +99 -0
  14. package/src/lapisApi/lapisTypes.ts +51 -0
  15. package/src/operator/Dataset.ts +3 -0
  16. package/src/operator/DivisionOperator.spec.ts +27 -0
  17. package/src/operator/DivisionOperator.ts +60 -0
  18. package/src/operator/FetchAggregatedOperator.ts +44 -0
  19. package/src/operator/FetchInsertionsOperator.ts +24 -0
  20. package/src/operator/FetchSubstitutionsOrDeletionsOperator.ts +49 -0
  21. package/src/operator/FillMissingOperator.spec.ts +26 -0
  22. package/src/operator/FillMissingOperator.ts +30 -0
  23. package/src/operator/GroupByAndSumOperator.spec.ts +26 -0
  24. package/src/operator/GroupByAndSumOperator.ts +26 -0
  25. package/src/operator/GroupByOperator.spec.ts +43 -0
  26. package/src/operator/GroupByOperator.ts +32 -0
  27. package/src/operator/MapOperator.spec.ts +13 -0
  28. package/src/operator/MapOperator.ts +16 -0
  29. package/src/operator/MockOperator.spec.ts +11 -0
  30. package/src/operator/MockOperator.ts +12 -0
  31. package/src/operator/Operator.ts +5 -0
  32. package/src/operator/SlidingOperator.spec.ts +52 -0
  33. package/src/operator/SlidingOperator.ts +23 -0
  34. package/src/operator/SortOperator.spec.ts +13 -0
  35. package/src/operator/SortOperator.ts +16 -0
  36. package/src/preact/LapisUrlContext.ts +3 -0
  37. package/src/preact/ReferenceGenomeContext.ts +5 -0
  38. package/src/preact/components/SegmentSelector.tsx +62 -0
  39. package/src/preact/components/chart.stories.tsx +42 -0
  40. package/src/preact/components/chart.tsx +32 -0
  41. package/src/preact/components/checkbox-selector.stories.tsx +56 -0
  42. package/src/preact/components/checkbox-selector.tsx +46 -0
  43. package/src/preact/components/confidence-interval-selector.tsx +45 -0
  44. package/src/preact/components/csv-download-button.stories.tsx +25 -0
  45. package/src/preact/components/csv-download-button.tsx +51 -0
  46. package/src/preact/components/error-display.stories.tsx +22 -0
  47. package/src/preact/components/error-display.tsx +5 -0
  48. package/src/preact/components/headline.stories.tsx +29 -0
  49. package/src/preact/components/headline.tsx +16 -0
  50. package/src/preact/components/info.stories.tsx +22 -0
  51. package/src/preact/components/info.tsx +16 -0
  52. package/src/preact/components/loading-display.stories.tsx +20 -0
  53. package/src/preact/components/loading-display.tsx +5 -0
  54. package/src/preact/components/min-max-percent-slider.css +40 -0
  55. package/src/preact/components/min-max-range-slider.tsx +95 -0
  56. package/src/preact/components/mutation-type-selector.tsx +30 -0
  57. package/src/preact/components/no-data-display.stories.tsx +20 -0
  58. package/src/preact/components/no-data-display.tsx +5 -0
  59. package/src/preact/components/percent-intput.tsx +49 -0
  60. package/src/preact/components/proportion-selector-dropdown.stories.tsx +66 -0
  61. package/src/preact/components/proportion-selector-dropdown.tsx +33 -0
  62. package/src/preact/components/proportion-selector.stories.tsx +81 -0
  63. package/src/preact/components/proportion-selector.tsx +43 -0
  64. package/src/preact/components/scaling-selector.stories.tsx +25 -0
  65. package/src/preact/components/scaling-selector.tsx +36 -0
  66. package/src/preact/components/select.stories.tsx +42 -0
  67. package/src/preact/components/select.tsx +21 -0
  68. package/src/preact/components/table.stories.tsx +24 -0
  69. package/src/preact/components/table.tsx +51 -0
  70. package/src/preact/components/tabs.stories.tsx +60 -0
  71. package/src/preact/components/tabs.tsx +49 -0
  72. package/src/preact/dateRangeSelector/date-range-selector.stories.tsx +32 -0
  73. package/src/preact/dateRangeSelector/date-range-selector.tsx +228 -0
  74. package/src/preact/dateRangeSelector/dateConversion.ts +8 -0
  75. package/src/preact/locationFilter/__mockData__/aggregated.json +775 -0
  76. package/src/preact/locationFilter/fetchAutocompletionList.spec.ts +36 -0
  77. package/src/preact/locationFilter/fetchAutocompletionList.ts +43 -0
  78. package/src/preact/locationFilter/location-filter.stories.tsx +50 -0
  79. package/src/preact/locationFilter/location-filter.tsx +112 -0
  80. package/src/preact/mutationComparison/__mockData__/nucleotideMutationsOtherVariant.json +295 -0
  81. package/src/preact/mutationComparison/__mockData__/nucleotideMutationsSomeVariant.json +304 -0
  82. package/src/preact/mutationComparison/fetchMutationData.spec.ts +118 -0
  83. package/src/preact/mutationComparison/getMutationComparisonTableData.spec.ts +125 -0
  84. package/src/preact/mutationComparison/getMutationComparisonTableData.ts +40 -0
  85. package/src/preact/mutationComparison/mutation-comparison-table.tsx +43 -0
  86. package/src/preact/mutationComparison/mutation-comparison-venn.tsx +122 -0
  87. package/src/preact/mutationComparison/mutation-comparison.stories.tsx +152 -0
  88. package/src/preact/mutationComparison/mutation-comparison.tsx +179 -0
  89. package/src/preact/mutationComparison/queryMutationData.ts +53 -0
  90. package/src/preact/mutationFilter/mutation-filter.stories.tsx +164 -0
  91. package/src/preact/mutationFilter/mutation-filter.tsx +268 -0
  92. package/src/preact/mutationFilter/parseAndValidateMutation.ts +54 -0
  93. package/src/preact/mutationFilter/parseMutation.spec.ts +150 -0
  94. package/src/preact/mutationFilter/sequenceTypeFromSegment.spec.ts +66 -0
  95. package/src/preact/mutationFilter/sequenceTypeFromSegment.ts +20 -0
  96. package/src/preact/mutations/__mockData__/nucleotideInsertions.json +252 -0
  97. package/src/preact/mutations/__mockData__/nucleotideMutations.json +880 -0
  98. package/src/preact/mutations/getInsertionsTableData.spec.ts +36 -0
  99. package/src/preact/mutations/getInsertionsTableData.ts +10 -0
  100. package/src/preact/mutations/getMutationsGridData.spec.ts +135 -0
  101. package/src/preact/mutations/getMutationsGridData.ts +92 -0
  102. package/src/preact/mutations/getMutationsTableData.spec.ts +94 -0
  103. package/src/preact/mutations/getMutationsTableData.ts +17 -0
  104. package/src/preact/mutations/mutations-grid.tsx +84 -0
  105. package/src/preact/mutations/mutations-insertions-table.tsx +33 -0
  106. package/src/preact/mutations/mutations-table.tsx +47 -0
  107. package/src/preact/mutations/mutations.stories.tsx +95 -0
  108. package/src/preact/mutations/mutations.tsx +192 -0
  109. package/src/preact/mutations/queryMutations.ts +55 -0
  110. package/src/preact/prevalenceOverTime/__mockData__/denominator.json +1700 -0
  111. package/src/preact/prevalenceOverTime/__mockData__/denominatorOneVariant.json +608 -0
  112. package/src/preact/prevalenceOverTime/__mockData__/numeratorEG.json +1560 -0
  113. package/src/preact/prevalenceOverTime/__mockData__/numeratorJN1.json +592 -0
  114. package/src/preact/prevalenceOverTime/__mockData__/numeratorOneVariant.json +604 -0
  115. package/src/preact/prevalenceOverTime/getPrevalenceOverTimeTableData.spec.ts +67 -0
  116. package/src/preact/prevalenceOverTime/getPrevalenceOverTimeTableData.ts +18 -0
  117. package/src/preact/prevalenceOverTime/prevalence-over-time-bar-chart.tsx +105 -0
  118. package/src/preact/prevalenceOverTime/prevalence-over-time-bubble-chart.tsx +86 -0
  119. package/src/preact/prevalenceOverTime/prevalence-over-time-line-chart.tsx +141 -0
  120. package/src/preact/prevalenceOverTime/prevalence-over-time-table.tsx +46 -0
  121. package/src/preact/prevalenceOverTime/prevalence-over-time.stories.tsx +165 -0
  122. package/src/preact/prevalenceOverTime/prevalence-over-time.tsx +202 -0
  123. package/src/preact/relativeGrowthAdvantage/__mockData__/denominator.json +376 -0
  124. package/src/preact/relativeGrowthAdvantage/__mockData__/numerator.json +332 -0
  125. package/src/preact/relativeGrowthAdvantage/relative-growth-advantage-chart.tsx +138 -0
  126. package/src/preact/relativeGrowthAdvantage/relative-growth-advantage.stories.tsx +71 -0
  127. package/src/preact/relativeGrowthAdvantage/relative-growth-advantage.tsx +136 -0
  128. package/src/preact/shared/charts/LogitScale.ts +48 -0
  129. package/src/preact/shared/charts/colors.ts +26 -0
  130. package/src/preact/shared/charts/confideceInterval.ts +29 -0
  131. package/src/preact/shared/charts/getYAxisScale.ts +16 -0
  132. package/src/preact/shared/charts/scales.ts +16 -0
  133. package/src/preact/shared/icons/DeleteIcon.tsx +17 -0
  134. package/src/preact/shared/sort/sortInsertions.spec.ts +47 -0
  135. package/src/preact/shared/sort/sortInsertions.ts +21 -0
  136. package/src/preact/shared/sort/sortMutationPositions.spec.ts +31 -0
  137. package/src/preact/shared/sort/sortMutationPositions.ts +14 -0
  138. package/src/preact/shared/sort/sortSubstitutionsAndDeletions.spec.ts +47 -0
  139. package/src/preact/shared/sort/sortSubstitutionsAndDeletions.ts +17 -0
  140. package/src/preact/shared/table/formatProportion.ts +3 -0
  141. package/src/preact/textInput/__mockData__/aggregated_hosts.json +24 -0
  142. package/src/preact/textInput/fetchAutocompleteList.ts +9 -0
  143. package/src/preact/textInput/text-input.stories.tsx +49 -0
  144. package/src/preact/textInput/text-input.tsx +73 -0
  145. package/src/preact/useQuery.ts +27 -0
  146. package/src/query/queryInsertions.ts +14 -0
  147. package/src/query/queryPrevalenceOverTime.ts +126 -0
  148. package/src/query/queryRelativeGrowthAdvantage.ts +131 -0
  149. package/src/query/querySubstitutionsOrDeletions.ts +19 -0
  150. package/src/styles/tailwind.css +3 -0
  151. package/src/styles/tailwind.d.ts +3 -0
  152. package/src/types.ts +23 -0
  153. package/src/utils/mutations.spec.ts +64 -0
  154. package/src/utils/mutations.ts +165 -0
  155. package/src/utils/temporal.spec.ts +97 -0
  156. package/src/utils/temporal.ts +348 -0
  157. package/src/utils/test-utils.ts +5 -0
  158. package/src/utils/type-utils.ts +15 -0
  159. package/src/utils/utils.spec.ts +16 -0
  160. package/src/utils/utils.ts +38 -0
  161. package/src/web-components/PreactLitAdapter.tsx +62 -0
  162. package/src/web-components/PreactLitAdapterWithGridJsStyles.tsx +12 -0
  163. package/src/web-components/app.ts +51 -0
  164. package/src/web-components/display/index.ts +4 -0
  165. package/src/web-components/display/mutation-comparison-component.stories.ts +138 -0
  166. package/src/web-components/display/mutation-comparison-component.tsx +31 -0
  167. package/src/web-components/display/mutations-component.stories.ts +107 -0
  168. package/src/web-components/display/mutations-component.tsx +27 -0
  169. package/src/web-components/display/prevalence-over-time-component.stories.ts +205 -0
  170. package/src/web-components/display/prevalence-over-time-component.tsx +46 -0
  171. package/src/web-components/display/relative-growth-advantage-component.stories.ts +89 -0
  172. package/src/web-components/display/relative-growth-advantage-component.tsx +37 -0
  173. package/src/web-components/index.ts +3 -0
  174. package/src/web-components/input/date-range-selector-component.stories.ts +53 -0
  175. package/src/web-components/input/date-range-selector-component.tsx +33 -0
  176. package/src/web-components/input/index.ts +4 -0
  177. package/src/web-components/input/location-filter-component.stories.ts +184 -0
  178. package/src/web-components/input/location-filter-component.tsx +68 -0
  179. package/src/web-components/input/location-filter.mdx +25 -0
  180. package/src/web-components/input/mutation-filter-component.stories.ts +97 -0
  181. package/src/web-components/input/mutation-filter-component.tsx +27 -0
  182. package/src/web-components/input/text-input-component.stories.ts +92 -0
  183. package/src/web-components/input/text-input-component.tsx +30 -0
  184. package/src/web-components/lapis-context.ts +3 -0
  185. package/src/web-components/reference-genome-context.ts +5 -0
  186. package/src/web-components/withinShadowRoot.story.ts +34 -0
@@ -0,0 +1,105 @@
1
+ import { Chart, type ChartConfiguration, registerables, type TooltipItem } from 'chart.js';
2
+ import { BarWithErrorBar, BarWithErrorBarsController } from 'chartjs-chart-error-bars';
3
+
4
+ import { type PrevalenceOverTimeData, type PrevalenceOverTimeVariantData } from '../../query/queryPrevalenceOverTime';
5
+ import GsChart from '../components/chart';
6
+ import { LogitScale } from '../shared/charts/LogitScale';
7
+ import { singleGraphColorRGBAById } from '../shared/charts/colors';
8
+ import { type ConfidenceIntervalMethod, wilson95PercentConfidenceInterval } from '../shared/charts/confideceInterval';
9
+ import { getYAxisScale, type ScaleType } from '../shared/charts/getYAxisScale';
10
+
11
+ interface PrevalenceOverTimeBarChartProps {
12
+ data: PrevalenceOverTimeData;
13
+ yAxisScaleType: ScaleType;
14
+ confidenceIntervalMethod: ConfidenceIntervalMethod;
15
+ }
16
+
17
+ Chart.register(...registerables, LogitScale, BarWithErrorBarsController, BarWithErrorBar);
18
+
19
+ const PrevalenceOverTimeBarChart = ({
20
+ data,
21
+ yAxisScaleType,
22
+ confidenceIntervalMethod,
23
+ }: PrevalenceOverTimeBarChartProps) => {
24
+ const config: ChartConfiguration = {
25
+ type: BarWithErrorBarsController.id,
26
+ data: {
27
+ labels: data[0]?.content.map((dateRange) => dateRange.dateRange?.toString() ?? 'Unknown') || [],
28
+ datasets: data.map((graphData, index) => datasets(graphData, index, confidenceIntervalMethod)),
29
+ },
30
+ options: {
31
+ animation: false,
32
+ scales: {
33
+ y: getYAxisScale(yAxisScaleType),
34
+ },
35
+ plugins: {
36
+ legend: {
37
+ display: false,
38
+ },
39
+ tooltip: tooltip(confidenceIntervalMethod),
40
+ },
41
+ },
42
+ };
43
+
44
+ return <GsChart configuration={config} />;
45
+ };
46
+
47
+ const datasets = (
48
+ prevalenceOverTimeVariant: PrevalenceOverTimeVariantData,
49
+ index: number,
50
+ confidenceIntervalMethod: ConfidenceIntervalMethod,
51
+ ) => {
52
+ const generalConfig = {
53
+ borderWidth: 1,
54
+ pointRadius: 0,
55
+ label: prevalenceOverTimeVariant.displayName,
56
+ backgroundColor: singleGraphColorRGBAById(index, 0.3),
57
+ borderColor: singleGraphColorRGBAById(index),
58
+ };
59
+
60
+ switch (confidenceIntervalMethod) {
61
+ case 'wilson':
62
+ return {
63
+ ...generalConfig,
64
+ data: prevalenceOverTimeVariant.content.map((dataPoint) => ({
65
+ y: dataPoint.prevalence,
66
+ yMin: wilson95PercentConfidenceInterval(dataPoint.count, dataPoint.total).lowerLimit,
67
+ yMax: wilson95PercentConfidenceInterval(dataPoint.count, dataPoint.total).upperLimit,
68
+ })),
69
+ };
70
+ default:
71
+ return {
72
+ ...generalConfig,
73
+ data: prevalenceOverTimeVariant.content.map((dataPoint) => dataPoint.prevalence),
74
+ };
75
+ }
76
+ };
77
+
78
+ const tooltip = (confidenceIntervalMethod: ConfidenceIntervalMethod) => {
79
+ const generalConfig = {
80
+ mode: 'index' as const,
81
+ intersect: false,
82
+ };
83
+
84
+ switch (confidenceIntervalMethod) {
85
+ case 'wilson':
86
+ return {
87
+ ...generalConfig,
88
+ callbacks: {
89
+ label: (context: TooltipItem<'barWithErrorBars'>) => {
90
+ const value = context.dataset.data[context.dataIndex] as {
91
+ y: number;
92
+ yMin: number;
93
+ yMax: number;
94
+ };
95
+
96
+ return `${context.dataset.label}: ${value.y.toFixed(3)} (${value.yMin.toFixed(3)} - ${value.yMax.toFixed(3)})`;
97
+ },
98
+ },
99
+ };
100
+ default:
101
+ return generalConfig;
102
+ }
103
+ };
104
+
105
+ export default PrevalenceOverTimeBarChart;
@@ -0,0 +1,86 @@
1
+ import { Chart, type ChartConfiguration, registerables } from 'chart.js';
2
+
3
+ import { type PrevalenceOverTimeData } from '../../query/queryPrevalenceOverTime';
4
+ import { addUnit, minusTemporal } from '../../utils/temporal';
5
+ import { getMinMaxNumber } from '../../utils/utils';
6
+ import GsChart from '../components/chart';
7
+ import { LogitScale } from '../shared/charts/LogitScale';
8
+ import { singleGraphColorRGBAById } from '../shared/charts/colors';
9
+ import { getYAxisScale, type ScaleType } from '../shared/charts/getYAxisScale';
10
+
11
+ interface PrevalenceOverTimeBubbleChartProps {
12
+ data: PrevalenceOverTimeData;
13
+ yAxisScaleType: ScaleType;
14
+ }
15
+
16
+ Chart.register(...registerables, LogitScale);
17
+
18
+ const PrevalenceOverTimeBubbleChart = ({ data, yAxisScaleType }: PrevalenceOverTimeBubbleChartProps) => {
19
+ const firstDate = data[0].content[0].dateRange!;
20
+ const total = data.map((graphData) => graphData.content.map((dataPoint) => dataPoint.total)).flat();
21
+ const [minTotal, maxTotal] = getMinMaxNumber(total)!;
22
+ const scaleBubble = (value: number) => {
23
+ return ((value - minTotal) / (maxTotal - minTotal)) * 4.5 + 0.5;
24
+ };
25
+
26
+ const config: ChartConfiguration = {
27
+ type: 'bubble',
28
+ data: {
29
+ datasets: data.map((graphData, index) => ({
30
+ label: graphData.displayName,
31
+ data: graphData.content
32
+ .filter((dataPoint) => dataPoint.dateRange !== null)
33
+ .map((dataPoint) => ({
34
+ x: minusTemporal(dataPoint.dateRange!, firstDate),
35
+ y: dataPoint.prevalence,
36
+ r: scaleBubble(dataPoint.total),
37
+ })),
38
+ borderWidth: 1,
39
+ pointRadius: 0,
40
+ backgroundColor: singleGraphColorRGBAById(index, 0.3),
41
+ borderColor: singleGraphColorRGBAById(index),
42
+ })),
43
+ },
44
+ options: {
45
+ animation: false,
46
+ scales: {
47
+ x: {
48
+ ticks: {
49
+ callback: (value) => addUnit(firstDate, value as number).toString(),
50
+ },
51
+ },
52
+ y: getYAxisScale(yAxisScaleType),
53
+ },
54
+ plugins: {
55
+ legend: {
56
+ display: false,
57
+ },
58
+ tooltip: {
59
+ mode: 'index',
60
+ intersect: false,
61
+ callbacks: {
62
+ title: (context) => {
63
+ const dataset = data[context[0].datasetIndex!];
64
+ const dataPoint = dataset.content[context[0].dataIndex!];
65
+ return dataPoint.dateRange?.toString();
66
+ },
67
+ label: (context) => {
68
+ const dataset = data[context.datasetIndex!];
69
+ const dataPoint = dataset.content[context.dataIndex!];
70
+
71
+ const percentage = (dataPoint.prevalence * 100).toFixed(2);
72
+ const count = dataPoint.count.toFixed(0);
73
+ const total = dataPoint.total.toFixed(0);
74
+
75
+ return `${dataset.displayName}: ${percentage}%, ${count}/${total} samples`;
76
+ },
77
+ },
78
+ },
79
+ },
80
+ },
81
+ };
82
+
83
+ return <GsChart configuration={config} />;
84
+ };
85
+
86
+ export default PrevalenceOverTimeBubbleChart;
@@ -0,0 +1,141 @@
1
+ import { Chart, type ChartConfiguration, registerables } from 'chart.js';
2
+ import { type TooltipItem } from 'chart.js/dist/types';
3
+
4
+ import { type PrevalenceOverTimeData, type PrevalenceOverTimeVariantData } from '../../query/queryPrevalenceOverTime';
5
+ import GsChart from '../components/chart';
6
+ import { LogitScale } from '../shared/charts/LogitScale';
7
+ import { singleGraphColorRGBAById } from '../shared/charts/colors';
8
+ import {
9
+ confidenceIntervalDataLabel,
10
+ type ConfidenceIntervalMethod,
11
+ wilson95PercentConfidenceInterval,
12
+ } from '../shared/charts/confideceInterval';
13
+ import { getYAxisScale, type ScaleType } from '../shared/charts/getYAxisScale';
14
+
15
+ interface PrevalenceOverTimeLineChartProps {
16
+ data: PrevalenceOverTimeData;
17
+ yAxisScaleType: ScaleType;
18
+ confidenceIntervalMethod: ConfidenceIntervalMethod;
19
+ }
20
+
21
+ Chart.register(...registerables, LogitScale);
22
+
23
+ const PrevalenceOverTimeLineChart = ({
24
+ data,
25
+ yAxisScaleType,
26
+ confidenceIntervalMethod,
27
+ }: PrevalenceOverTimeLineChartProps) => {
28
+ const datasets = data.map((graphData, index) => getDataset(graphData, index, confidenceIntervalMethod)).flat();
29
+ const labels = data[0]?.content.map((dateRange) => dateRange.dateRange?.toString() ?? 'Unknown') || [];
30
+
31
+ const config: ChartConfiguration = {
32
+ type: 'line',
33
+ data: {
34
+ labels,
35
+ datasets,
36
+ },
37
+ options: {
38
+ animation: false,
39
+ scales: {
40
+ y: getYAxisScale(yAxisScaleType),
41
+ },
42
+ plugins: {
43
+ legend: {
44
+ display: false,
45
+ },
46
+ tooltip: tooltip(confidenceIntervalMethod),
47
+ },
48
+ },
49
+ };
50
+
51
+ return <GsChart configuration={config} />;
52
+ };
53
+
54
+ const getDataset = (
55
+ prevalenceOverTimeVariant: PrevalenceOverTimeVariantData,
56
+ dataIndex: number,
57
+ confidenceIntervalMethod: ConfidenceIntervalMethod,
58
+ ) => {
59
+ switch (confidenceIntervalMethod) {
60
+ case 'wilson':
61
+ return [
62
+ getDatasetCIUpper(prevalenceOverTimeVariant, dataIndex),
63
+ getDatasetLine(prevalenceOverTimeVariant, dataIndex),
64
+ getDatasetCILower(prevalenceOverTimeVariant, dataIndex),
65
+ ].flat();
66
+ default:
67
+ return getDatasetLine(prevalenceOverTimeVariant, dataIndex);
68
+ }
69
+ };
70
+
71
+ const getDatasetCIUpper = (prevalenceOverTimeVariant: PrevalenceOverTimeVariantData, dataIndex: number) => ({
72
+ label: `${prevalenceOverTimeVariant.displayName} CI upper`,
73
+ data: prevalenceOverTimeVariant.content.map(
74
+ (dataPoint) => wilson95PercentConfidenceInterval(dataPoint.count, dataPoint.total).upperLimit,
75
+ ),
76
+ borderWidth: 0,
77
+ pointRadius: 0,
78
+ fill: '+1',
79
+ backgroundColor: singleGraphColorRGBAById(dataIndex, 0.3),
80
+ });
81
+
82
+ const getDatasetCILower = (prevalenceOverTimeVariant: PrevalenceOverTimeVariantData, dataIndex: number) => ({
83
+ label: `${prevalenceOverTimeVariant.displayName} CI lower`,
84
+ data: prevalenceOverTimeVariant.content.map(
85
+ (dataPoint) => wilson95PercentConfidenceInterval(dataPoint.count, dataPoint.total).lowerLimit,
86
+ ),
87
+ borderWidth: 0,
88
+ pointRadius: 0,
89
+ fill: '-1',
90
+ backgroundColor: singleGraphColorRGBAById(dataIndex, 0.3),
91
+ });
92
+
93
+ const getDatasetLine = (prevalenceOverTimeVariant: PrevalenceOverTimeVariantData, dataIndex: number) => ({
94
+ label: prevalenceOverTimeVariant.displayName,
95
+ data: prevalenceOverTimeVariant.content.map((dataPoint) => dataPoint.prevalence),
96
+ borderWidth: 1,
97
+ pointRadius: 0,
98
+ borderColor: singleGraphColorRGBAById(dataIndex),
99
+ backgroundColor: singleGraphColorRGBAById(dataIndex),
100
+ });
101
+
102
+ const tooltip = (confidenceIntervalMethod?: ConfidenceIntervalMethod) => {
103
+ const generalConfig = {
104
+ mode: 'index' as const,
105
+ intersect: false,
106
+ };
107
+
108
+ switch (confidenceIntervalMethod) {
109
+ case 'wilson':
110
+ return {
111
+ ...generalConfig,
112
+ filter: ({ datasetIndex }: TooltipItem<'line'>) => {
113
+ return datasetIndex % 3 === 1;
114
+ },
115
+ callbacks: {
116
+ label: (context: TooltipItem<'line'>) => {
117
+ if (context.datasetIndex % 3 === 1) {
118
+ const value = context.dataset.data[context.dataIndex];
119
+ const ciLower = context.dataset.data[context.dataIndex - 1];
120
+ const ciUpper = context.dataset.data[context.dataIndex + 1];
121
+
122
+ if (
123
+ typeof value !== 'number' ||
124
+ typeof ciLower !== 'number' ||
125
+ typeof ciUpper !== 'number'
126
+ ) {
127
+ return '';
128
+ }
129
+
130
+ return confidenceIntervalDataLabel(value, ciLower, ciUpper, context.dataset.label);
131
+ }
132
+ return context.dataset.label;
133
+ },
134
+ },
135
+ };
136
+ default:
137
+ return generalConfig;
138
+ }
139
+ };
140
+
141
+ export default PrevalenceOverTimeLineChart;
@@ -0,0 +1,46 @@
1
+ import { getPrevalenceOverTimeTableData } from './getPrevalenceOverTimeTableData';
2
+ import { type PrevalenceOverTimeData } from '../../query/queryPrevalenceOverTime';
3
+ import { type TemporalGranularity } from '../../types';
4
+ import { Table } from '../components/table';
5
+ import { formatProportion } from '../shared/table/formatProportion';
6
+
7
+ interface PrevalenceOverTimeTableProps {
8
+ data: PrevalenceOverTimeData;
9
+ granularity: TemporalGranularity;
10
+ }
11
+
12
+ const PrevalenceOverTimeTable = ({ data, granularity }: PrevalenceOverTimeTableProps) => {
13
+ const getSplitColumns = (data: PrevalenceOverTimeData) => {
14
+ return data.map((dataset) => ({
15
+ name: dataset.displayName,
16
+ columns: [
17
+ {
18
+ name: 'prevalence',
19
+ sort: true,
20
+ formatter: (cell: number) => formatProportion(cell),
21
+ },
22
+ {
23
+ name: 'count',
24
+ sort: true,
25
+ },
26
+ ],
27
+ }));
28
+ };
29
+
30
+ const getColumns = (data: PrevalenceOverTimeData) => [
31
+ {
32
+ name: granularity,
33
+ sort: true,
34
+ },
35
+ ...getSplitColumns(data),
36
+ ];
37
+
38
+ const getData = (data: PrevalenceOverTimeData, granularity: TemporalGranularity) => {
39
+ const dataByHeader = getPrevalenceOverTimeTableData(data, granularity);
40
+ return Object.values(dataByHeader).map((row) => Object.values(row));
41
+ };
42
+
43
+ return <Table data={getData(data, granularity)} columns={getColumns(data)} pagination={false} />;
44
+ };
45
+
46
+ export default PrevalenceOverTimeTable;
@@ -0,0 +1,165 @@
1
+ import denominator from './__mockData__/denominator.json';
2
+ import denominatorOneVariant from './__mockData__/denominatorOneVariant.json';
3
+ import numeratorEG from './__mockData__/numeratorEG.json';
4
+ import numeratorJN1 from './__mockData__/numeratorJN1.json';
5
+ import numeratorOneVariant from './__mockData__/numeratorOneVariant.json';
6
+ import { PrevalenceOverTime, type PrevalenceOverTimeProps } from './prevalence-over-time';
7
+ import { AGGREGATED_ENDPOINT, LAPIS_URL } from '../../constants';
8
+ import { LapisUrlContext } from '../LapisUrlContext';
9
+
10
+ export default {
11
+ title: 'Visualization/PrevalenceOverTime',
12
+ component: PrevalenceOverTime,
13
+ parameters: {
14
+ fetchMock: {},
15
+ },
16
+ argTypes: {
17
+ numerator: { control: 'object' },
18
+ denominator: { control: 'object' },
19
+ granularity: {
20
+ options: ['day', 'week', 'month', 'year'],
21
+ control: { type: 'radio' },
22
+ },
23
+ smoothingWindow: { control: 'number' },
24
+ views: {
25
+ options: ['bar', 'line', 'bubble', 'table'],
26
+ control: { type: 'check' },
27
+ },
28
+ confidenceIntervalMethods: {
29
+ options: ['wilson'],
30
+ control: { type: 'check' },
31
+ },
32
+ },
33
+ };
34
+
35
+ const Template = {
36
+ render: (args: PrevalenceOverTimeProps) => (
37
+ <LapisUrlContext.Provider value={LAPIS_URL}>
38
+ <PrevalenceOverTime
39
+ numerator={args.numerator}
40
+ denominator={args.denominator}
41
+ granularity={args.granularity}
42
+ smoothingWindow={args.smoothingWindow}
43
+ views={args.views}
44
+ confidenceIntervalMethods={args.confidenceIntervalMethods}
45
+ />
46
+ </LapisUrlContext.Provider>
47
+ ),
48
+ };
49
+
50
+ export const TwoVariants = {
51
+ ...Template,
52
+ args: {
53
+ numerator: [
54
+ { displayName: 'EG', country: 'USA', pangoLineage: 'EG*', dateFrom: '2023-01-01' },
55
+ { displayName: 'JN.1', country: 'USA', pangoLineage: 'JN.1*', dateFrom: '2023-01-01' },
56
+ ],
57
+ denominator: { country: 'USA', dateFrom: '2023-01-01' },
58
+ granularity: 'month',
59
+ smoothingWindow: 0,
60
+ views: ['bar', 'line', 'bubble', 'table'],
61
+ confidenceIntervalMethods: ['wilson'],
62
+ },
63
+ parameters: {
64
+ fetchMock: {
65
+ mocks: [
66
+ {
67
+ matcher: {
68
+ name: 'numeratorEG',
69
+ url: AGGREGATED_ENDPOINT,
70
+ body: {
71
+ country: 'USA',
72
+ pangoLineage: 'EG*',
73
+ dateFrom: '2023-01-01',
74
+ fields: ['date'],
75
+ },
76
+ },
77
+ response: {
78
+ status: 200,
79
+ body: numeratorEG,
80
+ },
81
+ },
82
+ {
83
+ matcher: {
84
+ name: 'numeratorJN1',
85
+ url: AGGREGATED_ENDPOINT,
86
+ body: {
87
+ country: 'USA',
88
+ pangoLineage: 'JN.1*',
89
+ dateFrom: '2023-01-01',
90
+ fields: ['date'],
91
+ },
92
+ },
93
+ response: {
94
+ status: 200,
95
+ body: numeratorJN1,
96
+ },
97
+ },
98
+ {
99
+ matcher: {
100
+ name: 'denominator',
101
+ url: AGGREGATED_ENDPOINT,
102
+ body: {
103
+ country: 'USA',
104
+ dateFrom: '2023-01-01',
105
+ fields: ['date'],
106
+ },
107
+ },
108
+ response: {
109
+ status: 200,
110
+ body: denominator,
111
+ },
112
+ },
113
+ ],
114
+ },
115
+ },
116
+ };
117
+
118
+ export const OneVariant = {
119
+ ...Template,
120
+ args: {
121
+ numerator: { displayName: 'EG', country: 'USA', pangoLineage: 'BA.2.86*', dateFrom: '2023-10-01' },
122
+ denominator: { country: 'USA', dateFrom: '2023-10-01' },
123
+ granularity: 'day',
124
+ smoothingWindow: 7,
125
+ views: ['bar', 'line', 'bubble', 'table'],
126
+ confidenceIntervalMethods: ['wilson'],
127
+ },
128
+ parameters: {
129
+ fetchMock: {
130
+ mocks: [
131
+ {
132
+ matcher: {
133
+ name: 'numeratorOneVariant',
134
+ url: AGGREGATED_ENDPOINT,
135
+ body: {
136
+ country: 'USA',
137
+ pangoLineage: 'BA.2.86*',
138
+ dateFrom: '2023-10-01',
139
+ fields: ['date'],
140
+ },
141
+ },
142
+ response: {
143
+ status: 200,
144
+ body: numeratorOneVariant,
145
+ },
146
+ },
147
+ {
148
+ matcher: {
149
+ name: 'denominatorOneVariant',
150
+ url: AGGREGATED_ENDPOINT,
151
+ body: {
152
+ country: 'USA',
153
+ dateFrom: '2023-10-01',
154
+ fields: ['date'],
155
+ },
156
+ },
157
+ response: {
158
+ status: 200,
159
+ body: denominatorOneVariant,
160
+ },
161
+ },
162
+ ],
163
+ },
164
+ },
165
+ };