@genspectrum/dashboard-components 0.20.0 → 0.21.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/custom-elements.json +8 -27
- package/dist/{NumberRangeFilterChangedEvent-RqWinxhE.js → NumberRangeFilterChangedEvent-B64OQZjX.js} +1 -6
- package/dist/NumberRangeFilterChangedEvent-B64OQZjX.js.map +1 -0
- package/dist/components.d.ts +16 -20
- package/dist/components.js +39 -80
- package/dist/components.js.map +1 -1
- package/dist/util.d.ts +14 -17
- package/dist/util.js +1 -1
- package/package.json +2 -2
- package/src/preact/dateRangeFilter/computeInitialValues.spec.ts +18 -29
- package/src/preact/dateRangeFilter/computeInitialValues.ts +29 -27
- package/src/preact/dateRangeFilter/date-range-filter.stories.tsx +6 -13
- package/src/preact/dateRangeFilter/date-range-filter.tsx +16 -35
- package/src/preact/dateRangeFilter/dateRangeOption.ts +0 -6
- package/src/preact/dateRangeFilter/selectableOptions.ts +0 -23
- package/src/preact/statistic/statistics.stories.tsx +67 -0
- package/src/preact/statistic/statistics.tsx +3 -1
- package/src/web-components/input/gs-date-range-filter.stories.ts +1 -10
- package/src/web-components/input/gs-date-range-filter.tsx +2 -12
- package/standalone-bundle/dashboard-components.js +6470 -6451
- package/standalone-bundle/dashboard-components.js.map +1 -1
- package/dist/NumberRangeFilterChangedEvent-RqWinxhE.js.map +0 -1
package/dist/util.d.ts
CHANGED
|
@@ -84,7 +84,6 @@ declare type DateRangeOptionPresets = {
|
|
|
84
84
|
last3Months: DateRangeOption;
|
|
85
85
|
last6Months: DateRangeOption;
|
|
86
86
|
lastYear: DateRangeOption;
|
|
87
|
-
allTimes: DateRangeOption;
|
|
88
87
|
};
|
|
89
88
|
|
|
90
89
|
/**
|
|
@@ -100,12 +99,10 @@ declare const dateRangeOptionSchema: default_2.ZodObject<{
|
|
|
100
99
|
label: default_2.ZodString;
|
|
101
100
|
/**
|
|
102
101
|
* The start date of the date range in the format `YYYY-MM-DD`.
|
|
103
|
-
* If not set, the date range selector will default to the `earliestDate` property.
|
|
104
102
|
*/
|
|
105
103
|
dateFrom: default_2.ZodOptional<default_2.ZodString>;
|
|
106
104
|
/**
|
|
107
105
|
* The end date of the date range in the format `YYYY-MM-DD`.
|
|
108
|
-
* If not set, the date range selector will default to the current date.
|
|
109
106
|
*/
|
|
110
107
|
dateTo: default_2.ZodOptional<default_2.ZodString>;
|
|
111
108
|
}, "strip", default_2.ZodTypeAny, {
|
|
@@ -936,7 +933,7 @@ declare global {
|
|
|
936
933
|
|
|
937
934
|
declare global {
|
|
938
935
|
interface HTMLElementTagNameMap {
|
|
939
|
-
'gs-
|
|
936
|
+
'gs-mutations': MutationsComponent;
|
|
940
937
|
}
|
|
941
938
|
}
|
|
942
939
|
|
|
@@ -944,7 +941,7 @@ declare global {
|
|
|
944
941
|
declare global {
|
|
945
942
|
namespace JSX {
|
|
946
943
|
interface IntrinsicElements {
|
|
947
|
-
'gs-
|
|
944
|
+
'gs-mutations': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
|
|
948
945
|
}
|
|
949
946
|
}
|
|
950
947
|
}
|
|
@@ -952,7 +949,7 @@ declare global {
|
|
|
952
949
|
|
|
953
950
|
declare global {
|
|
954
951
|
interface HTMLElementTagNameMap {
|
|
955
|
-
'gs-
|
|
952
|
+
'gs-mutation-comparison-component': MutationComparisonComponent;
|
|
956
953
|
}
|
|
957
954
|
}
|
|
958
955
|
|
|
@@ -960,7 +957,7 @@ declare global {
|
|
|
960
957
|
declare global {
|
|
961
958
|
namespace JSX {
|
|
962
959
|
interface IntrinsicElements {
|
|
963
|
-
'gs-
|
|
960
|
+
'gs-mutation-comparison-component': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
|
|
964
961
|
}
|
|
965
962
|
}
|
|
966
963
|
}
|
|
@@ -968,7 +965,7 @@ declare global {
|
|
|
968
965
|
|
|
969
966
|
declare global {
|
|
970
967
|
interface HTMLElementTagNameMap {
|
|
971
|
-
'gs-
|
|
968
|
+
'gs-relative-growth-advantage': RelativeGrowthAdvantageComponent;
|
|
972
969
|
}
|
|
973
970
|
}
|
|
974
971
|
|
|
@@ -976,7 +973,7 @@ declare global {
|
|
|
976
973
|
declare global {
|
|
977
974
|
namespace JSX {
|
|
978
975
|
interface IntrinsicElements {
|
|
979
|
-
'gs-
|
|
976
|
+
'gs-relative-growth-advantage': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
|
|
980
977
|
}
|
|
981
978
|
}
|
|
982
979
|
}
|
|
@@ -984,7 +981,7 @@ declare global {
|
|
|
984
981
|
|
|
985
982
|
declare global {
|
|
986
983
|
interface HTMLElementTagNameMap {
|
|
987
|
-
'gs-
|
|
984
|
+
'gs-prevalence-over-time': PrevalenceOverTimeComponent;
|
|
988
985
|
}
|
|
989
986
|
}
|
|
990
987
|
|
|
@@ -992,7 +989,7 @@ declare global {
|
|
|
992
989
|
declare global {
|
|
993
990
|
namespace JSX {
|
|
994
991
|
interface IntrinsicElements {
|
|
995
|
-
'gs-
|
|
992
|
+
'gs-prevalence-over-time': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
|
|
996
993
|
}
|
|
997
994
|
}
|
|
998
995
|
}
|
|
@@ -1000,7 +997,7 @@ declare global {
|
|
|
1000
997
|
|
|
1001
998
|
declare global {
|
|
1002
999
|
interface HTMLElementTagNameMap {
|
|
1003
|
-
'gs-
|
|
1000
|
+
'gs-aggregate': AggregateComponent;
|
|
1004
1001
|
}
|
|
1005
1002
|
}
|
|
1006
1003
|
|
|
@@ -1008,7 +1005,7 @@ declare global {
|
|
|
1008
1005
|
declare global {
|
|
1009
1006
|
namespace JSX {
|
|
1010
1007
|
interface IntrinsicElements {
|
|
1011
|
-
'gs-
|
|
1008
|
+
'gs-aggregate': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
|
|
1012
1009
|
}
|
|
1013
1010
|
}
|
|
1014
1011
|
}
|
|
@@ -1016,7 +1013,7 @@ declare global {
|
|
|
1016
1013
|
|
|
1017
1014
|
declare global {
|
|
1018
1015
|
interface HTMLElementTagNameMap {
|
|
1019
|
-
'gs-
|
|
1016
|
+
'gs-number-sequences-over-time': NumberSequencesOverTimeComponent;
|
|
1020
1017
|
}
|
|
1021
1018
|
}
|
|
1022
1019
|
|
|
@@ -1024,7 +1021,7 @@ declare global {
|
|
|
1024
1021
|
declare global {
|
|
1025
1022
|
namespace JSX {
|
|
1026
1023
|
interface IntrinsicElements {
|
|
1027
|
-
'gs-
|
|
1024
|
+
'gs-number-sequences-over-time': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
|
|
1028
1025
|
}
|
|
1029
1026
|
}
|
|
1030
1027
|
}
|
|
@@ -1032,7 +1029,7 @@ declare global {
|
|
|
1032
1029
|
|
|
1033
1030
|
declare global {
|
|
1034
1031
|
interface HTMLElementTagNameMap {
|
|
1035
|
-
'gs-
|
|
1032
|
+
'gs-mutations-over-time': MutationsOverTimeComponent;
|
|
1036
1033
|
}
|
|
1037
1034
|
}
|
|
1038
1035
|
|
|
@@ -1040,7 +1037,7 @@ declare global {
|
|
|
1040
1037
|
declare global {
|
|
1041
1038
|
namespace JSX {
|
|
1042
1039
|
interface IntrinsicElements {
|
|
1043
|
-
'gs-
|
|
1040
|
+
'gs-mutations-over-time': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
|
|
1044
1041
|
}
|
|
1045
1042
|
}
|
|
1046
1043
|
}
|
package/dist/util.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@genspectrum/dashboard-components",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.21.0",
|
|
4
4
|
"description": "GenSpectrum web components for building dashboards",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "AGPL-3.0-only",
|
|
@@ -111,7 +111,7 @@
|
|
|
111
111
|
"@storybook/preact": "^8.0.9",
|
|
112
112
|
"@storybook/preact-vite": "^8.0.9",
|
|
113
113
|
"@storybook/test": "^8.0.0",
|
|
114
|
-
"@storybook/test-runner": "^0.
|
|
114
|
+
"@storybook/test-runner": "^0.23.0",
|
|
115
115
|
"@storybook/types": "^8.0.9",
|
|
116
116
|
"@storybook/web-components": "^8.0.9",
|
|
117
117
|
"@storybook/web-components-vite": "^8.0.9",
|
|
@@ -2,9 +2,6 @@ import { describe, expect, it } from 'vitest';
|
|
|
2
2
|
|
|
3
3
|
import { computeInitialValues } from './computeInitialValues';
|
|
4
4
|
|
|
5
|
-
const today = new Date();
|
|
6
|
-
const earliestDate = '1900-01-01';
|
|
7
|
-
|
|
8
5
|
const fromOption = 'fromOption';
|
|
9
6
|
const toOption = 'toOption';
|
|
10
7
|
const fromToOption = 'fromToOption';
|
|
@@ -19,62 +16,60 @@ const dateRangeOptions = [
|
|
|
19
16
|
|
|
20
17
|
describe('computeInitialValues', () => {
|
|
21
18
|
it('should return undefined for null value', () => {
|
|
22
|
-
const result = computeInitialValues(null,
|
|
19
|
+
const result = computeInitialValues(null, dateRangeOptions);
|
|
23
20
|
|
|
24
21
|
expect(result).toBeUndefined();
|
|
25
22
|
});
|
|
26
23
|
|
|
27
24
|
it('should compute initial value if value is dateRangeOption label', () => {
|
|
28
|
-
const result = computeInitialValues(fromToOption,
|
|
25
|
+
const result = computeInitialValues(fromToOption, dateRangeOptions);
|
|
29
26
|
|
|
30
27
|
expect(result?.initialSelectedDateRange).toEqual(fromToOption);
|
|
31
28
|
expectDateMatches(result?.initialSelectedDateFrom, new Date(dateFromOptionValue));
|
|
32
29
|
expectDateMatches(result?.initialSelectedDateTo, new Date(dateToOptionValue));
|
|
33
30
|
});
|
|
34
31
|
|
|
35
|
-
it('should
|
|
36
|
-
const result = computeInitialValues(fromOption,
|
|
32
|
+
it('should compute initial value if value is dateRangeOption label with missing date to', () => {
|
|
33
|
+
const result = computeInitialValues(fromOption, dateRangeOptions);
|
|
37
34
|
|
|
38
35
|
expect(result?.initialSelectedDateRange).toEqual(fromOption);
|
|
39
36
|
expectDateMatches(result?.initialSelectedDateFrom, new Date(dateFromOptionValue));
|
|
40
|
-
expectDateMatches(result?.initialSelectedDateTo,
|
|
37
|
+
expectDateMatches(result?.initialSelectedDateTo, undefined);
|
|
41
38
|
});
|
|
42
39
|
|
|
43
|
-
it('should
|
|
44
|
-
const result = computeInitialValues(toOption,
|
|
40
|
+
it('should compute initial value if value is dateRangeOption label with missing date from', () => {
|
|
41
|
+
const result = computeInitialValues(toOption, dateRangeOptions);
|
|
45
42
|
|
|
46
43
|
expect(result?.initialSelectedDateRange).toEqual(toOption);
|
|
47
|
-
expectDateMatches(result?.initialSelectedDateFrom,
|
|
44
|
+
expectDateMatches(result?.initialSelectedDateFrom, undefined);
|
|
48
45
|
expectDateMatches(result?.initialSelectedDateTo, new Date(dateToOptionValue));
|
|
49
46
|
});
|
|
50
47
|
|
|
51
48
|
it('should throw when initial value is unknown', () => {
|
|
52
|
-
expect(() => computeInitialValues('not a known value',
|
|
49
|
+
expect(() => computeInitialValues('not a known value', dateRangeOptions)).toThrowError(
|
|
53
50
|
/Invalid value "not a known value", It must be one of/,
|
|
54
51
|
);
|
|
55
52
|
});
|
|
56
53
|
|
|
57
54
|
it('should throw when initial value is set but no options are provided', () => {
|
|
58
|
-
expect(() => computeInitialValues('not a known value',
|
|
59
|
-
/There are no selectable options/,
|
|
60
|
-
);
|
|
55
|
+
expect(() => computeInitialValues('not a known value', [])).toThrowError(/There are no selectable options/);
|
|
61
56
|
});
|
|
62
57
|
|
|
63
|
-
it('should
|
|
58
|
+
it('should compute initial date if only dateFrom is given', () => {
|
|
64
59
|
const initialDateFrom = '2020-01-01';
|
|
65
|
-
const result = computeInitialValues({ dateFrom: initialDateFrom },
|
|
60
|
+
const result = computeInitialValues({ dateFrom: initialDateFrom }, dateRangeOptions);
|
|
66
61
|
|
|
67
62
|
expect(result?.initialSelectedDateRange).toBeUndefined();
|
|
68
63
|
expectDateMatches(result?.initialSelectedDateFrom, new Date(initialDateFrom));
|
|
69
|
-
expectDateMatches(result?.initialSelectedDateTo,
|
|
64
|
+
expectDateMatches(result?.initialSelectedDateTo, undefined);
|
|
70
65
|
});
|
|
71
66
|
|
|
72
|
-
it('should
|
|
67
|
+
it('should compute initial date if only dateTo is given', () => {
|
|
73
68
|
const initialDateTo = '2020-01-01';
|
|
74
|
-
const result = computeInitialValues({ dateTo: initialDateTo },
|
|
69
|
+
const result = computeInitialValues({ dateTo: initialDateTo }, dateRangeOptions);
|
|
75
70
|
|
|
76
71
|
expect(result?.initialSelectedDateRange).toBeUndefined();
|
|
77
|
-
expectDateMatches(result?.initialSelectedDateFrom,
|
|
72
|
+
expectDateMatches(result?.initialSelectedDateFrom, undefined);
|
|
78
73
|
expectDateMatches(result?.initialSelectedDateTo, new Date(initialDateTo));
|
|
79
74
|
});
|
|
80
75
|
|
|
@@ -86,7 +81,6 @@ describe('computeInitialValues', () => {
|
|
|
86
81
|
dateFrom: initialDateFrom,
|
|
87
82
|
dateTo: initialDateTo,
|
|
88
83
|
},
|
|
89
|
-
earliestDate,
|
|
90
84
|
dateRangeOptions,
|
|
91
85
|
);
|
|
92
86
|
|
|
@@ -103,7 +97,6 @@ describe('computeInitialValues', () => {
|
|
|
103
97
|
dateFrom: initialDateFrom,
|
|
104
98
|
dateTo: initialDateTo,
|
|
105
99
|
},
|
|
106
|
-
earliestDate,
|
|
107
100
|
dateRangeOptions,
|
|
108
101
|
);
|
|
109
102
|
|
|
@@ -113,15 +106,11 @@ describe('computeInitialValues', () => {
|
|
|
113
106
|
});
|
|
114
107
|
|
|
115
108
|
it('should throw if initial "from" is not a valid date', () => {
|
|
116
|
-
expect(() => computeInitialValues({ dateFrom: 'not a date' },
|
|
117
|
-
'Invalid value.dateFrom',
|
|
118
|
-
);
|
|
109
|
+
expect(() => computeInitialValues({ dateFrom: 'not a date' }, [])).toThrowError('Invalid value.dateFrom');
|
|
119
110
|
});
|
|
120
111
|
|
|
121
112
|
it('should throw if initial "to" is not a valid date', () => {
|
|
122
|
-
expect(() => computeInitialValues({ dateTo: 'not a date' },
|
|
123
|
-
'Invalid value.dateTo',
|
|
124
|
-
);
|
|
113
|
+
expect(() => computeInitialValues({ dateTo: 'not a date' }, [])).toThrowError('Invalid value.dateTo');
|
|
125
114
|
});
|
|
126
115
|
|
|
127
116
|
function expectDateMatches(actual: Date | undefined, expected: Date | undefined) {
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
import { type DateRangeOption, type DateRangeValue } from './dateRangeOption';
|
|
2
|
-
import {
|
|
2
|
+
import { getSelectableOptions } from './selectableOptions';
|
|
3
3
|
import { UserFacingError } from '../components/error-display';
|
|
4
4
|
|
|
5
|
-
export function computeInitialValues(value: DateRangeValue,
|
|
5
|
+
export function computeInitialValues(value: DateRangeValue, dateRangeOptions: DateRangeOption[]) {
|
|
6
6
|
if (value === null) {
|
|
7
7
|
return undefined;
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
if (typeof value === 'string') {
|
|
11
11
|
const selectableOptions = getSelectableOptions(dateRangeOptions);
|
|
12
|
-
const
|
|
12
|
+
const matchingOption = dateRangeOptions.find((option) => option.label === value);
|
|
13
13
|
|
|
14
|
-
if (
|
|
14
|
+
if (matchingOption === undefined) {
|
|
15
15
|
if (selectableOptions.length === 0) {
|
|
16
16
|
throw new UserFacingError('Invalid value', 'There are no selectable options, but value is set.');
|
|
17
17
|
}
|
|
@@ -21,34 +21,24 @@ export function computeInitialValues(value: DateRangeValue, earliestDate: string
|
|
|
21
21
|
);
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
const { dateFrom, dateTo } = getDatesForSelectorValue(initialSelectedDateRange, dateRangeOptions, earliestDate);
|
|
25
|
-
|
|
26
24
|
return {
|
|
27
|
-
initialSelectedDateRange,
|
|
28
|
-
initialSelectedDateFrom:
|
|
29
|
-
|
|
25
|
+
initialSelectedDateRange: matchingOption.label,
|
|
26
|
+
initialSelectedDateFrom:
|
|
27
|
+
matchingOption.dateFrom !== undefined ? new Date(matchingOption.dateFrom) : undefined,
|
|
28
|
+
initialSelectedDateTo: matchingOption.dateTo !== undefined ? new Date(matchingOption.dateTo) : undefined,
|
|
30
29
|
};
|
|
31
30
|
}
|
|
32
31
|
|
|
33
32
|
const { dateFrom, dateTo } = value;
|
|
34
33
|
|
|
35
|
-
const initialSelectedDateFrom =
|
|
36
|
-
let initialSelectedDateTo =
|
|
37
|
-
|
|
38
|
-
if (isNaN(initialSelectedDateFrom.getTime())) {
|
|
39
|
-
throw new UserFacingError(
|
|
40
|
-
'Invalid value.dateFrom',
|
|
41
|
-
`Invalid value.dateFrom "${dateFrom}", It must be of the format YYYY-MM-DD`,
|
|
42
|
-
);
|
|
43
|
-
}
|
|
44
|
-
if (isNaN(initialSelectedDateTo.getTime())) {
|
|
45
|
-
throw new UserFacingError(
|
|
46
|
-
'Invalid value.dateTo',
|
|
47
|
-
`Invalid value.dateTo "${dateTo}", It must be of the format YYYY-MM-DD`,
|
|
48
|
-
);
|
|
49
|
-
}
|
|
34
|
+
const initialSelectedDateFrom = parseMaybeDate(dateFrom, 'dateFrom');
|
|
35
|
+
let initialSelectedDateTo = parseMaybeDate(dateTo, 'dateTo');
|
|
50
36
|
|
|
51
|
-
if (
|
|
37
|
+
if (
|
|
38
|
+
initialSelectedDateFrom !== undefined &&
|
|
39
|
+
initialSelectedDateTo !== undefined &&
|
|
40
|
+
initialSelectedDateFrom > initialSelectedDateTo
|
|
41
|
+
) {
|
|
52
42
|
initialSelectedDateTo = initialSelectedDateFrom;
|
|
53
43
|
}
|
|
54
44
|
|
|
@@ -59,6 +49,18 @@ export function computeInitialValues(value: DateRangeValue, earliestDate: string
|
|
|
59
49
|
};
|
|
60
50
|
}
|
|
61
51
|
|
|
62
|
-
function
|
|
63
|
-
|
|
52
|
+
function parseMaybeDate(date: string | undefined, name: string): Date | undefined {
|
|
53
|
+
if (date === undefined || date === '') {
|
|
54
|
+
return undefined;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const parsedDate = new Date(date);
|
|
58
|
+
if (isNaN(parsedDate.getTime())) {
|
|
59
|
+
throw new UserFacingError(
|
|
60
|
+
`Invalid value.${name}`,
|
|
61
|
+
`Invalid value.${name} "${date}", it must be of the format YYYY-MM-DD`,
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return parsedDate;
|
|
64
66
|
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { type Meta, type PreactRenderer, type StoryObj } from '@storybook/preact';
|
|
2
2
|
import { expect, fn, userEvent, waitFor, within } from '@storybook/test';
|
|
3
3
|
import type { StepFunction } from '@storybook/types';
|
|
4
|
-
import dayjs from 'dayjs/esm';
|
|
5
4
|
import { useEffect, useRef, useState } from 'preact/hooks';
|
|
6
5
|
|
|
7
6
|
import { DateRangeFilter, type DateRangeFilterProps } from './date-range-filter';
|
|
@@ -13,7 +12,9 @@ import { dateRangeOptionPresets, type DateRangeValue } from './dateRangeOption';
|
|
|
13
12
|
import { expectInvalidAttributesErrorMessage } from '../shared/stories/expectErrorMessage';
|
|
14
13
|
import { expectOptionSelected } from '../shared/stories/expectOptionSelected';
|
|
15
14
|
|
|
16
|
-
const
|
|
15
|
+
const allTimes = {
|
|
16
|
+
label: 'All times',
|
|
17
|
+
};
|
|
17
18
|
|
|
18
19
|
const customDateRange = {
|
|
19
20
|
label: 'CustomDateRange',
|
|
@@ -43,11 +44,6 @@ const meta: Meta<DateRangeFilterProps> = {
|
|
|
43
44
|
type: 'object',
|
|
44
45
|
},
|
|
45
46
|
},
|
|
46
|
-
earliestDate: {
|
|
47
|
-
control: {
|
|
48
|
-
type: 'text',
|
|
49
|
-
},
|
|
50
|
-
},
|
|
51
47
|
width: {
|
|
52
48
|
control: {
|
|
53
49
|
type: 'text',
|
|
@@ -55,8 +51,7 @@ const meta: Meta<DateRangeFilterProps> = {
|
|
|
55
51
|
},
|
|
56
52
|
},
|
|
57
53
|
args: {
|
|
58
|
-
dateRangeOptions: [dateRangeOptionPresets().lastMonth,
|
|
59
|
-
earliestDate,
|
|
54
|
+
dateRangeOptions: [dateRangeOptionPresets().lastMonth, allTimes, customDateRange],
|
|
60
55
|
value: null,
|
|
61
56
|
lapisDateField: 'aDateColumn',
|
|
62
57
|
width: '100%',
|
|
@@ -122,7 +117,7 @@ export const SetCorrectInitialDateFrom: StoryObj<DateRangeFilterProps> = {
|
|
|
122
117
|
await waitFor(async () => {
|
|
123
118
|
await expectOptionSelected(canvasElement, 'Custom');
|
|
124
119
|
await expect(dateFromPicker(canvas)).toHaveValue(initialDateFrom);
|
|
125
|
-
await expect(dateToPicker(canvas)).toHaveValue(
|
|
120
|
+
await expect(dateToPicker(canvas)).toHaveValue('');
|
|
126
121
|
});
|
|
127
122
|
},
|
|
128
123
|
};
|
|
@@ -140,7 +135,7 @@ export const SetCorrectInitialDateTo: StoryObj<DateRangeFilterProps> = {
|
|
|
140
135
|
|
|
141
136
|
await waitFor(async () => {
|
|
142
137
|
await expectOptionSelected(canvasElement, 'Custom');
|
|
143
|
-
await expect(dateFromPicker(canvas)).toHaveValue(
|
|
138
|
+
await expect(dateFromPicker(canvas)).toHaveValue('');
|
|
144
139
|
await expect(dateToPicker(canvas)).toHaveValue(initialDateTo);
|
|
145
140
|
});
|
|
146
141
|
},
|
|
@@ -173,7 +168,6 @@ export const SetsValueOnBlur: StoryObj<DateRangeFilterProps> = {
|
|
|
173
168
|
expect.objectContaining({
|
|
174
169
|
detail: {
|
|
175
170
|
aDateColumnFrom: '2000-01-01',
|
|
176
|
-
aDateColumnTo: dayjs().format('YYYY-MM-DD'),
|
|
177
171
|
},
|
|
178
172
|
}),
|
|
179
173
|
);
|
|
@@ -182,7 +176,6 @@ export const SetsValueOnBlur: StoryObj<DateRangeFilterProps> = {
|
|
|
182
176
|
expect.objectContaining({
|
|
183
177
|
detail: {
|
|
184
178
|
dateFrom: '2000-01-01',
|
|
185
|
-
dateTo: dayjs().format('YYYY-MM-DD'),
|
|
186
179
|
},
|
|
187
180
|
}),
|
|
188
181
|
);
|
|
@@ -14,11 +14,10 @@ import { gsEventNames } from '../../utils/gsEventNames';
|
|
|
14
14
|
import { ClearableSelect } from '../components/clearable-select';
|
|
15
15
|
import { ErrorBoundary } from '../components/error-boundary';
|
|
16
16
|
|
|
17
|
-
const
|
|
17
|
+
const CUSTOM_OPTION = 'Custom';
|
|
18
18
|
|
|
19
19
|
const dateRangeFilterInnerPropsSchema = z.object({
|
|
20
20
|
dateRangeOptions: z.array(dateRangeOptionSchema),
|
|
21
|
-
earliestDate: z.string().date(),
|
|
22
21
|
value: dateRangeValueSchema,
|
|
23
22
|
lapisDateField: z.string().min(1),
|
|
24
23
|
placeholder: z.string().optional(),
|
|
@@ -52,15 +51,11 @@ export const DateRangeFilter = (props: DateRangeFilterProps) => {
|
|
|
52
51
|
|
|
53
52
|
export const DateRangeFilterInner = ({
|
|
54
53
|
dateRangeOptions,
|
|
55
|
-
earliestDate = '1900-01-01',
|
|
56
54
|
value,
|
|
57
55
|
lapisDateField,
|
|
58
56
|
placeholder,
|
|
59
57
|
}: DateRangeFilterInnerProps) => {
|
|
60
|
-
const initialValues = useMemo(
|
|
61
|
-
() => computeInitialValues(value, earliestDate, dateRangeOptions),
|
|
62
|
-
[value, earliestDate, dateRangeOptions],
|
|
63
|
-
);
|
|
58
|
+
const initialValues = useMemo(() => computeInitialValues(value, dateRangeOptions), [value, dateRangeOptions]);
|
|
64
59
|
|
|
65
60
|
const divRef = useRef<HTMLDivElement>(null);
|
|
66
61
|
|
|
@@ -75,15 +70,15 @@ export const DateRangeFilterInner = ({
|
|
|
75
70
|
dateTo: initialValues.initialSelectedDateTo,
|
|
76
71
|
}
|
|
77
72
|
: {
|
|
78
|
-
label:
|
|
73
|
+
label: CUSTOM_OPTION,
|
|
79
74
|
dateFrom: initialValues.initialSelectedDateFrom,
|
|
80
75
|
dateTo: initialValues.initialSelectedDateTo,
|
|
81
76
|
};
|
|
82
77
|
}, [initialValues]);
|
|
83
78
|
|
|
84
|
-
const customComboboxValue = { label:
|
|
79
|
+
const customComboboxValue = { label: CUSTOM_OPTION };
|
|
85
80
|
const [options, setOptions] = useState(
|
|
86
|
-
getInitialState()?.label ===
|
|
81
|
+
getInitialState()?.label === CUSTOM_OPTION ? [...dateRangeOptions, customComboboxValue] : [...dateRangeOptions],
|
|
87
82
|
);
|
|
88
83
|
const [state, setState] = useState<DateRangeFilterState>(getInitialState());
|
|
89
84
|
|
|
@@ -101,42 +96,24 @@ export const DateRangeFilterInner = ({
|
|
|
101
96
|
updateState(
|
|
102
97
|
option !== null
|
|
103
98
|
? {
|
|
104
|
-
label: option
|
|
105
|
-
dateFrom:
|
|
106
|
-
dateTo:
|
|
99
|
+
label: option.label,
|
|
100
|
+
dateFrom: toMaybeDate(option.dateFrom),
|
|
101
|
+
dateTo: toMaybeDate(option.dateTo),
|
|
107
102
|
}
|
|
108
103
|
: null,
|
|
109
104
|
);
|
|
110
|
-
if (option?.label !==
|
|
105
|
+
if (option?.label !== CUSTOM_OPTION) {
|
|
111
106
|
setOptions([...dateRangeOptions]);
|
|
112
107
|
}
|
|
113
108
|
};
|
|
114
109
|
|
|
115
|
-
function getFromDate(option: DateRangeOption | null, earliestDate: string) {
|
|
116
|
-
if (!option || option.label === customOption) {
|
|
117
|
-
return undefined;
|
|
118
|
-
}
|
|
119
|
-
return new Date(option?.dateFrom ?? earliestDate);
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
function getToDate(option: DateRangeOption | null) {
|
|
123
|
-
if (!option || option.label === customOption) {
|
|
124
|
-
return undefined;
|
|
125
|
-
}
|
|
126
|
-
if (!option.dateTo) {
|
|
127
|
-
return new Date();
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
return new Date(option.dateTo);
|
|
131
|
-
}
|
|
132
|
-
|
|
133
110
|
const onChangeDateFrom = (date: Date | undefined) => {
|
|
134
111
|
if (date?.toDateString() === state?.dateFrom?.toDateString()) {
|
|
135
112
|
return;
|
|
136
113
|
}
|
|
137
114
|
|
|
138
115
|
updateState({
|
|
139
|
-
label:
|
|
116
|
+
label: CUSTOM_OPTION,
|
|
140
117
|
dateFrom: date,
|
|
141
118
|
dateTo: state?.dateTo,
|
|
142
119
|
});
|
|
@@ -149,7 +126,7 @@ export const DateRangeFilterInner = ({
|
|
|
149
126
|
}
|
|
150
127
|
|
|
151
128
|
updateState({
|
|
152
|
-
label:
|
|
129
|
+
label: CUSTOM_OPTION,
|
|
153
130
|
dateFrom: state?.dateFrom,
|
|
154
131
|
dateTo: date,
|
|
155
132
|
});
|
|
@@ -184,7 +161,7 @@ export const DateRangeFilterInner = ({
|
|
|
184
161
|
if (state === null) {
|
|
185
162
|
return null;
|
|
186
163
|
}
|
|
187
|
-
if (state.label ===
|
|
164
|
+
if (state.label === CUSTOM_OPTION) {
|
|
188
165
|
return {
|
|
189
166
|
dateFrom: state.dateFrom !== undefined ? toYYYYMMDD(state.dateFrom) : undefined,
|
|
190
167
|
dateTo: state.dateTo !== undefined ? toYYYYMMDD(state.dateTo) : undefined,
|
|
@@ -233,3 +210,7 @@ export const DateRangeFilterInner = ({
|
|
|
233
210
|
</div>
|
|
234
211
|
);
|
|
235
212
|
};
|
|
213
|
+
|
|
214
|
+
function toMaybeDate(dateString: string | undefined) {
|
|
215
|
+
return dateString ? new Date(dateString) : undefined;
|
|
216
|
+
}
|
|
@@ -11,12 +11,10 @@ export const dateRangeOptionSchema = z.object({
|
|
|
11
11
|
label: z.string(),
|
|
12
12
|
/**
|
|
13
13
|
* The start date of the date range in the format `YYYY-MM-DD`.
|
|
14
|
-
* If not set, the date range selector will default to the `earliestDate` property.
|
|
15
14
|
*/
|
|
16
15
|
dateFrom: z.string().date().optional(),
|
|
17
16
|
/**
|
|
18
17
|
* The end date of the date range in the format `YYYY-MM-DD`.
|
|
19
|
-
* If not set, the date range selector will default to the current date.
|
|
20
18
|
*/
|
|
21
19
|
dateTo: z.string().date().optional(),
|
|
22
20
|
});
|
|
@@ -52,7 +50,6 @@ type DateRangeOptionPresets = {
|
|
|
52
50
|
last3Months: DateRangeOption;
|
|
53
51
|
last6Months: DateRangeOption;
|
|
54
52
|
lastYear: DateRangeOption;
|
|
55
|
-
allTimes: DateRangeOption;
|
|
56
53
|
};
|
|
57
54
|
|
|
58
55
|
let dateRangeOptionsPresetsCacheDate: string | null = null;
|
|
@@ -115,9 +112,6 @@ export const dateRangeOptionPresets = (): DateRangeOptionPresets => {
|
|
|
115
112
|
label: 'Last year',
|
|
116
113
|
dateFrom: toYYYYMMDD(lastYear),
|
|
117
114
|
},
|
|
118
|
-
allTimes: {
|
|
119
|
-
label: 'All times',
|
|
120
|
-
},
|
|
121
115
|
};
|
|
122
116
|
}
|
|
123
117
|
|
|
@@ -5,26 +5,3 @@ export const getSelectableOptions = (dateRangeOptions: DateRangeOption[]) => {
|
|
|
5
5
|
return { label: customSelectOption.label, value: customSelectOption.label };
|
|
6
6
|
});
|
|
7
7
|
};
|
|
8
|
-
|
|
9
|
-
export const getDatesForSelectorValue = (
|
|
10
|
-
initialSelectedDateRange: string | undefined,
|
|
11
|
-
dateRangeOptions: DateRangeOption[],
|
|
12
|
-
earliestDate: string,
|
|
13
|
-
) => {
|
|
14
|
-
const today = new Date();
|
|
15
|
-
const defaultDates = { dateFrom: new Date(earliestDate), dateTo: today };
|
|
16
|
-
|
|
17
|
-
if (initialSelectedDateRange === undefined) {
|
|
18
|
-
return defaultDates;
|
|
19
|
-
}
|
|
20
|
-
|
|
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
|
-
};
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
return defaultDates;
|
|
30
|
-
};
|