@genspectrum/dashboard-components 0.14.2 → 0.16.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 (104) hide show
  1. package/custom-elements.json +281 -105
  2. package/dist/{LineageFilterChangedEvent-C9dXOxt6.js → LineageFilterChangedEvent-COWV-Y0k.js} +6 -6
  3. package/dist/LineageFilterChangedEvent-COWV-Y0k.js.map +1 -0
  4. package/dist/assets/{mutationOverTimeWorker-Dxnxrfe0.js.map → mutationOverTimeWorker-BL50C-yi.js.map} +1 -1
  5. package/dist/components.d.ts +40 -40
  6. package/dist/components.js +373 -243
  7. package/dist/components.js.map +1 -1
  8. package/dist/style.css +9 -0
  9. package/dist/util.d.ts +49 -49
  10. package/dist/util.js +2 -2
  11. package/package.json +1 -1
  12. package/src/preact/ReferenceGenomeContext.ts +14 -1
  13. package/src/preact/aggregatedData/aggregate-bar-chart.tsx +26 -5
  14. package/src/preact/aggregatedData/aggregate.stories.tsx +0 -1
  15. package/src/preact/aggregatedData/aggregate.tsx +5 -1
  16. package/src/preact/components/ReferenceGenomesAwaiter.tsx +1 -6
  17. package/src/preact/components/resize-container.tsx +1 -1
  18. package/src/preact/{dateRangeSelector/date-range-selector.stories.tsx → dateRangeFilter/date-range-filter.stories.tsx} +15 -15
  19. package/src/preact/{dateRangeSelector/date-range-selector.tsx → dateRangeFilter/date-range-filter.tsx} +9 -9
  20. package/src/preact/{dateRangeSelector → dateRangeFilter}/dateRangeOption.ts +2 -2
  21. package/src/preact/mutationComparison/mutation-comparison-venn.tsx +4 -2
  22. package/src/preact/mutationComparison/mutation-comparison.stories.tsx +0 -1
  23. package/src/preact/mutationComparison/mutation-comparison.tsx +5 -1
  24. package/src/preact/mutationFilter/mutation-filter.stories.tsx +17 -1
  25. package/src/preact/mutationFilter/mutation-filter.tsx +8 -0
  26. package/src/preact/mutations/mutations.stories.tsx +0 -1
  27. package/src/preact/mutations/mutations.tsx +1 -1
  28. package/src/preact/mutationsOverTime/mutations-over-time.stories.tsx +0 -2
  29. package/src/preact/mutationsOverTime/mutations-over-time.tsx +1 -1
  30. package/src/preact/numberSequencesOverTime/number-sequences-over-time-bar-chart.tsx +8 -3
  31. package/src/preact/numberSequencesOverTime/number-sequences-over-time-line-chart.tsx +8 -3
  32. package/src/preact/numberSequencesOverTime/number-sequences-over-time.stories.tsx +3 -1
  33. package/src/preact/numberSequencesOverTime/number-sequences-over-time.tsx +18 -3
  34. package/src/preact/prevalenceOverTime/prevalence-over-time-bar-chart.tsx +48 -35
  35. package/src/preact/prevalenceOverTime/prevalence-over-time-bubble-chart.tsx +83 -70
  36. package/src/preact/prevalenceOverTime/prevalence-over-time-line-chart.tsx +48 -37
  37. package/src/preact/prevalenceOverTime/prevalence-over-time.stories.tsx +0 -3
  38. package/src/preact/prevalenceOverTime/prevalence-over-time.tsx +6 -1
  39. package/src/preact/relativeGrowthAdvantage/relative-growth-advantage-chart.tsx +31 -23
  40. package/src/preact/relativeGrowthAdvantage/relative-growth-advantage.stories.tsx +0 -1
  41. package/src/preact/relativeGrowthAdvantage/relative-growth-advantage.tsx +5 -1
  42. package/src/preact/sequencesByLocation/__mockData__/worldAtlas.json +1 -0
  43. package/src/preact/{map → sequencesByLocation}/sequences-by-location-map.tsx +6 -3
  44. package/src/preact/{map → sequencesByLocation}/sequences-by-location-table.tsx +1 -1
  45. package/src/preact/{map → sequencesByLocation}/sequences-by-location.stories.tsx +58 -1
  46. package/src/preact/{map → sequencesByLocation}/sequences-by-location.tsx +10 -1
  47. package/src/preact/shared/aspectRatio/AspectRatio.tsx +13 -0
  48. package/src/preact/shared/charts/getMaintainAspectRatio.ts +3 -0
  49. package/src/preact/statistic/statistics.stories.tsx +0 -1
  50. package/src/preact/statistic/statistics.tsx +4 -4
  51. package/src/preact/{textInput/TextInputChangedEvent.ts → textFilter/TextFilterChangedEvent.ts} +2 -2
  52. package/src/preact/{textInput/text-input.stories.tsx → textFilter/text-filter.stories.tsx} +10 -10
  53. package/src/preact/{textInput/text-input.tsx → textFilter/text-filter.tsx} +10 -10
  54. package/src/preact/wastewater/mutationsOverTime/wastewater-mutations-over-time.stories.tsx +0 -1
  55. package/src/preact/wastewater/mutationsOverTime/wastewater-mutations-over-time.tsx +1 -1
  56. package/src/query/computeMapLocationData.spec.ts +1 -1
  57. package/src/query/computeMapLocationData.ts +1 -1
  58. package/src/query/querySequencesByLocationData.ts +1 -1
  59. package/src/utilEntrypoint.ts +3 -3
  60. package/src/web-components/ResizeContainer.mdx +4 -1
  61. package/src/web-components/input/{gs-date-range-selector.stories.ts → gs-date-range-filter.stories.ts} +59 -14
  62. package/src/web-components/input/{gs-date-range-selector.tsx → gs-date-range-filter.tsx} +28 -13
  63. package/src/web-components/input/{gs-text-input.stories.ts → gs-text-filter.stories.ts} +15 -15
  64. package/src/web-components/input/{gs-text-input.tsx → gs-text-filter.tsx} +16 -16
  65. package/src/web-components/input/index.ts +2 -2
  66. package/src/web-components/visualization/gs-aggregate.stories.ts +13 -6
  67. package/src/web-components/visualization/gs-aggregate.tsx +1 -1
  68. package/src/web-components/visualization/gs-mutation-comparison.stories.ts +8 -1
  69. package/src/web-components/visualization/gs-mutation-comparison.tsx +1 -1
  70. package/src/web-components/visualization/gs-mutations-over-time.stories.ts +9 -1
  71. package/src/web-components/visualization/gs-mutations-over-time.tsx +1 -1
  72. package/src/web-components/visualization/gs-mutations.stories.ts +8 -1
  73. package/src/web-components/visualization/gs-mutations.tsx +1 -1
  74. package/src/web-components/visualization/gs-number-sequences-over-time.stories.ts +11 -1
  75. package/src/web-components/visualization/gs-number-sequences-over-time.tsx +1 -1
  76. package/src/web-components/visualization/gs-prevalence-over-time.stories.ts +8 -2
  77. package/src/web-components/visualization/gs-prevalence-over-time.tsx +1 -1
  78. package/src/web-components/visualization/gs-relative-growth-advantage.stories.ts +8 -1
  79. package/src/web-components/visualization/gs-relative-growth-advantage.tsx +1 -1
  80. package/src/web-components/visualization/gs-sequences-by-location.stories.ts +13 -7
  81. package/src/web-components/visualization/gs-sequences-by-location.tsx +6 -3
  82. package/src/web-components/visualization/gs-statistics.stories.ts +0 -1
  83. package/src/web-components/visualization/gs-statistics.tsx +1 -1
  84. package/src/web-components/wastewaterVisualization/gs-wastewater-mutations-over-time.stories.ts +9 -1
  85. package/src/web-components/wastewaterVisualization/gs-wastewater-mutations-over-time.tsx +1 -1
  86. package/standalone-bundle/assets/{mutationOverTimeWorker-CmSrq4SZ.js.map → mutationOverTimeWorker-CFB5-Mdk.js.map} +1 -1
  87. package/standalone-bundle/dashboard-components.js +6032 -5937
  88. package/standalone-bundle/dashboard-components.js.map +1 -1
  89. package/standalone-bundle/style.css +1 -1
  90. package/dist/LineageFilterChangedEvent-C9dXOxt6.js.map +0 -1
  91. package/src/preact/map/__mockData__/worldAtlas.json +0 -497127
  92. /package/src/preact/{dateRangeSelector → dateRangeFilter}/computeInitialValues.spec.ts +0 -0
  93. /package/src/preact/{dateRangeSelector → dateRangeFilter}/computeInitialValues.ts +0 -0
  94. /package/src/preact/{dateRangeSelector → dateRangeFilter}/dateConversion.ts +0 -0
  95. /package/src/preact/{dateRangeSelector → dateRangeFilter}/selectableOptions.ts +0 -0
  96. /package/src/preact/{map → sequencesByLocation}/__mockData__/aggregatedGermany.json +0 -0
  97. /package/src/preact/{map → sequencesByLocation}/__mockData__/aggregatedWorld.json +0 -0
  98. /package/src/preact/{map → sequencesByLocation}/__mockData__/germanyMap.json +0 -0
  99. /package/src/preact/{map → sequencesByLocation}/__mockData__/howToGenerateWorldMap.md +0 -0
  100. /package/src/preact/{map → sequencesByLocation}/leafletStyleModifications.css +0 -0
  101. /package/src/preact/{map → sequencesByLocation}/loadMapSource.tsx +0 -0
  102. /package/src/preact/{textInput → textFilter}/__mockData__/aggregated_hosts.json +0 -0
  103. /package/src/preact/{textInput → textFilter}/fetchStringAutocompleteList.spec.ts +0 -0
  104. /package/src/preact/{textInput → textFilter}/fetchStringAutocompleteList.ts +0 -0
