@genspectrum/dashboard-components 0.19.5 → 0.19.7

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 (57) hide show
  1. package/README.md +24 -8
  2. package/custom-elements.json +101 -2
  3. package/dist/{LineageFilterChangedEvent-GgkxoF3X.js → NumberRangeFilterChangedEvent-RZ8haPHq.js} +31 -5
  4. package/dist/NumberRangeFilterChangedEvent-RZ8haPHq.js.map +1 -0
  5. package/dist/assets/mutationOverTimeWorker-DQGh08AS.js.map +1 -0
  6. package/dist/components.d.ts +43 -12
  7. package/dist/components.js +78 -63
  8. package/dist/components.js.map +1 -1
  9. package/dist/util.d.ts +21 -10
  10. package/dist/util.js +3 -1
  11. package/package.json +1 -1
  12. package/src/preact/aggregatedData/aggregate.stories.tsx +6 -0
  13. package/src/preact/aggregatedData/aggregate.tsx +10 -1
  14. package/src/preact/components/tabs.tsx +4 -4
  15. package/src/preact/genomeViewer/CDSPlot.tsx +4 -1
  16. package/src/preact/genomeViewer/genome-data-viewer.stories.tsx +6 -0
  17. package/src/preact/mutationComparison/mutation-comparison.stories.tsx +6 -0
  18. package/src/preact/mutationComparison/mutation-comparison.tsx +4 -0
  19. package/src/preact/mutations/mutations.stories.tsx +6 -0
  20. package/src/preact/mutations/mutations.tsx +4 -1
  21. package/src/preact/mutationsOverTime/mutations-over-time.stories.tsx +6 -0
  22. package/src/preact/mutationsOverTime/mutations-over-time.tsx +4 -1
  23. package/src/preact/numberRangeFilter/NumberRangeFilterChangedEvent.ts +1 -1
  24. package/src/preact/numberSequencesOverTime/number-sequences-over-time.stories.tsx +3 -1
  25. package/src/preact/numberSequencesOverTime/number-sequences-over-time.tsx +4 -0
  26. package/src/preact/prevalenceOverTime/prevalence-over-time.stories.tsx +6 -0
  27. package/src/preact/prevalenceOverTime/prevalence-over-time.tsx +4 -1
  28. package/src/preact/relativeGrowthAdvantage/relative-growth-advantage.stories.tsx +6 -0
  29. package/src/preact/relativeGrowthAdvantage/relative-growth-advantage.tsx +4 -1
  30. package/src/preact/sequencesByLocation/sequences-by-location.stories.tsx +6 -0
  31. package/src/preact/sequencesByLocation/sequences-by-location.tsx +4 -0
  32. package/src/preact/shared/stories/expectFinishedLoadingEvent.ts +12 -0
  33. package/src/preact/statistic/statistics.stories.tsx +6 -0
  34. package/src/preact/statistic/statistics.tsx +4 -1
  35. package/src/preact/wastewater/mutationsOverTime/wastewater-mutations-over-time.stories.tsx +2 -0
  36. package/src/preact/wastewater/mutationsOverTime/wastewater-mutations-over-time.tsx +4 -1
  37. package/src/utilEntrypoint.ts +6 -1
  38. package/src/utils/gsEventNames.ts +1 -0
  39. package/src/utils/useDispatchFinishedLoadingEvent.ts +19 -0
  40. package/src/web-components/input/gs-number-range-filter.tsx +2 -2
  41. package/src/web-components/visualization/gs-aggregate.tsx +3 -0
  42. package/src/web-components/visualization/gs-genome-data-viewer.tsx +2 -0
  43. package/src/web-components/visualization/gs-mutation-comparison.tsx +3 -0
  44. package/src/web-components/visualization/gs-mutations-over-time.tsx +3 -0
  45. package/src/web-components/visualization/gs-mutations.tsx +2 -0
  46. package/src/web-components/visualization/gs-number-sequences-over-time.tsx +3 -0
  47. package/src/web-components/visualization/gs-prevalence-over-time.tsx +3 -0
  48. package/src/web-components/visualization/gs-relative-growth-advantage.tsx +3 -0
  49. package/src/web-components/visualization/gs-sequences-by-location.tsx +3 -0
  50. package/src/web-components/visualization/gs-statistics.tsx +3 -0
  51. package/src/web-components/wastewaterVisualization/gs-wastewater-mutations-over-time.tsx +3 -0
  52. package/standalone-bundle/assets/mutationOverTimeWorker-DAf2_NiP.js.map +1 -0
  53. package/standalone-bundle/dashboard-components.js +6980 -6945
  54. package/standalone-bundle/dashboard-components.js.map +1 -1
  55. package/dist/LineageFilterChangedEvent-GgkxoF3X.js.map +0 -1
  56. package/dist/assets/mutationOverTimeWorker-ChQTFL68.js.map +0 -1
  57. package/standalone-bundle/assets/mutationOverTimeWorker-jChgWnwp.js.map +0 -1
