@genspectrum/dashboard-components 0.5.6 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/custom-elements.json +13 -127
  2. package/dist/dashboard-components.js +95 -109
  3. package/dist/dashboard-components.js.map +1 -1
  4. package/dist/genspectrum-components.d.ts +14 -28
  5. package/dist/style.css +0 -4
  6. package/package.json +1 -1
  7. package/src/preact/aggregatedData/aggregate.stories.tsx +0 -2
  8. package/src/preact/aggregatedData/aggregate.tsx +3 -12
  9. package/src/preact/components/error-boundary.stories.tsx +2 -6
  10. package/src/preact/components/error-boundary.tsx +2 -5
  11. package/src/preact/components/scaling-selector.tsx +9 -3
  12. package/src/preact/mutationComparison/mutation-comparison.stories.tsx +0 -3
  13. package/src/preact/mutationComparison/mutation-comparison.tsx +3 -12
  14. package/src/preact/mutations/mutations.stories.tsx +0 -3
  15. package/src/preact/mutations/mutations.tsx +4 -13
  16. package/src/preact/numberSequencesOverTime/number-sequences-over-time-bar-chart.tsx +9 -2
  17. package/src/preact/numberSequencesOverTime/number-sequences-over-time-line-chart.tsx +9 -2
  18. package/src/preact/numberSequencesOverTime/number-sequences-over-time.stories.tsx +0 -2
  19. package/src/preact/numberSequencesOverTime/number-sequences-over-time.tsx +70 -11
  20. package/src/preact/prevalenceOverTime/prevalence-over-time.stories.tsx +0 -4
  21. package/src/preact/prevalenceOverTime/prevalence-over-time.tsx +4 -13
  22. package/src/preact/relativeGrowthAdvantage/relative-growth-advantage.stories.tsx +0 -3
  23. package/src/preact/relativeGrowthAdvantage/relative-growth-advantage.tsx +2 -7
  24. package/src/web-components/visualization/gs-aggregate.stories.ts +0 -4
  25. package/src/web-components/visualization/gs-aggregate.tsx +0 -7
  26. package/src/web-components/visualization/gs-mutation-comparison.stories.ts +0 -4
  27. package/src/web-components/visualization/gs-mutation-comparison.tsx +0 -7
  28. package/src/web-components/visualization/gs-mutations.stories.ts +0 -4
  29. package/src/web-components/visualization/gs-mutations.tsx +0 -7
  30. package/src/web-components/visualization/gs-number-sequences-over-time.stories.ts +0 -3
  31. package/src/web-components/visualization/gs-number-sequences-over-time.tsx +14 -11
  32. package/src/web-components/visualization/gs-prevalence-over-time.stories.ts +0 -5
  33. package/src/web-components/visualization/gs-prevalence-over-time.tsx +0 -7
  34. package/src/web-components/visualization/gs-relative-growth-advantage.stories.ts +0 -4
  35. package/src/web-components/visualization/gs-relative-growth-advantage.tsx +0 -7
  36. package/src/preact/components/headline.stories.tsx +0 -47
  37. package/src/preact/components/headline.tsx +0 -36
  38. package/src/preact/shared/charts/scales.ts +0 -16
@@ -53,10 +53,6 @@ export declare class AggregateComponent extends PreactLitAdapterWithGridJsStyles
53
53
  * Visit https://genspectrum.github.io/dashboard-components/?path=/docs/components-size-of-components--docs for more information.
54
54
  */
55
55
  height: string;
56
- /**
57
- * The headline of the component. Set to an empty string to hide the headline.
58
- */
59
- headline: string;
60
56
  /**
61
57
  * The field by which the table is initially sorted.
62
58
  * Must be one of the fields specified in the fields property, 'count', or 'proportion'.
@@ -300,10 +296,6 @@ export declare class MutationComparisonComponent extends PreactLitAdapterWithGri
300
296
  * Visit https://genspectrum.github.io/dashboard-components/?path=/docs/components-size-of-components--docs for more information.
301
297
  */
302
298
  height: string;