@@ -6,6 +6,7 @@ import { useEffect, useRef } from 'preact/hooks';
6
6
  import type { EnhancedGeoJsonFeatureProperties } from '../../query/computeMapLocationData';
7
7
  import { InfoHeadline1, InfoParagraph } from '../components/info';
8
8
  import { Modal, useModalRef } from '../components/modal';
9
+ import { AspectRatio } from '../shared/aspectRatio/AspectRatio';
9
10
  import { formatProportion } from '../shared/table/formatProportion';
10
11
 
11
12
  type SequencesByLocationMapProps = {
@@ -20,6 +21,7 @@ type SequencesByLocationMapProps = {
20
21
  offsetX: number;
21
22
  offsetY: number;
22
23
  hasTableView: boolean;
24
+ maintainAspectRatio: boolean;
23
25
  };
24
26
 
25
27
  export const SequencesByLocationMap: FunctionComponent<SequencesByLocationMapProps> = ({
@@ -34,6 +36,7 @@ export const SequencesByLocationMap: FunctionComponent<SequencesByLocationMapPro
34
36
  offsetX,
35
37
  offsetY,
36
38
  hasTableView,
39
+ maintainAspectRatio,
37
40
  }) => {
38
41
  const ref = useRef<HTMLDivElement>(null);
39
42
 
@@ -69,8 +72,8 @@ export const SequencesByLocationMap: FunctionComponent<SequencesByLocationMapPro
69
72
  }, [ref, locations, enableMapNavigation, lapisLocationField, zoom, offsetX, offsetY]);
