@genspectrum/dashboard-components 0.6.18 → 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.
- package/README.md +5 -12
- package/custom-elements.json +22 -22
- package/dist/assets/mutationOverTimeWorker-BOCXtKzd.js.map +1 -0
- package/dist/dashboard-components.js +301 -302
- package/dist/dashboard-components.js.map +1 -1
- package/dist/genspectrum-components.d.ts +60 -10
- package/dist/style.css +3 -2
- package/package.json +13 -4
- package/src/index.ts +1 -0
- package/src/operator/FetchInsertionsOperator.ts +2 -2
- package/src/operator/FetchSubstitutionsOrDeletionsOperator.ts +3 -3
- package/src/preact/dateRangeSelector/computeInitialValues.spec.ts +53 -38
- package/src/preact/dateRangeSelector/computeInitialValues.ts +17 -23
- package/src/preact/dateRangeSelector/date-range-selector.stories.tsx +46 -32
- package/src/preact/dateRangeSelector/date-range-selector.tsx +24 -26
- package/src/preact/dateRangeSelector/dateRangeOption.ts +65 -0
- package/src/preact/dateRangeSelector/selectableOptions.ts +17 -66
- package/src/preact/mutationComparison/fetchMutationData.spec.ts +3 -3
- package/src/preact/mutationComparison/getMutationComparisonTableData.spec.ts +11 -11
- package/src/preact/mutationComparison/getMutationComparisonTableData.ts +4 -4
- package/src/preact/mutationComparison/mutation-comparison-table.tsx +2 -2
- package/src/preact/mutationFilter/mutation-filter.tsx +27 -18
- package/src/preact/mutationFilter/parseAndValidateMutation.ts +4 -4
- package/src/preact/mutationFilter/parseMutation.spec.ts +17 -17
- package/src/preact/mutations/getInsertionsTableData.spec.ts +3 -3
- package/src/preact/mutations/getMutationsGridData.spec.ts +9 -9
- package/src/preact/mutations/getMutationsTableData.spec.ts +7 -7
- package/src/preact/mutations/mutations-insertions-table.tsx +3 -3
- package/src/preact/mutations/mutations-table.tsx +3 -3
- package/src/preact/mutationsOverTime/MutationOverTimeData.ts +20 -0
- package/src/preact/mutationsOverTime/__mockData__/aminoAcidMutationsByDay.ts +45686 -0
- package/src/preact/mutationsOverTime/__mockData__/byWeek.ts +58989 -0
- package/src/preact/mutationsOverTime/__mockData__/defaultMockData.ts +103991 -0
- package/src/preact/mutationsOverTime/__mockData__/mockConversion.ts +54 -0
- package/src/preact/mutationsOverTime/__mockData__/showsMessageWhenTooManyMutations.ts +63690 -0
- package/src/preact/mutationsOverTime/getFilteredMutationsOverTime.spec.ts +177 -161
- package/src/preact/mutationsOverTime/getFilteredMutationsOverTimeData.ts +17 -59
- package/src/preact/mutationsOverTime/mutationOverTimeWorker.mock.ts +27 -0
- package/src/preact/mutationsOverTime/mutationOverTimeWorker.ts +29 -0
- package/src/preact/mutationsOverTime/mutations-over-time-grid.tsx +13 -14
- package/src/preact/mutationsOverTime/mutations-over-time.stories.tsx +9 -334
- package/src/preact/mutationsOverTime/mutations-over-time.tsx +59 -54
- package/src/preact/numberSequencesOverTime/getNumberOfSequencesOverTimeTableData.ts +3 -3
- package/src/preact/prevalenceOverTime/getPrevalenceOverTimeTableData.spec.ts +5 -5
- package/src/preact/prevalenceOverTime/prevalence-over-time-bubble-chart.tsx +1 -1
- package/src/preact/relativeGrowthAdvantage/relative-growth-advantage-chart.tsx +2 -2
- package/src/preact/shared/sort/sortInsertions.spec.ts +11 -11
- package/src/preact/shared/sort/sortInsertions.ts +2 -2
- package/src/preact/shared/sort/sortSubstitutionsAndDeletions.spec.ts +13 -13
- package/src/preact/shared/sort/sortSubstitutionsAndDeletions.ts +7 -4
- package/src/preact/webWorkers/useWebWorker.ts +51 -0
- package/src/preact/webWorkers/workerFunction.ts +14 -0
- package/src/query/queryAggregatedDataOverTime.ts +3 -3
- package/src/query/queryMutationsOverTime.spec.ts +272 -51
- package/src/query/queryMutationsOverTime.ts +114 -47
- package/src/query/queryPrevalenceOverTime.ts +2 -2
- package/src/query/queryRelativeGrowthAdvantage.ts +3 -3
- package/src/types.ts +25 -5
- package/src/utils/map2d.spec.ts +79 -12
- package/src/utils/map2d.ts +25 -5
- package/src/utils/mutations.spec.ts +20 -20
- package/src/utils/mutations.ts +80 -17
- package/src/utils/sort.ts +5 -2
- package/src/utils/temporal.spec.ts +27 -24
- package/src/utils/{temporal.ts → temporalClass.ts} +170 -72
- package/src/utils/temporalTestHelpers.ts +3 -3
- package/src/web-components/input/gs-date-range-selector.stories.ts +16 -28
- package/src/web-components/input/gs-date-range-selector.tsx +17 -32
- package/src/web-components/introduction.mdx +46 -0
- package/src/web-components/visualization/gs-mutations-over-time.stories.ts +6 -699
- package/src/web-components/visualization/gs-mutations-over-time.tsx +2 -2
- package/standalone-bundle/dashboard-components.js +12011 -12778
- package/standalone-bundle/dashboard-components.js.map +1 -1
- package/src/preact/mutationsOverTime/__mockData__/aggregated_2024_01.json +0 -13
- package/src/preact/mutationsOverTime/__mockData__/aggregated_2024_02.json +0 -13
- package/src/preact/mutationsOverTime/__mockData__/aggregated_2024_03.json +0 -13
- package/src/preact/mutationsOverTime/__mockData__/aggregated_2024_04.json +0 -13
- package/src/preact/mutationsOverTime/__mockData__/aggregated_2024_05.json +0 -13
- package/src/preact/mutationsOverTime/__mockData__/aggregated_2024_06.json +0 -13
- package/src/preact/mutationsOverTime/__mockData__/aggregated_2024_07.json +0 -13
- package/src/preact/mutationsOverTime/__mockData__/aggregated_20_01_2024.json +0 -13
- package/src/preact/mutationsOverTime/__mockData__/aggregated_21_01_2024.json +0 -13
- package/src/preact/mutationsOverTime/__mockData__/aggregated_22_01_2024.json +0 -13
- package/src/preact/mutationsOverTime/__mockData__/aggregated_23_01_2024.json +0 -13
- package/src/preact/mutationsOverTime/__mockData__/aggregated_24_01_2024.json +0 -13
- package/src/preact/mutationsOverTime/__mockData__/aggregated_25_01_2024.json +0 -13
- package/src/preact/mutationsOverTime/__mockData__/aggregated_26_01_2024.json +0 -13
- package/src/preact/mutationsOverTime/__mockData__/aggregated_byDay.json +0 -38
- package/src/preact/mutationsOverTime/__mockData__/aggregated_byWeek.json +0 -122
- package/src/preact/mutationsOverTime/__mockData__/aggregated_date.json +0 -642
- package/src/preact/mutationsOverTime/__mockData__/aggregated_tooManyMutations.json +0 -1470
- package/src/preact/mutationsOverTime/__mockData__/aggregated_tooManyMutations_total.json +0 -13
- package/src/preact/mutationsOverTime/__mockData__/aggregated_week3_2024.json +0 -13
- package/src/preact/mutationsOverTime/__mockData__/aggregated_week4_2024.json +0 -13
- package/src/preact/mutationsOverTime/__mockData__/aggregated_week5_2024.json +0 -13
- package/src/preact/mutationsOverTime/__mockData__/aggregated_week6_2024.json +0 -13
- package/src/preact/mutationsOverTime/__mockData__/aminoAcidMutations_20_01_2024.json +0 -6778
- package/src/preact/mutationsOverTime/__mockData__/aminoAcidMutations_21_01_2024.json +0 -7129
- package/src/preact/mutationsOverTime/__mockData__/aminoAcidMutations_22_01_2024.json +0 -4681
- package/src/preact/mutationsOverTime/__mockData__/aminoAcidMutations_23_01_2024.json +0 -10738
- package/src/preact/mutationsOverTime/__mockData__/aminoAcidMutations_24_01_2024.json +0 -11710
- package/src/preact/mutationsOverTime/__mockData__/aminoAcidMutations_25_01_2024.json +0 -11557
- package/src/preact/mutationsOverTime/__mockData__/aminoAcidMutations_26_01_2024.json +0 -8596
- package/src/preact/mutationsOverTime/__mockData__/aminoAcidMutations_byDayOverall.json +0 -4726
- package/src/preact/mutationsOverTime/__mockData__/nucleotideMutations_2024_01.json +0 -1747
- package/src/preact/mutationsOverTime/__mockData__/nucleotideMutations_2024_02.json +0 -1774
- package/src/preact/mutationsOverTime/__mockData__/nucleotideMutations_2024_03.json +0 -1819
- package/src/preact/mutationsOverTime/__mockData__/nucleotideMutations_2024_04.json +0 -1864
- package/src/preact/mutationsOverTime/__mockData__/nucleotideMutations_2024_05.json +0 -1927
- package/src/preact/mutationsOverTime/__mockData__/nucleotideMutations_2024_06.json +0 -1864
- package/src/preact/mutationsOverTime/__mockData__/nucleotideMutations_2024_07.json +0 -9
- package/src/preact/mutationsOverTime/__mockData__/nucleotideMutations_byMonthOverall.json +0 -11143
- package/src/preact/mutationsOverTime/__mockData__/nucleotideMutations_byWeekOverall.json +0 -9154
- package/src/preact/mutationsOverTime/__mockData__/nucleotideMutations_tooManyMutations.json +0 -16453
- package/src/preact/mutationsOverTime/__mockData__/nucleotideMutations_week3_2024.json +0 -8812
- package/src/preact/mutationsOverTime/__mockData__/nucleotideMutations_week4_2024.json +0 -9730
- package/src/preact/mutationsOverTime/__mockData__/nucleotideMutations_week5_2024.json +0 -9865
- package/src/preact/mutationsOverTime/__mockData__/nucleotideMutations_week6_2024.json +0 -11314
|
@@ -99,6 +99,53 @@ export declare class App extends LitElement {
|
|
|
99
99
|
createRenderRoot(): this;
|
|
100
100
|
}
|
|
101
101
|
|
|
102
|
+
/**
|
|
103
|
+
* A date range option that can be used in the `gs-date-range-selector` component.
|
|
104
|
+
*/
|
|
105
|
+
export declare type DateRangeOption = {
|
|
106
|
+
/** The label of the date range option that will be shown to the user */
|
|
107
|
+
label: string;
|
|
108
|
+
/**
|
|
109
|
+
* The start date of the date range in the format `YYYY-MM-DD`.
|
|
110
|
+
* If not set, the date range selector will default to the `earliestDate` property.
|
|
111
|
+
*/
|
|
112
|
+
dateFrom?: string;
|
|
113
|
+
/**
|
|
114
|
+
* The end date of the date range in the format `YYYY-MM-DD`.
|
|
115
|
+
* If not set, the date range selector will default to the current date.
|
|
116
|
+
*/
|
|
117
|
+
dateTo?: string;
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Presets for the `gs-date-range-selector` component that can be used as `dateRangeOptions`.
|
|
122
|
+
*/
|
|
123
|
+
export declare const dateRangeOptionPresets: {
|
|
124
|
+
last2Weeks: {
|
|
125
|
+
label: string;
|
|
126
|
+
dateFrom: string | undefined;
|
|
127
|
+
};
|
|
128
|
+
lastMonth: {
|
|
129
|
+
label: string;
|
|
130
|
+
dateFrom: string | undefined;
|
|
131
|
+
};
|
|
132
|
+
last2Months: {
|
|
133
|
+
label: string;
|
|
134
|
+
dateFrom: string | undefined;
|
|
135
|
+
};
|
|
136
|
+
last3Months: {
|
|
137
|
+
label: string;
|
|
138
|
+
dateFrom: string | undefined;
|
|
139
|
+
};
|
|
140
|
+
last6Months: {
|
|
141
|
+
label: string;
|
|
142
|
+
dateFrom: string | undefined;
|
|
143
|
+
};
|
|
144
|
+
allTimes: {
|
|
145
|
+
label: string;
|
|
146
|
+
};
|
|
147
|
+
};
|
|
148
|
+
|
|
102
149
|
/**
|
|
103
150
|
* ## Context
|
|
104
151
|
* This component is a group of input fields designed to specify date range filters
|
|
@@ -130,15 +177,16 @@ export declare class App extends LitElement {
|
|
|
130
177
|
*/
|
|
131
178
|
export declare class DateRangeSelectorComponent extends PreactLitAdapter {
|
|
132
179
|
/**
|
|
133
|
-
* An array of
|
|
134
|
-
* in addition to the predefined options.
|
|
180
|
+
* An array of date range options that the select field should provide.
|
|
135
181
|
* The `label` will be shown to the user, and it will be available as `initialValue`.
|
|
136
182
|
* The dates must be in the format `YYYY-MM-DD`.
|
|
183
|
+
*
|
|
184
|
+
* If dateFrom or dateTo is not set, the component will default to the `earliestDate` or the current date.
|
|
137
185
|
*/
|
|
138
|
-
|
|
186
|
+
dateRangeOptions: {
|
|
139
187
|
label: string;
|
|
140
|
-
dateFrom
|
|
141
|
-
dateTo
|
|
188
|
+
dateFrom?: string;
|
|
189
|
+
dateTo?: string;
|
|
142
190
|
}[];
|
|
143
191
|
/**
|
|
144
192
|
* The `dateFrom` value to use in the `allTimes` preset in the format `YYYY-MM-DD`.
|
|
@@ -146,13 +194,15 @@ export declare class DateRangeSelectorComponent extends PreactLitAdapter {
|
|
|
146
194
|
earliestDate: string;
|
|
147
195
|
/**
|
|
148
196
|
* The initial value to use for this date range selector.
|
|
149
|
-
* Must be a valid label from the
|
|
197
|
+
* Must be a valid label from the `dateRangeOptions`.
|
|
150
198
|
*
|
|
151
|
-
* If the value is
|
|
199
|
+
* If the value is not set, the component will default to the range `earliestDate` until today.
|
|
152
200
|
*
|
|
153
201
|
* It will be overwritten if `initialDateFrom` or `initialDateTo` is set.
|
|
202
|
+
*
|
|
203
|
+
* We provide some options in `dateRangeOptionPresets` for convenience.
|
|
154
204
|
*/
|
|
155
|
-
initialValue:
|
|
205
|
+
initialValue: string | undefined;
|
|
156
206
|
/**
|
|
157
207
|
* A date string in the format `YYYY-MM-DD`.
|
|
158
208
|
* If set, the date range selector will be initialized with the given date (overwriting `initialValue` to `custom`).
|
|
@@ -1181,7 +1231,7 @@ declare global {
|
|
|
1181
1231
|
|
|
1182
1232
|
declare global {
|
|
1183
1233
|
interface HTMLElementTagNameMap {
|
|
1184
|
-
'gs-mutations-over-time
|
|
1234
|
+
'gs-mutations-over-time': MutationsOverTimeComponent;
|
|
1185
1235
|
}
|
|
1186
1236
|
}
|
|
1187
1237
|
|
|
@@ -1189,7 +1239,7 @@ declare global {
|
|
|
1189
1239
|
declare global {
|
|
1190
1240
|
namespace JSX {
|
|
1191
1241
|
interface IntrinsicElements {
|
|
1192
|
-
'gs-mutations-over-time
|
|
1242
|
+
'gs-mutations-over-time': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
|
|
1193
1243
|
}
|
|
1194
1244
|
}
|
|
1195
1245
|
}
|
package/dist/style.css
CHANGED
|
@@ -482,7 +482,7 @@ input[type="range"] {
|
|
|
482
482
|
--tw-contain-paint: ;
|
|
483
483
|
--tw-contain-style: ;
|
|
484
484
|
}/*
|
|
485
|
-
! tailwindcss v3.4.
|
|
485
|
+
! tailwindcss v3.4.14 | MIT License | https://tailwindcss.com
|
|
486
486
|
*//*
|
|
487
487
|
1. Prevent padding and border from affecting element width. (https://github.com/mozdevs/cssremedy/issues/4)
|
|
488
488
|
2. Allow adding a border to an element by just adding a border-width. (https://github.com/tailwindcss/tailwindcss/pull/116)
|
|
@@ -873,7 +873,7 @@ video {
|
|
|
873
873
|
}
|
|
874
874
|
|
|
875
875
|
/* Make elements with the HTML hidden attribute stay hidden by default */
|
|
876
|
-
[hidden] {
|
|
876
|
+
[hidden]:where(:not([hidden="until-found"])) {
|
|
877
877
|
display: none;
|
|
878
878
|
}
|
|
879
879
|
|
|
@@ -2707,6 +2707,7 @@ input.tab:checked + .tab-content,
|
|
|
2707
2707
|
}
|
|
2708
2708
|
.join.join-horizontal > :where(*:not(:first-child)):is(.btn) {
|
|
2709
2709
|
margin-inline-start: calc(var(--border-btn) * -1);
|
|
2710
|
+
margin-top: 0px;
|
|
2710
2711
|
}
|
|
2711
2712
|
.modal-top :where(.modal-box) {
|
|
2712
2713
|
width: 100%;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@genspectrum/dashboard-components",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "GenSpectrum web components for building dashboards",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "AGPL-3.0-only",
|
|
@@ -10,6 +10,17 @@
|
|
|
10
10
|
"publishConfig": {
|
|
11
11
|
"access": "public"
|
|
12
12
|
},
|
|
13
|
+
"imports": {
|
|
14
|
+
"#mutationOverTime": {
|
|
15
|
+
"storybook": "./src/preact/mutationsOverTime/mutationOverTimeWorker.mock.ts",
|
|
16
|
+
"default": "./src/preact/mutationsOverTime/mutationOverTimeWorker.ts"
|
|
17
|
+
},
|
|
18
|
+
"#*": [
|
|
19
|
+
"./*",
|
|
20
|
+
"./*.ts",
|
|
21
|
+
"./*.tsx"
|
|
22
|
+
]
|
|
23
|
+
},
|
|
13
24
|
"exports": {
|
|
14
25
|
".": {
|
|
15
26
|
"import": "./dist/dashboard-components.js",
|
|
@@ -69,7 +80,6 @@
|
|
|
69
80
|
"flatpickr": "^4.6.13",
|
|
70
81
|
"gridjs": "^6.2.0",
|
|
71
82
|
"lit": "^3.1.3",
|
|
72
|
-
"object-hash": "^3.0.0",
|
|
73
83
|
"preact": "^10.20.1",
|
|
74
84
|
"zod": "^3.23.0"
|
|
75
85
|
},
|
|
@@ -92,7 +102,6 @@
|
|
|
92
102
|
"@storybook/web-components": "^8.0.9",
|
|
93
103
|
"@storybook/web-components-vite": "^8.0.9",
|
|
94
104
|
"@types/node": "^22.0.0",
|
|
95
|
-
"@types/object-hash": "^3.0.6",
|
|
96
105
|
"@typescript-eslint/eslint-plugin": "^8.2.0",
|
|
97
106
|
"@typescript-eslint/parser": "^8.2.0",
|
|
98
107
|
"autoprefixer": "^10.4.19",
|
|
@@ -102,7 +111,7 @@
|
|
|
102
111
|
"eslint-config-preact": "^1.3.0",
|
|
103
112
|
"eslint-plugin-import": "^2.29.1",
|
|
104
113
|
"eslint-plugin-jest": "^28.2.0",
|
|
105
|
-
"eslint-plugin-storybook": "^0.
|
|
114
|
+
"eslint-plugin-storybook": "^0.10.1",
|
|
106
115
|
"http-server": "^14.1.1",
|
|
107
116
|
"lit-analyzer": "^2.0.3",
|
|
108
117
|
"msw": "^2.2.14",
|
package/src/index.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export * from './web-components';
|
|
2
2
|
|
|
3
3
|
export { type ErrorEvent, UserFacingError } from './preact/components/error-display';
|
|
4
|
+
export { type DateRangeOption, dateRangeOptionPresets } from './preact/dateRangeSelector/dateRangeOption';
|
|
4
5
|
|
|
5
6
|
declare global {
|
|
6
7
|
interface HTMLElementEventMap {
|
|
@@ -2,7 +2,7 @@ import { type Dataset } from './Dataset';
|
|
|
2
2
|
import { type Operator } from './Operator';
|
|
3
3
|
import { fetchInsertions } from '../lapisApi/lapisApi';
|
|
4
4
|
import { type InsertionEntry, type LapisFilter, type SequenceType } from '../types';
|
|
5
|
-
import {
|
|
5
|
+
import { InsertionClass } from '../utils/mutations';
|
|
6
6
|
|
|
7
7
|
export class FetchInsertionsOperator implements Operator<InsertionEntry> {
|
|
8
8
|
constructor(
|
|
@@ -15,7 +15,7 @@ export class FetchInsertionsOperator implements Operator<InsertionEntry> {
|
|
|
15
15
|
|
|
16
16
|
const content: InsertionEntry[] = insertions.map(({ count, insertedSymbols, sequenceName, position }) => ({
|
|
17
17
|
type: 'insertion',
|
|
18
|
-
mutation: new
|
|
18
|
+
mutation: new InsertionClass(sequenceName ?? undefined, position, insertedSymbols),
|
|
19
19
|
count,
|
|
20
20
|
}));
|
|
21
21
|
|
|
@@ -2,7 +2,7 @@ import { type Dataset } from './Dataset';
|
|
|
2
2
|
import { type Operator } from './Operator';
|
|
3
3
|
import { fetchSubstitutionsOrDeletions } from '../lapisApi/lapisApi';
|
|
4
4
|
import { type LapisFilter, type SequenceType, type SubstitutionOrDeletionEntry } from '../types';
|
|
5
|
-
import {
|
|
5
|
+
import { DeletionClass, SubstitutionClass } from '../utils/mutations';
|
|
6
6
|
|
|
7
7
|
export class FetchSubstitutionsOrDeletionsOperator implements Operator<SubstitutionOrDeletionEntry> {
|
|
8
8
|
constructor(
|
|
@@ -19,14 +19,14 @@ export class FetchSubstitutionsOrDeletionsOperator implements Operator<Substitut
|
|
|
19
19
|
if (mutationTo === '-') {
|
|
20
20
|
return {
|
|
21
21
|
type: 'deletion',
|
|
22
|
-
mutation: new
|
|
22
|
+
mutation: new DeletionClass(sequenceName ?? undefined, mutationFrom, position),
|
|
23
23
|
count,
|
|
24
24
|
proportion,
|
|
25
25
|
};
|
|
26
26
|
}
|
|
27
27
|
return {
|
|
28
28
|
type: 'substitution',
|
|
29
|
-
mutation: new
|
|
29
|
+
mutation: new SubstitutionClass(sequenceName ?? undefined, mutationFrom, mutationTo, position),
|
|
30
30
|
count,
|
|
31
31
|
proportion,
|
|
32
32
|
};
|
|
@@ -1,48 +1,75 @@
|
|
|
1
1
|
import { describe, expect, it } from 'vitest';
|
|
2
2
|
|
|
3
3
|
import { computeInitialValues } from './computeInitialValues';
|
|
4
|
-
import { PRESET_VALUE_CUSTOM, PRESET_VALUE_LAST_3_MONTHS, PRESET_VALUE_LAST_6_MONTHS } from './selectableOptions';
|
|
5
4
|
|
|
6
5
|
const today = new Date();
|
|
7
6
|
const earliestDate = '1900-01-01';
|
|
8
7
|
|
|
8
|
+
const fromOption = 'fromOption';
|
|
9
|
+
const toOption = 'toOption';
|
|
10
|
+
const fromToOption = 'fromToOption';
|
|
11
|
+
const dateFromOptionValue = '2010-06-30';
|
|
12
|
+
const dateToOptionValue = '2020-01-01';
|
|
13
|
+
|
|
14
|
+
const dateRangeOptions = [
|
|
15
|
+
{ label: fromOption, dateFrom: dateFromOptionValue },
|
|
16
|
+
{ label: toOption, dateTo: dateToOptionValue },
|
|
17
|
+
{ label: fromToOption, dateFrom: dateFromOptionValue, dateTo: dateToOptionValue },
|
|
18
|
+
];
|
|
19
|
+
|
|
9
20
|
describe('computeInitialValues', () => {
|
|
10
21
|
it('should compute for initial value if initial "from" and "to" are unset', () => {
|
|
11
|
-
const result = computeInitialValues(
|
|
22
|
+
const result = computeInitialValues(fromToOption, undefined, undefined, earliestDate, dateRangeOptions);
|
|
12
23
|
|
|
13
|
-
|
|
14
|
-
|
|
24
|
+
expect(result.initialSelectedDateRange).toEqual(fromToOption);
|
|
25
|
+
expectDateMatches(result.initialSelectedDateFrom, new Date(dateFromOptionValue));
|
|
26
|
+
expectDateMatches(result.initialSelectedDateTo, new Date(dateToOptionValue));
|
|
27
|
+
});
|
|
15
28
|
|
|
16
|
-
|
|
17
|
-
|
|
29
|
+
it('should use today as "dateTo" if it is unset in selected option', () => {
|
|
30
|
+
const result = computeInitialValues(fromOption, undefined, undefined, earliestDate, dateRangeOptions);
|
|
31
|
+
|
|
32
|
+
expect(result.initialSelectedDateRange).toEqual(fromOption);
|
|
33
|
+
expectDateMatches(result.initialSelectedDateFrom, new Date(dateFromOptionValue));
|
|
18
34
|
expectDateMatches(result.initialSelectedDateTo, today);
|
|
19
35
|
});
|
|
20
36
|
|
|
21
|
-
it('should
|
|
22
|
-
const result = computeInitialValues(
|
|
37
|
+
it('should use earliest date as "dateFrom" if it is unset in selected option', () => {
|
|
38
|
+
const result = computeInitialValues(toOption, undefined, undefined, earliestDate, dateRangeOptions);
|
|
23
39
|
|
|
24
|
-
|
|
25
|
-
|
|
40
|
+
expect(result.initialSelectedDateRange).toEqual(toOption);
|
|
41
|
+
expectDateMatches(result.initialSelectedDateFrom, new Date(earliestDate));
|
|
42
|
+
expectDateMatches(result.initialSelectedDateTo, new Date(dateToOptionValue));
|
|
43
|
+
});
|
|
26
44
|
|
|
27
|
-
|
|
28
|
-
|
|
45
|
+
it('should fall back to full range if initial value is not set', () => {
|
|
46
|
+
const result = computeInitialValues(undefined, undefined, undefined, earliestDate, dateRangeOptions);
|
|
47
|
+
|
|
48
|
+
expect(result.initialSelectedDateRange).toBeUndefined();
|
|
49
|
+
expectDateMatches(result.initialSelectedDateFrom, new Date(earliestDate));
|
|
29
50
|
expectDateMatches(result.initialSelectedDateTo, today);
|
|
30
51
|
});
|
|
31
52
|
|
|
53
|
+
it('should fall back to default when initial value is unknown', () => {
|
|
54
|
+
expect(() => computeInitialValues('not a known value', undefined, undefined, earliestDate, [])).toThrowError(
|
|
55
|
+
/Invalid initialValue "not a known value", It must be one of/,
|
|
56
|
+
);
|
|
57
|
+
});
|
|
58
|
+
|
|
32
59
|
it('should overwrite initial value if initial "from" is set', () => {
|
|
33
60
|
const initialDateFrom = '2020-01-01';
|
|
34
|
-
const result = computeInitialValues(
|
|
61
|
+
const result = computeInitialValues(fromOption, initialDateFrom, undefined, earliestDate, dateRangeOptions);
|
|
35
62
|
|
|
36
|
-
expect(result.initialSelectedDateRange).
|
|
63
|
+
expect(result.initialSelectedDateRange).toBeUndefined();
|
|
37
64
|
expectDateMatches(result.initialSelectedDateFrom, new Date(initialDateFrom));
|
|
38
65
|
expectDateMatches(result.initialSelectedDateTo, today);
|
|
39
66
|
});
|
|
40
67
|
|
|
41
68
|
it('should overwrite initial value if initial "to" is set', () => {
|
|
42
69
|
const initialDateTo = '2020-01-01';
|
|
43
|
-
const result = computeInitialValues(
|
|
70
|
+
const result = computeInitialValues(fromOption, undefined, initialDateTo, earliestDate, dateRangeOptions);
|
|
44
71
|
|
|
45
|
-
expect(result.initialSelectedDateRange).
|
|
72
|
+
expect(result.initialSelectedDateRange).toBeUndefined();
|
|
46
73
|
expectDateMatches(result.initialSelectedDateFrom, new Date(earliestDate));
|
|
47
74
|
expectDateMatches(result.initialSelectedDateTo, new Date(initialDateTo));
|
|
48
75
|
});
|
|
@@ -50,15 +77,9 @@ describe('computeInitialValues', () => {
|
|
|
50
77
|
it('should overwrite initial value if initial "to" and "from" are set', () => {
|
|
51
78
|
const initialDateFrom = '2020-01-01';
|
|
52
79
|
const initialDateTo = '2022-01-01';
|
|
53
|
-
const result = computeInitialValues(
|
|
54
|
-
PRESET_VALUE_LAST_3_MONTHS,
|
|
55
|
-
initialDateFrom,
|
|
56
|
-
initialDateTo,
|
|
57
|
-
earliestDate,
|
|
58
|
-
[],
|
|
59
|
-
);
|
|
80
|
+
const result = computeInitialValues(fromOption, initialDateFrom, initialDateTo, earliestDate, dateRangeOptions);
|
|
60
81
|
|
|
61
|
-
expect(result.initialSelectedDateRange).
|
|
82
|
+
expect(result.initialSelectedDateRange).toBeUndefined();
|
|
62
83
|
expectDateMatches(result.initialSelectedDateFrom, new Date(initialDateFrom));
|
|
63
84
|
expectDateMatches(result.initialSelectedDateTo, new Date(initialDateTo));
|
|
64
85
|
});
|
|
@@ -66,29 +87,23 @@ describe('computeInitialValues', () => {
|
|
|
66
87
|
it('should set initial "to" to "from" if "from" is after "to"', () => {
|
|
67
88
|
const initialDateFrom = '2020-01-01';
|
|
68
89
|
const initialDateTo = '1900-01-01';
|
|
69
|
-
const result = computeInitialValues(
|
|
70
|
-
PRESET_VALUE_LAST_3_MONTHS,
|
|
71
|
-
initialDateFrom,
|
|
72
|
-
initialDateTo,
|
|
73
|
-
earliestDate,
|
|
74
|
-
[],
|
|
75
|
-
);
|
|
90
|
+
const result = computeInitialValues(undefined, initialDateFrom, initialDateTo, earliestDate, dateRangeOptions);
|
|
76
91
|
|
|
77
|
-
expect(result.initialSelectedDateRange).
|
|
92
|
+
expect(result.initialSelectedDateRange).toBeUndefined();
|
|
78
93
|
expectDateMatches(result.initialSelectedDateFrom, new Date(initialDateFrom));
|
|
79
94
|
expectDateMatches(result.initialSelectedDateTo, new Date(initialDateFrom));
|
|
80
95
|
});
|
|
81
96
|
|
|
82
97
|
it('should throw if initial "from" is not a valid date', () => {
|
|
83
|
-
expect(() =>
|
|
84
|
-
|
|
85
|
-
)
|
|
98
|
+
expect(() => computeInitialValues(undefined, 'not a date', undefined, earliestDate, [])).toThrowError(
|
|
99
|
+
'Invalid initialDateFrom',
|
|
100
|
+
);
|
|
86
101
|
});
|
|
87
102
|
|
|
88
103
|
it('should throw if initial "to" is not a valid date', () => {
|
|
89
|
-
expect(() =>
|
|
90
|
-
|
|
91
|
-
)
|
|
104
|
+
expect(() => computeInitialValues(undefined, undefined, 'not a date', earliestDate, [])).toThrowError(
|
|
105
|
+
'Invalid initialDateTo',
|
|
106
|
+
);
|
|
92
107
|
});
|
|
93
108
|
|
|
94
109
|
function expectDateMatches(actual: Date, expected: Date) {
|
|
@@ -1,36 +1,30 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
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
|
|
12
|
-
initialValue:
|
|
5
|
+
export function computeInitialValues(
|
|
6
|
+
initialValue: string | undefined,
|
|
13
7
|
initialDateFrom: string | undefined,
|
|
14
8
|
initialDateTo: string | undefined,
|
|
15
9
|
earliestDate: string,
|
|
16
|
-
|
|
10
|
+
dateRangeOptions: DateRangeOption[],
|
|
17
11
|
): {
|
|
18
|
-
initialSelectedDateRange:
|
|
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(
|
|
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
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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:
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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:
|
|
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
|
|
66
|
+
export const Primary: StoryObj<DateRangeSelectorProps> = {
|
|
76
67
|
render: (args) => (
|
|
77
68
|
<LapisUrlContext.Provider value={LAPIS_URL}>
|
|
78
69
|
<DateRangeSelector
|
|
79
|
-
|
|
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
|
|
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
|
|
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(
|
|
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
|
|
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(
|
|
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
|
|
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,
|