303
- /**
304
- * The headline of the component. Set to an empty string to hide the headline.
305
- */
306
- headline: string;
307
299
  /**
308
300
  * The maximum number of rows to display in the table view.
309
301
  * Set to `false` to disable pagination. Set to `true` to enable pagination with a default limit (10).
@@ -441,10 +433,6 @@ export declare class MutationsComponent extends PreactLitAdapterWithGridJsStyles
441
433
  * Visit https://genspectrum.github.io/dashboard-components/?path=/docs/components-size-of-components--docs for more information.
442
434
  */
443
435
  height: string;
444
- /**
445
- * The headline of the component. Set to an empty string to hide the headline.
446
- */
447
- headline: string;
448
436
  /**
449
437
  * The maximum number of rows to display in the table view.
450
438
  * Set to `false` to disable pagination. Set to `true` to enable pagination with a default limit (10).
@@ -454,17 +442,27 @@ export declare class MutationsComponent extends PreactLitAdapterWithGridJsStyles
454
442
  }
455
443
 
456
444
  /**
457
- * TODO(#330) write docs
445
+ * ## Context
446
+ *
447
+ * This component displays the number of sequences over time of one or more datasets,
448
+ * selected by LAPIS filters.
449
+ *
450
+ * In the chart views, the user can select the y-axis scale (linear, logistic).
451
+ * The x-axis shows all available time intervals available in the datasets in the selected `granularity`.
452
+ * Thus, the `lapisFilter` implicitly also defines the range that is shown on the x-axis.
453
+ * If you want to restrict the x-axis to a smaller date range,
454
+ * then you need to set appropriate filter values in the `lapisFilter`.
458
455
  */
