@genspectrum/dashboard-components 0.14.2 → 0.16.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.
Files changed (104) hide show
  1. package/custom-elements.json +281 -105
  2. package/dist/{LineageFilterChangedEvent-C9dXOxt6.js → LineageFilterChangedEvent-COWV-Y0k.js} +6 -6
  3. package/dist/LineageFilterChangedEvent-COWV-Y0k.js.map +1 -0
  4. package/dist/assets/{mutationOverTimeWorker-Dxnxrfe0.js.map → mutationOverTimeWorker-BL50C-yi.js.map} +1 -1
  5. package/dist/components.d.ts +40 -40
  6. package/dist/components.js +373 -243
  7. package/dist/components.js.map +1 -1
  8. package/dist/style.css +9 -0
  9. package/dist/util.d.ts +49 -49
  10. package/dist/util.js +2 -2
  11. package/package.json +1 -1
  12. package/src/preact/ReferenceGenomeContext.ts +14 -1
  13. package/src/preact/aggregatedData/aggregate-bar-chart.tsx +26 -5
  14. package/src/preact/aggregatedData/aggregate.stories.tsx +0 -1
  15. package/src/preact/aggregatedData/aggregate.tsx +5 -1
  16. package/src/preact/components/ReferenceGenomesAwaiter.tsx +1 -6
  17. package/src/preact/components/resize-container.tsx +1 -1
  18. package/src/preact/{dateRangeSelector/date-range-selector.stories.tsx → dateRangeFilter/date-range-filter.stories.tsx} +15 -15
  19. package/src/preact/{dateRangeSelector/date-range-selector.tsx → dateRangeFilter/date-range-filter.tsx} +9 -9
  20. package/src/preact/{dateRangeSelector → dateRangeFilter}/dateRangeOption.ts +2 -2
  21. package/src/preact/mutationComparison/mutation-comparison-venn.tsx +4 -2
  22. package/src/preact/mutationComparison/mutation-comparison.stories.tsx +0 -1
  23. package/src/preact/mutationComparison/mutation-comparison.tsx +5 -1
  24. package/src/preact/mutationFilter/mutation-filter.stories.tsx +17 -1
  25. package/src/preact/mutationFilter/mutation-filter.tsx +8 -0
  26. package/src/preact/mutations/mutations.stories.tsx +0 -1
  27. package/src/preact/mutations/mutations.tsx +1 -1
  28. package/src/preact/mutationsOverTime/mutations-over-time.stories.tsx +0 -2
  29. package/src/preact/mutationsOverTime/mutations-over-time.tsx +1 -1
  30. package/src/preact/numberSequencesOverTime/number-sequences-over-time-bar-chart.tsx +8 -3
  31. package/src/preact/numberSequencesOverTime/number-sequences-over-time-line-chart.tsx +8 -3
  32. package/src/preact/numberSequencesOverTime/number-sequences-over-time.stories.tsx +3 -1
  33. package/src/preact/numberSequencesOverTime/number-sequences-over-time.tsx +18 -3
  34. package/src/preact/prevalenceOverTime/prevalence-over-time-bar-chart.tsx +48 -35
  35. package/src/preact/prevalenceOverTime/prevalence-over-time-bubble-chart.tsx +83 -70
  36. package/src/preact/prevalenceOverTime/prevalence-over-time-line-chart.tsx +48 -37
  37. package/src/preact/prevalenceOverTime/prevalence-over-time.stories.tsx +0 -3
  38. package/src/preact/prevalenceOverTime/prevalence-over-time.tsx +6 -1
  39. package/src/preact/relativeGrowthAdvantage/relative-growth-advantage-chart.tsx +31 -23
  40. package/src/preact/relativeGrowthAdvantage/relative-growth-advantage.stories.tsx +0 -1
  41. package/src/preact/relativeGrowthAdvantage/relative-growth-advantage.tsx +5 -1
  42. package/src/preact/sequencesByLocation/__mockData__/worldAtlas.json +1 -0
  43. package/src/preact/{map → sequencesByLocation}/sequences-by-location-map.tsx +6 -3
  44. package/src/preact/{map → sequencesByLocation}/sequences-by-location-table.tsx +1 -1
  45. package/src/preact/{map → sequencesByLocation}/sequences-by-location.stories.tsx +58 -1
  46. package/src/preact/{map → sequencesByLocation}/sequences-by-location.tsx +10 -1
  47. package/src/preact/shared/aspectRatio/AspectRatio.tsx +13 -0
  48. package/src/preact/shared/charts/getMaintainAspectRatio.ts +3 -0
  49. package/src/preact/statistic/statistics.stories.tsx +0 -1
  50. package/src/preact/statistic/statistics.tsx +4 -4
  51. package/src/preact/{textInput/TextInputChangedEvent.ts → textFilter/TextFilterChangedEvent.ts} +2 -2
  52. package/src/preact/{textInput/text-input.stories.tsx → textFilter/text-filter.stories.tsx} +10 -10
  53. package/src/preact/{textInput/text-input.tsx → textFilter/text-filter.tsx} +10 -10
  54. package/src/preact/wastewater/mutationsOverTime/wastewater-mutations-over-time.stories.tsx +0 -1
  55. package/src/preact/wastewater/mutationsOverTime/wastewater-mutations-over-time.tsx +1 -1
  56. package/src/query/computeMapLocationData.spec.ts +1 -1
  57. package/src/query/computeMapLocationData.ts +1 -1
  58. package/src/query/querySequencesByLocationData.ts +1 -1
  59. package/src/utilEntrypoint.ts +3 -3
  60. package/src/web-components/ResizeContainer.mdx +4 -1
  61. package/src/web-components/input/{gs-date-range-selector.stories.ts → gs-date-range-filter.stories.ts} +59 -14
  62. package/src/web-components/input/{gs-date-range-selector.tsx → gs-date-range-filter.tsx} +28 -13
  63. package/src/web-components/input/{gs-text-input.stories.ts → gs-text-filter.stories.ts} +15 -15
  64. package/src/web-components/input/{gs-text-input.tsx → gs-text-filter.tsx} +16 -16
  65. package/src/web-components/input/index.ts +2 -2
  66. package/src/web-components/visualization/gs-aggregate.stories.ts +13 -6
  67. package/src/web-components/visualization/gs-aggregate.tsx +1 -1
  68. package/src/web-components/visualization/gs-mutation-comparison.stories.ts +8 -1
  69. package/src/web-components/visualization/gs-mutation-comparison.tsx +1 -1
  70. package/src/web-components/visualization/gs-mutations-over-time.stories.ts +9 -1
  71. package/src/web-components/visualization/gs-mutations-over-time.tsx +1 -1
  72. package/src/web-components/visualization/gs-mutations.stories.ts +8 -1
  73. package/src/web-components/visualization/gs-mutations.tsx +1 -1
  74. package/src/web-components/visualization/gs-number-sequences-over-time.stories.ts +11 -1
  75. package/src/web-components/visualization/gs-number-sequences-over-time.tsx +1 -1
  76. package/src/web-components/visualization/gs-prevalence-over-time.stories.ts +8 -2
  77. package/src/web-components/visualization/gs-prevalence-over-time.tsx +1 -1
  78. package/src/web-components/visualization/gs-relative-growth-advantage.stories.ts +8 -1
  79. package/src/web-components/visualization/gs-relative-growth-advantage.tsx +1 -1
  80. package/src/web-components/visualization/gs-sequences-by-location.stories.ts +13 -7
  81. package/src/web-components/visualization/gs-sequences-by-location.tsx +6 -3
  82. package/src/web-components/visualization/gs-statistics.stories.ts +0 -1
  83. package/src/web-components/visualization/gs-statistics.tsx +1 -1
  84. package/src/web-components/wastewaterVisualization/gs-wastewater-mutations-over-time.stories.ts +9 -1
  85. package/src/web-components/wastewaterVisualization/gs-wastewater-mutations-over-time.tsx +1 -1
  86. package/standalone-bundle/assets/{mutationOverTimeWorker-CmSrq4SZ.js.map → mutationOverTimeWorker-CFB5-Mdk.js.map} +1 -1
  87. package/standalone-bundle/dashboard-components.js +6032 -5937
  88. package/standalone-bundle/dashboard-components.js.map +1 -1
  89. package/standalone-bundle/style.css +1 -1
  90. package/dist/LineageFilterChangedEvent-C9dXOxt6.js.map +0 -1
  91. package/src/preact/map/__mockData__/worldAtlas.json +0 -497127
  92. /package/src/preact/{dateRangeSelector → dateRangeFilter}/computeInitialValues.spec.ts +0 -0
  93. /package/src/preact/{dateRangeSelector → dateRangeFilter}/computeInitialValues.ts +0 -0
  94. /package/src/preact/{dateRangeSelector → dateRangeFilter}/dateConversion.ts +0 -0
  95. /package/src/preact/{dateRangeSelector → dateRangeFilter}/selectableOptions.ts +0 -0
  96. /package/src/preact/{map → sequencesByLocation}/__mockData__/aggregatedGermany.json +0 -0
  97. /package/src/preact/{map → sequencesByLocation}/__mockData__/aggregatedWorld.json +0 -0
  98. /package/src/preact/{map → sequencesByLocation}/__mockData__/germanyMap.json +0 -0
  99. /package/src/preact/{map → sequencesByLocation}/__mockData__/howToGenerateWorldMap.md +0 -0
  100. /package/src/preact/{map → sequencesByLocation}/leafletStyleModifications.css +0 -0
  101. /package/src/preact/{map → sequencesByLocation}/loadMapSource.tsx +0 -0
  102. /package/src/preact/{textInput → textFilter}/__mockData__/aggregated_hosts.json +0 -0
  103. /package/src/preact/{textInput → textFilter}/fetchStringAutocompleteList.spec.ts +0 -0
  104. /package/src/preact/{textInput → textFilter}/fetchStringAutocompleteList.ts +0 -0