70
73
 
71
74
  return (
72
- <div className='h-full'>
73
- <div ref={ref} className='h-full' />
75
+ <AspectRatio aspectRatio={maintainAspectRatio ? 50 : undefined}>
76
+ <div className='h-full' ref={ref} />
74
77
  <div className='relative'>
75
78
  <DataMatchInformation
76
79
  totalCount={totalCount}
@@ -80,7 +83,7 @@ export const SequencesByLocationMap: FunctionComponent<SequencesByLocationMapPro
80
83
  hasTableView={hasTableView}
81
84
  />
82
85
  </div>
83
- </div>
86
+ </AspectRatio>
84
87
  );
85
88
  };
86
89
 
@@ -32,7 +32,7 @@ export const SequencesByLocationTable: FunctionComponent<SequencesByLocationTabl
32
32
  sort: true,
33
33
  formatter: (cell: number) => formatProportion(cell),
34
34
  },
35
- ...('isShownOnMap' in tableData[0]
35
+ ...(tableData.length > 0 && 'isShownOnMap' in tableData[0]
36
36
  ? [{ id: 'isShownOnMap', name: 'shown on map', sort: true, width: '20%' }]
37
37
  : []),
38
38
  ];
@@ -1,4 +1,5 @@
1
1
  import { type Meta, type StoryObj } from '@storybook/preact';
2
+ import { expect, waitFor, within } from '@storybook/test';
2
3
 
3
4
  import worldAtlas from './__mockData__/worldAtlas.json';
4
5
  import { AGGREGATED_ENDPOINT, LAPIS_URL } from '../../constants';
@@ -13,6 +14,21 @@ import './leafletStyleModifications.css';
13
14
  const meta: Meta<SequencesByLocationProps> = {
14
15
  title: 'Visualization/SequencesByLocation',
15
16
  component: SequencesByLocation,
17
+ argTypes: {
18
+ lapisFilter: { control: 'object' },
19
+ lapisLocationField: { control: { type: 'text' } },
20
+ enableMapNavigation: { control: { type: 'boolean' } },
21
+ width: { control: { type: 'text' } },
22
+ height: { control: { type: 'text' } },
23
+ views: {
24
+ options: ['map', 'table'],
25
+ control: { type: 'check' },
26
+ },
27
+ zoom: { control: { type: 'number' } },
28
+ offsetX: { control: { type: 'number' } },
29
+ offsetY: { control: { type: 'number' } },
30
+ pageSize: { control: 'object' },
31
+ },
16
32
  };
17
33
 
18
34
  export default meta;
@@ -51,7 +67,6 @@ export const Default: StoryObj<SequencesByLocationProps> = {
51
67
  },
52
68
  enableMapNavigation: false,
53
69
  width: '1100px',
54
- height: '800px',
55
70
  views: ['map', 'table'],
56
71
  zoom: 2,
57
72
  offsetX: 0,
@@ -77,6 +92,48 @@ export const Default: StoryObj<SequencesByLocationProps> = {
77
92
  },
78
93
  };
79
94
 