459
456
  export declare class NumberSequencesOverTimeComponent extends PreactLitAdapterWithGridJsStyles {
460
457
  /**
461
- * Required.
462
- * Either a LAPIS filter or an array of LAPIS filters to calculate the prevalence for.
458
+ * Required.
459
+ *
460
+ * Either a LAPIS filter or an array of LAPIS filters to fetch the number of sequences for.
463
461
  *
464
462
  * The `lapisFilter` will be sent as is to LAPIS to select the data.
465
463
  * It must be a valid LAPIS filter object.
466
464
  *
467
- * The `displayName` will be used as the label the prevalence in the views.
465
+ * The `displayName` will be used to label the component views.
468
466
  * It should be human-readable.
469
467
  *
470
468
  */ lapisFilter: {
@@ -486,10 +484,6 @@ export declare class NumberSequencesOverTimeComponent extends PreactLitAdapterWi
486
484
  * A list of tabs with views that this component should provide.
487
485
  */
488
486
  views: ('bar' | 'line' | 'table')[];
489
- /**
490
- * The headline of the component. Set to an empty string to hide the headline.
491
- */
492
- headline: string;
493
487
  /**
494
488
  * The width of the component.
495
489
  *
@@ -623,10 +617,6 @@ export declare class PrevalenceOverTimeComponent extends PreactLitAdapterWithGri
623
617
  * Pass an empty array to disable the confidence interval selector.
624
618
  */
625
619
  confidenceIntervalMethods: ('wilson' | 'none')[];
626
- /**
627
- * The headline of the component. Set to an empty string to hide the headline.
628
- */
629
- headline: string;
630
620
  /**
631
621
  * The width of the component.
632
622
  *
@@ -764,10 +754,6 @@ export declare class RelativeGrowthAdvantageComponent extends PreactLitAdapter {
764
754
  * A list of tabs with views that this component should provide.
765
755
  */
766
756
  views: 'line'[];
767
- /**
768
- * The headline of the component. Set to an empty string to hide the headline.
769
- */
770
- headline: string;
771
757
  /**
772
758
  * The width of the component.
773
759
  *
package/dist/style.css CHANGED
@@ -2995,10 +2995,6 @@ input.tab:checked + .tab-content,
2995
2995
  --tw-border-opacity: 1;
2996
2996
  border-color: rgb(239 68 68 / var(--tw-border-opacity));
2997
2997
  }
2998
- .bg-base-200 {
2999
- --tw-bg-opacity: 1;
3000
- background-color: var(--fallback-b2,oklch(var(--b2)/var(--tw-bg-opacity)));
3001
- }
3002
2998
  .bg-white {
3003
2999
  --tw-bg-opacity: 1;
3004
3000
  background-color: rgb(255 255 255 / var(--tw-bg-opacity));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@genspectrum/dashboard-components",
3
- "version": "0.5.6",
3
+ "version": "0.6.0",
4
4
  "description": "GenSpectrum web components for building dashboards",
5
5
  "type": "module",
6
6
  "license": "AGPL-3.0-only",
@@ -12,7 +12,6 @@ const meta: Meta<AggregateProps> = {
12
12
  fields: [{ control: 'object' }],
13
13
  width: { control: 'text' },
14
14
  height: { control: 'text' },
15
- headline: { control: 'text' },
16
15
  initialSortField: { control: 'text' },
17
16
  initialSortDirection: { control: 'radio', options: ['ascending', 'descending'] },
18
17
  pageSize: { control: 'object' },
@@ -55,7 +54,6 @@ export const Default: StoryObj<AggregateProps> = {
55
54
  },
56
55
  width: '100%',
57
56
  height: '700px',
58
- headline: 'Aggregate',
59
57
  initialSortField: 'count',
60
58
  initialSortDirection: 'descending',
61
59
  pageSize: 10,
@@ -8,7 +8,6 @@ import { LapisUrlContext } from '../LapisUrlContext';
8
8
  import { CsvDownloadButton } from '../components/csv-download-button';
9
9
  import { ErrorBoundary } from '../components/error-boundary';
10
10
  import { ErrorDisplay } from '../components/error-display';
11
- import Headline from '../components/headline';
12
11
  import Info from '../components/info';
13
12
  import { LoadingDisplay } from '../components/loading-display';
14
13
  import { NoDataDisplay } from '../components/no-data-display';
@@ -22,7 +21,6 @@ export type InitialSort = { field: string; direction: 'ascending' | 'descending'
22
21
  export type AggregateProps = {
23
22
  width: string;
24
23
  height: string;
25
- headline?: string;
26
24
  } & AggregateInnerProps;
27
25
 
28
26
  export interface AggregateInnerProps {
@@ -34,20 +32,13 @@ export interface AggregateInnerProps {
34
32
  pageSize: boolean | number;
35
33
  }
36
34
 
37
- export const Aggregate: FunctionComponent<AggregateProps> = ({
38
- width,
39
- height,
40
- headline = 'Mutations',
41
- ...innerProps
42
- }) => {
35
+ export const Aggregate: FunctionComponent<AggregateProps> = ({ width, height, ...innerProps }) => {
43
36
  const size = { height, width };
44
37
 
45
38
  return (
46
- <ErrorBoundary size={size} headline={headline}>
39
+ <ErrorBoundary size={size}>
47
40
  <ResizeContainer size={size}>
48
- <Headline heading={headline}>
49
- <AggregateInner {...innerProps} />
50
- </Headline>
41
+ <AggregateInner {...innerProps} />
51
42
  </ResizeContainer>
52
43
  </ErrorBoundary>
53
44
  );
@@ -10,11 +10,9 @@ const meta: Meta = {
10
10
  argTypes: {
11
11
  size: { control: 'object' },
12
12
  defaultSize: { control: 'object' },
13
- headline: { control: 'text' },
14
13
  },
15
14
  args: {
16
15
  size: { height: '600px', width: '100%' },
17
- headline: 'Some headline',
18
16
  },
19
17
  };
20
18
 
@@ -22,7 +20,7 @@ export default meta;
22
20
 
23
21
  export const ErrorBoundaryWithoutErrorStory: StoryObj = {
24
22
  render: (args) => (
25
- <ErrorBoundary size={args.size} headline={args.headline}>
23
+ <ErrorBoundary size={args.size}>
26
24
  <div>Some content</div>
27
25
  </ErrorBoundary>
28
26
  ),
@@ -30,13 +28,12 @@ export const ErrorBoundaryWithoutErrorStory: StoryObj = {
30
28
  const canvas = within(canvasElement);
31
29
  const content = canvas.getByText('Some content', { exact: false });
32
30
  await waitFor(() => expect(content).toBeInTheDocument());
33
- await waitFor(() => expect(canvas.queryByText('Some headline')).not.toBeInTheDocument());
34
31
  },
35
32
  };
36
33
 
37
34
  export const ErrorBoundaryWithErrorStory: StoryObj = {
38
35
  render: (args) => (
39
- <ErrorBoundary size={args.size} headline={args.headline}>
36
+ <ErrorBoundary size={args.size}>
40
37
  <ContentThatThrowsError />
41
38
  </ErrorBoundary>
42
39
  ),
@@ -44,7 +41,6 @@ export const ErrorBoundaryWithErrorStory: StoryObj = {
44
41
  const canvas = within(canvasElement);
45
42
  const content = canvas.queryByText('Some content.', { exact: false });
46
43
  await waitFor(() => expect(content).not.toBeInTheDocument());
47
- await waitFor(() => expect(canvas.getByText('Some headline')).toBeInTheDocument());
48
44
  await waitFor(() => expect(canvas.getByText('Error')).toBeInTheDocument());
49
45
  },
50
46
  };
@@ -3,17 +3,14 @@ import { useErrorBoundary } from 'preact/hooks';
3
3
 
4
4
  import { ErrorDisplay } from './error-display';
5
5
  import { ResizeContainer, type Size } from './resize-container';
6
- import Headline from '../components/headline';
7
6
 
8
- export const ErrorBoundary: FunctionComponent<{ size: Size; headline?: string }> = ({ size, headline, children }) => {
7
+ export const ErrorBoundary: FunctionComponent<{ size: Size }> = ({ size, children }) => {
9
8
  const [internalError] = useErrorBoundary();
10
9
 
11
10
  if (internalError) {
12
11
  return (
13
12
  <ResizeContainer size={size}>
14
- <Headline heading={headline}>
15
- <ErrorDisplay error={internalError} />
16
- </Headline>
13
+ <ErrorDisplay error={internalError} />
17
14
  </ResizeContainer>
18
15
  );
19
16
  }
@@ -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 (
@@ -29,7 +29,6 @@ const meta: Meta<MutationComparisonProps> = {
29
29
  },
30
30
  width: { control: 'text' },
31
31
  height: { control: 'text' },
32
- headline: { control: 'text' },
33
32
  pageSize: { control: 'object' },
34
33
  },
35
34
  parameters: {
@@ -85,7 +84,6 @@ const Template: StoryObj<MutationComparisonProps> = {
85
84
  views={args.views}
86
85
  width={args.width}
87
86
  height={args.height}
88
- headline={args.headline}
89
87
  pageSize={args.pageSize}
90
88
  />
91
89
  </ReferenceGenomeContext.Provider>
@@ -115,7 +113,6 @@ export const TwoVariants: StoryObj<MutationComparisonProps> = {
115
113
  views: ['table', 'venn'],
116
114
  width: '100%',
117
115
  height: '700px',
118
- headline: 'Mutation comparison',
119
116
  pageSize: 10,
120
117
  },
121
118
  };
@@ -11,7 +11,6 @@ import { type DisplayedSegment, SegmentSelector, useDisplayedSegments } from '..
11
11
  import { CsvDownloadButton } from '../components/csv-download-button';
12
12
  import { ErrorBoundary } from '../components/error-boundary';
13
13
  import { ErrorDisplay } from '../components/error-display';
14
- import Headline from '../components/headline';
15
14
  import Info from '../components/info';
16
15
  import { LoadingDisplay } from '../components/loading-display';
17
16
  import { type DisplayedMutationType, MutationTypeSelector } from '../components/mutation-type-selector';
@@ -27,7 +26,6 @@ export type View = 'table' | 'venn';
27
26
  export interface MutationComparisonProps extends MutationComparisonInnerProps {
28
27
  width: string;
29
28
  height: string;
30
- headline?: string;
31
29
  }
32
30
 
33
31
  export interface MutationComparisonInnerProps {
@@ -37,20 +35,13 @@ export interface MutationComparisonInnerProps {
37
35
  pageSize: boolean | number;
38
36
  }
39
37
 
40
- export const MutationComparison: FunctionComponent<MutationComparisonProps> = ({
41
- width,
42
- height,
43
- headline = 'Mutation comparison',
44
- ...innerProps
45
- }) => {
38
+ export const MutationComparison: FunctionComponent<MutationComparisonProps> = ({ width, height, ...innerProps }) => {
46
39
  const size = { height, width };
47
40
 
48
41
  return (
49
- <ErrorBoundary size={size} headline={headline}>
42
+ <ErrorBoundary size={size}>
50
43
  <ResizeContainer size={size}>
51
- <Headline heading={headline}>
52
- <MutationComparisonInner {...innerProps} />
53
- </Headline>
44
+ <MutationComparisonInner {...innerProps} />
54
45
  </ResizeContainer>
55
46
  </ErrorBoundary>
56
47
  );
@@ -24,7 +24,6 @@ const meta: Meta<MutationsProps> = {
24
24
  },
25
25
  width: { control: 'text' },
26
26
  height: { control: 'text' },
27
- headline: { control: 'text' },
28
27
  pageSize: { control: 'object' },
29
28
  },
30
29
  };
@@ -41,7 +40,6 @@ const Template = {
41
40
  views={args.views}
42
41
  width={args.width}
43
42
  height={args.height}
44
- headline={args.headline}
45
43
  pageSize={args.pageSize}
46
44
  />
47
45
  </ReferenceGenomeContext.Provider>
@@ -57,7 +55,6 @@ export const Default: StoryObj<MutationsProps> = {
57
55
  views: ['grid', 'table', 'insertions'],
58
56
  width: '100%',
59
57
  height: '700px',
60
- headline: 'Mutations',
61
58
  pageSize: 10,
62
59
  },
63
60
  parameters: {
@@ -18,7 +18,6 @@ import { type DisplayedSegment, SegmentSelector, useDisplayedSegments } from '..
18
18
  import { CsvDownloadButton } from '../components/csv-download-button';
19
19
  import { ErrorBoundary } from '../components/error-boundary';
20
20
  import { ErrorDisplay } from '../components/error-display';
21
- import Headline from '../components/headline';
22
21
  import Info from '../components/info';
23
22
  import { LoadingDisplay } from '../components/loading-display';
24
23
  import { type DisplayedMutationType, MutationTypeSelector } from '../components/mutation-type-selector';
@@ -41,23 +40,15 @@ export interface MutationsInnerProps {
41
40
  export interface MutationsProps extends MutationsInnerProps {
42
41
  width: string;
43
42
  height: string;
44
- headline?: string;
45
43
  }
46
44
 
47
- export const Mutations: FunctionComponent<MutationsProps> = ({
48
- width,
49
- height,
50
- headline = 'Mutations',
51
- ...innerProps
52
- }) => {
45
+ export const Mutations: FunctionComponent<MutationsProps> = ({ width, height, ...innerProps }) => {
53
46
  const size = { height, width };
54
47
 
55
48
  return (
56
- <ErrorBoundary size={size} headline={headline}>
49
+ <ErrorBoundary size={size}>
57
50
  <ResizeContainer size={size}>
58
- <Headline heading={headline}>
59
- <MutationsInner {...innerProps} />
60
- </Headline>
51
+ <MutationsInner {...innerProps} />
61
52
  </ResizeContainer>
62
53
  </ErrorBoundary>
63
54
  );
@@ -198,7 +189,7 @@ const Toolbar: FunctionComponent<ToolbarProps> = ({
198
189
  <CsvDownloadButton
199
190
  className='mx-1 btn btn-xs'
200
191
  getData={() => getMutationsTableData(filteredData.tableData, proportionInterval)}
201
- filename='substitutionsAndDeletions.csv'
192
+ filename='substitutions_and_deletions.csv'
202
193
  />
203
194
  </>
204
195
  )}
@@ -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} />;
@@ -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 NumberSequencesOverTimeLineChart = ({ data }: NumberSequencesOverBarChartProps) => {
17
+ export const NumberSequencesOverTimeLineChart = ({ data, yAxisScaleType }: NumberSequencesOverBarChartProps) => {
16
18
  const config: ChartConfiguration = useMemo(
17
19
  () => ({
18
20
  type: 'line',
@@ -22,6 +24,11 @@ export const NumberSequencesOverTimeLineChart = ({ data }: NumberSequencesOverBa
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 NumberSequencesOverTimeLineChart = ({ data }: NumberSequencesOverBa
33
40
  },
34
41
  },
35
42
  }),
36
- [data],
43
+ [data, yAxisScaleType],
37
44
  );
38
45
 
39
46
  return <GsChart configuration={config} />;
@@ -35,7 +35,6 @@ const Template: StoryObj<NumberSequencesOverTimeProps> = {
35
35
  views={args.views}
36
36
  width={args.width}
37
37
  height={args.height}
38
- headline={args.headline}
39
38
  granularity={args.granularity}
40
39
  smoothingWindow={args.smoothingWindow}
41
40
  pageSize={args.pageSize}
@@ -50,7 +49,6 @@ const Template: StoryObj<NumberSequencesOverTimeProps> = {
50
49
  lapisDateField: 'date',
51
50
  width: '100%',
52
51
  height: '700px',
53
- headline: 'Number of sequences over time',
54
52
  smoothingWindow: 0,
55
53
  granularity: 'month',
56
54
  pageSize: 10,
@@ -1,5 +1,6 @@
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';
4
5
  import { NumberSequencesOverTimeLineChart } from './number-sequences-over-time-line-chart';
5
6
  import { NumberSequencesOverTimeTable } from './number-sequences-over-time-table';
@@ -9,13 +10,16 @@ import {
9
10
  } from '../../query/queryNumberOfSequencesOverTime';
10
11
  import type { NamedLapisFilter, TemporalGranularity } from '../../types';
11
12
  import { LapisUrlContext } from '../LapisUrlContext';
13
+ import { CsvDownloadButton } from '../components/csv-download-button';
12
14
  import { ErrorBoundary } from '../components/error-boundary';
13
15
  import { ErrorDisplay } from '../components/error-display';
14
- import Headline from '../components/headline';
16
+ import Info, { InfoHeadline1, InfoParagraph } from '../components/info';
15
17
  import { LoadingDisplay } from '../components/loading-display';
16
18
  import { NoDataDisplay } from '../components/no-data-display';
17
19
  import { ResizeContainer } from '../components/resize-container';
20
+ import { ScalingSelector } from '../components/scaling-selector';
18
21
  import Tabs from '../components/tabs';
22
+ import type { ScaleType } from '../shared/charts/getYAxisScale';
19
23
  import { useQuery } from '../useQuery';
20
24
 
21
25
  type NumberSequencesOverTimeView = 'bar' | 'line' | 'table';
@@ -23,7 +27,6 @@ type NumberSequencesOverTimeView = 'bar' | 'line' | 'table';
23
27
  export interface NumberSequencesOverTimeProps extends NumberSequencesOverTimeInnerProps {
24
28
  width: string;
25
29
  height: string;
26
- headline: string;
27
30
  }
28
31
 
29
32
  interface NumberSequencesOverTimeInnerProps {
@@ -35,15 +38,13 @@ interface NumberSequencesOverTimeInnerProps {
35
38
  pageSize: boolean | number;
36
39
  }
37
40
 
38
- export const NumberSequencesOverTime = ({ width, height, headline, ...innerProps }: NumberSequencesOverTimeProps) => {
41
+ export const NumberSequencesOverTime = ({ width, height, ...innerProps }: NumberSequencesOverTimeProps) => {
39
42
  const size = { height, width };
40
43
 
41
44
  return (
42
- <ErrorBoundary size={size} headline={headline}>
45
+ <ErrorBoundary size={size}>
43
46
  <ResizeContainer size={size}>
44
- <Headline heading={headline}>
45
- <NumberSequencesOverTimeInner {...innerProps} />
46
- </Headline>
47
+ <NumberSequencesOverTimeInner {...innerProps} />
47
48
  </ResizeContainer>
48
49
  </ErrorBoundary>
49
50
  );
@@ -86,12 +87,20 @@ interface NumberSequencesOverTimeTabsProps {
86
87
  }
87
88
 
88
89
  const NumberSequencesOverTimeTabs = ({ views, data, granularity, pageSize }: NumberSequencesOverTimeTabsProps) => {
90
+ const [yAxisScaleType, setYAxisScaleType] = useState<ScaleType>('linear');
91
+
89
92
  const getTab = (view: NumberSequencesOverTimeView) => {
90
93
  switch (view) {
91
94
  case 'bar':
92
- return { title: 'Bar', content: <NumberSequencesOverTimeBarChart data={data} /> };
95
+ return {
96
+ title: 'Bar',
97
+ content: <NumberSequencesOverTimeBarChart data={data} yAxisScaleType={yAxisScaleType} />,
98
+ };
93
99
  case 'line':
94
- return { title: 'Line', content: <NumberSequencesOverTimeLineChart data={data} /> };
100
+ return {
101
+ title: 'Line',
102
+ content: <NumberSequencesOverTimeLineChart data={data} yAxisScaleType={yAxisScaleType} />,
103
+ };
95
104
  case 'table':
96
105
  return {
97
106
  title: 'Table',
@@ -102,5 +111,55 @@ const NumberSequencesOverTimeTabs = ({ views, data, granularity, pageSize }: Num
102
111
  }
103
112
  };
104
113
 
105
- return <Tabs tabs={views.map((view) => getTab(view))} />;
114
+ return (
115
+ <Tabs
116
+ tabs={views.map((view) => getTab(view))}
117
+ toolbar={(activeTab) => (
118
+ <Toolbar
119
+ activeTab={activeTab}
120
+ data={data}
121
+ granularity={granularity}
122
+ yAxisScaleType={yAxisScaleType}
123
+ setYAxisScaleType={setYAxisScaleType}
124
+ />
125
+ )}
126
+ />
127
+ );
128
+ };
129
+
130
+ interface ToolbarProps {
131
+ activeTab: string;
132
+ data: NumberOfSequencesDatasets;
133
+ granularity: TemporalGranularity;
134
+ yAxisScaleType: ScaleType;
135
+ setYAxisScaleType: (scaleType: ScaleType) => void;
136
+ }
137
+
138
+ const Toolbar = ({ activeTab, data, granularity, yAxisScaleType, setYAxisScaleType }: ToolbarProps) => {
139
+ return (
140
+ <>
141
+ {activeTab !== 'Table' && (
142
+ <ScalingSelector
143
+ yAxisScaleType={yAxisScaleType}
144
+ setYAxisScaleType={setYAxisScaleType}
145
+ enabledTypes={['linear', 'logarithmic']}
146
+ />
147
+ )}
148
+ <CsvDownloadButton
149
+ className='mx-1 btn btn-xs'
150
+ getData={() => getNumberOfSequencesOverTimeTableData(data, granularity)}
151
+ filename='number_of_sequences_over_time.csv'
152
+ />
153
+ <NumberSequencesOverTimeInfo />
154
+ </>
155
+ );
106
156
  };
157
+
158
+ const NumberSequencesOverTimeInfo = () => (
159
+ <Info height='100px'>
160
+ <InfoHeadline1>Number of sequences over time</InfoHeadline1>
161
+ <InfoParagraph>
162
+ <a href='https://github.com/GenSpectrum/dashboard-components/issues/315'>TODO</a>
163
+ </InfoParagraph>
164
+ </Info>
165
+ );
@@ -31,7 +31,6 @@ export default {
31
31
  },
32
32
  width: { control: 'text' },
33
33
  height: { control: 'text' },
34
- headline: { control: 'text' },
35
34
  pageSize: { control: 'object' },
36
35
  yAxisMaxConfig: { control: 'object' },
37
36
  },
@@ -49,7 +48,6 @@ const Template = {
49
48
  confidenceIntervalMethods={args.confidenceIntervalMethods}
50
49
  width={args.width}
51
50
  height={args.height}
52
- headline={args.headline}
53
51
  lapisDateField={args.lapisDateField}
54
52
  pageSize={args.pageSize}
55
53
  yAxisMaxConfig={args.yAxisMaxConfig}
@@ -72,7 +70,6 @@ export const TwoVariants = {
72
70
  confidenceIntervalMethods: ['wilson'],
73
71
  width: '100%',
74
72
  height: '700px',
75
- headline: 'Prevalence over time',
76
73
  lapisDateField: 'date',
77
74
  pageSize: 10,
78
75
  yAxisMaxConfig: {
@@ -149,7 +146,6 @@ export const OneVariant = {
149
146
  confidenceIntervalMethods: ['wilson'],
150
147
  width: '100%',
151
148
  height: '700px',
152
- headline: 'Prevalence over time',
153
149
  lapisDateField: 'date',
154
150
  pageSize: 10,
155
151
  yAxisMaxConfig: {