@genspectrum/dashboard-components 0.6.19 → 0.7.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 (26) 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 +96 -99
  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/dateRangeSelector/computeInitialValues.spec.ts +53 -38
  9. package/src/preact/dateRangeSelector/computeInitialValues.ts +17 -23
  10. package/src/preact/dateRangeSelector/date-range-selector.stories.tsx +46 -32
  11. package/src/preact/dateRangeSelector/date-range-selector.tsx +24 -26
  12. package/src/preact/dateRangeSelector/dateRangeOption.ts +65 -0
  13. package/src/preact/dateRangeSelector/selectableOptions.ts +17 -66
  14. package/src/preact/mutationsOverTime/MutationOverTimeData.ts +20 -0
  15. package/src/preact/mutationsOverTime/getFilteredMutationsOverTime.spec.ts +2 -3
  16. package/src/preact/mutationsOverTime/getFilteredMutationsOverTimeData.ts +2 -2
  17. package/src/preact/mutationsOverTime/mutations-over-time-grid.tsx +3 -3
  18. package/src/preact/mutationsOverTime/mutations-over-time.tsx +7 -18
  19. package/src/query/queryMutationsOverTime.ts +3 -5
  20. package/src/utils/map2d.spec.ts +52 -13
  21. package/src/utils/map2d.ts +3 -4
  22. package/src/web-components/input/gs-date-range-selector.stories.ts +16 -28
  23. package/src/web-components/input/gs-date-range-selector.tsx +17 -32
  24. package/standalone-bundle/dashboard-components.js +11687 -12463
  25. package/standalone-bundle/dashboard-components.js.map +1 -1
  26. package/dist/assets/mutationOverTimeWorker-BdzqDqvO.js.map +0 -1
@@ -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
  };
@@ -0,0 +1,20 @@
1
+ import {
2
+ type MutationOverTimeMutationValue,
3
+ serializeSubstitutionOrDeletion,
4
+ serializeTemporal,
5
+ } from '../../query/queryMutationsOverTime';
6
+ import { type Map2d, Map2dBase, type Map2DContents } from '../../utils/map2d';
7
+ import type { Deletion, Substitution } from '../../utils/mutations';
8
+ import type { Temporal } from '../../utils/temporalClass';
9
+
10
+ export type MutationOverTimeDataMap = Map2d<Substitution | Deletion, Temporal, MutationOverTimeMutationValue>;
11
+
12
+ export class BaseMutationOverTimeDataMap extends Map2dBase<
13
+ Substitution | Deletion,
14
+ Temporal,
15
+ MutationOverTimeMutationValue
16
+ > {
17
+ constructor(initialContent?: Map2DContents<Substitution | Deletion, Temporal, MutationOverTimeMutationValue>) {
18
+ super(serializeSubstitutionOrDeletion, serializeTemporal, initialContent);
19
+ }
20
+ }
@@ -1,9 +1,8 @@
1
1
  import { describe, expect, it } from 'vitest';
2
2
 
3
+ import { BaseMutationOverTimeDataMap } from './MutationOverTimeData';
3
4
  import { getFilteredMutationOverTimeData } from './getFilteredMutationsOverTimeData';
4
- import { type MutationOverTimeMutationValue } from '../../query/queryMutationsOverTime';
5
5
  import { type DeletionEntry, type SubstitutionEntry } from '../../types';
6
- import { Map2dBase } from '../../utils/map2d';
7
6
  import { type Deletion, type Substitution } from '../../utils/mutations';
8
7
  import { type TemporalClass } from '../../utils/temporalClass';
9
8
  import { yearMonthDay } from '../../utils/temporalTestHelpers';
