@genspectrum/dashboard-components 0.6.19 → 0.7.1

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 (31) hide show
  1. package/custom-elements.json +18 -18
  2. package/dist/assets/mutationOverTimeWorker-BOCXtKzd.js.map +1 -0
  3. package/dist/dashboard-components.js +296 -302
  4. package/dist/dashboard-components.js.map +1 -1
  5. package/dist/genspectrum-components.d.ts +98 -48
  6. package/package.json +1 -3
  7. package/src/index.ts +1 -0
  8. package/src/preact/aggregatedData/aggregate.tsx +41 -33
  9. package/src/preact/dateRangeSelector/computeInitialValues.spec.ts +53 -38
  10. package/src/preact/dateRangeSelector/computeInitialValues.ts +17 -23
  11. package/src/preact/dateRangeSelector/date-range-selector.stories.tsx +46 -32
  12. package/src/preact/dateRangeSelector/date-range-selector.tsx +24 -26
  13. package/src/preact/dateRangeSelector/dateRangeOption.ts +65 -0
  14. package/src/preact/dateRangeSelector/selectableOptions.ts +17 -66
  15. package/src/preact/mutationComparison/mutation-comparison.tsx +32 -34
  16. package/src/preact/mutations/mutations.tsx +63 -56
  17. package/src/preact/mutationsOverTime/MutationOverTimeData.ts +20 -0
  18. package/src/preact/mutationsOverTime/getFilteredMutationsOverTime.spec.ts +2 -3
  19. package/src/preact/mutationsOverTime/getFilteredMutationsOverTimeData.ts +2 -2
  20. package/src/preact/mutationsOverTime/mutations-over-time-grid.tsx +3 -3
  21. package/src/preact/mutationsOverTime/mutations-over-time.tsx +40 -43
  22. package/src/preact/numberSequencesOverTime/number-sequences-over-time.tsx +46 -64
  23. package/src/preact/relativeGrowthAdvantage/relative-growth-advantage.tsx +29 -36
  24. package/src/query/queryMutationsOverTime.ts +3 -5
  25. package/src/utils/map2d.spec.ts +52 -13
  26. package/src/utils/map2d.ts +3 -4
  27. package/src/web-components/input/gs-date-range-selector.stories.ts +16 -28
  28. package/src/web-components/input/gs-date-range-selector.tsx +17 -32
  29. package/standalone-bundle/dashboard-components.js +14322 -15115
  30. package/standalone-bundle/dashboard-components.js.map +1 -1
  31. package/dist/assets/mutationOverTimeWorker-BdzqDqvO.js.map +0 -1
@@ -1,36 +1,30 @@
1
- import {
2
- type CustomSelectOption,
3
- getDatesForSelectorValue,
4
- getSelectableOptions,
5
- PRESET_VALUE_CUSTOM,
6
- PRESET_VALUE_LAST_6_MONTHS,
7
- type PresetOptionValues,
8
- } from './selectableOptions';
1
+ import { type DateRangeOption } from './dateRangeOption';
2
+ import { getDatesForSelectorValue, getSelectableOptions } from './selectableOptions';
9
3
  import { UserFacingError } from '../components/error-display';
10
4
 