95
+ export const NoData: StoryObj<SequencesByLocationProps> = {
96
+ ...Default,
97
+ parameters: {
98
+ fetchMock: {
99
+ mocks: [
100
+ {
101
+ matcher: {
102
+ name: 'worldMap',
103
+ url: worldMapUrl,
104
+ },
105
+ response: {
106
+ status: 200,
107
+ body: worldAtlas,
108
+ },
109
+ },
110
+ {
111
+ matcher: {
112
+ name: 'aggregatedData',
113
+ url: AGGREGATED_ENDPOINT,
114
+ body: {
115
+ fields: ['country'],
116
+ dateFrom: '2022-01-01',
117
+ dateTo: '2022-04-01',
118
+ },
119
+ },
120
+ response: {
121
+ status: 200,
122
+ body: { data: [] },
123
+ },
124
+ },
125
+ ],
126
+ },
127
+ },
128
+ play: async ({ canvasElement }) => {
129
+ const canvas = within(canvasElement);
130
+
131
+ await waitFor(async () => {
132
+ await expect(canvas.getByText('No data available.')).toBeVisible();
133
+ });
134
+ },
135
+ };
136
+
80
137
  export const InvalidTopoJsonTopology: StoryObj<SequencesByLocationProps> = {
81
138
  ...Default,
82
139
  parameters: {
@@ -20,7 +20,9 @@ import { ResizeContainer } from '../components/resize-container';
20
20
  import { useQuery } from '../useQuery';
21
21
  import { mapSourceSchema } from './loadMapSource';
22
22
  import { lapisFilterSchema, views } from '../../types';
23
+ import { NoDataDisplay } from '../components/no-data-display';
23
24
  import Tabs from '../components/tabs';
25
+ import { getMaintainAspectRatio } from '../shared/charts/getMaintainAspectRatio';
24
26
 
25
27
  export const sequencesByLocationViewSchema = z.union([z.literal(views.map), z.literal(views.table)]);
26
28
  export type SequencesByLocationMapView = z.infer<typeof sequencesByLocationViewSchema>;
@@ -31,7 +33,7 @@ const sequencesByLocationPropsSchema = z.object({
31
33
  mapSource: mapSourceSchema.optional(),
32
34
  enableMapNavigation: z.boolean(),
33
35
  width: z.string(),
34
- height: z.string(),
36
+ height: z.string().optional(),
35
37
  views: z.array(sequencesByLocationViewSchema),
36
38
  zoom: z.number(),
37
39
  offsetX: z.number(),
@@ -75,6 +77,10 @@ const SequencesByLocationMapInner: FunctionComponent<SequencesByLocationProps> =
75
77
  throw error;
76
78
  }
77
79
 
80
+ if (data.tableData.length === 0) {
81
+ return <NoDataDisplay />;
82
+ }
83
+
78
84
  return <SequencesByLocationMapTabs data={data} originalComponentProps={props} />;
79
85
  };
80
86
 
@@ -87,6 +93,8 @@ const SequencesByLocationMapTabs: FunctionComponent<SequencesByLocationMapTabsPr
87
93
  originalComponentProps,
88
94
  data,
89
95
  }) => {
96
+ const maintainAspectRatio = getMaintainAspectRatio(originalComponentProps.height);
97
+
90
98
  const getTab = (view: SequencesByLocationMapView) => {
91
99
  switch (view) {
92
100
  case views.map: {
@@ -105,6 +113,7 @@ const SequencesByLocationMapTabs: FunctionComponent<SequencesByLocationMapTabsPr
105
113
  offsetX={originalComponentProps.offsetX}
106
114
  offsetY={originalComponentProps.offsetY}
107
115
  hasTableView={originalComponentProps.views.includes(views.table)}
116
+ maintainAspectRatio={maintainAspectRatio}
108
117
  />
109
118
  ),
110
119
  };
@@ -0,0 +1,13 @@
1
+ import { type PropsWithChildren } from 'react';
2
+
3
+ export function AspectRatio({ children, aspectRatio }: PropsWithChildren<{ aspectRatio?: number }>) {
4
+ if (aspectRatio === undefined) {
5
+ return children;
6
+ }
7
+
8
+ return (
9
+ <div class={`w-full relative`} style={{ paddingTop: `${aspectRatio}%` }}>
10
+ <div className='absolute inset-0'>{children}</div>
11
+ </div>
12
+ );
13
+ }
@@ -0,0 +1,3 @@
1
+ export function getMaintainAspectRatio(height: string | undefined) {
2
+ return height === undefined || height === '';
3
+ }
@@ -68,7 +68,6 @@ export const Default: StoryObj<StatisticsProps> = {
68
68
  division: 'Alabama',
69
69
  },
70
70
  width: '100%',
71
- height: '100%',
72
71
  },
73
72
  play: async ({ canvasElement }) => {
74
73
  const canvas = within(canvasElement);
@@ -13,7 +13,7 @@ import { useQuery } from '../useQuery';
13
13
 
14
14
  const statisticsPropsSchema = z.object({
15
15
  width: z.string(),
16
- height: z.string(),
16
+ height: z.string().optional(),
17
17
  numeratorFilter: lapisFilterSchema,
18
18
  denominatorFilter: lapisFilterSchema,
19
19
  });
@@ -62,17 +62,17 @@ type MetricDataTabsProps = {
62
62
  const MetricDataTabs: FunctionComponent<MetricDataTabsProps> = ({ data }) => {
63
63
  const { count, proportion } = data;
64
64
  return (
65
- <div className='flex flex-col sm:flex-row rounded-md border-2 border-gray-100'>
65
+ <div className='flex flex-col sm:flex-row rounded-md border-2 border-gray-100 min-w-[180px]'>
66
66
  <div className='stat'>
67
67
  <div className='stat-title'>Sequences</div>
68
68
  <div className='stat-value text-2xl sm:text-4xl'>{count.toLocaleString('en-us')}</div>
69
- <div className='stat-desc'>The total number of sequenced samples</div>
69
+ <div className='stat-desc text-wrap'>The total number of sequenced samples</div>
70
70
  </div>
71
71
 
72
72
  <div className='stat'>
73
73
  <div className='stat-title'>Overall proportion</div>
74
74
  <div className='stat-value text-2xl sm:text-4xl'>{formatProportion(proportion)}</div>
75
- <div className='stat-desc'>The proportion among all sequenced samples</div>
75
+ <div className='stat-desc text-wrap'>The proportion among all sequenced samples</div>
76
76
  </div>
77
77
  </div>
78
78
  );
@@ -1,8 +1,8 @@
1
1
  type LapisTextFilter = Record<string, string | undefined>;
2
2
 
3
- export class TextInputChangedEvent extends CustomEvent<LapisTextFilter> {
3
+ export class TextFilterChangedEvent extends CustomEvent<LapisTextFilter> {
4
4
  constructor(detail: LapisTextFilter) {
5
- super('gs-text-input-changed', {
5
+ super('gs-text-filter-changed', {
6
6
  detail,
7
7
  bubbles: true,
8
8
  composed: true,
@@ -2,18 +2,18 @@ import { type Meta, type StoryObj } from '@storybook/preact';
2
2
  import { expect, fireEvent, fn, waitFor, within } from '@storybook/test';
3
3
 
4
4
  import data from './__mockData__/aggregated_hosts.json';
5
- import { TextInput, type TextInputProps } from './text-input';
5
+ import { TextFilter, type TextFilterProps } from './text-filter';
6
6
  import { previewHandles } from '../../../.storybook/preview';
7
7
  import { AGGREGATED_ENDPOINT, LAPIS_URL } from '../../constants';
8
8
  import { LapisUrlContextProvider } from '../LapisUrlContext';
9
9
  import { expectInvalidAttributesErrorMessage } from '../shared/stories/expectErrorMessage';
10
10
 
11
- const meta: Meta<TextInputProps> = {
12
- title: 'Input/TextInput',
13
- component: TextInput,
11
+ const meta: Meta<TextFilterProps> = {
12
+ title: 'Input/TextFilter',
13
+ component: TextFilter,
14
14
  parameters: {
15
15
  actions: {
16
- handles: ['gs-text-input-changed', ...previewHandles],
16
+ handles: ['gs-text-filter-changed', ...previewHandles],
17
17
  },
18
18
  fetchMock: {
19
19
  mocks: [
@@ -65,10 +65,10 @@ const meta: Meta<TextInputProps> = {
65
65
 
66
66
  export default meta;
67
67
 
68
- export const Default: StoryObj<TextInputProps> = {
68
+ export const Default: StoryObj<TextFilterProps> = {
69
69
  render: (args) => (
70
70
  <LapisUrlContextProvider value={LAPIS_URL}>
71
- <TextInput {...args} />
71
+ <TextFilter {...args} />
72
72
  </LapisUrlContextProvider>
73
73
  ),
74
74
  args: {
@@ -82,7 +82,7 @@ export const Default: StoryObj<TextInputProps> = {
82
82
  },
83
83
  };
84
84
 
85
- export const RemoveInitialValue: StoryObj<TextInputProps> = {
85
+ export const RemoveInitialValue: StoryObj<TextFilterProps> = {
86
86
  ...Default,
87
87
  args: {
88
88
  ...Default.args,
@@ -93,7 +93,7 @@ export const RemoveInitialValue: StoryObj<TextInputProps> = {
93
93
 
94
94
  const changedListenerMock = fn();
95
95
  await step('Setup event listener mock', () => {
96
- canvasElement.addEventListener('gs-text-input-changed', changedListenerMock);
96
+ canvasElement.addEventListener('gs-text-filter-changed', changedListenerMock);
97
97
  });
98
98
 
99
99
  await waitFor(async () => {
@@ -115,7 +115,7 @@ export const RemoveInitialValue: StoryObj<TextInputProps> = {
115
115
  },
116
116
  };
117
117
 
118
- export const WithNoLapisField: StoryObj<TextInputProps> = {
118
+ export const WithNoLapisField: StoryObj<TextFilterProps> = {
119
119
  ...Default,
120
120
  args: {
121
121
  ...Default.args,
@@ -2,7 +2,7 @@ import { type FunctionComponent } from 'preact';
2
2
  import z from 'zod';
3
3
 
4
4
  import { useLapisUrl } from '../LapisUrlContext';
5
- import { TextInputChangedEvent } from './TextInputChangedEvent';
5
+ import { TextFilterChangedEvent } from './TextFilterChangedEvent';
6
6
  import { fetchStringAutocompleteList } from './fetchStringAutocompleteList';
7
7
  import { lapisFilterSchema } from '../../types';
8
8
  import { DownshiftCombobox } from '../components/downshift-combobox';
@@ -17,29 +17,29 @@ const textSelectorPropsSchema = z.object({
17
17
  placeholderText: z.string().optional(),
18
18
  value: z.string().optional(),
19
19
  });
20
- const textInputInnerPropsSchema = textSelectorPropsSchema.extend({ lapisFilter: lapisFilterSchema });
21
- const textInputPropsSchema = textInputInnerPropsSchema.extend({
20
+ const textFilterInnerPropsSchema = textSelectorPropsSchema.extend({ lapisFilter: lapisFilterSchema });
21
+ const textFilterPropsSchema = textFilterInnerPropsSchema.extend({
22
22
  width: z.string(),
23
23
  });
24
24
 
25
- export type TextInputInnerProps = z.infer<typeof textInputInnerPropsSchema>;
26
- export type TextInputProps = z.infer<typeof textInputPropsSchema>;
25
+ export type TextFilterInnerProps = z.infer<typeof textFilterInnerPropsSchema>;
26
+ export type TextFilterProps = z.infer<typeof textFilterPropsSchema>;
27
27
  type TextSelectorProps = z.infer<typeof textSelectorPropsSchema>;
28
28
 
29
- export const TextInput: FunctionComponent<TextInputProps> = (props) => {
29
+ export const TextFilter: FunctionComponent<TextFilterProps> = (props) => {
30
30
  const { width, ...innerProps } = props;
31
31
  const size = { width, height: '3rem' };
32
32
 
33
33
  return (
34
- <ErrorBoundary size={size} layout='horizontal' componentProps={props} schema={textInputPropsSchema}>
34
+ <ErrorBoundary size={size} layout='horizontal' componentProps={props} schema={textFilterPropsSchema}>
35
35
  <ResizeContainer size={size}>
36
- <TextInputInner {...innerProps} />
36
+ <TextFilterInner {...innerProps} />
37
37
  </ResizeContainer>
38
38
  </ErrorBoundary>
39
39
  );
40
40
  };
41
41
 
42
- const TextInputInner: FunctionComponent<TextInputInnerProps> = ({
42
+ const TextFilterInner: FunctionComponent<TextFilterInnerProps> = ({
43
43
  value,
44
44
  lapisField,
45
45
  placeholderText,
@@ -88,7 +88,7 @@ const TextSelector = ({
88
88
  value={initialSelectedItem}
89
89
  filterItemsByInputValue={filterByInputValue}
90
90
  createEvent={(item: SelectItem | null) =>
91
- new TextInputChangedEvent({ [lapisField]: item?.value ?? undefined })
91
+ new TextFilterChangedEvent({ [lapisField]: item?.value ?? undefined })
92
92
  }
93
93
  itemToString={(item: SelectItem | undefined | null) => item?.value ?? ''}
94
94
  placeholderText={placeholderText}
@@ -40,7 +40,6 @@ export const Default: StoryObj<WastewaterMutationsOverTimeProps> = {
40
40
  ...Template,
41
41
  args: {
42
42
  width: '100%',
43
- height: '700px',
44
43
  lapisFilter: {},
45
44
  sequenceType: 'nucleotide',
46
45
  maxNumberOfGridRows: 100,
@@ -22,7 +22,7 @@ const wastewaterMutationOverTimeSchema = z.object({
22
22
  lapisFilter: lapisFilterSchema,
23
23
  sequenceType: sequenceTypeSchema,
24
24
  width: z.string(),
25
- height: z.string(),
25
+ height: z.string().optional(),
26
26
  maxNumberOfGridRows: z.number(),
27
27
  });
28
28
 
@@ -2,7 +2,7 @@ import type { FeatureCollection, GeometryObject } from 'geojson';
2
2
  import { describe, expect, test } from 'vitest';
3
3
 
4
4
  import { computeMapLocationData } from './computeMapLocationData';
5
- import type { GeoJsonFeatureProperties } from '../preact/map/loadMapSource';
5
+ import type { GeoJsonFeatureProperties } from '../preact/sequencesByLocation/loadMapSource';
6
6
 
7
7
  const lapisLocationField = 'locationField';
8
8
 
@@ -1,7 +1,7 @@
1
1
  import type { Feature, FeatureCollection, GeometryObject } from 'geojson';
2
2
 
3
3
  import type { AggregateData } from './queryAggregateData';
4
- import type { GeoJsonFeatureProperties } from '../preact/map/loadMapSource';
4
+ import type { GeoJsonFeatureProperties } from '../preact/sequencesByLocation/loadMapSource';
5
5
 
6
6
  export type FeatureData = { proportion: number; count: number };
7
7
 
@@ -1,6 +1,6 @@
1
1
  import { computeMapLocationData } from './computeMapLocationData';
2
2
  import { queryAggregateData } from './queryAggregateData';
3
- import { loadMapSource, type MapSource } from '../preact/map/loadMapSource';
3
+ import { loadMapSource, type MapSource } from '../preact/sequencesByLocation/loadMapSource';
4
4
  import type { LapisFilter } from '../types';
5
5
 
6
6
  export async function querySequencesByLocationData(
@@ -3,7 +3,7 @@ export {
3
3
  type DateRangeSelectOption,
4
4
  dateRangeOptionPresets,
5
5
  DateRangeOptionChangedEvent,
6
- } from './preact/dateRangeSelector/dateRangeOption';
6
+ } from './preact/dateRangeFilter/dateRangeOption';
7
7
 
8
8
  export {
9
9
  type NamedLapisFilter,
@@ -27,7 +27,7 @@ export type {
27
27
  RelativeGrowthAdvantageProps,
28
28
  } from './preact/relativeGrowthAdvantage/relative-growth-advantage';
29
29
  export type { StatisticsProps } from './preact/statistic/statistics';
30
- export type { MapSource } from './preact/map/loadMapSource';
30
+ export type { MapSource } from './preact/sequencesByLocation/loadMapSource';
31
31
 
32
32
  export type { ConfidenceIntervalMethod } from './preact/shared/charts/confideceInterval';
33
33
 
@@ -35,4 +35,4 @@ export type { AxisMax, YAxisMaxConfig } from './preact/shared/charts/getYAxisMax
35
35
 
36
36
  export { LocationChangedEvent } from './preact/locationFilter/LocationChangedEvent';
37
37
  export { LineageFilterChangedEvent } from './preact/lineageFilter/LineageFilterChangedEvent';
38
- export { TextInputChangedEvent } from './preact/textInput/TextInputChangedEvent';
38
+ export { TextFilterChangedEvent } from './preact/textFilter/TextFilterChangedEvent';
@@ -5,7 +5,10 @@ import { Meta } from '@storybook/blocks';
5
5
  # Size of components
6
6
 
7
7
  All visualization and input components can be provided with a width prop (or height, when applicable) to control the size of the component.
8
- If not provided, the default width or height is used. In most cases, the default is a width of '100%' of its parent container and a predefined height.
8
+ If the width is not provided, the default is used. In most cases, the default is a width of '100%' of its parent container.
9
+ If the height is not set, the component takes up as much space as needed in the vertical direction.
10
+ For table views, this includes showing the complete table.
11
+ For diagrams, a fixed aspect ratio of 2:1 (width/height) is used.
9
12
 
10
13
  Both width and height can be set using the css units (e.g. 'px', 'em', 'rem', '%', 'vh', 'vw', etc.).
11
14
  By using '%', the size of the component can be controlled by the parent component.
@@ -5,27 +5,27 @@ import { html } from 'lit';
5
5
  import { withComponentDocs } from '../../../.storybook/ComponentDocsBlock';
6
6
  import { previewHandles } from '../../../.storybook/preview';
7
7
  import { LAPIS_URL } from '../../constants';
8
- import { type DateRangeSelectorProps } from '../../preact/dateRangeSelector/date-range-selector';
9
- import './gs-date-range-selector';
8
+ import { type DateRangeFilterProps } from '../../preact/dateRangeFilter/date-range-filter';
9
+ import './gs-date-range-filter';
10
10
  import '../gs-app';
11
- import { toYYYYMMDD } from '../../preact/dateRangeSelector/dateConversion';
12
- import { dateRangeOptionPresets } from '../../preact/dateRangeSelector/dateRangeOption';
11
+ import { toYYYYMMDD } from '../../preact/dateRangeFilter/dateConversion';
12
+ import { dateRangeOptionPresets } from '../../preact/dateRangeFilter/dateRangeOption';
13
13
  import { withinShadowRoot } from '../withinShadowRoot.story';
14
14
 
15
15
  const codeExample = String.raw`
16
- <gs-date-range-selector
16
+ <gs-date-range-filter
17
17
  dateRangeOptions='[{ "label": "Year 2021", "dateFrom": "2021-01-01", "dateTo": "2021-12-31" }]'
18
18
  earliestDate="1970-01-01"
19
19
  value="Year 2021"
20
20
  width="100%"
21
21
  lapisDateField="myDateColumn"
22
- ></gs-date-range-selector>`;
22
+ ></gs-date-range-filter>`;
23
23
 
24
24
  const customDateRange = { label: 'CustomDateRange', dateFrom: '2021-01-01', dateTo: '2021-12-31' };
25
25
 
26
- const meta: Meta<Required<DateRangeSelectorProps>> = {
27
- title: 'Input/DateRangeSelector',
28
- component: 'gs-date-range-selector',
26
+ const meta: Meta<Required<DateRangeFilterProps>> = {
27
+ title: 'Input/DateRangeFilter',
28
+ component: 'gs-date-range-filter',
29
29
  parameters: withComponentDocs({
30
30
  actions: {
31
31
  handles: ['gs-date-range-filter-changed', 'gs-date-range-option-changed', ...previewHandles],
@@ -65,6 +65,7 @@ const meta: Meta<Required<DateRangeSelectorProps>> = {
65
65
  dateRangeOptionPresets.lastMonth,
66
66
  dateRangeOptionPresets.last3Months,
67
67
  dateRangeOptionPresets.allTimes,
68
+ { label: '2021', dateFrom: '2021-01-01', dateTo: '2021-12-31' },
68
69
  customDateRange,
69
70
  ],
70
71
  earliestDate: '1970-01-01',
@@ -77,25 +78,69 @@ const meta: Meta<Required<DateRangeSelectorProps>> = {
77
78
 
78
79
  export default meta;
79
80
 
80
- export const Default: StoryObj<Required<DateRangeSelectorProps>> = {
81
+ export const Default: StoryObj<Required<DateRangeFilterProps>> = {
81
82
  render: (args) =>
82
83
  html` <gs-app lapis="${LAPIS_URL}">
83
84
  <div class="max-w-screen-lg">
84
- <gs-date-range-selector
85
+ <gs-date-range-filter
85
86
  .dateRangeOptions=${args.dateRangeOptions}
86
87
  .earliestDate=${args.earliestDate}
87
88
  .value=${args.value}
88
89
  .width=${args.width}
89
90
  .lapisDateField=${args.lapisDateField}
90
- ></gs-date-range-selector>
91
+ ></gs-date-range-filter>
91
92
  </div>
92
93
  </gs-app>`,
93
94
  };
94
95
 
95
- export const FiresEvents: StoryObj<Required<DateRangeSelectorProps>> = {
96
+ export const TestRenderAttributesInHtmlInsteadOfUsingPropertyExpression: StoryObj<Required<DateRangeFilterProps>> = {
97
+ render: (args) =>
98
+ html` <gs-app lapis="${LAPIS_URL}">
99
+ <div class="max-w-screen-lg">
100
+ <gs-date-range-filter
101
+ .dateRangeOptions=${args.dateRangeOptions}
102
+ earliestDate="${args.earliestDate}"
103
+ value="${args.value}"
104
+ width="${args.width}"
105
+ lapisDateField="${args.lapisDateField}"
106
+ ></gs-date-range-filter>
107
+ </div>
108
+ </gs-app>`,
109
+ play: async ({ canvasElement }) => {
110
+ const canvas = await withinShadowRoot(canvasElement, 'gs-date-range-filter');
111
+
112
+ await waitFor(async () => {
113
+ await expect(selectField(canvas)).toHaveValue('Last month');
114
+ });
115
+ },
116
+ argTypes: {
117
+ value: {
118
+ control: {
119
+ type: 'text',
120
+ },
121
+ },
122
+ },
123
+ };
124
+
125
+ export const TestSettingANumericValueIsTreatedAsString: StoryObj<Required<DateRangeFilterProps>> = {
126
+ ...TestRenderAttributesInHtmlInsteadOfUsingPropertyExpression,
127
+ args: {
128
+ ...TestRenderAttributesInHtmlInsteadOfUsingPropertyExpression.args,
129
+ value: '2021',
130
+ },
131
+ play: async ({ canvasElement }) => {
132
+ const canvas = await withinShadowRoot(canvasElement, 'gs-date-range-filter');
133
+
134
+ await waitFor(async () => {
135
+ await expect(selectField(canvas)).toHaveValue('2021');
136
+ });
137
+ },
138
+ };
139
+
140
+ export const FiresEvents: StoryObj<Required<DateRangeFilterProps>> = {
96
141
  ...Default,
97
142
  play: async ({ canvasElement, step }) => {
98
- const canvas = await withinShadowRoot(canvasElement, 'gs-date-range-selector');
143
+ const canvas = await withinShadowRoot(canvasElement, 'gs-date-range-filter');
99
144
 
100
145
  const filterChangedListenerMock = fn();
101
146
  const optionChangedListenerMock = fn();