@@ -194,7 +193,7 @@ describe('getFilteredMutationOverTimeData', () => {
194
193
  mutationEntries: (SubstitutionEntry<Substitution> | DeletionEntry<Deletion>)[],
195
194
  temporals: TemporalClass[] = [someTemporal, anotherTemporal],
196
195
  ) {
197
- const data = new Map2dBase<Substitution | Deletion, TemporalClass, MutationOverTimeMutationValue>();
196
+ const data = new BaseMutationOverTimeDataMap();
198
197
 
199
198
  temporals.forEach((temporal) => {
200
199
  mutationEntries.forEach((entry) => {
@@ -1,4 +1,4 @@
1
- import { type MutationOverTimeData } from './mutations-over-time';
1
+ import { type MutationOverTimeDataMap } from './MutationOverTimeData';
2
2
  import { type SubstitutionOrDeletionEntry } from '../../types';
3
3
  import { Map2dView } from '../../utils/map2d';
4
4
  import type { Deletion, Substitution } from '../../utils/mutations';
@@ -6,7 +6,7 @@ import type { DisplayedMutationType } from '../components/mutation-type-selector
6
6
  import type { DisplayedSegment } from '../components/segment-selector';
7
7
 
8
8
  export function getFilteredMutationOverTimeData(
9
- data: MutationOverTimeData,
9
+ data: MutationOverTimeDataMap,
10
10
  overallMutationData: SubstitutionOrDeletionEntry<Substitution, Deletion>[],
11
11
  displayedSegments: DisplayedSegment[],
12
12
  displayedMutationTypes: DisplayedMutationType[],
@@ -1,16 +1,16 @@
1
1
  import { Fragment, type FunctionComponent, type RefObject } from 'preact';
2
2
  import { useEffect, useRef, useState } from 'preact/hooks';
3
3
 
4
- import { type MutationOverTimeData } from './mutations-over-time';
4
+ import { type MutationOverTimeDataMap } from './MutationOverTimeData';
5
5
  import { type MutationOverTimeMutationValue } from '../../query/queryMutationsOverTime';
6
6
  import { type Deletion, type Substitution } from '../../utils/mutations';
7
- import { toTemporalClass, type Temporal, type TemporalClass, YearMonthDayClass } from '../../utils/temporalClass';
7
+ import { type Temporal, type TemporalClass, toTemporalClass, YearMonthDayClass } from '../../utils/temporalClass';
8
8
  import { type ColorScale, getColorWithingScale, getTextColorForScale } from '../components/color-scale-selector';
9
9
  import Tooltip, { type TooltipPosition } from '../components/tooltip';
10
10
  import { formatProportion } from '../shared/table/formatProportion';
11
11
 
12
12
  export interface MutationsOverTimeGridProps {
13
- data: MutationOverTimeData;
13
+ data: MutationOverTimeDataMap;
14
14
  colorScale: ColorScale;
15
15
  }
16
16
 
@@ -3,24 +3,19 @@ import { type Dispatch, type StateUpdater, useContext, useMemo, useState } from
3
3
 
4
4
  // @ts-expect-error -- uses subpath imports and vite worker import
5
5
  import MutationOverTimeWorker from '#mutationOverTime?worker&inline';
6
+ import { BaseMutationOverTimeDataMap, type MutationOverTimeDataMap } from './MutationOverTimeData';
6
7
  import { getFilteredMutationOverTimeData } from './getFilteredMutationsOverTimeData';
7
8
  import { type MutationOverTimeWorkerResponse } from './mutationOverTimeWorker';
8
9
  import MutationsOverTimeGrid from './mutations-over-time-grid';
9
- import {
10
- type MutationOverTimeMutationValue,
11
- type MutationOverTimeQuery,
12
- serializeSubstitutionOrDeletion,
13
- serializeTemporal,
14
- } from '../../query/queryMutationsOverTime';
10
+ import { type MutationOverTimeQuery } from '../../query/queryMutationsOverTime';
15
11
  import {
16
12
  type LapisFilter,
17
13
  type SequenceType,
18
14
  type SubstitutionOrDeletionEntry,
19
15
  type TemporalGranularity,
20
16
  } from '../../types';
21
- import { type Map2d, Map2dBase } from '../../utils/map2d';
22
17
  import { type Deletion, type Substitution } from '../../utils/mutations';
23
- import { type Temporal, toTemporalClass } from '../../utils/temporalClass';
18
+ import { toTemporalClass } from '../../utils/temporalClass';
24
19
  import { LapisUrlContext } from '../LapisUrlContext';
25
20
  import { type ColorScale } from '../components/color-scale-selector';
26
21
  import { ColorScaleSelectorDropdown } from '../components/color-scale-selector-dropdown';
@@ -99,11 +94,7 @@ export const MutationsOverTimeInner: FunctionComponent<MutationsOverTimeInnerPro
99
94
  }
100
95
 
101
96
  const { overallMutationData, mutationOverTimeSerialized } = data;
102
- const mutationOverTimeData = new Map2dBase(
103
- serializeSubstitutionOrDeletion,
104
- serializeTemporal,
105
- mutationOverTimeSerialized,
106
- );
97
+ const mutationOverTimeData = new BaseMutationOverTimeDataMap(mutationOverTimeSerialized);
107
98
  return (
108
99
  <MutationsOverTimeTabs
109
100
  overallMutationData={overallMutationData}
@@ -114,10 +105,8 @@ export const MutationsOverTimeInner: FunctionComponent<MutationsOverTimeInnerPro
114
105
  );
115
106
  };
116
107
 
117
- export type MutationOverTimeData = Map2d<Substitution | Deletion, Temporal, MutationOverTimeMutationValue>;
118
-
119
108
  type MutationOverTimeTabsProps = {
120
- mutationOverTimeData: MutationOverTimeData;
109
+ mutationOverTimeData: BaseMutationOverTimeDataMap;
121
110
  sequenceType: SequenceType;
122
111
  views: View[];
123
112
  overallMutationData: SubstitutionOrDeletionEntry<Substitution, Deletion>[];
@@ -192,7 +181,7 @@ type ToolbarProps = {
192
181
  setDisplayedMutationTypes: (types: DisplayedMutationType[]) => void;
193
182
  proportionInterval: ProportionInterval;
194
183
  setProportionInterval: Dispatch<StateUpdater<ProportionInterval>>;
195
- filteredData: MutationOverTimeData;
184
+ filteredData: MutationOverTimeDataMap;
196
185
  colorScale: ColorScale;
197
186
  setColorScale: Dispatch<StateUpdater<ColorScale>>;
198
187
  };
@@ -236,7 +225,7 @@ const Toolbar: FunctionComponent<ToolbarProps> = ({
236
225
  );
237
226
  };
238
227
 
239
- function getDownloadData(filteredData: MutationOverTimeData) {
228
+ function getDownloadData(filteredData: MutationOverTimeDataMap) {
240
229
  const dates = filteredData.getSecondAxisKeys().map((date) => toTemporalClass(date));
241
230
 
242
231
  return filteredData.getFirstAxisKeys().map((mutation) => {
@@ -6,6 +6,7 @@ import { MapOperator } from '../operator/MapOperator';
6
6
  import { RenameFieldOperator } from '../operator/RenameFieldOperator';
7
7
  import { SortOperator } from '../operator/SortOperator';
8
8
  import { UserFacingError } from '../preact/components/error-display';
9
+ import { BaseMutationOverTimeDataMap } from '../preact/mutationsOverTime/MutationOverTimeData';
9
10
  import { sortSubstitutionsAndDeletions } from '../preact/shared/sort/sortSubstitutionsAndDeletions';
10
11
  import {
11
12
  type DeletionEntry,
@@ -15,7 +16,7 @@ import {
15
16
  type SubstitutionOrDeletionEntry,
16
17
  type TemporalGranularity,
17
18
  } from '../types';
18
- import { type Map2d, Map2dBase } from '../utils/map2d';
19
+ import { type Map2d } from '../utils/map2d';
19
20
  import {
20
21
  type Deletion,
21
22
  type DeletionClass,
@@ -218,10 +219,7 @@ export function groupByMutation(
218
219
  data: MutationOverTimeData[],
219
220
  overallMutationData: (SubstitutionEntry | DeletionEntry)[],
220
221
  ) {
221
- const dataArray = new Map2dBase<Substitution | Deletion, Temporal, MutationOverTimeMutationValue>(
222
- serializeSubstitutionOrDeletion,
223
- serializeTemporal,
224
- );
222
+ const dataArray = new BaseMutationOverTimeDataMap();
225
223
 
226
224
  const allDates = data.map((mutationData) => mutationData.date);
227
225