11
- export function computeInitialValues<CustomLabel extends string>(
12
- initialValue: PresetOptionValues | CustomLabel | undefined,
5
+ export function computeInitialValues(
6
+ initialValue: string | undefined,
13
7
  initialDateFrom: string | undefined,
14
8
  initialDateTo: string | undefined,
15
9
  earliestDate: string,
16
- customSelectOptions: CustomSelectOption<CustomLabel>[],
10
+ dateRangeOptions: DateRangeOption[],
17
11
  ): {
18
- initialSelectedDateRange: CustomLabel | PresetOptionValues;
12
+ initialSelectedDateRange: string | undefined;
19
13
  initialSelectedDateFrom: Date;
20
14
  initialSelectedDateTo: Date;
21
15
  } {
22
16
  if (isUndefinedOrEmpty(initialDateFrom) && isUndefinedOrEmpty(initialDateTo)) {
23
- const selectableOptions = getSelectableOptions(customSelectOptions);
24
- const initialSelectedDateRange =
25
- initialValue !== undefined && selectableOptions.some((option) => option.value === initialValue)
26
- ? initialValue
27
- : PRESET_VALUE_LAST_6_MONTHS;
17
+ const selectableOptions = getSelectableOptions(dateRangeOptions);
18
+ const initialSelectedDateRange = selectableOptions.find((option) => option.value === initialValue)?.value;
28
19
 
29
- const { dateFrom, dateTo } = getDatesForSelectorValue(
30
- initialSelectedDateRange,
31
- customSelectOptions,
32
- earliestDate,
33
- );
20
+ if (initialValue !== undefined && initialSelectedDateRange === undefined) {
21
+ throw new UserFacingError(
22
+ 'Invalid initialValue',
23
+ `Invalid initialValue "${initialValue}", It must be one of ${selectableOptions.map((option) => `'${option.value}'`).join(', ')}`,
24
+ );
25
+ }
26
+
27
+ const { dateFrom, dateTo } = getDatesForSelectorValue(initialSelectedDateRange, dateRangeOptions, earliestDate);
34
28
 
35
29
  return {
36
30
  initialSelectedDateRange,
@@ -62,7 +56,7 @@ export function computeInitialValues<CustomLabel extends string>(
62
56
  }
63
57
 
64
58
  return {
65
- initialSelectedDateRange: PRESET_VALUE_CUSTOM,
59
+ initialSelectedDateRange: undefined,
66
60
  initialSelectedDateFrom,
67
61
  initialSelectedDateTo,
68
62
  };
@@ -1,24 +1,16 @@
1
1
  import { type Meta, type StoryObj } from '@storybook/preact';
2
- import { expect, waitFor, within } from '@storybook/test';
2
+ import { expect, userEvent, waitFor, within } from '@storybook/test';
3
3
  import dayjs from 'dayjs/esm';
4
4
 
5
5
  import { DateRangeSelector, type DateRangeSelectorProps } from './date-range-selector';
6
- import {
7
- PRESET_VALUE_ALL_TIMES,
8
- PRESET_VALUE_CUSTOM,
9
- PRESET_VALUE_LAST_2_MONTHS,
10
- PRESET_VALUE_LAST_2_WEEKS,
11
- PRESET_VALUE_LAST_3_MONTHS,
12
- PRESET_VALUE_LAST_6_MONTHS,
13
- PRESET_VALUE_LAST_MONTH,
14
- } from './selectableOptions';
15
6
  import { previewHandles } from '../../../.storybook/preview';
16
7
  import { LAPIS_URL } from '../../constants';
17
8
  import { LapisUrlContext } from '../LapisUrlContext';
9
+ import { dateRangeOptionPresets } from './dateRangeOption';
18
10
 
19
11
  const earliestDate = '1970-01-01';
20
12
 
21
- const meta: Meta<DateRangeSelectorProps<'CustomDateRange'>> = {
13
+ const meta: Meta<DateRangeSelectorProps> = {
22
14
  title: 'Input/DateRangeSelector',
23
15
  component: DateRangeSelector,
24
16
  parameters: {
@@ -32,18 +24,9 @@ const meta: Meta<DateRangeSelectorProps<'CustomDateRange'>> = {
32
24
  control: {
33
25
  type: 'select',
34
26
  },
35
- options: [
36
- PRESET_VALUE_CUSTOM,
37
- PRESET_VALUE_ALL_TIMES,
38
- PRESET_VALUE_LAST_2_WEEKS,
39
- PRESET_VALUE_LAST_MONTH,
40
- PRESET_VALUE_LAST_2_MONTHS,
41
- PRESET_VALUE_LAST_3_MONTHS,
42
- PRESET_VALUE_LAST_6_MONTHS,
43
- 'CustomDateRange',
44
- ],
27
+ options: [dateRangeOptionPresets.lastMonth.label, dateRangeOptionPresets.allTimes.label, 'CustomDateRange'],
45
28
  },
46
- customSelectOptions: {
29
+ dateRangeOptions: {
47
30
  control: {
48
31
  type: 'object',
49
32
  },
@@ -60,9 +43,17 @@ const meta: Meta<DateRangeSelectorProps<'CustomDateRange'>> = {
60
43
  },
61
44
  },
62
45
  args: {
63
- customSelectOptions: [{ label: 'CustomDateRange', dateFrom: '2021-01-01', dateTo: '2021-12-31' }],
46
+ dateRangeOptions: [
47
+ dateRangeOptionPresets.lastMonth,
48
+ dateRangeOptionPresets.allTimes,
49
+ {
50
+ label: 'CustomDateRange',
51
+ dateFrom: '2021-01-01',
52
+ dateTo: '2021-12-31',
53
+ },
54
+ ],
64
55
  earliestDate,
65
- initialValue: PRESET_VALUE_LAST_3_MONTHS,
56
+ initialValue: dateRangeOptionPresets.lastMonth.label,
66
57
  dateColumn: 'aDateColumn',
67
58
  width: '100%',
68
59
  initialDateFrom: '',
@@ -72,11 +63,11 @@ const meta: Meta<DateRangeSelectorProps<'CustomDateRange'>> = {
72
63
 
73
64
  export default meta;
74
65
 
75
- export const Primary: StoryObj<DateRangeSelectorProps<'CustomDateRange'>> = {
66
+ export const Primary: StoryObj<DateRangeSelectorProps> = {
76
67
  render: (args) => (
77
68
  <LapisUrlContext.Provider value={LAPIS_URL}>
78
69
  <DateRangeSelector
79
- customSelectOptions={args.customSelectOptions}
70
+ dateRangeOptions={args.dateRangeOptions}
80
71
  earliestDate={args.earliestDate}
81
72
  initialValue={args.initialValue}
82
73
  initialDateFrom={args.initialDateFrom}
@@ -88,7 +79,7 @@ export const Primary: StoryObj<DateRangeSelectorProps<'CustomDateRange'>> = {
88
79
  ),
89
80
  };
90
81
 
91
- export const SetCorrectInitialValues: StoryObj<DateRangeSelectorProps<'CustomDateRange'>> = {
82
+ export const SetCorrectInitialValues: StoryObj<DateRangeSelectorProps> = {
92
83
  ...Primary,
93
84
  args: {
94
85
  ...Primary.args,
@@ -111,7 +102,7 @@ export const SetCorrectInitialValues: StoryObj<DateRangeSelectorProps<'CustomDat
111
102
 
112
103
  const initialDateFrom = '2000-01-01';
113
104
 
114
- export const SetCorrectInitialDateFrom: StoryObj<DateRangeSelectorProps<'CustomDateRange'>> = {
105
+ export const SetCorrectInitialDateFrom: StoryObj<DateRangeSelectorProps> = {
115
106
  ...Primary,
116
107
  args: {
117
108
  ...Primary.args,
@@ -125,7 +116,7 @@ export const SetCorrectInitialDateFrom: StoryObj<DateRangeSelectorProps<'CustomD
125
116
  const selectField = () => canvas.getByRole('combobox');
126
117
 
127
118
  await waitFor(() => {
128
- expect(selectField()).toHaveValue(PRESET_VALUE_CUSTOM);
119
+ expect(selectField()).toHaveValue('Custom');
129
120
  expect(dateFrom()).toHaveValue(initialDateFrom);
130
121
  expect(dateTo()).toHaveValue(dayjs().format('YYYY-MM-DD'));
131
122
  });
@@ -134,7 +125,7 @@ export const SetCorrectInitialDateFrom: StoryObj<DateRangeSelectorProps<'CustomD
134
125
 
135
126
  const initialDateTo = '2000-01-01';
136
127
 
137
- export const SetCorrectInitialDateTo: StoryObj<DateRangeSelectorProps<'CustomDateRange'>> = {
128
+ export const SetCorrectInitialDateTo: StoryObj<DateRangeSelectorProps> = {
138
129
  ...Primary,
139
130
  args: {
140
131
  ...Primary.args,
@@ -148,14 +139,37 @@ export const SetCorrectInitialDateTo: StoryObj<DateRangeSelectorProps<'CustomDat
148
139
  const selectField = () => canvas.getByRole('combobox');
149
140
 
150
141
  await waitFor(() => {
151
- expect(selectField()).toHaveValue(PRESET_VALUE_CUSTOM);
142
+ expect(selectField()).toHaveValue('Custom');
152
143
  expect(dateFrom()).toHaveValue(earliestDate);
153
144
  expect(dateTo()).toHaveValue(initialDateTo);
154
145
  });
155
146
  },
156
147
  };
157
148
 
158
- export const HandlesInvalidInitialDateFrom: StoryObj<DateRangeSelectorProps<'CustomDateRange'>> = {
149
+ export const ChangingDateSetsOptionToCustom: StoryObj<DateRangeSelectorProps> = {
150
+ ...Primary,
151
+ play: async ({ canvasElement }) => {
152
+ const canvas = within(canvasElement);
153
+
154
+ const dateFrom = () => canvas.getByPlaceholderText('Date from');
155
+ const dateTo = () => canvas.getByPlaceholderText('Date to');
156
+ const selectField = () => canvas.getByRole('combobox');
157
+
158
+ await waitFor(() => {
159
+ expect(selectField()).toHaveValue('Last month');
160
+ });
161
+
162
+ await userEvent.type(dateFrom(), '{backspace>12}');
163
+ await userEvent.type(dateFrom(), '2000-01-01');
164
+ await userEvent.click(dateTo());
165
+
166
+ await waitFor(() => {
167
+ expect(selectField()).toHaveValue('Custom');
168
+ });
169
+ },
170
+ };
171
+
172
+ export const HandlesInvalidInitialDateFrom: StoryObj<DateRangeSelectorProps> = {
159
173
  ...Primary,
160
174
  args: {
161
175
  ...Primary.args,
@@ -4,33 +4,28 @@ import { useEffect, useRef, useState } from 'preact/hooks';
4
4
 
5
5
  import { computeInitialValues } from './computeInitialValues';
6
6
  import { toYYYYMMDD } from './dateConversion';
7
- import {
8
- type CustomSelectOption,
9
- getDatesForSelectorValue,
10
- getSelectableOptions,
11
- type PresetOptionValues,
12
- } from './selectableOptions';
7
+ import { type DateRangeOption } from './dateRangeOption';
8
+ import { getDatesForSelectorValue, getSelectableOptions } from './selectableOptions';
13
9
  import { ErrorBoundary } from '../components/error-boundary';
14
10
  import { Select } from '../components/select';
15
11
  import type { ScaleType } from '../shared/charts/getYAxisScale';
16
12
 
17
- export interface DateRangeSelectorProps<CustomLabel extends string> extends DateRangeSelectorPropsInner<CustomLabel> {
13
+ const customOption = 'Custom';
14
+
15
+ export interface DateRangeSelectorProps extends DateRangeSelectorPropsInner {
18
16
  width: string;
19
17
  }
20
18
 
21
- export interface DateRangeSelectorPropsInner<CustomLabel extends string> {
22
- customSelectOptions: CustomSelectOption<CustomLabel>[];
19
+ export interface DateRangeSelectorPropsInner {
20
+ dateRangeOptions: DateRangeOption[];
23
21
  earliestDate: string;
24
- initialValue: PresetOptionValues | CustomLabel;
22
+ initialValue: string | undefined;
25
23
  initialDateFrom: string;
26
24
  initialDateTo: string;
27
25
  dateColumn: string;
28
26
  }
29
27
 
30
- export const DateRangeSelector = <CustomLabel extends string>({
31
- width,
32
- ...innerProps
33
- }: DateRangeSelectorProps<CustomLabel>) => {
28
+ export const DateRangeSelector = ({ width, ...innerProps }: DateRangeSelectorProps) => {
34
29
  const size = { width, height: '3rem' };
35
30
 
36
31
  return (
@@ -42,20 +37,20 @@ export const DateRangeSelector = <CustomLabel extends string>({
42
37
  );
43
38
  };
44
39
 
45
- export const DateRangeSelectorInner = <CustomLabel extends string>({
46
- customSelectOptions,
40
+ export const DateRangeSelectorInner = ({
41
+ dateRangeOptions,
47
42
  earliestDate = '1900-01-01',
48
43
  initialValue,
49
44
  dateColumn,
50
45
  initialDateFrom,
51
46
  initialDateTo,
52
- }: DateRangeSelectorPropsInner<CustomLabel>) => {
47
+ }: DateRangeSelectorPropsInner) => {
53
48
  const initialValues = computeInitialValues(
54
49
  initialValue,
55
50
  initialDateFrom,
56
51
  initialDateTo,
57
52
  earliestDate,
58
- customSelectOptions,
53
+ dateRangeOptions,
59
54
  );
60
55
 
61
56
  const fromDatePickerRef = useRef<HTMLInputElement>(null);
@@ -64,7 +59,7 @@ export const DateRangeSelectorInner = <CustomLabel extends string>({
64
59
  const [dateFromPicker, setDateFromPicker] = useState<flatpickr.Instance | null>(null);
65
60
  const [dateToPicker, setDateToPicker] = useState<flatpickr.Instance | null>(null);
66
61
 
67
- const [selectedDateRange, setSelectedDateRange] = useState<CustomLabel | PresetOptionValues>(
62
+ const [selectedDateRange, setSelectedDateRange] = useState<string | undefined>(
68
63
  initialValues.initialSelectedDateRange,
69
64
  );
70
65
 
@@ -104,10 +99,10 @@ export const DateRangeSelectorInner = <CustomLabel extends string>({
104
99
  // eslint-disable-next-line react-hooks/exhaustive-deps
105
100
  }, [fromDatePickerRef, toDatePickerRef]);
106
101
 
107
- const onSelectChange = (value: CustomLabel | PresetOptionValues) => {
102
+ const onSelectChange = (value: string) => {
108
103
  setSelectedDateRange(value);
109
104
 
110
- const dateRange = getDatesForSelectorValue(value, customSelectOptions, earliestDate);
105
+ const dateRange = getDatesForSelectorValue(value, dateRangeOptions, earliestDate);
111
106
 
112
107
  dateToPicker?.set('minDate', dateRange.dateFrom);
113
108
  dateFromPicker?.set('maxDate', dateRange.dateTo);
@@ -130,7 +125,7 @@ export const DateRangeSelectorInner = <CustomLabel extends string>({
130
125
 
131
126
  selectedDates.dateFrom = dateFromPicker?.selectedDates[0] || new Date();
132
127
  dateToPicker?.set('minDate', dateFromPicker?.selectedDates[0]);
133
- setSelectedDateRange('custom');
128
+ setSelectedDateRange(customOption);
134
129
 
135
130
  submit();
136
131
  };
@@ -142,7 +137,7 @@ export const DateRangeSelectorInner = <CustomLabel extends string>({
142
137
 
143
138
  selectedDates.dateTo = dateToPicker?.selectedDates[0] || new Date();
144
139
  dateFromPicker?.set('maxDate', dateToPicker?.selectedDates[0]);
145
- setSelectedDateRange('custom');
140
+ setSelectedDateRange(customOption);
146
141
 
147
142
  submit();
148
143
  };
@@ -168,14 +163,17 @@ export const DateRangeSelectorInner = <CustomLabel extends string>({
168
163
  return (
169
164
  <div class='flex flex-wrap' ref={divRef}>
170
165
  <Select
171
- items={getSelectableOptions(customSelectOptions)}
172
- selected={selectedDateRange}
166
+ items={[
167
+ ...getSelectableOptions(dateRangeOptions),
168
+ { label: customOption, value: customOption, disabled: true },
169
+ ]}
170
+ selected={selectedDateRange ?? customOption}
173
171
  selectStyle='select-bordered rounded-none flex-grow min-w-[7.5rem]'
174
172
  onChange={(event: Event) => {
175
173
  event.preventDefault();
176
174
  const select = event.target as HTMLSelectElement;
177
175
  const value = select.value as ScaleType;
178
- onSelectChange(value as CustomLabel | PresetOptionValues);
176
+ onSelectChange(value);
179
177
  }}
180
178
  />
181
179
  <div className={'flex flex-wrap flex-grow'}>
@@ -0,0 +1,65 @@
1
+ import { toYYYYMMDD } from './dateConversion';
2
+
3
+ /**
4
+ * A date range option that can be used in the `gs-date-range-selector` component.
5
+ */
6
+ export type DateRangeOption = {
7
+ /** The label of the date range option that will be shown to the user */
8
+ label: string;
9
+ /**
10
+ * The start date of the date range in the format `YYYY-MM-DD`.
11
+ * If not set, the date range selector will default to the `earliestDate` property.
12
+ */
13
+ dateFrom?: string;
14
+ /**
15
+ * The end date of the date range in the format `YYYY-MM-DD`.
16
+ * If not set, the date range selector will default to the current date.
17
+ */
18
+ dateTo?: string;
19
+ };
20
+
21
+ const today = new Date();
22
+
23
+ const twoWeeksAgo = new Date();
24
+ twoWeeksAgo.setDate(today.getDate() - 14);
25
+
26
+ const lastMonth = new Date(today);
27
+ lastMonth.setMonth(today.getMonth() - 1);
28
+
29
+ const last2Months = new Date(today);
30
+ last2Months.setMonth(today.getMonth() - 2);
31
+
32
+ const last3Months = new Date(today);
33
+ last3Months.setMonth(today.getMonth() - 3);
34
+
35
+ const last6Months = new Date(today);
36
+ last6Months.setMonth(today.getMonth() - 6);
37
+
38
+ /**
39
+ * Presets for the `gs-date-range-selector` component that can be used as `dateRangeOptions`.
40
+ */
41
+ export const dateRangeOptionPresets = {
42
+ last2Weeks: {
43
+ label: 'Last 2 weeks',
44
+ dateFrom: toYYYYMMDD(twoWeeksAgo),
45
+ },
46
+ lastMonth: {
47
+ label: 'Last month',
48
+ dateFrom: toYYYYMMDD(lastMonth),
49
+ },
50
+ last2Months: {
51
+ label: 'Last 2 months',
52
+ dateFrom: toYYYYMMDD(last2Months),
53
+ },
54
+ last3Months: {
55
+ label: 'Last 3 months',
56
+ dateFrom: toYYYYMMDD(last3Months),
57
+ },
58
+ last6Months: {
59
+ label: 'Last 6 months',
60
+ dateFrom: toYYYYMMDD(last6Months),
61
+ },
62
+ allTimes: {
63
+ label: 'All times',
64
+ },
65
+ } satisfies Record<string, DateRangeOption>;
@@ -1,79 +1,30 @@
1
- export const PRESET_VALUE_CUSTOM = 'custom';
2
- export const PRESET_VALUE_ALL_TIMES = 'allTimes';
3
- export const PRESET_VALUE_LAST_2_WEEKS = 'last2Weeks';
4
- export const PRESET_VALUE_LAST_MONTH = 'lastMonth';
5
- export const PRESET_VALUE_LAST_2_MONTHS = 'last2Months';
6
- export const PRESET_VALUE_LAST_3_MONTHS = 'last3Months';
7
- export const PRESET_VALUE_LAST_6_MONTHS = 'last6Months';
1
+ import { type DateRangeOption } from './dateRangeOption';
8
2
 
9
- export const presets = {
10
- [PRESET_VALUE_CUSTOM]: { label: 'Custom' },
11
- [PRESET_VALUE_ALL_TIMES]: { label: 'All times' },
12
- [PRESET_VALUE_LAST_2_WEEKS]: { label: 'Last 2 weeks' },
13
- [PRESET_VALUE_LAST_MONTH]: { label: 'Last month' },
14
- [PRESET_VALUE_LAST_2_MONTHS]: { label: 'Last 2 months' },
15
- [PRESET_VALUE_LAST_3_MONTHS]: { label: 'Last 3 months' },
16
- [PRESET_VALUE_LAST_6_MONTHS]: { label: 'Last 6 months' },
17
- };
18
-
19
- export type PresetOptionValues = keyof typeof presets;
20
-
21
- export type CustomSelectOption<CustomLabel extends string> = { label: CustomLabel; dateFrom: string; dateTo: string };
22
-
23
- export const getSelectableOptions = <Label extends string>(customSelectOptions: CustomSelectOption<Label>[]) => {
24
- const presetOptions = Object.entries(presets).map(([key, value]) => {
25
- return { label: value.label, value: key };
26
- });
27
-
28
- const customOptions = customSelectOptions.map((customSelectOption) => {
3
+ export const getSelectableOptions = (dateRangeOptions: DateRangeOption[]) => {
4
+ return dateRangeOptions.map((customSelectOption) => {
29
5
  return { label: customSelectOption.label, value: customSelectOption.label };
30
6
  });
31
-
32
- return [...presetOptions, ...customOptions];
33
7
  };
34
8
 
35
- export const getDatesForSelectorValue = <Label extends string>(
36
- selectorValue: string,
37
- customSelectOptions: CustomSelectOption<Label>[],
9
+ export const getDatesForSelectorValue = (
10
+ initialSelectedDateRange: string | undefined,
11
+ dateRangeOptions: DateRangeOption[],
38
12
  earliestDate: string,
39
13
  ) => {
40
14
  const today = new Date();
15
+ const defaultDates = { dateFrom: new Date(earliestDate), dateTo: today };
41
16
 
42
- const customSelectOption = customSelectOptions.find((option) => option.label === selectorValue);
43
- if (customSelectOption) {
44
- return { dateFrom: new Date(customSelectOption.dateFrom), dateTo: new Date(customSelectOption.dateTo) };
17
+ if (initialSelectedDateRange === undefined) {
18
+ return defaultDates;
45
19
  }
46
20
 
47
- switch (selectorValue) {
48
- case PRESET_VALUE_LAST_2_WEEKS: {
49
- const twoWeeksAgo = new Date(today);
50
- twoWeeksAgo.setDate(today.getDate() - 14);
51
- return { dateFrom: twoWeeksAgo, dateTo: today };
52
- }
53
- case PRESET_VALUE_LAST_MONTH: {
54
- const lastMonth = new Date(today);
55
- lastMonth.setMonth(today.getMonth() - 1);
56
- return { dateFrom: lastMonth, dateTo: today };
57
- }
58
- case PRESET_VALUE_LAST_2_MONTHS: {
59
- const twoMonthsAgo = new Date(today);
60
- twoMonthsAgo.setMonth(today.getMonth() - 2);
61
- return { dateFrom: twoMonthsAgo, dateTo: today };
62
- }
63
- case PRESET_VALUE_LAST_3_MONTHS: {
64
- const threeMonthsAgo = new Date(today);
65
- threeMonthsAgo.setMonth(today.getMonth() - 3);
66
- return { dateFrom: threeMonthsAgo, dateTo: today };
67
- }
68
- case PRESET_VALUE_LAST_6_MONTHS: {
69
- const sixMonthsAgo = new Date(today);
70
- sixMonthsAgo.setMonth(today.getMonth() - 6);
71
- return { dateFrom: sixMonthsAgo, dateTo: today };
72
- }
73
- case PRESET_VALUE_ALL_TIMES: {
74
- return { dateFrom: new Date(earliestDate), dateTo: today };
75
- }
76
- default:
77
- return { dateFrom: today, dateTo: today };
21
+ const dateRangeOption = dateRangeOptions.find((option) => option.label === initialSelectedDateRange);
22
+ if (dateRangeOption) {
23
+ return {
24
+ dateFrom: new Date(dateRangeOption.dateFrom ?? earliestDate),
25
+ dateTo: new Date(dateRangeOption.dateTo ?? today),
26
+ };
78
27
  }
28
+
29
+ return defaultDates;
79
30
  };
@@ -11,7 +11,7 @@ import { CsvDownloadButton } from '../components/csv-download-button';
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 from '../components/info';
14
+ import Info, { InfoComponentCode, InfoHeadline1, InfoParagraph } from '../components/info';
15
15
  import { LoadingDisplay } from '../components/loading-display';
16
16
  import { type DisplayedMutationType, MutationTypeSelector } from '../components/mutation-type-selector';
17
17
  import { NoDataDisplay } from '../components/no-data-display';
@@ -24,36 +24,30 @@ import { useQuery } from '../useQuery';
24
24
 
25
25
  export type View = 'table' | 'venn';
26
26
 
27
- export interface MutationComparisonProps extends MutationComparisonInnerProps {
27
+ export interface MutationComparisonProps {
28
28
  width: string;
29
29
  height: string;
30
- }
31
-
32
- export interface MutationComparisonInnerProps {
33
30
  lapisFilters: NamedLapisFilter[];
34
31
  sequenceType: SequenceType;
35
32
  views: View[];
36
33
  pageSize: boolean | number;
37
34
  }
38
35
 
39
- export const MutationComparison: FunctionComponent<MutationComparisonProps> = ({ width, height, ...innerProps }) => {
36
+ export const MutationComparison: FunctionComponent<MutationComparisonProps> = (componentProps) => {
37
+ const { width, height } = componentProps;
40
38
  const size = { height, width };
41
39
 
42
40
  return (
43
41
  <ErrorBoundary size={size}>
44
42
  <ResizeContainer size={size}>
45
- <MutationComparisonInner {...innerProps} />
43
+ <MutationComparisonInner {...componentProps} />
46
44
  </ResizeContainer>
47
45
  </ErrorBoundary>
48
46
  );
49
47
  };
50
48
 
51
- export const MutationComparisonInner: FunctionComponent<MutationComparisonInnerProps> = ({
52
- lapisFilters,
53
- sequenceType,
54
- views,
55
- pageSize,
56
- }) => {
49
+ export const MutationComparisonInner: FunctionComponent<MutationComparisonProps> = (componentProps) => {
50
+ const { lapisFilters, sequenceType } = componentProps;
57
51
  const lapis = useContext(LapisUrlContext);
58
52
 
59
53
  const { data, error, isLoading } = useQuery(async () => {
@@ -72,35 +66,21 @@ export const MutationComparisonInner: FunctionComponent<MutationComparisonInnerP
72
66
  return <NoDataDisplay />;
73
67
  }
74
68
 
75
- return (
76
- <MutationComparisonTabs
77
- data={data.mutationData}
78
- sequenceType={sequenceType}
79
- views={views}
80
- pageSize={pageSize}
81
- />
82
- );
69
+ return <MutationComparisonTabs data={data.mutationData} originalComponentProps={componentProps} />;
83
70
  };
84
71
 
85
72
  type MutationComparisonTabsProps = {
86
73
  data: MutationData[];
87
- views: View[];
88
- sequenceType: SequenceType;
89
- pageSize: boolean | number;
74
+ originalComponentProps: MutationComparisonProps;
90
75
  };
91
76
 
92
- const MutationComparisonTabs: FunctionComponent<MutationComparisonTabsProps> = ({
93
- data,
94
- views,
95
- sequenceType,
96
- pageSize,
97
- }) => {
77
+ const MutationComparisonTabs: FunctionComponent<MutationComparisonTabsProps> = ({ data, originalComponentProps }) => {
98
78
  const [proportionInterval, setProportionInterval] = useState({ min: 0.5, max: 1 });
99
79
  const [displayedMutationTypes, setDisplayedMutationTypes] = useState<DisplayedMutationType[]>([
100
80
  { label: 'Substitutions', checked: true, type: 'substitution' },
101
81
  { label: 'Deletions', checked: true, type: 'deletion' },
102
82
  ]);
103
- const [displayedSegments, setDisplayedSegments] = useDisplayedSegments(sequenceType);
83
+ const [displayedSegments, setDisplayedSegments] = useDisplayedSegments(originalComponentProps.sequenceType);
104
84
 
105
85
  const filteredData = useMemo(
106
86
  () => filterMutationData(data, displayedSegments, displayedMutationTypes),
@@ -116,7 +96,7 @@ const MutationComparisonTabs: FunctionComponent<MutationComparisonTabsProps> = (
116
96
  <MutationComparisonTable
117
97
  data={{ content: filteredData }}
118
98
  proportionInterval={proportionInterval}
119
- pageSize={pageSize}
99
+ pageSize={originalComponentProps.pageSize}
120
100
  />
121
101
  ),
122
102
  };
@@ -133,7 +113,7 @@ const MutationComparisonTabs: FunctionComponent<MutationComparisonTabsProps> = (
133
113
  }
134
114
  };
135
115
 
136
- const tabs = views.map((view) => getTab(view));
116
+ const tabs = originalComponentProps.views.map((view) => getTab(view));
137
117
 
138
118
  return (
139
119
  <Tabs
@@ -147,6 +127,7 @@ const MutationComparisonTabs: FunctionComponent<MutationComparisonTabsProps> = (
147
127
  filteredData={filteredData}
148
128
  proportionInterval={proportionInterval}
149
129
  setProportionInterval={setProportionInterval}
130
+ originalComponentProps={originalComponentProps}
150
131
  />
151
132
  }
152
133
  />
@@ -161,6 +142,7 @@ type ToolbarProps = {
161
142
  filteredData: MutationData[];
162
143
  proportionInterval: ProportionInterval;
163
144
  setProportionInterval: Dispatch<StateUpdater<ProportionInterval>>;
145
+ originalComponentProps: MutationComparisonProps;
164
146
  };
165
147
 
166
148
  const Toolbar: FunctionComponent<ToolbarProps> = ({
@@ -171,6 +153,7 @@ const Toolbar: FunctionComponent<ToolbarProps> = ({
171
153
  filteredData,
172
154
  proportionInterval,
173
155
  setProportionInterval,
156
+ originalComponentProps,
174
157
  }) => {
175
158
  return (
176
159
  <>
@@ -189,8 +172,23 @@ const Toolbar: FunctionComponent<ToolbarProps> = ({
189
172
  getData={() => getMutationComparisonTableData({ content: filteredData }, proportionInterval)}
190
173
  filename='mutation_comparison.csv'
191
174
  />
192
- <Info>Info for mutation comparison</Info>
175
+ <MutationComparisonInfo originalComponentProps={originalComponentProps} />
193
176
  <Fullscreen />
194
177
  </>
195
178
  );
196
179
  };
180
+
181
+ type MutationComparisonInfoProps = {
182
+ originalComponentProps: MutationComparisonProps;
183
+ };
184
+
185
+ const MutationComparisonInfo: FunctionComponent<MutationComparisonInfoProps> = ({ originalComponentProps }) => {
186
+ const lapis = useContext(LapisUrlContext);
187
+ return (
188
+ <Info>
189
+ <InfoHeadline1>Info for mutation comparison</InfoHeadline1>
190
+ <InfoParagraph>TODO: https://github.com/GenSpectrum/dashboard-components/issues/465</InfoParagraph>
191
+ <InfoComponentCode componentName='mutation-comparison' params={originalComponentProps} lapisUrl={lapis} />
192
+ </Info>
193
+ );
194
+ };