package/dist/util.d.ts CHANGED
@@ -151,6 +151,7 @@ declare const dateRangeValueSchema: default_2.ZodNullable<default_2.ZodUnion<[de
151
151
 
152
152
  export declare const gsEventNames: {
153
153
  readonly error: "gs-error";
154
+ readonly componentFinishedLoading: "gs-component-finished-loading";
154
155
  readonly dateRangeFilterChanged: "gs-date-range-filter-changed";
155
156
  readonly dateRangeOptionChanged: "gs-date-range-option-changed";
156
157
  readonly mutationFilterChanged: "gs-mutation-filter-changed";
@@ -186,6 +187,8 @@ declare type LapisLocationFilter = default_2.infer<typeof lapisLocationFilterSch
186
187
 
187
188
  declare const lapisLocationFilterSchema: default_2.ZodRecord<default_2.ZodString, default_2.ZodUnion<[default_2.ZodString, default_2.ZodUndefined]>>;
188
189
 
190
+ export declare type LapisNumberFilter = Record<string, number | undefined>;
191
+
189
192
  declare type LapisTextFilter = Record<string, string | undefined>;
190
193
 
191
194
  export declare class LineageFilterChangedEvent extends CustomEvent<LapisLineageFilter> {
@@ -488,6 +491,10 @@ declare const namedLapisFilterSchema: default_2.ZodObject<{
488
491
 
489
492
  export declare type NumberRange = default_2.infer<typeof numberRangeSchema>;
490
493
 
494
+ export declare class NumberRangeFilterChangedEvent extends CustomEvent<LapisNumberFilter> {
495
+ constructor(detail: LapisNumberFilter);
496
+ }
497
+
491
498
  declare const numberRangeSchema: default_2.ZodObject<{
492
499
  min: default_2.ZodOptional<default_2.ZodNumber>;
493
500
  max: default_2.ZodOptional<default_2.ZodNumber>;
@@ -499,6 +506,10 @@ declare const numberRangeSchema: default_2.ZodObject<{
499
506
  max?: number | undefined;
500
507
  }>;
501
508
 
509
+ export declare class NumberRangeValueChangedEvent extends CustomEvent<NumberRange> {
510
+ constructor(detail: NumberRange);
511
+ }
512
+
502
513
  export declare type NumberSequencesOverTimeProps = default_2.infer<typeof numberSequencesOverTimePropsSchema>;
503
514
 
504
515
  declare const numberSequencesOverTimePropsSchema: default_2.ZodObject<{
@@ -943,7 +954,7 @@ declare global {
943
954
 
944
955
  declare global {
945
956
  interface HTMLElementTagNameMap {
946
- 'gs-mutation-comparison-component': MutationComparisonComponent;
957
+ 'gs-mutations-component': MutationsComponent;
947
958
  }
948
959
  }
949
960
 
@@ -951,7 +962,7 @@ declare global {
951
962
  declare global {
952
963
  namespace JSX {
953
964
  interface IntrinsicElements {
954
- 'gs-mutation-comparison-component': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
965
+ 'gs-mutations-component': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
955
966
  }
956
967
  }
957
968
  }
@@ -959,7 +970,7 @@ declare global {
959
970
 
960
971
  declare global {
961
972
  interface HTMLElementTagNameMap {
962
- 'gs-mutations-component': MutationsComponent;
973
+ 'gs-mutation-comparison-component': MutationComparisonComponent;
963
974
  }
964
975
  }
965
976
 
@@ -967,7 +978,7 @@ declare global {
967
978
  declare global {
968
979
  namespace JSX {
969
980
  interface IntrinsicElements {
970
- 'gs-mutations-component': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
981
+ 'gs-mutation-comparison-component': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
971
982
  }
972
983
  }
973
984
  }
@@ -1123,10 +1134,10 @@ declare global {
1123
1134
 
1124
1135
  declare global {
1125
1136
  interface HTMLElementTagNameMap {
1126
- 'gs-text-filter': TextFilterComponent;
1137
+ 'gs-location-filter': LocationFilterComponent;
1127
1138
  }
1128
1139
  interface HTMLElementEventMap {
1129
- [gsEventNames.textFilterChanged]: TextFilterChangedEvent;
1140
+ [gsEventNames.locationChanged]: LocationChangedEvent;
1130
1141
  }
1131
1142
  }
1132
1143
 
@@ -1134,7 +1145,7 @@ declare global {
1134
1145
  declare global {
1135
1146
  namespace JSX {
1136
1147
  interface IntrinsicElements {
1137
- 'gs-text-filter': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1148
+ 'gs-location-filter': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1138
1149
  }
1139
1150
  }
1140
1151
  }
@@ -1142,10 +1153,10 @@ declare global {
1142
1153
 
1143
1154
  declare global {
1144
1155
  interface HTMLElementTagNameMap {
1145
- 'gs-location-filter': LocationFilterComponent;
1156
+ 'gs-text-filter': TextFilterComponent;
1146
1157
  }
1147
1158
  interface HTMLElementEventMap {
1148
- [gsEventNames.locationChanged]: LocationChangedEvent;
1159
+ [gsEventNames.textFilterChanged]: TextFilterChangedEvent;
1149
1160
  }
1150
1161
  }
1151
1162
 
@@ -1153,7 +1164,7 @@ declare global {
1153
1164
  declare global {
1154
1165
  namespace JSX {
1155
1166
  interface IntrinsicElements {
1156
- 'gs-location-filter': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1167
+ 'gs-text-filter': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1157
1168
  }
1158
1169
  }
1159
1170
  }
package/dist/util.js CHANGED
@@ -1,8 +1,10 @@
1
- import { D, a, L, T, d, g, v } from "./LineageFilterChangedEvent-GgkxoF3X.js";
1
+ import { D, a, L, N, b, T, d, g, v } from "./NumberRangeFilterChangedEvent-RZ8haPHq.js";
2
2
  export {
3
3
  D as DateRangeOptionChangedEvent,
4
4
  a as LineageFilterChangedEvent,
5
5
  L as LocationChangedEvent,
6
+ N as NumberRangeFilterChangedEvent,
7
+ b as NumberRangeValueChangedEvent,
6
8
  T as TextFilterChangedEvent,
7
9
  d as dateRangeOptionPresets,
8
10
  g as gsEventNames,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@genspectrum/dashboard-components",
3
- "version": "0.19.5",
3
+ "version": "0.19.7",
4
4
  "description": "GenSpectrum web components for building dashboards",
5
5
  "type": "module",
6
6
  "license": "AGPL-3.0-only",
@@ -6,6 +6,7 @@ import { Aggregate, type AggregateProps } from './aggregate';
6
6
  import { AGGREGATED_ENDPOINT, LAPIS_URL } from '../../constants';
7
7
  import { LapisUrlContextProvider } from '../LapisUrlContext';
8
8
  import { expectInvalidAttributesErrorMessage, playThatExpectsErrorMessage } from '../shared/stories/expectErrorMessage';
9
+ import { playThatExpectsFinishedLoadingEvent } from '../shared/stories/expectFinishedLoadingEvent';
9
10
 
10
11
  const meta: Meta<AggregateProps> = {
11
12
  title: 'Visualization/Aggregate',
@@ -62,6 +63,11 @@ export const Default: StoryObj<AggregateProps> = {
62
63
  },
63
64
  };
64
65
 
66
+ export const FiresFinishedLoadingEvent: StoryObj<AggregateProps> = {
67
+ ...Default,
68
+ play: playThatExpectsFinishedLoadingEvent(),
69
+ };
70
+
65
71
  export const FailsLoadingData: StoryObj<AggregateProps> = {
66
72
  ...Default,
67
73
  parameters: {
@@ -5,6 +5,7 @@ import { useLapisUrl } from '../LapisUrlContext';
5
5
  import { AggregateTable } from './aggregate-table';
6
6
  import { type AggregateData, queryAggregateData } from '../../query/queryAggregateData';
7
7
  import { lapisFilterSchema, views } from '../../types';
8
+ import { useDispatchFinishedLoadingEvent } from '../../utils/useDispatchFinishedLoadingEvent';
8
9
  import { CsvDownloadButton } from '../components/csv-download-button';
9
10
  import { ErrorBoundary } from '../components/error-boundary';
10
11
  import { Fullscreen } from '../components/fullscreen';
@@ -75,6 +76,8 @@ type AggregatedDataTabsProps = {
75
76
  };
76
77
 
77
78
  const AggregatedDataTabs: FunctionComponent<AggregatedDataTabsProps> = ({ data, originalComponentProps }) => {
79
+ const tabsRef = useDispatchFinishedLoadingEvent();
80
+
78
81
  const maintainAspectRatio = getMaintainAspectRatio(originalComponentProps.height);
79
82
 
80
83
  const getTab = (view: AggregateView) => {
@@ -109,7 +112,13 @@ const AggregatedDataTabs: FunctionComponent<AggregatedDataTabsProps> = ({ data,
109
112
 
110
113
  const tabs = originalComponentProps.views.map((view) => getTab(view));
111
114
 
112
- return <Tabs tabs={tabs} toolbar={<Toolbar data={data} originalComponentProps={originalComponentProps} />} />;
115
+ return (
116
+ <Tabs
117
+ ref={tabsRef}
118
+ tabs={tabs}
119
+ toolbar={<Toolbar data={data} originalComponentProps={originalComponentProps} />}
120
+ />
121
+ );
113
122
  };
114
123
 
115
124
  type ToolbarProps = {
@@ -1,4 +1,4 @@
1
- import { type FunctionComponent } from 'preact';
1
+ import { forwardRef } from 'preact/compat';
2
2
  import { useState } from 'preact/hooks';
3
3
  import { type JSXInternal } from 'preact/src/jsx';
4
4
 
@@ -12,7 +12,7 @@ interface ComponentTabsProps {
12
12
  toolbar?: JSXInternal.Element | ((activeTab: string) => JSXInternal.Element);
13
13
  }
14
14
 
15
- const Tabs: FunctionComponent<ComponentTabsProps> = ({ tabs, toolbar }) => {
15
+ const Tabs = forwardRef<HTMLDivElement, ComponentTabsProps>(({ tabs, toolbar }, ref) => {
16
16
  const [activeTab, setActiveTab] = useState(tabs[0]?.title);
17
17
 
18
18
  const tabElements = (
@@ -40,7 +40,7 @@ const Tabs: FunctionComponent<ComponentTabsProps> = ({ tabs, toolbar }) => {
40
40
  const toolbarElement = typeof toolbar === 'function' ? toolbar(activeTab) : toolbar;
41
41
 
42
42
  return (
43
- <div className='h-full w-full flex flex-col'>
43
+ <div ref={ref} className='h-full w-full flex flex-col'>
44
44
  <div className='flex flex-row justify-between flex-wrap'>
45
45
  {tabElements}
46
46
  {toolbar && <div className='py-2 flex flex-wrap gap-y-1'>{toolbarElement}</div>}
@@ -54,6 +54,6 @@ const Tabs: FunctionComponent<ComponentTabsProps> = ({ tabs, toolbar }) => {
54
54
  </div>
55
55
  </div>
56
56
  );
57
- };
57
+ });
58
58
 
59
59
  export default Tabs;
@@ -2,6 +2,7 @@ import { Fragment, type FunctionComponent } from 'preact';
2
2
  import { useMemo, useState } from 'preact/hooks';
3
3
 
4
4
  import { type CDSFeature } from './loadGff3';
5
+ import { useDispatchFinishedLoadingEvent } from '../../utils/useDispatchFinishedLoadingEvent';
5
6
  import { MinMaxRangeSlider } from '../components/min-max-range-slider';
6
7
  import Tooltip from '../components/tooltip';
7
8
  import { singleGraphColorRGBByName } from '../shared/charts/colors';
@@ -183,6 +184,8 @@ interface CDSProps {
183
184
  }
184
185
 
185
186
  const CDSPlot: FunctionComponent<CDSProps> = (componentProps) => {
187
+ const ref = useDispatchFinishedLoadingEvent();
188
+
186
189
  const { gffData, genomeLength, width } = componentProps;
187
190
 
188
191
  const [zoomStart, setZoomStart] = useState(0);
@@ -197,7 +200,7 @@ const CDSPlot: FunctionComponent<CDSProps> = (componentProps) => {
197
200
  };
198
201
 
199
202
  return (
200
- <div class='p-4'>
203
+ <div ref={ref} class='p-4'>
201
204
  <CDSBars gffData={gffData} zoomStart={zoomStart} zoomEnd={zoomEnd} />
202
205
  <XAxis zoomStart={zoomStart} zoomEnd={zoomEnd} fullWidth={width} />
203
206
  <div class='relative w-full h-5'>
@@ -2,6 +2,7 @@ import { type Meta, type StoryObj } from '@storybook/preact';
2
2
 
3
3
  import { GenomeDataViewer, type GenomeDataViewerProps } from './genome-data-viewer';
4
4
  import { playThatExpectsErrorMessage } from '../shared/stories/expectErrorMessage';
5
+ import { playThatExpectsFinishedLoadingEvent } from '../shared/stories/expectFinishedLoadingEvent';
5
6
 
6
7
  const meta: Meta<GenomeDataViewerProps> = {
7
8
  title: 'Visualization/GenomeDataViewer',
@@ -75,6 +76,11 @@ export const Default: StoryObj<GenomeDataViewerProps> = {
75
76
  },
76
77
  };
77
78
 
79
+ export const FiresFinishedLoadingEvent: StoryObj<GenomeDataViewerProps> = {
80
+ ...Default,
81
+ play: playThatExpectsFinishedLoadingEvent(),
82
+ };
83
+
78
84
  export const InvalidProps: StoryObj<GenomeDataViewerProps> = {
79
85
  ...Default,
80
86
  args: {
@@ -10,6 +10,7 @@ import { type MutationAnnotations } from '../../web-components/mutation-annotati
10
10
  import { LapisUrlContextProvider } from '../LapisUrlContext';
11
11
  import { MutationAnnotationsContextProvider } from '../MutationAnnotationsContext';
12
12
  import { ReferenceGenomeContext } from '../ReferenceGenomeContext';
13
+ import { playThatExpectsFinishedLoadingEvent } from '../shared/stories/expectFinishedLoadingEvent';
13
14
  import { expectMutationAnnotation } from '../shared/stories/expectMutationAnnotation';
14
15
 
15
16
  const dateToSomeDataset = '2022-01-01';
@@ -142,6 +143,11 @@ export const TwoVariants: StoryObj<MutationComparisonProps> = {
142
143
  },
143
144
  };
144
145
 
146
+ export const FiresFinishedLoadingEvent: StoryObj<MutationComparisonProps> = {
147
+ ...TwoVariants,
148
+ play: playThatExpectsFinishedLoadingEvent(),
149
+ };
150
+
145
151
  export const FilterForOnlyDeletions: StoryObj<MutationComparisonProps> = {
146
152
  ...TwoVariants,
147
153
  play: async ({ canvasElement }) => {
@@ -7,6 +7,7 @@ import { MutationComparisonTable } from './mutation-comparison-table';
7
7
  import { MutationComparisonVenn } from './mutation-comparison-venn';
8
8
  import { filterMutationData, type MutationData, queryMutationData } from './queryMutationData';
9
9
  import { namedLapisFilterSchema, sequenceTypeSchema, views } from '../../types';
10
+ import { useDispatchFinishedLoadingEvent } from '../../utils/useDispatchFinishedLoadingEvent';
10
11
  import { useLapisUrl } from '../LapisUrlContext';
11
12
  import { CsvDownloadButton } from '../components/csv-download-button';
12
13
  import { ErrorBoundary } from '../components/error-boundary';
@@ -80,6 +81,8 @@ type MutationComparisonTabsProps = {
80
81
  };
81
82
 
82
83
  const MutationComparisonTabs: FunctionComponent<MutationComparisonTabsProps> = ({ data, originalComponentProps }) => {
84
+ const tabsRef = useDispatchFinishedLoadingEvent();
85
+
83
86
  const [proportionInterval, setProportionInterval] = useState({ min: 0.5, max: 1 });
84
87
  const [displayedMutationTypes, setDisplayedMutationTypes] = useState<DisplayedMutationType[]>([
85
88
  { label: 'Substitutions', checked: true, type: 'substitution' },
@@ -127,6 +130,7 @@ const MutationComparisonTabs: FunctionComponent<MutationComparisonTabsProps> = (
127
130
 
128
131
  return (
129
132
  <Tabs
133
+ ref={tabsRef}
130
134
  tabs={tabs}
131
135
  toolbar={
132
136
  <Toolbar
@@ -17,6 +17,7 @@ import { type MutationAnnotations } from '../../web-components/mutation-annotati
17
17
  import { LapisUrlContextProvider } from '../LapisUrlContext';
18
18
  import { MutationAnnotationsContextProvider } from '../MutationAnnotationsContext';
19
19
  import { ReferenceGenomeContext } from '../ReferenceGenomeContext';
20
+ import { playThatExpectsFinishedLoadingEvent } from '../shared/stories/expectFinishedLoadingEvent';
20
21
  import { expectMutationAnnotation } from '../shared/stories/expectMutationAnnotation';
21
22
 
22
23
  const meta: Meta<MutationsProps> = {
@@ -147,6 +148,11 @@ export const Default: StoryObj<MutationsProps> = {
147
148
  },
148
149
  };
149
150
 
151
+ export const FiresFinishedLoadingEvent: StoryObj<MutationsProps> = {
152
+ ...Default,
153
+ play: playThatExpectsFinishedLoadingEvent(),
154
+ };
155
+
150
156
  export const GridTab: StoryObj<MutationsProps> = {
151
157
  ...Default,
152
158
  play: async ({ canvasElement, step }) => {
@@ -15,6 +15,7 @@ import {
15
15
  type SubstitutionOrDeletionEntry,
16
16
  views,
17
17
  } from '../../types';
18
+ import { useDispatchFinishedLoadingEvent } from '../../utils/useDispatchFinishedLoadingEvent';
18
19
  import { useLapisUrl } from '../LapisUrlContext';
19
20
  import { CsvDownloadButton } from '../components/csv-download-button';
20
21
  import { ErrorBoundary } from '../components/error-boundary';
@@ -87,6 +88,8 @@ type MutationTabsProps = {
87
88
  };
88
89
 
89
90
  const MutationsTabs: FunctionComponent<MutationTabsProps> = ({ mutationsData, originalComponentProps }) => {
91
+ const tabsRef = useDispatchFinishedLoadingEvent();
92
+
90
93
  const [proportionInterval, setProportionInterval] = useState({ min: 0.05, max: 1 });
91
94
 
92
95
  const [displayedSegments, setDisplayedSegments] = useDisplayedSegments(originalComponentProps.sequenceType);
@@ -153,7 +156,7 @@ const MutationsTabs: FunctionComponent<MutationTabsProps> = ({ mutationsData, or
153
156
  />
154
157
  );
155
158
 
156
- return <Tabs tabs={tabs} toolbar={toolbar} />;
159
+ return <Tabs ref={tabsRef} tabs={tabs} toolbar={toolbar} />;
157
160
  };
158
161
 
159
162
  type ToolbarProps = {
@@ -10,6 +10,7 @@ import { LapisUrlContextProvider } from '../LapisUrlContext';
10
10
  import { MutationAnnotationsContextProvider } from '../MutationAnnotationsContext';
11
11
  import { ReferenceGenomeContext } from '../ReferenceGenomeContext';
12
12
  import { expectInvalidAttributesErrorMessage } from '../shared/stories/expectErrorMessage';
13
+ import { playThatExpectsFinishedLoadingEvent } from '../shared/stories/expectFinishedLoadingEvent';
13
14
  import { expectMutationAnnotation } from '../shared/stories/expectMutationAnnotation';
14
15
 
15
16
  const meta: Meta<MutationsOverTimeProps> = {
@@ -83,6 +84,11 @@ export const Default: StoryObj<MutationsOverTimeProps> = {
83
84
  },
84
85
  };
85
86
 
87
+ export const FiresFinishedLoadingEvent: StoryObj<MutationsOverTimeProps> = {
88
+ ...Default,
89
+ play: playThatExpectsFinishedLoadingEvent(),
90
+ };
91
+
86
92
  export const ShowsMutationAnnotations: StoryObj<MutationsOverTimeProps> = {
87
93
  ...Default,
88
94
  play: async ({ canvasElement }) => {
@@ -18,6 +18,7 @@ import {
18
18
  } from '../../types';
19
19
  import { type Deletion, type Substitution } from '../../utils/mutations';
20
20
  import { toTemporalClass } from '../../utils/temporalClass';
21
+ import { useDispatchFinishedLoadingEvent } from '../../utils/useDispatchFinishedLoadingEvent';
21
22
  import { useLapisUrl } from '../LapisUrlContext';
22
23
  import { useMutationAnnotationsProvider } from '../MutationAnnotationsContext';
23
24
  import { type ColorScale } from '../components/color-scale-selector';
@@ -126,6 +127,8 @@ const MutationsOverTimeTabs: FunctionComponent<MutationOverTimeTabsProps> = ({
126
127
  originalComponentProps,
127
128
  overallMutationData,
128
129
  }) => {
130
+ const tabsRef = useDispatchFinishedLoadingEvent();
131
+
129
132
  const [mutationFilterValue, setMutationFilterValue] = useState('');
130
133
  const annotationProvider = useMutationAnnotationsProvider();
131
134
 
@@ -207,7 +210,7 @@ const MutationsOverTimeTabs: FunctionComponent<MutationOverTimeTabsProps> = ({
207
210
 
208
211
  return (
209
212
  <PageSizeContextProvider pageSizes={originalComponentProps.pageSizes}>
210
- <Tabs tabs={tabs} toolbar={toolbar} />
213
+ <Tabs ref={tabsRef} tabs={tabs} toolbar={toolbar} />
211
214
  </PageSizeContextProvider>
212
215
  );
213
216
  };
@@ -2,7 +2,7 @@ import z from 'zod';
2
2
 
3
3
  import { gsEventNames } from '../../utils/gsEventNames';
4
4
 
5
- type LapisNumberFilter = Record<string, number | undefined>;
5
+ export type LapisNumberFilter = Record<string, number | undefined>;
6
6
 
7
7
  export const numberRangeSchema = z.object({
8
8
  min: z.number().optional(),
@@ -7,6 +7,7 @@ import twoVariantsEG from '../../preact/numberSequencesOverTime/__mockData__/two
7
7
  import twoVariantsJN1 from '../../preact/numberSequencesOverTime/__mockData__/twoVariantsJN1.json';
8
8
  import { LapisUrlContextProvider } from '../LapisUrlContext';
9
9
  import { expectInvalidAttributesErrorMessage } from '../shared/stories/expectErrorMessage';
10
+ import { playThatExpectsFinishedLoadingEvent } from '../shared/stories/expectFinishedLoadingEvent';
10
11
 
11
12
  export default {
12
13
  title: 'Visualization/NumberSequencesOverTime',
@@ -71,8 +72,9 @@ const Template: StoryObj<NumberSequencesOverTimeProps> = {
71
72
  },
72
73
  };
73
74
 
74
- export const Table = {
75
+ export const FiresFinishedLoadingEvent: StoryObj<NumberSequencesOverTimeProps> = {
75
76
  ...Template,
77
+ play: playThatExpectsFinishedLoadingEvent(),
76
78
  };
77
79
 
78
80
  export const TwoVariants: StoryObj<NumberSequencesOverTimeProps> = {
@@ -11,6 +11,7 @@ import {
11
11
  queryNumberOfSequencesOverTime,
12
12
  } from '../../query/queryNumberOfSequencesOverTime';
13
13
  import { namedLapisFilterSchema, temporalGranularitySchema, views } from '../../types';
14
+ import { useDispatchFinishedLoadingEvent } from '../../utils/useDispatchFinishedLoadingEvent';
14
15
  import { useLapisUrl } from '../LapisUrlContext';
15
16
  import { CsvDownloadButton } from '../components/csv-download-button';
16
17
  import { ErrorBoundary } from '../components/error-boundary';
@@ -88,6 +89,8 @@ interface NumberSequencesOverTimeTabsProps {
88
89
  }
89
90
 
90
91
  const NumberSequencesOverTimeTabs = ({ data, originalComponentProps }: NumberSequencesOverTimeTabsProps) => {
92
+ const tabsRef = useDispatchFinishedLoadingEvent();
93
+
91
94
  const [yAxisScaleType, setYAxisScaleType] = useState<ScaleType>('linear');
92
95
 
93
96
  const maintainAspectRatio = getMaintainAspectRatio(originalComponentProps.height);
@@ -134,6 +137,7 @@ const NumberSequencesOverTimeTabs = ({ data, originalComponentProps }: NumberSeq
134
137
 
135
138
  return (
136
139
  <Tabs
140
+ ref={tabsRef}
137
141
  tabs={originalComponentProps.views.map((view) => getTab(view))}
138
142
  toolbar={(activeTab) => (
139
143
  <Toolbar
@@ -11,6 +11,7 @@ import numeratorOneDataset from './__mockData__/numeratorFilterOneDataset.json';
11
11
  import { PrevalenceOverTime, type PrevalenceOverTimeProps } from './prevalence-over-time';
12
12
  import { AGGREGATED_ENDPOINT, LAPIS_URL } from '../../constants';
13
13
  import { expectInvalidAttributesErrorMessage } from '../shared/stories/expectErrorMessage';
14
+ import { playThatExpectsFinishedLoadingEvent } from '../shared/stories/expectFinishedLoadingEvent';
14
15
 
15
16
  export default {
16
17
  title: 'Visualization/PrevalenceOverTime',
@@ -121,6 +122,11 @@ export const TwoVariants: StoryObj<PrevalenceOverTimeProps> = {
121
122
  },
122
123
  };
123
124
 
125
+ export const FiresFinishedLoadingEvent: StoryObj<PrevalenceOverTimeProps> = {
126
+ ...TwoVariants,
127
+ play: playThatExpectsFinishedLoadingEvent(),
128
+ };
129
+
124
130
  export const OneVariant: StoryObj<PrevalenceOverTimeProps> = {
125
131
  ...Template,
126
132
  args: {
@@ -9,6 +9,7 @@ import PrevalenceOverTimeLineChart from './prevalence-over-time-line-chart';
9
9
  import PrevalenceOverTimeTable from './prevalence-over-time-table';
10
10
  import { type PrevalenceOverTimeData, queryPrevalenceOverTime } from '../../query/queryPrevalenceOverTime';
11
11
  import { lapisFilterSchema, namedLapisFilterSchema, temporalGranularitySchema, views } from '../../types';
12
+ import { useDispatchFinishedLoadingEvent } from '../../utils/useDispatchFinishedLoadingEvent';
12
13
  import { useLapisUrl } from '../LapisUrlContext';
13
14
  import { ConfidenceIntervalSelector } from '../components/confidence-interval-selector';
14
15
  import { CsvDownloadButton } from '../components/csv-download-button';
@@ -101,6 +102,8 @@ type PrevalenceOverTimeTabsProps = PrevalenceOverTimeProps & {
101
102
  };
102
103
 
103
104
  const PrevalenceOverTimeTabs: FunctionComponent<PrevalenceOverTimeTabsProps> = ({ data, ...componentProps }) => {
105
+ const tabsRef = useDispatchFinishedLoadingEvent();
106
+
104
107
  const { views, granularity, confidenceIntervalMethods, pageSize, yAxisMaxLinear, yAxisMaxLogarithmic } =
105
108
  componentProps;
106
109
  const [yAxisScaleType, setYAxisScaleType] = useState<ScaleType>('linear');
@@ -182,7 +185,7 @@ const PrevalenceOverTimeTabs: FunctionComponent<PrevalenceOverTimeTabsProps> = (
182
185
  />
183
186
  );
184
187
 
185
- return <Tabs tabs={tabs} toolbar={toolbar} />;
188
+ return <Tabs ref={tabsRef} tabs={tabs} toolbar={toolbar} />;
186
189
  };
187
190
 
188
191
  type ToolbarProps = PrevalenceOverTimeProps & {
@@ -7,6 +7,7 @@ import { RelativeGrowthAdvantage, type RelativeGrowthAdvantageProps } from './re
7
7
  import { AGGREGATED_ENDPOINT, LAPIS_URL } from '../../constants';
8
8
  import { LapisUrlContextProvider } from '../LapisUrlContext';
9
9
  import { expectInvalidAttributesErrorMessage } from '../shared/stories/expectErrorMessage';
10
+ import { playThatExpectsFinishedLoadingEvent } from '../shared/stories/expectFinishedLoadingEvent';
10
11
 
11
12
  export default {
12
13
  title: 'Visualization/RelativeGrowthAdvantage',
@@ -90,6 +91,11 @@ export const Primary: StoryObj<RelativeGrowthAdvantageProps> = {
90
91
  },
91
92
  };
92
93
 
94
+ export const FiresFinishedLoadingEvent: StoryObj<RelativeGrowthAdvantageProps> = {
95
+ ...Primary,
96
+ play: playThatExpectsFinishedLoadingEvent(),
97
+ };
98
+
93
99
  export const TooFewDataToComputeGrowthAdvantage: StoryObj<RelativeGrowthAdvantageProps> = {
94
100
  ...Primary,
95
101
  args: {
@@ -9,6 +9,7 @@ import {
9
9
  type RelativeGrowthAdvantageData,
10
10
  } from '../../query/queryRelativeGrowthAdvantage';
11
11
  import { lapisFilterSchema, views } from '../../types';
12
+ import { useDispatchFinishedLoadingEvent } from '../../utils/useDispatchFinishedLoadingEvent';
12
13
  import { useLapisUrl } from '../LapisUrlContext';
13
14
  import { ErrorBoundary } from '../components/error-boundary';
14
15
  import { Fullscreen } from '../components/fullscreen';
@@ -103,6 +104,8 @@ const RelativeGrowthAdvantageTabs: FunctionComponent<RelativeGrowthAdvantageTabs
103
104
  setYAxisScaleType,
104
105
  originalComponentProps,
105
106
  }) => {
107
+ const tabsRef = useDispatchFinishedLoadingEvent();
108
+
106
109
  const maintainAspectRatio = getMaintainAspectRatio(originalComponentProps.height);
107
110
 
108
111
  const getTab = (view: RelativeGrowthAdvantageView) => {
@@ -138,7 +141,7 @@ const RelativeGrowthAdvantageTabs: FunctionComponent<RelativeGrowthAdvantageTabs
138
141
  />
139
142
  );
140
143
 
141
- return <Tabs tabs={tabs} toolbar={toolbar} />;
144
+ return <Tabs ref={tabsRef} tabs={tabs} toolbar={toolbar} />;
142
145
  };
143
146
 
144
147
  type RelativeGrowthAdvantageToolbarProps = {
@@ -7,6 +7,7 @@ import { LapisUrlContextProvider } from '../LapisUrlContext';
7
7
  import aggregatedWorld from './__mockData__/aggregatedWorld.json';
8
8
  import { SequencesByLocation, type SequencesByLocationProps } from './sequences-by-location';
9
9
  import { expectInvalidAttributesErrorMessage, playThatExpectsErrorMessage } from '../shared/stories/expectErrorMessage';
10
+ import { playThatExpectsFinishedLoadingEvent } from '../shared/stories/expectFinishedLoadingEvent';
10
11
 
11
12
  import 'leaflet/dist/leaflet.css';
12
13
  import './leafletStyleModifications.css';
@@ -92,6 +93,11 @@ export const Default: StoryObj<SequencesByLocationProps> = {
92
93
  },
93
94
  };
94
95
 
96
+ export const FiresFinishedLoadingEvent: StoryObj<SequencesByLocationProps> = {
97
+ ...Default,
98
+ play: playThatExpectsFinishedLoadingEvent(),
99
+ };
100
+
95
101
  export const NoData: StoryObj<SequencesByLocationProps> = {
96
102
  ...Default,
97
103
  parameters: {
@@ -11,6 +11,7 @@ import {
11
11
  } from '../../query/computeMapLocationData';
12
12
  import { type AggregateData } from '../../query/queryAggregateData';
13
13
  import { querySequencesByLocationData } from '../../query/querySequencesByLocationData';
14
+ import { useDispatchFinishedLoadingEvent } from '../../utils/useDispatchFinishedLoadingEvent';
14
15
  import { CsvDownloadButton } from '../components/csv-download-button';
15
16
  import { ErrorBoundary } from '../components/error-boundary';
16
17
  import { Fullscreen } from '../components/fullscreen';
@@ -93,6 +94,8 @@ const SequencesByLocationMapTabs: FunctionComponent<SequencesByLocationMapTabsPr
93
94
  originalComponentProps,
94
95
  data,
95
96
  }) => {
97
+ const tabsRef = useDispatchFinishedLoadingEvent();
98
+
96
99
  const maintainAspectRatio = getMaintainAspectRatio(originalComponentProps.height);
97
100
 
98
101
  const getTab = (view: SequencesByLocationMapView) => {
@@ -136,6 +139,7 @@ const SequencesByLocationMapTabs: FunctionComponent<SequencesByLocationMapTabsPr
136
139
 
137
140
  return (
138
141
  <Tabs
142
+ ref={tabsRef}
139
143
  tabs={tabs}
140
144
  toolbar={<Toolbar originalComponentProps={originalComponentProps} tableData={data.tableData} />}
141
145
  />
@@ -0,0 +1,12 @@
1
+ import { expect, fn, waitFor } from '@storybook/test';
2
+
3
+ import { gsEventNames } from '../../../utils/gsEventNames';
4
+
5
+ export function playThatExpectsFinishedLoadingEvent() {
6
+ return async ({ canvasElement }: { canvasElement: HTMLElement }) => {
7
+ const componentFinishedLoadingListenerMock = fn();
8
+ canvasElement.addEventListener(gsEventNames.componentFinishedLoading, componentFinishedLoadingListenerMock);
9
+
10
+ await waitFor(() => expect(componentFinishedLoadingListenerMock).toHaveBeenCalled());
11
+ };
12
+ }
@@ -6,6 +6,7 @@ import { LapisUrlContextProvider } from '../LapisUrlContext';
6
6
  import denominatorData from './__mockData__/denominator.json';
7
7
  import numeratorData from './__mockData__/numerator.json';
8
8
  import { Statistics, type StatisticsProps } from './statistics';
9
+ import { playThatExpectsFinishedLoadingEvent } from '../shared/stories/expectFinishedLoadingEvent';
9
10
 
10
11
  const meta: Meta<StatisticsProps> = {
11
12
  title: 'Visualization/Statistics',
@@ -78,3 +79,8 @@ export const Default: StoryObj<StatisticsProps> = {
78
79
  });
79
80
  },
80
81
  };
82
+
83
+ export const FiresFinishedLoadingEvent: StoryObj<StatisticsProps> = {
84
+ ...Default,
85
+ play: playThatExpectsFinishedLoadingEvent(),
86
+ };