@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
@@ -4,7 +4,7 @@ import type { StepFunction } from '@storybook/types';
4
4
  import dayjs from 'dayjs/esm';
5
5
  import { useEffect, useRef, useState } from 'preact/hooks';
6
6
 
7
- import { DateRangeSelector, type DateRangeSelectorProps } from './date-range-selector';
7
+ import { DateRangeFilter, type DateRangeFilterProps } from './date-range-filter';
8
8
  import { previewHandles } from '../../../.storybook/preview';
9
9
  import { LAPIS_URL } from '../../constants';
10
10
  import { LapisUrlContextProvider } from '../LapisUrlContext';
@@ -19,9 +19,9 @@ const customDateRange = {
19
19
  dateTo: '2021-12-31',
20
20
  };
21
21
 
22
- const meta: Meta<DateRangeSelectorProps> = {
23
- title: 'Input/DateRangeSelector',
24
- component: DateRangeSelector,
22
+ const meta: Meta<DateRangeFilterProps> = {
23
+ title: 'Input/DateRangeFilter',
24
+ component: DateRangeFilter,
25
25
  parameters: {
26
26
  actions: {
27
27
  handles: ['gs-date-range-filter-changed', 'gs-date-range-option-changed', ...previewHandles],
@@ -61,15 +61,15 @@ const meta: Meta<DateRangeSelectorProps> = {
61
61
 
62
62
  export default meta;
63
63
 
64
- const Primary: StoryObj<DateRangeSelectorProps> = {
64
+ const Primary: StoryObj<DateRangeFilterProps> = {
65
65
  render: (args) => (
66
66
  <LapisUrlContextProvider value={LAPIS_URL}>
67
- <DateRangeSelector {...args} />
67
+ <DateRangeFilter {...args} />
68
68
  </LapisUrlContextProvider>
69
69
  ),
70
70
  };
71
71
 
72
- export const SetCorrectInitialValues: StoryObj<DateRangeSelectorProps> = {
72
+ export const SetCorrectInitialValues: StoryObj<DateRangeFilterProps> = {
73
73
  ...Primary,
74
74
  args: {
75
75
  ...Primary.args,
@@ -88,7 +88,7 @@ export const SetCorrectInitialValues: StoryObj<DateRangeSelectorProps> = {
88
88
 
89
89
  const initialDateFrom = '2000-01-01';
90
90
 
91
- export const SetCorrectInitialDateFrom: StoryObj<DateRangeSelectorProps> = {
91
+ export const SetCorrectInitialDateFrom: StoryObj<DateRangeFilterProps> = {
92
92
  ...Primary,
93
93
  args: {
94
94
  ...Primary.args,
@@ -107,7 +107,7 @@ export const SetCorrectInitialDateFrom: StoryObj<DateRangeSelectorProps> = {
107
107
 
108
108
  const initialDateTo = '2000-01-01';
109
109
 
110
- export const SetCorrectInitialDateTo: StoryObj<DateRangeSelectorProps> = {
110
+ export const SetCorrectInitialDateTo: StoryObj<DateRangeFilterProps> = {
111
111
  ...Primary,
112
112
  args: {
113
113
  ...Primary.args,
@@ -124,7 +124,7 @@ export const SetCorrectInitialDateTo: StoryObj<DateRangeSelectorProps> = {
124
124
  },
125
125
  };
126
126
 
127
- export const ChangingDateSetsOptionToCustom: StoryObj<DateRangeSelectorProps> = {
127
+ export const ChangingDateSetsOptionToCustom: StoryObj<DateRangeFilterProps> = {
128
128
  ...Primary,
129
129
  args: {
130
130
  ...Primary.args,
@@ -167,7 +167,7 @@ export const ChangingDateSetsOptionToCustom: StoryObj<DateRangeSelectorProps> =
167
167
  },
168
168
  };
169
169
 
170
- export const ChangingTheValueProgrammatically: StoryObj<DateRangeSelectorProps> = {
170
+ export const ChangingTheValueProgrammatically: StoryObj<DateRangeFilterProps> = {
171
171
  ...Primary,
172
172
  render: (args) => {
173
173
  const StatefulWrapper = () => {
@@ -183,7 +183,7 @@ export const ChangingTheValueProgrammatically: StoryObj<DateRangeSelectorProps>
183
183
  return (
184
184
  <div ref={ref}>
185
185
  <LapisUrlContextProvider value={LAPIS_URL}>
186
- <DateRangeSelector {...args} value={value} />
186
+ <DateRangeFilter {...args} value={value} />
187
187
  </LapisUrlContextProvider>
188
188
  <button className='btn' onClick={() => setValue(customDateRange.label)}>
189
189
  Set to Custom
@@ -230,7 +230,7 @@ export const ChangingTheValueProgrammatically: StoryObj<DateRangeSelectorProps>
230
230
  },
231
231
  };
232
232
 
233
- export const ChangingDateOption: StoryObj<DateRangeSelectorProps> = {
233
+ export const ChangingDateOption: StoryObj<DateRangeFilterProps> = {
234
234
  ...Primary,
235
235
  play: async ({ canvasElement, step }) => {
236
236
  const { canvas, filterChangedListenerMock, optionChangedListenerMock } = await prepare(canvasElement, step);
@@ -263,7 +263,7 @@ export const ChangingDateOption: StoryObj<DateRangeSelectorProps> = {
263
263
  },
264
264
  };
265
265
 
266
- export const HandlesInvalidInitialDateFrom: StoryObj<DateRangeSelectorProps> = {
266
+ export const HandlesInvalidInitialDateFrom: StoryObj<DateRangeFilterProps> = {
267
267
  ...Primary,
268
268
  args: {
269
269
  ...Primary.args,
@@ -278,7 +278,7 @@ export const HandlesInvalidInitialDateFrom: StoryObj<DateRangeSelectorProps> = {
278
278
  },
279
279
  };
280
280
 
281
- export const WithNoDateColumn: StoryObj<DateRangeSelectorProps> = {
281
+ export const WithNoDateColumn: StoryObj<DateRangeFilterProps> = {
282
282
  ...Primary,
283
283
  args: {
284
284
  ...Primary.args,
@@ -18,39 +18,39 @@ import type { ScaleType } from '../shared/charts/getYAxisScale';
18
18
 
19
19
  const customOption = 'Custom';
20
20
 
21
- const dateRangeSelectorInnerPropsSchema = z.object({
21
+ const dateRangeFilterInnerPropsSchema = z.object({
22
22
  dateRangeOptions: z.array(dateRangeOptionSchema),
23
23
  earliestDate: z.string().date(),
24
24
  value: dateRangeValueSchema.optional(),
25
25
  lapisDateField: z.string().min(1),
26
26
  });
27
27
 
28
- const dateRangeSelectorPropsSchema = dateRangeSelectorInnerPropsSchema.extend({
28
+ const dateRangeFilterPropsSchema = dateRangeFilterInnerPropsSchema.extend({
29
29
  width: z.string(),
30
30
  });
31
31
 
32
- export type DateRangeSelectorProps = z.infer<typeof dateRangeSelectorPropsSchema>;
33
- export type DateRangeSelectorInnerProps = z.infer<typeof dateRangeSelectorInnerPropsSchema>;
32
+ export type DateRangeFilterProps = z.infer<typeof dateRangeFilterPropsSchema>;
33
+ export type DateRangeFilterInnerProps = z.infer<typeof dateRangeFilterInnerPropsSchema>;
34
34
 
35
- export const DateRangeSelector = (props: DateRangeSelectorProps) => {
35
+ export const DateRangeFilter = (props: DateRangeFilterProps) => {
36
36
  const { width, ...innerProps } = props;
37
37
  const size = { width, height: '3rem' };
38
38
 
39
39
  return (
40
- <ErrorBoundary size={size} layout='horizontal' componentProps={props} schema={dateRangeSelectorPropsSchema}>
40
+ <ErrorBoundary size={size} layout='horizontal' componentProps={props} schema={dateRangeFilterPropsSchema}>
41
41
  <div style={{ width }}>
42
- <DateRangeSelectorInner {...innerProps} />
42
+ <DateRangeFilterInner {...innerProps} />
43
43
  </div>
44
44
  </ErrorBoundary>
45
45
  );
46
46
  };
47
47
 
48
- export const DateRangeSelectorInner = ({
48
+ export const DateRangeFilterInner = ({
49
49
  dateRangeOptions,
50
50
  earliestDate = '1900-01-01',
51
51
  value,
52
52
  lapisDateField,
53
- }: DateRangeSelectorInnerProps) => {
53
+ }: DateRangeFilterInnerProps) => {
54
54
  const initialValues = useMemo(
55
55
  () => computeInitialValues(value, earliestDate, dateRangeOptions),
56
56
  [value, earliestDate, dateRangeOptions],
@@ -3,7 +3,7 @@ import z from 'zod';
3
3
  import { toYYYYMMDD } from './dateConversion';
4
4
 
5
5
  /**
6
- * A date range option that can be used in the `gs-date-range-selector` component.
6
+ * A date range option that can be used in the `gs-date-range-filter` component.
7
7
  */
8
8
  export const dateRangeOptionSchema = z.object({
9
9
  /** The label of the date range option that will be shown to the user */
@@ -65,7 +65,7 @@ const lastYear = new Date(today);
65
65
  lastYear.setFullYear(today.getFullYear() - 1);
66
66
 
67
67
  /**
68
- * Presets for the `gs-date-range-selector` component that can be used as `dateRangeOptions`.
68
+ * Presets for the `gs-date-range-filter` component that can be used as `dateRangeOptions`.
69
69
  */
70
70
  export const dateRangeOptionPresets = {
71
71
  last2Weeks: {
@@ -13,11 +13,13 @@ Chart.register(...registerables, VennDiagramController, ArcSlice);
13
13
  export interface MutationComparisonVennProps {
14
14
  data: Dataset<MutationData>;
15
15
  proportionInterval: ProportionInterval;
16
+ maintainAspectRatio: boolean;
16
17
  }
17
18
 
18
19
  export const MutationComparisonVenn: FunctionComponent<MutationComparisonVennProps> = ({
19
20
  data,
20
21
  proportionInterval,
22
+ maintainAspectRatio,
21
23
  }) => {
22
24
  const [selectedDatasetIndex, setSelectedDatasetIndex] = useState<null | number>(null);
23
25
 
@@ -48,7 +50,7 @@ export const MutationComparisonVenn: FunctionComponent<MutationComparisonVennPro
48
50
  type: 'venn',
49
51
  data: sets,
50
52
  options: {
51
- maintainAspectRatio: false,
53
+ maintainAspectRatio,
52
54
  scales: {
53
55
  x: {
54
56
  ticks: {
@@ -91,7 +93,7 @@ export const MutationComparisonVenn: FunctionComponent<MutationComparisonVennPro
91
93
  },
92
94
  },
93
95
  }),
94
- [sets],
96
+ [maintainAspectRatio, sets],
95
97
  );
96
98
 
97
99
  if (data.content.length > 5) {
@@ -112,7 +112,6 @@ export const TwoVariants: StoryObj<MutationComparisonProps> = {
112
112
  sequenceType: 'nucleotide',
113
113
  views: ['table', 'venn'],
114
114
  width: '100%',
115
- height: '700px',
116
115
  pageSize: 10,
117
116
  },
118
117
  };
@@ -21,6 +21,7 @@ import { ProportionSelectorDropdown } from '../components/proportion-selector-dr
21
21
  import { ResizeContainer } from '../components/resize-container';
22
22
  import { type DisplayedSegment, SegmentSelector, useDisplayedSegments } from '../components/segment-selector';
23
23
  import Tabs from '../components/tabs';
24
+ import { getMaintainAspectRatio } from '../shared/charts/getMaintainAspectRatio';
24
25
  import { useQuery } from '../useQuery';
25
26
 
26
27
  export const mutationComparisonViewSchema = z.union([z.literal(views.table), z.literal(views.venn)]);
@@ -28,7 +29,7 @@ export type MutationComparisonView = z.infer<typeof mutationComparisonViewSchema
28
29
 
29
30
  const mutationComparisonPropsSchema = z.object({
30
31
  width: z.string(),
31
- height: z.string(),
32
+ height: z.string().optional(),
32
33
  lapisFilters: z.array(namedLapisFilterSchema).min(1),
33
34
  sequenceType: sequenceTypeSchema,
34
35
  views: z.array(mutationComparisonViewSchema),
@@ -91,6 +92,8 @@ const MutationComparisonTabs: FunctionComponent<MutationComparisonTabsProps> = (
91
92
  [data, displayedSegments, displayedMutationTypes],
92
93
  );
93
94
 
95
+ const maintainAspectRatio = getMaintainAspectRatio(originalComponentProps.height);
96
+
94
97
  const getTab = (view: MutationComparisonView) => {
95
98
  switch (view) {
96
99
  case 'table':
@@ -111,6 +114,7 @@ const MutationComparisonTabs: FunctionComponent<MutationComparisonTabsProps> = (
111
114
  <MutationComparisonVenn
112
115
  data={{ content: filteredData }}
113
116
  proportionInterval={proportionInterval}
117
+ maintainAspectRatio={maintainAspectRatio}
114
118
  />
115
119
  ),
116
120
  };
@@ -1,4 +1,4 @@
1
- import { type PreactRenderer, type Meta, type StoryObj } from '@storybook/preact';
1
+ import { type Meta, type PreactRenderer, type StoryObj } from '@storybook/preact';
2
2
  import { expect, fireEvent, fn, userEvent, waitFor, within } from '@storybook/test';
3
3
  import { type StepFunction } from '@storybook/types';
4
4
 
@@ -8,6 +8,7 @@ import { LAPIS_URL } from '../../constants';
8
8
  import referenceGenome from '../../lapisApi/__mockData__/referenceGenome.json';
9
9
  import { LapisUrlContextProvider } from '../LapisUrlContext';
10
10
  import { ReferenceGenomeContext } from '../ReferenceGenomeContext';
11
+ import { playThatExpectsErrorMessage } from '../shared/stories/expectErrorMessage';
11
12
 
12
13
  const meta: Meta<MutationFilterProps> = {
13
14
  title: 'Input/MutationFilter',
@@ -219,6 +220,21 @@ export const WithInitialValue: StoryObj<MutationFilterProps> = {
219
220
  },
220
221
  };
221
222
 
223
+ export const WithNoReferenceSequencesDefined: StoryObj<MutationFilterProps> = {
224
+ ...Default,
225
+ render: (args) => (
226
+ <LapisUrlContextProvider value={LAPIS_URL}>
227
+ <ReferenceGenomeContext.Provider value={{ nucleotideSequences: [], genes: [] }}>
228
+ <MutationFilter {...args} />
229
+ </ReferenceGenomeContext.Provider>
230
+ </LapisUrlContextProvider>
231
+ ),
232
+ play: playThatExpectsErrorMessage(
233
+ 'Error - No reference sequences available',
234
+ 'This organism has neither nucleotide nor amino acid sequences',
235
+ ),
236
+ };
237
+
222
238
  async function prepare(canvasElement: HTMLElement, step: StepFunction<PreactRenderer, unknown>) {
223
239
  const canvas = within(canvasElement);
224
240
 
@@ -10,6 +10,7 @@ import { type MutationsFilter, mutationsFilterSchema } from '../../types';
10
10
  import { type DeletionClass, type InsertionClass, type SubstitutionClass } from '../../utils/mutations';
11
11
  import { ReferenceGenomeContext } from '../ReferenceGenomeContext';
12
12
  import { ErrorBoundary } from '../components/error-boundary';
13
+ import { UserFacingError } from '../components/error-display';
13
14
  import { singleGraphColorRGBByName } from '../shared/charts/colors';
14
15
 
15
16
  const mutationFilterInnerPropsSchema = z.object({
@@ -54,6 +55,13 @@ export const MutationFilterInner: FunctionComponent<MutationFilterInnerProps> =
54
55
 
55
56
  const filterRef = useRef<HTMLDivElement>(null);
56
57
 
58
+ if (referenceGenome.nucleotideSequences.length === 0 && referenceGenome.genes.length === 0) {
59
+ throw new UserFacingError(
60
+ 'No reference sequences available',
61
+ 'This organism has neither nucleotide nor amino acid sequences configured in its reference genome. You cannot filter by mutations.',
62
+ );
63
+ }
64
+
57
65
  const handleRemoveValue = (option: ParsedMutationFilter) => {
58
66
  const newSelectedFilters = {
59
67
  ...selectedFilters,
@@ -55,7 +55,6 @@ export const Default: StoryObj<MutationsProps> = {
55
55
  sequenceType: 'nucleotide',
56
56
  views: ['grid', 'table', 'insertions'],
57
57
  width: '100%',
58
- height: '700px',
59
58
  pageSize: 10,
60
59
  },
61
60
  parameters: {
@@ -41,7 +41,7 @@ const mutationsPropsSchema = z.object({
41
41
  views: mutationsViewSchema.array(),
42
42
  pageSize: z.union([z.boolean(), z.number()]),
43
43
  width: z.string(),
44
- height: z.string(),
44
+ height: z.string().optional(),
45
45
  });
46
46
  export type MutationsProps = z.infer<typeof mutationsPropsSchema>;
47
47
 
@@ -62,7 +62,6 @@ export const Default: StoryObj<MutationsOverTimeProps> = {
62
62
  sequenceType: 'nucleotide',
63
63
  views: ['grid'],
64
64
  width: '100%',
65
- height: '700px',
66
65
  granularity: 'month',
67
66
  lapisDateField: 'date',
68
67
  },
@@ -76,7 +75,6 @@ export const ShowsMessageWhenTooManyMutations: StoryObj<MutationsOverTimeProps>
76
75
  sequenceType: 'nucleotide',
77
76
  views: ['grid'],
78
77
  width: '100%',
79
- height: '700px',
80
78
  granularity: 'year',
81
79
  lapisDateField: 'date',
82
80
  },
@@ -45,7 +45,7 @@ const mutationOverTimeSchema = z.object({
45
45
  granularity: temporalGranularitySchema,
46
46
  lapisDateField: z.string().min(1),
47
47
  width: z.string(),
48
- height: z.string(),
48
+ height: z.string().optional(),
49
49
  });
50
50
  export type MutationsOverTimeProps = z.infer<typeof mutationOverTimeSchema>;
51
51
 
@@ -10,11 +10,16 @@ import { getYAxisScale, type ScaleType } from '../shared/charts/getYAxisScale';
10
10
  interface NumberSequencesOverBarChartProps {
11
11
  data: NumberOfSequencesDatasets;
12
12
  yAxisScaleType: ScaleType;
13
+ maintainAspectRatio: boolean;
13
14
  }
14
15
 
15
16
  Chart.register(...registerables);
16
17
 
17
- export const NumberSequencesOverTimeBarChart = ({ data, yAxisScaleType }: NumberSequencesOverBarChartProps) => {
18
+ export const NumberSequencesOverTimeBarChart = ({
19
+ data,
20
+ yAxisScaleType,
21
+ maintainAspectRatio,
22
+ }: NumberSequencesOverBarChartProps) => {
18
23
  const config: ChartConfiguration = useMemo(
19
24
  () => ({
20
25
  type: 'bar',
@@ -22,7 +27,7 @@ export const NumberSequencesOverTimeBarChart = ({ data, yAxisScaleType }: Number
22
27
  datasets: getDatasets(data),
23
28
  },
24
29
  options: {
25
- maintainAspectRatio: false,
30
+ maintainAspectRatio,
26
31
  animation: false,
27
32
  scales: {
28
33
  y: {
@@ -40,7 +45,7 @@ export const NumberSequencesOverTimeBarChart = ({ data, yAxisScaleType }: Number
40
45
  },
41
46
  },
42
47
  }),
43
- [data, yAxisScaleType],
48
+ [data, maintainAspectRatio, yAxisScaleType],
44
49
  );
45
50
 
46
51
  return <GsChart configuration={config} />;
@@ -10,11 +10,16 @@ import { getYAxisScale, type ScaleType } from '../shared/charts/getYAxisScale';
10
10
  interface NumberSequencesOverBarChartProps {
11
11
  data: NumberOfSequencesDatasets;
12
12
  yAxisScaleType: ScaleType;
13
+ maintainAspectRatio: boolean;
13
14
  }
14
15
 
15
16
  Chart.register(...registerables);
16
17
 
17
- export const NumberSequencesOverTimeLineChart = ({ data, yAxisScaleType }: NumberSequencesOverBarChartProps) => {
18
+ export const NumberSequencesOverTimeLineChart = ({
19
+ data,
20
+ yAxisScaleType,
21
+ maintainAspectRatio,
22
+ }: NumberSequencesOverBarChartProps) => {
18
23
  const config: ChartConfiguration = useMemo(
19
24
  () => ({
20
25
  type: 'line',
@@ -22,7 +27,7 @@ export const NumberSequencesOverTimeLineChart = ({ data, yAxisScaleType }: Numbe
22
27
  datasets: getDatasets(data),
23
28
  },
24
29
  options: {
25
- maintainAspectRatio: false,
30
+ maintainAspectRatio,
26
31
  animation: false,
27
32
  scales: {
28
33
  y: {
@@ -40,7 +45,7 @@ export const NumberSequencesOverTimeLineChart = ({ data, yAxisScaleType }: Numbe
40
45
  },
41
46
  },
42
47
  }),
43
- [data, yAxisScaleType],
48
+ [data, maintainAspectRatio, yAxisScaleType],
44
49
  );
45
50
 
46
51
  return <GsChart configuration={config} />;
@@ -24,6 +24,9 @@ export default {
24
24
  control: { type: 'check' },
25
25
  },
26
26
  pageSize: { control: 'object' },
27
+ height: {
28
+ control: 'text',
29
+ },
27
30
  },
28
31
  };
29
32
 
@@ -40,7 +43,6 @@ const Template: StoryObj<NumberSequencesOverTimeProps> = {
40
43
  ],
41
44
  lapisDateField: 'date',
42
45
  width: '100%',
43
- height: '700px',
44
46
  smoothingWindow: 0,
45
47
  granularity: 'month',
46
48
  pageSize: 10,
@@ -21,6 +21,7 @@ import { NoDataDisplay } from '../components/no-data-display';
21
21
  import { ResizeContainer } from '../components/resize-container';
22
22
  import { ScalingSelector } from '../components/scaling-selector';
23
23
  import Tabs from '../components/tabs';
24
+ import { getMaintainAspectRatio } from '../shared/charts/getMaintainAspectRatio';
24
25
  import type { ScaleType } from '../shared/charts/getYAxisScale';
25
26
  import { useQuery } from '../useQuery';
26
27
 
@@ -33,7 +34,7 @@ export type NumberSequencesOverTimeView = z.infer<typeof numberSequencesOverTime
33
34
 
34
35
  const numberSequencesOverTimePropsSchema = z.object({
35
36
  width: z.string(),
36
- height: z.string(),
37
+ height: z.string().optional(),
37
38
  lapisFilters: z.array(namedLapisFilterSchema).min(1),
38
39
  lapisDateField: z.string().min(1),
39
40
  views: z.array(numberSequencesOverTimeViewSchema),
@@ -89,17 +90,31 @@ interface NumberSequencesOverTimeTabsProps {
89
90
  const NumberSequencesOverTimeTabs = ({ data, originalComponentProps }: NumberSequencesOverTimeTabsProps) => {
90
91
  const [yAxisScaleType, setYAxisScaleType] = useState<ScaleType>('linear');
91
92
 
93
+ const maintainAspectRatio = getMaintainAspectRatio(originalComponentProps.height);
94
+
92
95
  const getTab = (view: NumberSequencesOverTimeView) => {
93
96
  switch (view) {
94
97
  case 'bar':
95
98
  return {
96
99
  title: 'Bar',
97
- content: <NumberSequencesOverTimeBarChart data={data} yAxisScaleType={yAxisScaleType} />,
100
+ content: (
101
+ <NumberSequencesOverTimeBarChart
102
+ data={data}
103
+ yAxisScaleType={yAxisScaleType}
104
+ maintainAspectRatio={maintainAspectRatio}
105
+ />
106
+ ),
98
107
  };
99
108
  case 'line':
100
109
  return {
101
110
  title: 'Line',
102
- content: <NumberSequencesOverTimeLineChart data={data} yAxisScaleType={yAxisScaleType} />,
111
+ content: (
112
+ <NumberSequencesOverTimeLineChart
113
+ data={data}
114
+ yAxisScaleType={yAxisScaleType}
115
+ maintainAspectRatio={maintainAspectRatio}
116
+ />
117
+ ),
103
118
  };
104
119
  case 'table':
105
120
  return {
@@ -1,5 +1,6 @@
1
1
  import { Chart, type ChartConfiguration, type ChartDataset, registerables, type TooltipItem } from 'chart.js';
2
2
  import { BarWithErrorBar, BarWithErrorBarsController } from 'chartjs-chart-error-bars';
3
+ import { useMemo } from 'preact/hooks';
3
4
 
4
5
  import { maxInData } from './prevalence-over-time';
5
6
  import {
@@ -21,55 +22,67 @@ interface PrevalenceOverTimeBarChartProps {
21
22
  yAxisScaleType: ScaleType;
22
23
  confidenceIntervalMethod: ConfidenceIntervalMethod;
23
24
  yAxisMaxConfig: YAxisMaxConfig;
25
+ maintainAspectRatio: boolean;
24
26
  }
25
27
 
26
28
  Chart.register(...registerables, LogitScale, BarWithErrorBarsController, BarWithErrorBar);
27
29
 
30
+ const NO_DATA = 'noData';
31
+
28
32
  const PrevalenceOverTimeBarChart = ({
29
33
  data,
30
34
  yAxisScaleType,
31
35
  confidenceIntervalMethod,
32
36
  yAxisMaxConfig,
37
+ maintainAspectRatio,
33
38
  }: PrevalenceOverTimeBarChartProps) => {
34
- const nullFirstData = data
35
- .filter((prevalenceOverTimeData) => prevalenceOverTimeData.content.length > 0)
36
- .map((variantData) => {
37
- return {
38
- content: variantData.content.sort(sortNullToBeginningThenByDate),
39
- displayName: variantData.displayName,
40
- };
41
- });
39
+ const config = useMemo<ChartConfiguration | typeof NO_DATA>(() => {
40
+ const nullFirstData = data
41
+ .filter((prevalenceOverTimeData) => prevalenceOverTimeData.content.length > 0)
42
+ .map((variantData) => {
43
+ return {
44
+ content: variantData.content.sort(sortNullToBeginningThenByDate),
45
+ displayName: variantData.displayName,
46
+ };
47
+ });
48
+
49
+ if (nullFirstData.length === 0) {
50
+ return NO_DATA;
51
+ }
52
+
53
+ const datasets = nullFirstData.map((graphData, index) =>
54
+ getDataset(graphData, index, confidenceIntervalMethod),
55
+ );
56
+
57
+ const maxY =
58
+ yAxisScaleType !== 'logit'
59
+ ? getYAxisMax(maxInData(nullFirstData), yAxisMaxConfig?.[yAxisScaleType])
60
+ : undefined;
42
61
 
43
- if (nullFirstData.length === 0) {
44
- return <NoDataDisplay />;
45
- }
46
-
47
- const datasets = nullFirstData.map((graphData, index) => getDataset(graphData, index, confidenceIntervalMethod));
48
-
49
- const maxY =
50
- yAxisScaleType !== 'logit'
51
- ? getYAxisMax(maxInData(nullFirstData), yAxisMaxConfig?.[yAxisScaleType])
52
- : undefined;
53
-
54
- const config: ChartConfiguration = {
55
- type: BarWithErrorBarsController.id,
56
- data: {
57
- datasets,
58
- },
59
- options: {
60
- maintainAspectRatio: false,
61
- animation: false,
62
- scales: {
63
- y: { ...getYAxisScale(yAxisScaleType), max: maxY },
62
+ return {
63
+ type: BarWithErrorBarsController.id,
64
+ data: {
65
+ datasets,
64
66
  },
65
- plugins: {
66
- legend: {
67
- display: false,
67
+ options: {
68
+ maintainAspectRatio,
69
+ animation: false,
70
+ scales: {
71
+ y: { ...getYAxisScale(yAxisScaleType), max: maxY },
72
+ },
73
+ plugins: {
74
+ legend: {
75
+ display: false,
76
+ },
77
+ tooltip: tooltip(confidenceIntervalMethod),
68
78
  },
69
- tooltip: tooltip(confidenceIntervalMethod),
70
79
  },
71
- },
72
- };
80
+ };
81
+ }, [data, yAxisScaleType, confidenceIntervalMethod, yAxisMaxConfig, maintainAspectRatio]);
82
+
83
+ if (config === NO_DATA) {
84
+ return <NoDataDisplay />;
85
+ }
73
86
 
74
87
  return <GsChart configuration={config} />;
75
88
  };