@genspectrum/dashboard-components 0.7.0 → 0.7.2

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.
@@ -4,6 +4,7 @@ import { type TooltipItem } from 'chart.js/dist/types';
4
4
  import { maxInData } from './prevalence-over-time';
5
5
  import { type PrevalenceOverTimeData, type PrevalenceOverTimeVariantData } from '../../query/queryPrevalenceOverTime';
6
6
  import GsChart from '../components/chart';
7
+ import { NoDataDisplay } from '../components/no-data-display';
7
8
  import { LogitScale } from '../shared/charts/LogitScale';
8
9
  import { singleGraphColorRGBAById } from '../shared/charts/colors';
9
10
  import {
@@ -29,12 +30,18 @@ const PrevalenceOverTimeLineChart = ({
29
30
  confidenceIntervalMethod,
30
31
  yAxisMaxConfig,
31
32
  }: PrevalenceOverTimeLineChartProps) => {
32
- const nonNullDateRangeData = data.map((variantData) => {
33
- return {
34
- content: variantData.content.filter((dataPoint) => dataPoint.dateRange !== null),
35
- displayName: variantData.displayName,
36
- };
37
- });
33
+ const nonNullDateRangeData = data
34
+ .filter((prevalenceOverTimeData) => prevalenceOverTimeData.content.length > 0)
35
+ .map((variantData) => {
36
+ return {
37
+ content: variantData.content.filter((dataPoint) => dataPoint.dateRange !== null),
38
+ displayName: variantData.displayName,
39
+ };
40
+ });
41
+
42
+ if (nonNullDateRangeData.length === 0) {
43
+ return <NoDataDisplay />;
44
+ }
38
45
 
39
46
  const datasets = nonNullDateRangeData
40
47
  .map((graphData, index) => getDataset(graphData, index, confidenceIntervalMethod))
@@ -1,11 +1,15 @@
1
+ import type { StoryObj } from '@storybook/preact';
2
+ import { expect, waitFor } from '@storybook/test';
3
+
4
+ import { LapisUrlContext } from '../LapisUrlContext';
1
5
  import denominatorFilter from './__mockData__/denominatorFilter.json';
2
6
  import denominatorOneDataset from './__mockData__/denominatorFilterOneDataset.json';
3
7
  import numeratorFilterEG from './__mockData__/numeratorFilterEG.json';
4
8
  import numeratorFilterJN1 from './__mockData__/numeratorFilterJN1.json';
9
+ import numeratorFilterNoData from './__mockData__/numeratorFilterNoData.json';
5
10
  import numeratorOneDataset from './__mockData__/numeratorFilterOneDataset.json';
6
11
  import { PrevalenceOverTime, type PrevalenceOverTimeProps } from './prevalence-over-time';
7
12
  import { AGGREGATED_ENDPOINT, LAPIS_URL } from '../../constants';
8
- import { LapisUrlContext } from '../LapisUrlContext';
9
13
 
10
14
  export default {
11
15
  title: 'Visualization/PrevalenceOverTime',
@@ -58,7 +62,7 @@ const Template = {
58
62
  ),
59
63
  };
60
64
 
61
- export const TwoVariants = {
65
+ export const TwoVariants: StoryObj<PrevalenceOverTimeProps> = {
62
66
  ...Template,
63
67
  args: {
64
68
  numeratorFilter: [
@@ -74,10 +78,8 @@ export const TwoVariants = {
74
78
  height: '700px',
75
79
  lapisDateField: 'date',
76
80
  pageSize: 10,
77
- yAxisMaxConfig: {
78
- linear: 1,
79
- logarithmic: 1,
80
- },
81
+ yAxisMaxLinear: 1,
82
+ yAxisMaxLogarithmic: 1,
81
83
  },
82
84
  parameters: {
83
85
  fetchMock: {
@@ -134,7 +136,7 @@ export const TwoVariants = {
134
136
  },
135
137
  };
136
138
 
137
- export const OneVariant = {
139
+ export const OneVariant: StoryObj<PrevalenceOverTimeProps> = {
138
140
  ...Template,
139
141
  args: {
140
142
  numeratorFilter: {
@@ -150,10 +152,8 @@ export const OneVariant = {
150
152
  height: '700px',
151
153
  lapisDateField: 'date',
152
154
  pageSize: 10,
153
- yAxisMaxConfig: {
154
- linear: 1,
155
- logarithmic: 1,
156
- },
155
+ yAxisMaxLinear: 1,
156
+ yAxisMaxLogarithmic: 1,
157
157
  },
158
158
  parameters: {
159
159
  fetchMock: {
@@ -193,3 +193,66 @@ export const OneVariant = {
193
193
  },
194
194
  },
195
195
  };
196
+
197
+ export const ShowsNoDataBanner: StoryObj<PrevalenceOverTimeProps> = {
198
+ ...Template,
199
+ args: {
200
+ numeratorFilter: {
201
+ displayName: 'EG',
202
+ lapisFilter: { country: 'USA', pangoLineage: 'BA.2.86*', dateFrom: '2023-10-01' },
203
+ },
204
+ denominatorFilter: { country: 'USA', dateFrom: '2023-10-01' },
205
+ granularity: 'day',
206
+ smoothingWindow: 7,
207
+ views: ['bar', 'line', 'bubble', 'table'],
208
+ confidenceIntervalMethods: ['wilson'],
209
+ width: '100%',
210
+ height: '700px',
211
+ lapisDateField: 'date',
212
+ pageSize: 10,
213
+ yAxisMaxLinear: 1,
214
+ yAxisMaxLogarithmic: 1,
215
+ },
216
+ parameters: {
217
+ fetchMock: {
218
+ mocks: [
219
+ {
220
+ matcher: {
221
+ name: 'numeratorOneVariant',
222
+ url: AGGREGATED_ENDPOINT,
223
+ body: {
224
+ country: 'USA',
225
+ pangoLineage: 'BA.2.86*',
226
+ dateFrom: '2023-10-01',
227
+ fields: ['date'],
228
+ },
229
+ },
230
+ response: {
231
+ status: 200,
232
+ body: numeratorFilterNoData,
233
+ },
234
+ },
235
+ {
236
+ matcher: {
237
+ name: 'denominatorOneVariant',
238
+ url: AGGREGATED_ENDPOINT,
239
+ body: {
240
+ country: 'USA',
241
+ dateFrom: '2023-10-01',
242
+ fields: ['date'],
243
+ },
244
+ },
245
+ response: {
246
+ status: 200,
247
+ body: numeratorFilterNoData,
248
+ },
249
+ },
250
+ ],
251
+ },
252
+ },
253
+ play: async ({ canvas }) => {
254
+ await waitFor(() => expect(canvas.getByText('No data available.', { exact: false })).toBeVisible(), {
255
+ timeout: 10000,
256
+ });
257
+ },
258
+ };
@@ -80,7 +80,7 @@ export const PrevalenceOverTimeInner: FunctionComponent<PrevalenceOverTimeProps>
80
80
  return <ErrorDisplay error={error} />;
81
81
  }
82
82
 
83
- if (data === null) {
83
+ if (data === null || data.every((variant) => variant.content.length === 0)) {
84
84
  return <NoDataDisplay />;
85
85
  }
86
86
 
@@ -1,3 +1,5 @@
1
+ import { type StoryObj } from '@storybook/preact';
2
+
1
3
  import denominator from './__mockData__/denominatorFilter.json';
2
4
  import numerator from './__mockData__/numeratorFilter.json';
3
5
  import { RelativeGrowthAdvantage, type RelativeGrowthAdvantageProps } from './relative-growth-advantage';
@@ -11,8 +13,8 @@ export default {
11
13
  fetchMock: {},
12
14
  },
13
15
  argTypes: {
14
- numerator: { control: 'object' },
15
- denominator: { control: 'object' },
16
+ numeratorFilter: { control: 'object' },
17
+ denominatorFilter: { control: 'object' },
16
18
  generationTime: { control: 'number' },
17
19
  views: {
18
20
  options: ['line'],
@@ -24,7 +26,7 @@ export default {
24
26
  },
25
27
  };
26
28
 
27
- export const Primary = {
29
+ export const Primary: StoryObj<RelativeGrowthAdvantageProps> = {
28
30
  render: (args: RelativeGrowthAdvantageProps) => (
29
31
  <LapisUrlContext.Provider value={LAPIS_URL}>
30
32
  <RelativeGrowthAdvantage
@@ -40,8 +42,13 @@ export const Primary = {
40
42
  </LapisUrlContext.Provider>
41
43
  ),
42
44
  args: {
43
- numerator: { country: 'Switzerland', pangoLineage: 'B.1.1.7', dateFrom: '2020-12-01', dateTo: '2021-03-01' },
44
- denominator: { country: 'Switzerland', dateFrom: '2020-12-01', dateTo: '2021-03-01' },
45
+ numeratorFilter: {
46
+ country: 'Switzerland',
47
+ pangoLineage: 'B.1.1.7',
48
+ dateFrom: '2020-12-01',
49
+ dateTo: '2021-03-01',
50
+ },
51
+ denominatorFilter: { country: 'Switzerland', dateFrom: '2020-12-01', dateTo: '2021-03-01' },
45
52
  generationTime: 7,
46
53
  views: ['line'],
47
54
  width: '100%',
@@ -11,7 +11,7 @@ import { LapisUrlContext } from '../LapisUrlContext';
11
11
  import { ErrorBoundary } from '../components/error-boundary';
12
12
  import { ErrorDisplay } from '../components/error-display';
13
13
  import { Fullscreen } from '../components/fullscreen';
14
- import Info, { InfoHeadline1, InfoHeadline2, InfoLink, InfoParagraph } from '../components/info';
14
+ import Info, { InfoComponentCode, InfoHeadline1, InfoHeadline2, InfoLink, InfoParagraph } from '../components/info';
15
15
  import { LoadingDisplay } from '../components/loading-display';
16
16
  import { NoDataDisplay } from '../components/no-data-display';
17
17
  import { ResizeContainer } from '../components/resize-container';
@@ -23,12 +23,9 @@ import { useQuery } from '../useQuery';
23
23
 
24
24
  export type View = 'line';
25
25
 
26
- export interface RelativeGrowthAdvantageProps extends RelativeGrowthAdvantagePropsInner {
26
+ export interface RelativeGrowthAdvantageProps {
27
27
  width: string;
28
28
  height: string;
29
- }
30
-
31
- export interface RelativeGrowthAdvantagePropsInner {
32
29
  numeratorFilter: LapisFilter;
33
30
  denominatorFilter: LapisFilter;
34
31
  generationTime: number;
@@ -37,36 +34,28 @@ export interface RelativeGrowthAdvantagePropsInner {
37
34
  yAxisMaxConfig: YAxisMaxConfig;
38
35
  }
39
36
 
40
- export const RelativeGrowthAdvantage: FunctionComponent<RelativeGrowthAdvantageProps> = ({
41
- width,
42
- height,
43
- ...innerProps
44
- }) => {
37
+ export const RelativeGrowthAdvantage: FunctionComponent<RelativeGrowthAdvantageProps> = (componentProps) => {
38
+ const { width, height } = componentProps;
45
39
  const size = { height, width };
46
40
 
47
41
  return (
48
42
  <ErrorBoundary size={size}>
49
43
  <ResizeContainer size={size}>
50
- <RelativeGrowthAdvantageInner {...innerProps} />
44
+ <RelativeGrowthAdvantageInner {...componentProps} />
51
45
  </ResizeContainer>
52
46
  </ErrorBoundary>
53
47
  );
54
48
  };
55
49
 
56
- export const RelativeGrowthAdvantageInner: FunctionComponent<RelativeGrowthAdvantagePropsInner> = ({
57
- numeratorFilter,
58
- denominatorFilter,
59
- generationTime,
60
- views,
61
- lapisDateField,
62
- yAxisMaxConfig,
63
- }) => {
50
+ export const RelativeGrowthAdvantageInner: FunctionComponent<RelativeGrowthAdvantageProps> = (componentProps) => {
64
51
  const lapis = useContext(LapisUrlContext);
52
+ const { numeratorFilter, denominatorFilter, generationTime, lapisDateField } = componentProps;
53
+
65
54
  const [yAxisScaleType, setYAxisScaleType] = useState<ScaleType>('linear');
66
55
 
67
56
  const { data, error, isLoading } = useQuery(
68
57
  () => queryRelativeGrowthAdvantage(numeratorFilter, denominatorFilter, generationTime, lapis, lapisDateField),
69
- [lapis, numeratorFilter, denominatorFilter, generationTime, views, lapisDateField],
58
+ [lapis, numeratorFilter, denominatorFilter, generationTime, lapisDateField],
70
59
  );
71
60
 
72
61
  if (isLoading) {
@@ -86,9 +75,7 @@ export const RelativeGrowthAdvantageInner: FunctionComponent<RelativeGrowthAdvan
86
75
  data={data}
87
76
  yAxisScaleType={yAxisScaleType}
88
77
  setYAxisScaleType={setYAxisScaleType}
89
- views={views}
90
- generationTime={generationTime}
91
- yAxisMaxConfig={yAxisMaxConfig}
78
+ originalComponentProps={componentProps}
92
79
  />
93
80
  );
94
81
  };
@@ -97,18 +84,14 @@ type RelativeGrowthAdvantageTabsProps = {
97
84
  data: NonNullable<RelativeGrowthAdvantageData>;
98
85
  yAxisScaleType: ScaleType;
99
86
  setYAxisScaleType: (scaleType: ScaleType) => void;
100
- views: View[];
101
- generationTime: number;
102
- yAxisMaxConfig: YAxisMaxConfig;
87
+ originalComponentProps: RelativeGrowthAdvantageProps;
103
88
  };
104
89
 
105
90
  const RelativeGrowthAdvantageTabs: FunctionComponent<RelativeGrowthAdvantageTabsProps> = ({
106
91
  data,
107
92
  yAxisScaleType,
108
93
  setYAxisScaleType,
109
- views,
110
- generationTime,
111
- yAxisMaxConfig,
94
+ originalComponentProps,
112
95
  }) => {
113
96
  const getTab = (view: View) => {
114
97
  switch (view) {
@@ -123,17 +106,17 @@ const RelativeGrowthAdvantageTabs: FunctionComponent<RelativeGrowthAdvantageTabs
123
106
  params: data.params,
124
107
  }}
125
108
  yAxisScaleType={yAxisScaleType}
126
- yAxisMaxConfig={yAxisMaxConfig}
109
+ yAxisMaxConfig={originalComponentProps.yAxisMaxConfig}
127
110
  />
128
111
  ),
129
112
  };
130
113
  }
131
114
  };
132
115
 
133
- const tabs = views.map((view) => getTab(view));
116
+ const tabs = originalComponentProps.views.map((view) => getTab(view));
134
117
  const toolbar = () => (
135
118
  <RelativeGrowthAdvantageToolbar
136
- generationTime={generationTime}
119
+ originalComponentProps={originalComponentProps}
137
120
  yAxisScaleType={yAxisScaleType}
138
121
  setYAxisScaleType={setYAxisScaleType}
139
122
  />
@@ -145,24 +128,29 @@ const RelativeGrowthAdvantageTabs: FunctionComponent<RelativeGrowthAdvantageTabs
145
128
  type RelativeGrowthAdvantageToolbarProps = {
146
129
  yAxisScaleType: ScaleType;
147
130
  setYAxisScaleType: (scaleType: ScaleType) => void;
148
- generationTime: number;
131
+ originalComponentProps: RelativeGrowthAdvantageProps;
149
132
  };
150
133
 
151
134
  const RelativeGrowthAdvantageToolbar: FunctionComponent<RelativeGrowthAdvantageToolbarProps> = ({
152
135
  yAxisScaleType,
153
136
  setYAxisScaleType,
154
- generationTime,
137
+ originalComponentProps,
155
138
  }) => {
156
139
  return (
157
140
  <>
158
141
  <ScalingSelector yAxisScaleType={yAxisScaleType} setYAxisScaleType={setYAxisScaleType} />
159
- <RelativeGrowthAdvantageInfo generationTime={generationTime} />
142
+ <RelativeGrowthAdvantageInfo originalComponentProps={originalComponentProps} />
160
143
  <Fullscreen />
161
144
  </>
162
145
  );
163
146
  };
164
147
 
165
- const RelativeGrowthAdvantageInfo: FunctionComponent<{ generationTime: number }> = ({ generationTime }) => {
148
+ const RelativeGrowthAdvantageInfo: FunctionComponent<{ originalComponentProps: RelativeGrowthAdvantageProps }> = ({
149
+ originalComponentProps,
150
+ }) => {
151
+ const lapis = useContext(LapisUrlContext);
152
+ const generationTime = originalComponentProps.generationTime;
153
+
166
154
  return (
167
155
  <Info>
168
156
  <InfoHeadline1>Relative growth advantage</InfoHeadline1>
@@ -193,6 +181,11 @@ const RelativeGrowthAdvantageInfo: FunctionComponent<{ generationTime: number }>
193
181
  10.1016/j.epidem.2021.100480
194
182
  </InfoLink>
195
183
  </InfoParagraph>
184
+ <InfoComponentCode
185
+ componentName='relative-growth-advantage'
186
+ params={originalComponentProps}
187
+ lapisUrl={lapis}
188
+ />
196
189
  </Info>
197
190
  );
198
191
  };