@@ -1,4 +1,5 @@
1
1
  import { Chart, type ChartConfiguration, registerables } from 'chart.js';
2
+ import { useMemo } from 'preact/hooks';
2
3
 
3
4
  import { maxInData } from './prevalence-over-time';
4
5
  import { type PrevalenceOverTimeData } from '../../query/queryPrevalenceOverTime';
@@ -15,97 +16,109 @@ interface PrevalenceOverTimeBubbleChartProps {
15
16
  data: PrevalenceOverTimeData;
16
17
  yAxisScaleType: ScaleType;
17
18
  yAxisMaxConfig: YAxisMaxConfig;
19
+ maintainAspectRatio: boolean;
18
20
  }
19
21
 
20
22
  Chart.register(...registerables, LogitScale);
21
23
 
24
+ const NO_DATA = 'noData';
25
+
22
26
  const PrevalenceOverTimeBubbleChart = ({
23
27
  data,
24
28
  yAxisScaleType,
25
29
  yAxisMaxConfig,
30
+ maintainAspectRatio,
26
31
  }: PrevalenceOverTimeBubbleChartProps) => {
27
- const nonNullDateRangeData = data
28
- .filter((prevalenceOverTimeData) => prevalenceOverTimeData.content.length > 0)
29
- .map((variantData) => {
30
- return {
31
- content: variantData.content.filter((dataPoint) => dataPoint.dateRange !== null),
32
- displayName: variantData.displayName,
33
- };
34
- });
32
+ const config = useMemo<ChartConfiguration | typeof NO_DATA>(() => {
33
+ const nonNullDateRangeData = data
34
+ .filter((prevalenceOverTimeData) => prevalenceOverTimeData.content.length > 0)
35
+ .map((variantData) => {
36
+ return {
37
+ content: variantData.content.filter((dataPoint) => dataPoint.dateRange !== null),
38
+ displayName: variantData.displayName,
39
+ };
40
+ });
35
41
 
36
- if (nonNullDateRangeData.length === 0) {
37
- return <NoDataDisplay />;
38
- }
42
+ if (nonNullDateRangeData.length === 0) {
43
+ return NO_DATA;
44
+ }
39
45
 
40
- const firstDate = nonNullDateRangeData[0].content[0].dateRange!;
41
- const total = nonNullDateRangeData.map((graphData) => graphData.content.map((dataPoint) => dataPoint.total)).flat();
42
- const [minTotal, maxTotal] = getMinMaxNumber(total)!;
43
- const scaleBubble = (value: number) => {
44
- return ((value - minTotal) / (maxTotal - minTotal)) * 4.5 + 0.5;
45
- };
46
+ const firstDate = nonNullDateRangeData[0].content[0].dateRange!;
47
+ const total = nonNullDateRangeData
48
+ .map((graphData) => graphData.content.map((dataPoint) => dataPoint.total))
49
+ .flat();
50
+ const [minTotal, maxTotal] = getMinMaxNumber(total)!;
51
+ const scaleBubble = (value: number) => {
52
+ return ((value - minTotal) / (maxTotal - minTotal)) * 4.5 + 0.5;
53
+ };
46
54
 
47
- const maxY =
48
- yAxisScaleType !== 'logit'
49
- ? getYAxisMax(maxInData(nonNullDateRangeData), yAxisMaxConfig?.[yAxisScaleType])
50
- : undefined;
55
+ const maxY =
56
+ yAxisScaleType !== 'logit'
57
+ ? getYAxisMax(maxInData(nonNullDateRangeData), yAxisMaxConfig?.[yAxisScaleType])
58
+ : undefined;
51
59
 
52
- const config: ChartConfiguration = {
53
- type: 'bubble',
54
- data: {
55
- datasets: nonNullDateRangeData.map((graphData, index) => ({
56
- label: graphData.displayName,
57
- data: graphData.content
58
- .filter((dataPoint) => dataPoint.dateRange !== null)
59
- .map((dataPoint) => ({
60
- x: minusTemporal(dataPoint.dateRange!, firstDate),
61
- y: dataPoint.prevalence,
62
- r: scaleBubble(dataPoint.total),
63
- })),
64
- borderWidth: 1,
65
- pointRadius: 0,
66
- backgroundColor: singleGraphColorRGBAById(index, 0.3),
67
- borderColor: singleGraphColorRGBAById(index),
68
- })),
69
- },
70
- options: {
71
- animation: false,
72
- maintainAspectRatio: false,
73
- scales: {
74
- x: {
75
- ticks: {
76
- callback: (value) => addUnit(firstDate, value as number).toString(),
77
- },
78
- },
79
- y: { ...getYAxisScale(yAxisScaleType), max: maxY },
60
+ return {
61
+ type: 'bubble',
62
+ data: {
63
+ datasets: nonNullDateRangeData.map((graphData, index) => ({
64
+ label: graphData.displayName,
65
+ data: graphData.content
66
+ .filter((dataPoint) => dataPoint.dateRange !== null)
67
+ .map((dataPoint) => ({
68
+ x: minusTemporal(dataPoint.dateRange!, firstDate),
69
+ y: dataPoint.prevalence,
70
+ r: scaleBubble(dataPoint.total),
71
+ })),
72
+ borderWidth: 1,
73
+ pointRadius: 0,
74
+ backgroundColor: singleGraphColorRGBAById(index, 0.3),
75
+ borderColor: singleGraphColorRGBAById(index),
76
+ })),
80
77
  },
81
- plugins: {
82
- legend: {
83
- display: false,
84
- },
85
- tooltip: {
86
- mode: 'index',
87
- intersect: false,
88
- callbacks: {
89
- title: (context) => {
90
- const dataset = nonNullDateRangeData[context[0].datasetIndex];
91
- const dataPoint = dataset.content[context[0].dataIndex];
92
- return dataPoint.dateRange?.toString();
78
+ options: {
79
+ animation: false,
80
+ maintainAspectRatio,
81
+ scales: {
82
+ x: {
83
+ ticks: {
84
+ callback: (value) => addUnit(firstDate, value as number).toString(),
93
85
  },
94
- label: (context) => {
95
- const dataset = nonNullDateRangeData[context.datasetIndex];
96
- const dataPoint = dataset.content[context.dataIndex];
86
+ },
87
+ y: { ...getYAxisScale(yAxisScaleType), max: maxY },
88
+ },
89
+ plugins: {
90
+ legend: {
91
+ display: false,
92
+ },
93
+ tooltip: {
94
+ mode: 'index',
95
+ intersect: false,
96
+ callbacks: {
97
+ title: (context) => {
98
+ const dataset = nonNullDateRangeData[context[0].datasetIndex];
99
+ const dataPoint = dataset.content[context[0].dataIndex];
100
+ return dataPoint.dateRange?.toString();
101
+ },
102
+ label: (context) => {
103
+ const dataset = nonNullDateRangeData[context.datasetIndex];
104
+ const dataPoint = dataset.content[context.dataIndex];
97
105
 
98
- const percentage = (dataPoint.prevalence * 100).toFixed(2);
99
- const count = dataPoint.count.toFixed(0);
100
- const total = dataPoint.total.toFixed(0);
106
+ const percentage = (dataPoint.prevalence * 100).toFixed(2);
107
+ const count = dataPoint.count.toFixed(0);
108
+ const total = dataPoint.total.toFixed(0);
101
109
 
102
- return `${dataset.displayName}: ${percentage}%, ${count}/${total} samples`;
110
+ return `${dataset.displayName}: ${percentage}%, ${count}/${total} samples`;
111
+ },
103
112
  },
104
113
  },
105
114
  },
106
115
  },
107
- },
108
- };
116
+ } satisfies ChartConfiguration;
117
+ }, [data, maintainAspectRatio, yAxisMaxConfig, yAxisScaleType]);
118
+
119
+ if (config === NO_DATA) {
120
+ return <NoDataDisplay />;
121
+ }
109
122
 
110
123
  return <GsChart configuration={config} />;
111
124
  };
@@ -1,5 +1,6 @@
1
1
  import { Chart, type ChartConfiguration, registerables } from 'chart.js';
2
2
  import { type TooltipItem } from 'chart.js/dist/types';
3
+ import { useMemo } from 'preact/hooks';
3
4
 
4
5
  import { maxInData } from './prevalence-over-time';
5
6
  import { type PrevalenceOverTimeData, type PrevalenceOverTimeVariantData } from '../../query/queryPrevalenceOverTime';
@@ -20,57 +21,67 @@ interface PrevalenceOverTimeLineChartProps {
20
21
  yAxisScaleType: ScaleType;
21
22
  confidenceIntervalMethod: ConfidenceIntervalMethod;
22
23
  yAxisMaxConfig: YAxisMaxConfig;
24
+ maintainAspectRatio: boolean;
23
25
  }
24
26
 
25
27
  Chart.register(...registerables, LogitScale);
26
28
 
29
+ const NO_DATA = 'noData';
30
+
27
31
  const PrevalenceOverTimeLineChart = ({
28
32
  data,
29
33
  yAxisScaleType,
30
34
  confidenceIntervalMethod,
31
35
  yAxisMaxConfig,
36
+ maintainAspectRatio,
32
37
  }: PrevalenceOverTimeLineChartProps) => {
33
- const nonNullDateRangeData = data
34
- .filter((prevalenceOverTimeData) => prevalenceOverTimeData.content.length > 0)
35
- .map((variantData) => {
36
- return {
37
- content: variantData.content.filter((dataPoint) => dataPoint.dateRange !== null),
38
- displayName: variantData.displayName,
39
- };
40
- });
38
+ const config = useMemo<ChartConfiguration | typeof NO_DATA>(() => {
39
+ const nonNullDateRangeData = data
40
+ .filter((prevalenceOverTimeData) => prevalenceOverTimeData.content.length > 0)
41
+ .map((variantData) => {
42
+ return {
43
+ content: variantData.content.filter((dataPoint) => dataPoint.dateRange !== null),
44
+ displayName: variantData.displayName,
45
+ };
46
+ });
47
+
48
+ if (nonNullDateRangeData.length === 0) {
49
+ return NO_DATA;
50
+ }
51
+
52
+ const datasets = nonNullDateRangeData
53
+ .map((graphData, index) => getDataset(graphData, index, confidenceIntervalMethod))
54
+ .flat();
55
+
56
+ const maxY =
57
+ yAxisScaleType !== 'logit'
58
+ ? getYAxisMax(maxInData(nonNullDateRangeData), yAxisMaxConfig?.[yAxisScaleType])
59
+ : undefined;
41
60
 
42
- if (nonNullDateRangeData.length === 0) {
43
- return <NoDataDisplay />;
44
- }
45
-
46
- const datasets = nonNullDateRangeData
47
- .map((graphData, index) => getDataset(graphData, index, confidenceIntervalMethod))
48
- .flat();
49
-
50
- const maxY =
51
- yAxisScaleType !== 'logit'
52
- ? getYAxisMax(maxInData(nonNullDateRangeData), yAxisMaxConfig?.[yAxisScaleType])
53
- : undefined;
54
-
55
- const config: ChartConfiguration = {
56
- type: 'line',
57
- data: {
58
- datasets,
59
- },
60
- options: {
61
- animation: false,
62
- maintainAspectRatio: false,
63
- scales: {
64
- y: { ...getYAxisScale(yAxisScaleType), max: maxY },
61
+ return {
62
+ type: 'line',
63
+ data: {
64
+ datasets,
65
65
  },
66
- plugins: {
67
- legend: {
68
- display: false,
66
+ options: {
67
+ animation: false,
68
+ maintainAspectRatio,
69
+ scales: {
70
+ y: { ...getYAxisScale(yAxisScaleType), max: maxY },
71
+ },
72
+ plugins: {
73
+ legend: {
74
+ display: false,
75
+ },
76
+ tooltip: tooltip(confidenceIntervalMethod),
69
77
  },
70
- tooltip: tooltip(confidenceIntervalMethod),
71
78
  },
72
- },
73
- };
79
+ };
80
+ }, [data, yAxisScaleType, confidenceIntervalMethod, yAxisMaxConfig, maintainAspectRatio]);
81
+
82
+ if (config === NO_DATA) {
83
+ return <NoDataDisplay />;
84
+ }
74
85
 
75
86
  return <GsChart configuration={config} />;
76
87
  };
@@ -61,7 +61,6 @@ export const TwoVariants: StoryObj<PrevalenceOverTimeProps> = {
61
61
  views: ['bar', 'line', 'bubble', 'table'],
62
62
  confidenceIntervalMethods: ['none', 'wilson'],
63
63
  width: '100%',
64
- height: '700px',
65
64
  lapisDateField: 'date',
66
65
  pageSize: 10,
67
66
  yAxisMaxLinear: 1,
@@ -137,7 +136,6 @@ export const OneVariant: StoryObj<PrevalenceOverTimeProps> = {
137
136
  views: ['bar', 'line', 'bubble', 'table'],
138
137
  confidenceIntervalMethods: ['none', 'wilson'],
139
138
  width: '100%',
140
- height: '700px',
141
139
  lapisDateField: 'date',
142
140
  pageSize: 10,
143
141
  yAxisMaxLinear: 1,
@@ -197,7 +195,6 @@ export const ShowsNoDataBanner: StoryObj<PrevalenceOverTimeProps> = {
197
195
  views: ['bar', 'line', 'bubble', 'table'],
198
196
  confidenceIntervalMethods: ['none', 'wilson'],
199
197
  width: '100%',
200
- height: '700px',
201
198
  lapisDateField: 'date',
202
199
  pageSize: 10,
203
200
  yAxisMaxLinear: 1,
@@ -21,6 +21,7 @@ import { ResizeContainer } from '../components/resize-container';
21
21
  import { ScalingSelector } from '../components/scaling-selector';
22
22
  import Tabs from '../components/tabs';
23
23
  import { type ConfidenceIntervalMethod, confidenceIntervalMethodSchema } from '../shared/charts/confideceInterval';
24
+ import { getMaintainAspectRatio } from '../shared/charts/getMaintainAspectRatio';
24
25
  import { axisMaxSchema } from '../shared/charts/getYAxisMax';
25
26
  import { type ScaleType } from '../shared/charts/getYAxisScale';
26
27
  import { useQuery } from '../useQuery';
@@ -35,7 +36,7 @@ export type PrevalenceOverTimeView = z.infer<typeof prevalenceOverTimeViewSchema
35
36
 
36
37
  const prevalenceOverTimePropsSchema = z.object({
37
38
  width: z.string(),
38
- height: z.string(),
39
+ height: z.string().optional(),
39
40
  numeratorFilters: z.array(namedLapisFilterSchema).min(1),
40
41
  denominatorFilter: lapisFilterSchema,
41
42
  granularity: temporalGranularitySchema,
@@ -117,6 +118,7 @@ const PrevalenceOverTimeTabs: FunctionComponent<PrevalenceOverTimeTabsProps> = (
117
118
  }, [confidenceIntervalMethods]);
118
119
 
119
120
  const yAxisMaxConfig = { linear: yAxisMaxLinear, logarithmic: yAxisMaxLogarithmic };
121
+ const maintainAspectRatio = getMaintainAspectRatio(componentProps.height);
120
122
 
121
123
  const getTab = (view: PrevalenceOverTimeView) => {
122
124
  switch (view) {
@@ -129,6 +131,7 @@ const PrevalenceOverTimeTabs: FunctionComponent<PrevalenceOverTimeTabsProps> = (
129
131
  yAxisScaleType={yAxisScaleType}
130
132
  confidenceIntervalMethod={confidenceIntervalMethod}
131
133
  yAxisMaxConfig={yAxisMaxConfig}
134
+ maintainAspectRatio={maintainAspectRatio}
132
135
  />
133
136
  ),
134
137
  };
@@ -141,6 +144,7 @@ const PrevalenceOverTimeTabs: FunctionComponent<PrevalenceOverTimeTabsProps> = (
141
144
  yAxisScaleType={yAxisScaleType}
142
145
  confidenceIntervalMethod={confidenceIntervalMethod}
143
146
  yAxisMaxConfig={yAxisMaxConfig}
147
+ maintainAspectRatio={maintainAspectRatio}
144
148
  />
145
149
  ),
146
150
  };
@@ -152,6 +156,7 @@ const PrevalenceOverTimeTabs: FunctionComponent<PrevalenceOverTimeTabsProps> = (
152
156
  data={data}
153
157
  yAxisScaleType={yAxisScaleType}
154
158
  yAxisMaxConfig={yAxisMaxConfig}
159
+ maintainAspectRatio={maintainAspectRatio}
155
160
  />
156
161
  ),
157
162
  };
@@ -1,4 +1,5 @@
1
1
  import { Chart, type ChartConfiguration, registerables, type TooltipItem } from 'chart.js';
2
+ import { useMemo } from 'preact/hooks';
2
3
 
3
4
  import { type YearMonthDayClass } from '../../utils/temporalClass';
4
5
  import GsChart from '../components/chart';
@@ -28,37 +29,44 @@ interface RelativeGrowthAdvantageChartProps {
28
29
  data: RelativeGrowthAdvantageChartData;
29
30
  yAxisScaleType: ScaleType;
30
31
  yAxisMaxConfig: YAxisMaxConfig;
32
+ maintainAspectRatio: boolean;
31
33
  }
32
34
 
33
35
  Chart.register(...registerables, LogitScale);
34
36
 
35
- const RelativeGrowthAdvantageChart = ({ data, yAxisScaleType, yAxisMaxConfig }: RelativeGrowthAdvantageChartProps) => {
36
- const maxY =
37
- yAxisScaleType !== 'logit'
38
- ? getYAxisMax(Math.max(...data.proportion), yAxisMaxConfig?.[yAxisScaleType])
39
- : undefined;
37
+ const RelativeGrowthAdvantageChart = ({
38
+ data,
39
+ yAxisScaleType,
40
+ yAxisMaxConfig,
41
+ maintainAspectRatio,
42
+ }: RelativeGrowthAdvantageChartProps) => {
43
+ const config = useMemo<ChartConfiguration>(() => {
44
+ const maxY =
45
+ yAxisScaleType !== 'logit'
46
+ ? getYAxisMax(Math.max(...data.proportion), yAxisMaxConfig?.[yAxisScaleType])
47
+ : undefined;
40
48
 
41
- const config: ChartConfiguration = {
42
- type: 'line',
43
- data: {
44
- labels: data.t,
45
- datasets: datasets(data),
46
- },
47
- options: {
48
- maintainAspectRatio: false,
49
- animation: false,
50
-
51
- scales: {
52
- y: { ...getYAxisScale(yAxisScaleType), max: maxY },
49
+ return {
50
+ type: 'line',
51
+ data: {
52
+ labels: data.t,
53
+ datasets: datasets(data),
53
54
  },
54
- plugins: {
55
- legend: {
56
- display: false,
55
+ options: {
56
+ maintainAspectRatio,
57
+ animation: false,
58
+ scales: {
59
+ y: { ...getYAxisScale(yAxisScaleType), max: maxY },
60
+ },
61
+ plugins: {
62
+ legend: {
63
+ display: false,
64
+ },
65
+ tooltip: tooltip(),
57
66
  },
58
- tooltip: tooltip(),
59
67
  },
60
- },
61
- };
68
+ };
69
+ }, [data, yAxisScaleType, yAxisMaxConfig, maintainAspectRatio]);
62
70
 
63
71
  return (
64
72
  <div className='flex h-full flex-col'>
@@ -45,7 +45,6 @@ export const Primary: StoryObj<RelativeGrowthAdvantageProps> = {
45
45
  generationTime: 7,
46
46
  views: ['line'],
47
47
  width: '100%',
48
- height: '700px',
49
48
  lapisDateField: 'date',
50
49
  yAxisMaxLinear: 1,
51
50
  yAxisMaxLogarithmic: 1,
@@ -18,6 +18,7 @@ import { NoDataDisplay } from '../components/no-data-display';
18
18
  import { ResizeContainer } from '../components/resize-container';
19
19
  import { ScalingSelector } from '../components/scaling-selector';
20
20
  import Tabs from '../components/tabs';
21
+ import { getMaintainAspectRatio } from '../shared/charts/getMaintainAspectRatio';
21
22
  import { axisMaxSchema } from '../shared/charts/getYAxisMax';
22
23
  import { type ScaleType } from '../shared/charts/getYAxisScale';
23
24
  import { useQuery } from '../useQuery';
@@ -27,7 +28,7 @@ export type RelativeGrowthAdvantageView = z.infer<typeof relativeGrowthAdvantage
27
28
 
28
29
  export const relativeGrowthAdvantagePropsSchema = z.object({
29
30
  width: z.string(),
30
- height: z.string(),
31
+ height: z.string().optional(),
31
32
  numeratorFilter: lapisFilterSchema,
32
33
  denominatorFilter: lapisFilterSchema,
33
34
  generationTime: z.number(),
@@ -102,6 +103,8 @@ const RelativeGrowthAdvantageTabs: FunctionComponent<RelativeGrowthAdvantageTabs
102
103
  setYAxisScaleType,
103
104
  originalComponentProps,
104
105
  }) => {
106
+ const maintainAspectRatio = getMaintainAspectRatio(originalComponentProps.height);
107
+
105
108
  const getTab = (view: RelativeGrowthAdvantageView) => {
106
109
  switch (view) {
107
110
  case 'line':
@@ -119,6 +122,7 @@ const RelativeGrowthAdvantageTabs: FunctionComponent<RelativeGrowthAdvantageTabs
119
122
  linear: originalComponentProps.yAxisMaxLinear,
120
123
  logarithmic: originalComponentProps.yAxisMaxLogarithmic,
121
124
  }}
125
+ maintainAspectRatio={maintainAspectRatio}
122
126
  />
123
127
  ),
124
128
  };