@genspectrum/dashboard-components 0.19.5 → 0.19.6

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 (52) hide show
  1. package/custom-elements.json +99 -0
  2. package/dist/{LineageFilterChangedEvent-GgkxoF3X.js → LineageFilterChangedEvent-Cqa8Frcf.js} +2 -1
  3. package/dist/{LineageFilterChangedEvent-GgkxoF3X.js.map → LineageFilterChangedEvent-Cqa8Frcf.js.map} +1 -1
  4. package/dist/assets/mutationOverTimeWorker-DQGh08AS.js.map +1 -0
  5. package/dist/components.d.ts +45 -14
  6. package/dist/components.js +48 -15
  7. package/dist/components.js.map +1 -1
  8. package/dist/util.d.ts +15 -14
  9. package/dist/util.js +1 -1
  10. package/package.json +1 -1
  11. package/src/preact/aggregatedData/aggregate.stories.tsx +6 -0
  12. package/src/preact/aggregatedData/aggregate.tsx +10 -1
  13. package/src/preact/components/tabs.tsx +4 -4
  14. package/src/preact/genomeViewer/CDSPlot.tsx +4 -1
  15. package/src/preact/genomeViewer/genome-data-viewer.stories.tsx +6 -0
  16. package/src/preact/mutationComparison/mutation-comparison.stories.tsx +6 -0
  17. package/src/preact/mutationComparison/mutation-comparison.tsx +4 -0
  18. package/src/preact/mutations/mutations.stories.tsx +6 -0
  19. package/src/preact/mutations/mutations.tsx +4 -1
  20. package/src/preact/mutationsOverTime/mutations-over-time.stories.tsx +6 -0
  21. package/src/preact/mutationsOverTime/mutations-over-time.tsx +4 -1
  22. package/src/preact/numberSequencesOverTime/number-sequences-over-time.stories.tsx +3 -1
  23. package/src/preact/numberSequencesOverTime/number-sequences-over-time.tsx +4 -0
  24. package/src/preact/prevalenceOverTime/prevalence-over-time.stories.tsx +6 -0
  25. package/src/preact/prevalenceOverTime/prevalence-over-time.tsx +4 -1
  26. package/src/preact/relativeGrowthAdvantage/relative-growth-advantage.stories.tsx +6 -0
  27. package/src/preact/relativeGrowthAdvantage/relative-growth-advantage.tsx +4 -1
  28. package/src/preact/sequencesByLocation/sequences-by-location.stories.tsx +6 -0
  29. package/src/preact/sequencesByLocation/sequences-by-location.tsx +4 -0
  30. package/src/preact/shared/stories/expectFinishedLoadingEvent.ts +12 -0
  31. package/src/preact/statistic/statistics.stories.tsx +6 -0
  32. package/src/preact/statistic/statistics.tsx +4 -1
  33. package/src/preact/wastewater/mutationsOverTime/wastewater-mutations-over-time.stories.tsx +2 -0
  34. package/src/preact/wastewater/mutationsOverTime/wastewater-mutations-over-time.tsx +4 -1
  35. package/src/utils/gsEventNames.ts +1 -0
  36. package/src/utils/useDispatchFinishedLoadingEvent.ts +19 -0
  37. package/src/web-components/visualization/gs-aggregate.tsx +3 -0
  38. package/src/web-components/visualization/gs-genome-data-viewer.tsx +2 -0
  39. package/src/web-components/visualization/gs-mutation-comparison.tsx +3 -0
  40. package/src/web-components/visualization/gs-mutations-over-time.tsx +3 -0
  41. package/src/web-components/visualization/gs-mutations.tsx +2 -0
  42. package/src/web-components/visualization/gs-number-sequences-over-time.tsx +3 -0
  43. package/src/web-components/visualization/gs-prevalence-over-time.tsx +3 -0
  44. package/src/web-components/visualization/gs-relative-growth-advantage.tsx +3 -0
  45. package/src/web-components/visualization/gs-sequences-by-location.tsx +3 -0
  46. package/src/web-components/visualization/gs-statistics.tsx +3 -0
  47. package/src/web-components/wastewaterVisualization/gs-wastewater-mutations-over-time.tsx +3 -0
  48. package/standalone-bundle/assets/mutationOverTimeWorker-DAf2_NiP.js.map +1 -0
  49. package/standalone-bundle/dashboard-components.js +6933 -6904
  50. package/standalone-bundle/dashboard-components.js.map +1 -1
  51. package/dist/assets/mutationOverTimeWorker-ChQTFL68.js.map +0 -1
  52. 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";
