@genspectrum/dashboard-components 0.5.5 → 0.5.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.
@@ -876,14 +876,14 @@ declare global {
876
876
 
877
877
  declare global {
878
878
  interface HTMLElementTagNameMap {
879
- 'gs-relative-growth-advantage': RelativeGrowthAdvantageComponent;
879
+ 'gs-prevalence-over-time': PrevalenceOverTimeComponent;
880
880
  }
881
881
  }
882
882
 
883
883
 
884
884
  declare global {
885
885
  interface HTMLElementTagNameMap {
886
- 'gs-prevalence-over-time': PrevalenceOverTimeComponent;
886
+ 'gs-relative-growth-advantage': RelativeGrowthAdvantageComponent;
887
887
  }
888
888
  }
889
889
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@genspectrum/dashboard-components",
3
- "version": "0.5.5",
3
+ "version": "0.5.7",
4
4
  "description": "GenSpectrum web components for building dashboards",
5
5
  "type": "module",
6
6
  "license": "AGPL-3.0-only",
@@ -7,18 +7,24 @@ export type ScalingSelectorProps = {
7
7
  yAxisScaleType: ScaleType;
8
8
  setYAxisScaleType: (scaleType: ScaleType) => void;
9
9
  className?: string;
10
+ enabledTypes?: ScaleType[];
10
11
  };
11
12
 
13
+ const scaleTypeItem: { label: string; value: ScaleType }[] = [
14
+ { label: 'Linear', value: 'linear' },
15
+ { label: 'Logarithmic', value: 'logarithmic' },
16
+ { label: 'Logit', value: 'logit' },
17
+ ];
18
+
12
19
  export const ScalingSelector: FunctionComponent<ScalingSelectorProps> = ({
13
20
  yAxisScaleType,
14
21
  setYAxisScaleType,
15
22
  className,
23
+ enabledTypes,
16
24
  }) => {
17
25
  const items = [
18
26
  { label: 'y axis scaling type', value: 'none', disabled: true },
19
- { label: 'Linear', value: 'linear' },
20
- { label: 'Logarithmic', value: 'logarithmic' },
21
- { label: 'Logit', value: 'logit' },
27
+ ...scaleTypeItem.filter((item) => enabledTypes === undefined || enabledTypes.includes(item.value)),
22
28
  ];
23
29
 
24
30
  return (
@@ -198,7 +198,7 @@ const Toolbar: FunctionComponent<ToolbarProps> = ({
198
198
  <CsvDownloadButton
199
199
  className='mx-1 btn btn-xs'
200
200
  getData={() => getMutationsTableData(filteredData.tableData, proportionInterval)}
201
- filename='substitutionsAndDeletions.csv'
201
+ filename='substitutions_and_deletions.csv'
202
202
  />
203
203
  </>
204
204
  )}
@@ -5,14 +5,16 @@ import { getNumberOfSequencesOverTimeTableData } from './getNumberOfSequencesOve
5
5
  import { type NumberOfSequencesDatasets } from '../../query/queryNumberOfSequencesOverTime';
6
6
  import GsChart from '../components/chart';
7
7
  import { singleGraphColorRGBAById } from '../shared/charts/colors';
8
+ import { getYAxisScale, type ScaleType } from '../shared/charts/getYAxisScale';
8
9
 
9
10
  interface NumberSequencesOverBarChartProps {
10
11
  data: NumberOfSequencesDatasets;
12
+ yAxisScaleType: ScaleType;
11
13
  }
12
14
 
13
15
  Chart.register(...registerables);
14
16
 
15
- export const NumberSequencesOverTimeBarChart = ({ data }: NumberSequencesOverBarChartProps) => {
17
+ export const NumberSequencesOverTimeBarChart = ({ data, yAxisScaleType }: NumberSequencesOverBarChartProps) => {
16
18
  const config: ChartConfiguration = useMemo(
17
19
  () => ({
18
20
  type: 'bar',
@@ -22,6 +24,11 @@ export const NumberSequencesOverTimeBarChart = ({ data }: NumberSequencesOverBar
22
24
  options: {
23
25
  maintainAspectRatio: false,
24
26
  animation: false,
27
+ scales: {
28
+ y: {
29
+ type: getYAxisScale(yAxisScaleType).type,
30
+ },
31
+ },
25
32
  plugins: {
26
33
  legend: {
27
34
  display: false,
@@ -33,7 +40,7 @@ export const NumberSequencesOverTimeBarChart = ({ data }: NumberSequencesOverBar
33
40
  },
34
41
  },
35
42
  }),
36
- [data],
43
+ [data, yAxisScaleType],
37
44
  );
38
45
 
39
46
  return <GsChart configuration={config} />;
@@ -0,0 +1,66 @@
1
+ import { Chart, type ChartConfiguration, type ChartDataset, registerables } from 'chart.js';
2
+ import { useMemo } from 'preact/hooks';
3
+
4
+ import { getNumberOfSequencesOverTimeTableData } from './getNumberOfSequencesOverTimeTableData';
5
+ import { type NumberOfSequencesDatasets } from '../../query/queryNumberOfSequencesOverTime';
6
+ import GsChart from '../components/chart';
7
+ import { singleGraphColorRGBAById } from '../shared/charts/colors';
8
+ import { getYAxisScale, type ScaleType } from '../shared/charts/getYAxisScale';
9
+
10
+ interface NumberSequencesOverBarChartProps {
11
+ data: NumberOfSequencesDatasets;
12
+ yAxisScaleType: ScaleType;
13
+ }
14
+
15
+ Chart.register(...registerables);
16
+
17
+ export const NumberSequencesOverTimeLineChart = ({ data, yAxisScaleType }: NumberSequencesOverBarChartProps) => {
18
+ const config: ChartConfiguration = useMemo(
19
+ () => ({
20
+ type: 'line',
21
+ data: {
22
+ datasets: getDatasets(data),
23
+ },
24
+ options: {
25
+ maintainAspectRatio: false,
26
+ animation: false,
27
+ scales: {
28
+ y: {
29
+ type: getYAxisScale(yAxisScaleType).type,
30
+ },
31
+ },
32
+ plugins: {
33
+ legend: {
34
+ display: false,
35
+ },
36
+ tooltip: {
37
+ mode: 'index',
38
+ intersect: false,
39
+ },
40
+ },
41
+ },
42
+ }),
43
+ [data, yAxisScaleType],
44
+ );
45
+
46
+ return <GsChart configuration={config} />;
47
+ };
48
+
49
+ const getDatasets = (data: NumberOfSequencesDatasets) => {
50
+ const tableData = getNumberOfSequencesOverTimeTableData(data, 'date');
51
+
52
+ return data.map(
53
+ ({ displayName }, index) =>
54
+ ({
55
+ borderWidth: 1,
56
+ pointRadius: 0,
57
+ label: displayName,
58
+ backgroundColor: singleGraphColorRGBAById(index, 0.3),
59
+ borderColor: singleGraphColorRGBAById(index),
60
+ data: tableData.map((row) => ({
61
+ x: row.date,
62
+ y: row[displayName],
63
+ })),
64
+ }) as ChartDataset<'line', { x: string; y: number }[]>,
65
+ );
66
+ };
@@ -1,6 +1,8 @@
1
- import { useContext } from 'preact/hooks';
1
+ import { useContext, useState } from 'preact/hooks';
2
2
 
3
+ import { getNumberOfSequencesOverTimeTableData } from './getNumberOfSequencesOverTimeTableData';
3
4
  import { NumberSequencesOverTimeBarChart } from './number-sequences-over-time-bar-chart';
5
+ import { NumberSequencesOverTimeLineChart } from './number-sequences-over-time-line-chart';
4
6
  import { NumberSequencesOverTimeTable } from './number-sequences-over-time-table';
5
7
  import {
6
8
  type NumberOfSequencesDatasets,
@@ -8,13 +10,17 @@ import {
8
10
  } from '../../query/queryNumberOfSequencesOverTime';
9
11
  import type { NamedLapisFilter, TemporalGranularity } from '../../types';
10
12
  import { LapisUrlContext } from '../LapisUrlContext';
13
+ import { CsvDownloadButton } from '../components/csv-download-button';
11
14
  import { ErrorBoundary } from '../components/error-boundary';
12
15
  import { ErrorDisplay } from '../components/error-display';
13
16
  import Headline from '../components/headline';
17
+ import Info, { InfoHeadline1, InfoParagraph } from '../components/info';
14
18
  import { LoadingDisplay } from '../components/loading-display';
15
19
  import { NoDataDisplay } from '../components/no-data-display';
16
20
  import { ResizeContainer } from '../components/resize-container';
21
+ import { ScalingSelector } from '../components/scaling-selector';
17
22
  import Tabs from '../components/tabs';
23
+ import type { ScaleType } from '../shared/charts/getYAxisScale';
18
24
  import { useQuery } from '../useQuery';
19
25
 
20
26
  type NumberSequencesOverTimeView = 'bar' | 'line' | 'table';
@@ -85,12 +91,20 @@ interface NumberSequencesOverTimeTabsProps {
85
91
  }
86
92
 
87
93
  const NumberSequencesOverTimeTabs = ({ views, data, granularity, pageSize }: NumberSequencesOverTimeTabsProps) => {
94
+ const [yAxisScaleType, setYAxisScaleType] = useState<ScaleType>('linear');
95
+
88
96
  const getTab = (view: NumberSequencesOverTimeView) => {
89
97
  switch (view) {
90
98
  case 'bar':
91
- return { title: 'Bar', content: <NumberSequencesOverTimeBarChart data={data} /> };
99
+ return {
100
+ title: 'Bar',
101
+ content: <NumberSequencesOverTimeBarChart data={data} yAxisScaleType={yAxisScaleType} />,
102
+ };
92
103
  case 'line':
93
- return { title: 'Line', content: <div>not implemented, TODO #317</div> };
104
+ return {
105
+ title: 'Line',
106
+ content: <NumberSequencesOverTimeLineChart data={data} yAxisScaleType={yAxisScaleType} />,
107
+ };
94
108
  case 'table':
95
109
  return {
96
110
  title: 'Table',
@@ -101,5 +115,55 @@ const NumberSequencesOverTimeTabs = ({ views, data, granularity, pageSize }: Num
101
115
  }
102
116
  };
103
117
 
104
- return <Tabs tabs={views.map((view) => getTab(view))} />;
118
+ return (
119
+ <Tabs
120
+ tabs={views.map((view) => getTab(view))}
121
+ toolbar={(activeTab) => (
122
+ <Toolbar
123
+ activeTab={activeTab}
124
+ data={data}
125
+ granularity={granularity}
126
+ yAxisScaleType={yAxisScaleType}
127
+ setYAxisScaleType={setYAxisScaleType}
128
+ />
129
+ )}
130
+ />
131
+ );
132
+ };
133
+
134
+ interface ToolbarProps {
135
+ activeTab: string;
136
+ data: NumberOfSequencesDatasets;
137
+ granularity: TemporalGranularity;
138
+ yAxisScaleType: ScaleType;
139
+ setYAxisScaleType: (scaleType: ScaleType) => void;
140
+ }
141
+
142
+ const Toolbar = ({ activeTab, data, granularity, yAxisScaleType, setYAxisScaleType }: ToolbarProps) => {
143
+ return (
144
+ <>
145
+ {activeTab !== 'Table' && (
146
+ <ScalingSelector
147
+ yAxisScaleType={yAxisScaleType}
148
+ setYAxisScaleType={setYAxisScaleType}
149
+ enabledTypes={['linear', 'logarithmic']}
150
+ />
151
+ )}
152
+ <CsvDownloadButton
153
+ className='mx-1 btn btn-xs'
154
+ getData={() => getNumberOfSequencesOverTimeTableData(data, granularity)}
155
+ filename='number_of_sequences_over_time.csv'
156
+ />
157
+ <NumberSequencesOverTimeInfo />
158
+ </>
159
+ );
105
160
  };
161
+
162
+ const NumberSequencesOverTimeInfo = () => (
163
+ <Info height='100px'>
164
+ <InfoHeadline1>Number of sequences over time</InfoHeadline1>
165
+ <InfoParagraph>
166
+ <a href='https://github.com/GenSpectrum/dashboard-components/issues/315'>TODO</a>
167
+ </InfoParagraph>
168
+ </Info>
169
+ );
@@ -235,7 +235,7 @@ const Toolbar: FunctionComponent<ToolbarProps> = ({
235
235
  <CsvDownloadButton
236
236
  className='mx-1 btn btn-xs'
237
237
  getData={() => getPrevalenceOverTimeTableData(data, granularity)}
238
- filename='prevalence-over-time.csv'
238
+ filename='prevalence_over_time.csv'
239
239
  />
240
240
 
241
241
  <PrevalenceOverTimeInfo />
@@ -116,6 +116,17 @@ export const OneDatasetBarChart: StoryObj<NumberSequencesOverTimeProps> = {
116
116
  },
117
117
  };
118
118
 
119
+ export const OneDatasetLineChart: StoryObj<NumberSequencesOverTimeProps> = {
120
+ ...Template,
121
+ play: async ({ canvasElement }) => {
122
+ const canvas = await withinShadowRoot(canvasElement, 'gs-number-sequences-over-time');
123
+
124
+ await waitFor(() => expect(canvas.getByRole('button', { name: 'Line' })).toBeVisible());
125
+
126
+ await fireEvent.click(canvas.getByRole('button', { name: 'Line' }));
127
+ },
128
+ };
129
+
119
130
  export const OneDatasetTable: StoryObj<NumberSequencesOverTimeProps> = {
120
131
  ...Template,
121
132
  play: async ({ canvasElement }) => {
@@ -1,16 +0,0 @@
1
- export type ScaleType = 'linear' | 'logarithmic' | 'logit';
2
-
3
- export function getYAxisScale(scaleType: ScaleType) {
4
- switch (scaleType) {
5
- case 'linear': {
6
- return { beginAtZero: true, type: 'linear' as const };
7
- }
8
- case 'logarithmic': {
9
- return { type: 'logarithmic' as const };
10
- }
11
- case 'logit':
12
- return { type: 'logit' as const };
13
- default:
14
- return { beginAtZero: true, type: 'linear' as const };
15
- }
16
- }