@@ -943,7 +944,7 @@ declare global {
943
944
 
944
945
  declare global {
945
946
  interface HTMLElementTagNameMap {
946
- 'gs-mutation-comparison-component': MutationComparisonComponent;
947
+ 'gs-mutations-component': MutationsComponent;
947
948
  }
948
949
  }
949
950
 
@@ -951,7 +952,7 @@ declare global {
951
952
  declare global {
952
953
  namespace JSX {
953
954
  interface IntrinsicElements {
954
- 'gs-mutation-comparison-component': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
955
+ 'gs-mutations-component': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
955
956
  }
956
957
  }
957
958
  }
@@ -959,7 +960,7 @@ declare global {
959
960
 
960
961
  declare global {
961
962
  interface HTMLElementTagNameMap {
962
- 'gs-mutations-component': MutationsComponent;
963
+ 'gs-mutation-comparison-component': MutationComparisonComponent;
963
964
  }
964
965
  }
965
966
 
@@ -967,7 +968,7 @@ declare global {
967
968
  declare global {
968
969
  namespace JSX {
969
970
  interface IntrinsicElements {
970
- 'gs-mutations-component': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
971
+ 'gs-mutation-comparison-component': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
971
972
  }
972
973
  }
973
974
  }
@@ -1007,7 +1008,7 @@ declare global {
1007
1008
 
1008
1009
  declare global {
1009
1010
  interface HTMLElementTagNameMap {
1010
- 'gs-aggregate': AggregateComponent;
1011
+ 'gs-number-sequences-over-time': NumberSequencesOverTimeComponent;
1011
1012
  }
1012
1013
  }
1013
1014
 
@@ -1015,7 +1016,7 @@ declare global {
1015
1016
  declare global {
1016
1017
  namespace JSX {
1017
1018
  interface IntrinsicElements {
1018
- 'gs-aggregate': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1019
+ 'gs-number-sequences-over-time': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1019
1020
  }
1020
1021
  }
1021
1022
  }
@@ -1023,7 +1024,7 @@ declare global {
1023
1024
 
1024
1025
  declare global {
1025
1026
  interface HTMLElementTagNameMap {
1026
- 'gs-number-sequences-over-time': NumberSequencesOverTimeComponent;
1027
+ 'gs-aggregate': AggregateComponent;
1027
1028
  }
1028
1029
  }
1029
1030
 
@@ -1031,7 +1032,7 @@ declare global {
1031
1032
  declare global {
1032
1033
  namespace JSX {
1033
1034
  interface IntrinsicElements {
1034
- 'gs-number-sequences-over-time': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1035
+ 'gs-aggregate': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1035
1036
  }
1036
1037
  }
1037
1038
  }
@@ -1123,10 +1124,10 @@ declare global {
1123
1124
 
1124
1125
  declare global {
1125
1126
  interface HTMLElementTagNameMap {
1126
- 'gs-text-filter': TextFilterComponent;
1127
+ 'gs-location-filter': LocationFilterComponent;
1127
1128
  }
1128
1129
  interface HTMLElementEventMap {
1129
- [gsEventNames.textFilterChanged]: TextFilterChangedEvent;
1130
+ [gsEventNames.locationChanged]: LocationChangedEvent;
1130
1131
  }
1131
1132
  }
1132
1133
 
@@ -1134,7 +1135,7 @@ declare global {
1134
1135
  declare global {
1135
1136
  namespace JSX {
1136
1137
  interface IntrinsicElements {
1137
- 'gs-text-filter': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1138
+ 'gs-location-filter': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1138
1139
  }
1139
1140
  }
1140
1141
  }
@@ -1142,10 +1143,10 @@ declare global {
1142
1143
 
1143
1144
  declare global {
1144
1145
  interface HTMLElementTagNameMap {
1145
- 'gs-location-filter': LocationFilterComponent;
1146
+ 'gs-text-filter': TextFilterComponent;
1146
1147
  }
1147
1148
  interface HTMLElementEventMap {
1148
- [gsEventNames.locationChanged]: LocationChangedEvent;
1149
+ [gsEventNames.textFilterChanged]: TextFilterChangedEvent;
1149
1150
  }
1150
1151
  }
1151
1152
 
@@ -1153,7 +1154,7 @@ declare global {
1153
1154
  declare global {
1154
1155
  namespace JSX {
1155
1156
  interface IntrinsicElements {
1156
- 'gs-location-filter': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1157
+ 'gs-text-filter': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
1157
1158
  }
1158
1159
  }
1159
1160
  }
package/dist/util.js CHANGED
@@ -1,4 +1,4 @@
1
- import { D, a, L, T, d, g, v } from "./LineageFilterChangedEvent-GgkxoF3X.js";
1
+ import { D, a, L, T, d, g, v } from "./LineageFilterChangedEvent-Cqa8Frcf.js";
2
2
  export {
3
3
  D as DateRangeOptionChangedEvent,
4
4
  a as LineageFilterChangedEvent,
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.6",
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
  };
@@ -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
+ };
@@ -3,6 +3,7 @@ import z from 'zod';
3
3
 
4
4
  import { queryGeneralStatistics } from '../../query/queryGeneralStatistics';
5
5
  import { lapisFilterSchema } from '../../types';
6
+ import { useDispatchFinishedLoadingEvent } from '../../utils/useDispatchFinishedLoadingEvent';
6
7
  import { useLapisUrl } from '../LapisUrlContext';
7
8
  import { ErrorBoundary } from '../components/error-boundary';
8
9
  import { LoadingDisplay } from '../components/loading-display';
@@ -60,9 +61,11 @@ type MetricDataTabsProps = {
60
61
  };
61
62
 
62
63
  const MetricDataTabs: FunctionComponent<MetricDataTabsProps> = ({ data }) => {
64
+ const ref = useDispatchFinishedLoadingEvent();
65
+
63
66
  const { count, proportion } = data;
64
67
  return (
65
- <div className='flex flex-col sm:flex-row rounded-md border-2 border-gray-100 min-w-[180px]'>
68
+ <div ref={ref} className='flex flex-col sm:flex-row rounded-md border-2 border-gray-100 min-w-[180px]'>
66
69
  <div className='stat'>
67
70
  <div className='stat-title'>Sequences</div>
68
71
  <div className='stat-value text-2xl sm:text-4xl'>{count.toLocaleString('en-us')}</div>