@genspectrum/dashboard-components 0.3.1 → 0.4.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 +86 -29
- package/dist/dashboard-components.js +391 -207
- package/dist/dashboard-components.js.map +1 -1
- package/dist/genspectrum-components.d.ts +37 -12
- package/dist/style.css +58 -50
- package/package.json +2 -1
- package/src/operator/RenameFieldOperator.spec.ts +28 -0
- package/src/operator/RenameFieldOperator.ts +19 -0
- package/src/preact/aggregatedData/aggregate.tsx +1 -1
- package/src/preact/components/info.stories.tsx +8 -8
- package/src/preact/components/info.tsx +113 -20
- package/src/preact/dateRangeSelector/computeInitialValues.spec.ts +99 -0
- package/src/preact/dateRangeSelector/computeInitialValues.ts +73 -0
- package/src/preact/dateRangeSelector/date-range-selector.stories.tsx +93 -4
- package/src/preact/dateRangeSelector/date-range-selector.tsx +27 -88
- package/src/preact/dateRangeSelector/selectableOptions.ts +79 -0
- package/src/preact/locationFilter/location-filter.tsx +1 -1
- package/src/preact/mutationComparison/mutation-comparison.tsx +1 -1
- package/src/preact/mutationFilter/mutation-filter.stories.tsx +3 -6
- package/src/preact/mutationFilter/mutation-filter.tsx +48 -54
- package/src/preact/mutations/mutations.tsx +1 -1
- package/src/preact/prevalenceOverTime/prevalence-over-time.stories.tsx +3 -0
- package/src/preact/prevalenceOverTime/prevalence-over-time.tsx +7 -3
- package/src/preact/relativeGrowthAdvantage/relative-growth-advantage.stories.tsx +2 -0
- package/src/preact/relativeGrowthAdvantage/relative-growth-advantage.tsx +6 -2
- package/src/query/queryPrevalenceOverTime.ts +13 -7
- package/src/query/queryRelativeGrowthAdvantage.ts +11 -7
- package/src/web-components/input/gs-date-range-selector.stories.ts +11 -5
- package/src/web-components/input/gs-date-range-selector.tsx +22 -5
- package/src/web-components/input/gs-location-filter.stories.ts +6 -7
- package/src/web-components/input/gs-location-filter.tsx +3 -2
- package/src/web-components/input/gs-mutation-filter.stories.ts +1 -8
- package/src/web-components/input/gs-mutation-filter.tsx +1 -9
- package/src/web-components/visualization/gs-prevalence-over-time.stories.ts +6 -2
- package/src/web-components/visualization/gs-prevalence-over-time.tsx +11 -0
- package/src/web-components/visualization/gs-relative-growth-advantage.stories.ts +3 -0
- package/src/web-components/visualization/gs-relative-growth-advantage.tsx +11 -0
|
@@ -7,7 +7,6 @@ import { type Deletion, type Insertion, type Mutation, type Substitution } from
|
|
|
7
7
|
import { ReferenceGenomeContext } from '../ReferenceGenomeContext';
|
|
8
8
|
import { ErrorBoundary } from '../components/error-boundary';
|
|
9
9
|
import Info from '../components/info';
|
|
10
|
-
import { ResizeContainer } from '../components/resize-container';
|
|
11
10
|
import { singleGraphColorRGBByName } from '../shared/charts/colors';
|
|
12
11
|
import { DeleteIcon } from '../shared/icons/DeleteIcon';
|
|
13
12
|
|
|
@@ -17,7 +16,6 @@ export interface MutationFilterInnerProps {
|
|
|
17
16
|
|
|
18
17
|
export interface MutationFilterProps extends MutationFilterInnerProps {
|
|
19
18
|
width: string;
|
|
20
|
-
height: string;
|
|
21
19
|
}
|
|
22
20
|
|
|
23
21
|
export type SelectedFilters = {
|
|
@@ -31,14 +29,12 @@ export type SelectedMutationFilterStrings = {
|
|
|
31
29
|
[Key in keyof SelectedFilters]: string[];
|
|
32
30
|
};
|
|
33
31
|
|
|
34
|
-
export const MutationFilter: FunctionComponent<MutationFilterProps> = ({ initialValue, width
|
|
35
|
-
const size = { height, width };
|
|
36
|
-
|
|
32
|
+
export const MutationFilter: FunctionComponent<MutationFilterProps> = ({ initialValue, width }) => {
|
|
37
33
|
return (
|
|
38
|
-
<ErrorBoundary size={
|
|
39
|
-
<
|
|
34
|
+
<ErrorBoundary size={{ height: '3.375rem', width }}>
|
|
35
|
+
<div style={width}>
|
|
40
36
|
<MutationFilterInner initialValue={initialValue} />
|
|
41
|
-
</
|
|
37
|
+
</div>
|
|
42
38
|
</ErrorBoundary>
|
|
43
39
|
);
|
|
44
40
|
};
|
|
@@ -54,6 +50,9 @@ export const MutationFilterInner: FunctionComponent<MutationFilterInnerProps> =
|
|
|
54
50
|
|
|
55
51
|
const handleSubmit = (event: Event) => {
|
|
56
52
|
event.preventDefault();
|
|
53
|
+
if (inputValue === '') {
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
57
56
|
|
|
58
57
|
const parsedMutation = parseAndValidateMutation(inputValue, referenceGenome);
|
|
59
58
|
|
|
@@ -102,30 +101,33 @@ export const MutationFilterInner: FunctionComponent<MutationFilterInnerProps> =
|
|
|
102
101
|
};
|
|
103
102
|
|
|
104
103
|
return (
|
|
105
|
-
<
|
|
106
|
-
<div
|
|
104
|
+
<form className='w-full border boder-gray-300 rounded-md relative' onSubmit={handleSubmit} ref={formRef}>
|
|
105
|
+
<div className='absolute -top-3 -right-3'>
|
|
106
|
+
<Info height={'100px'}>Info for mutation filter</Info>
|
|
107
|
+
</div>
|
|
108
|
+
<div className='w-full flex p-2 flex-wrap items-center'>
|
|
107
109
|
<SelectedMutationDisplay
|
|
108
110
|
selectedFilters={selectedFilters}
|
|
109
111
|
setSelectedFilters={setSelectedFilters}
|
|
110
112
|
fireChangeEvent={fireChangeEvent}
|
|
111
113
|
/>
|
|
112
|
-
<
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
<form className='mt-2 w-full' onSubmit={handleSubmit} ref={formRef}>
|
|
116
|
-
<label className={`input flex items-center gap-2 ${isError ? 'input-error' : 'input-bordered'}`}>
|
|
114
|
+
<div
|
|
115
|
+
className={`w-full flex border ${isError ? 'border-red-500' : 'border-gray-300'} border-solid m-2 text-sm focus-within:border-gray-400 `}
|
|
116
|
+
>
|
|
117
117
|
<input
|
|
118
|
-
className='grow
|
|
118
|
+
className='grow flex-1 p-1 border-none focus:outline-none focus:ring-0'
|
|
119
119
|
type='text'
|
|
120
120
|
value={inputValue}
|
|
121
121
|
onInput={handleInputChange}
|
|
122
122
|
placeholder={getPlaceholder(referenceGenome)}
|
|
123
123
|
onBlur={handleOnBlur}
|
|
124
124
|
/>
|
|
125
|
-
<button className='btn btn-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
125
|
+
<button type='submit' className='btn btn-xs m-1'>
|
|
126
|
+
+
|
|
127
|
+
</button>
|
|
128
|
+
</div>
|
|
129
|
+
</div>
|
|
130
|
+
</form>
|
|
129
131
|
);
|
|
130
132
|
};
|
|
131
133
|
|
|
@@ -193,44 +195,36 @@ const SelectedMutationDisplay: FunctionComponent<{
|
|
|
193
195
|
};
|
|
194
196
|
|
|
195
197
|
return (
|
|
196
|
-
|
|
198
|
+
<>
|
|
197
199
|
{selectedFilters.nucleotideMutations.map((mutation) => (
|
|
198
|
-
<
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
}
|
|
204
|
-
/>
|
|
205
|
-
</li>
|
|
200
|
+
<SelectedNucleotideMutation
|
|
201
|
+
key={mutation.toString()}
|
|
202
|
+
mutation={mutation}
|
|
203
|
+
onDelete={(mutation: Substitution | Deletion) => onSelectedRemoved(mutation, 'nucleotideMutations')}
|
|
204
|
+
/>
|
|
206
205
|
))}
|
|
207
206
|
{selectedFilters.aminoAcidMutations.map((mutation) => (
|
|
208
|
-
<
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
}
|
|
214
|
-
/>
|
|
215
|
-
</li>
|
|
207
|
+
<SelectedAminoAcidMutation
|
|
208
|
+
key={mutation.toString()}
|
|
209
|
+
mutation={mutation}
|
|
210
|
+
onDelete={(mutation: Substitution | Deletion) => onSelectedRemoved(mutation, 'aminoAcidMutations')}
|
|
211
|
+
/>
|
|
216
212
|
))}
|
|
217
213
|
{selectedFilters.nucleotideInsertions.map((insertion) => (
|
|
218
|
-
<
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
</li>
|
|
214
|
+
<SelectedNucleotideInsertion
|
|
215
|
+
key={insertion.toString()}
|
|
216
|
+
insertion={insertion}
|
|
217
|
+
onDelete={(insertion) => onSelectedRemoved(insertion, 'nucleotideInsertions')}
|
|
218
|
+
/>
|
|
224
219
|
))}
|
|
225
220
|
{selectedFilters.aminoAcidInsertions.map((insertion) => (
|
|
226
|
-
<
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
</li>
|
|
221
|
+
<SelectedAminoAcidInsertion
|
|
222
|
+
key={insertion.toString()}
|
|
223
|
+
insertion={insertion}
|
|
224
|
+
onDelete={(insertion: Insertion) => onSelectedRemoved(insertion, 'aminoAcidInsertions')}
|
|
225
|
+
/>
|
|
232
226
|
))}
|
|
233
|
-
|
|
227
|
+
</>
|
|
234
228
|
);
|
|
235
229
|
};
|
|
236
230
|
|
|
@@ -313,15 +307,15 @@ const SelectedFilter = <MutationType extends Mutation>({
|
|
|
313
307
|
textColor,
|
|
314
308
|
}: SelectedFilterProps<MutationType>) => {
|
|
315
309
|
return (
|
|
316
|
-
<
|
|
317
|
-
class='
|
|
310
|
+
<span
|
|
311
|
+
class='inline-block mx-1 px-2 py-1 font-medium text-xs rounded-full'
|
|
318
312
|
style={{ backgroundColor, color: textColor }}
|
|
319
313
|
>
|
|
320
|
-
|
|
314
|
+
{mutation.toString()}
|
|
321
315
|
<button type='button' onClick={() => onDelete(mutation)}>
|
|
322
316
|
<DeleteIcon />
|
|
323
317
|
</button>
|
|
324
|
-
</
|
|
318
|
+
</span>
|
|
325
319
|
);
|
|
326
320
|
};
|
|
327
321
|
|
|
@@ -48,6 +48,7 @@ const Template = {
|
|
|
48
48
|
width={args.width}
|
|
49
49
|
height={args.height}
|
|
50
50
|
headline={args.headline}
|
|
51
|
+
lapisDateField={args.lapisDateField}
|
|
51
52
|
/>
|
|
52
53
|
</LapisUrlContext.Provider>
|
|
53
54
|
),
|
|
@@ -68,6 +69,7 @@ export const TwoVariants = {
|
|
|
68
69
|
width: '100%',
|
|
69
70
|
height: '700px',
|
|
70
71
|
headline: 'Prevalence over time',
|
|
72
|
+
lapisDateField: 'date',
|
|
71
73
|
},
|
|
72
74
|
parameters: {
|
|
73
75
|
fetchMock: {
|
|
@@ -139,6 +141,7 @@ export const OneVariant = {
|
|
|
139
141
|
width: '100%',
|
|
140
142
|
height: '700px',
|
|
141
143
|
headline: 'Prevalence over time',
|
|
144
|
+
lapisDateField: 'date',
|
|
142
145
|
},
|
|
143
146
|
parameters: {
|
|
144
147
|
fetchMock: {
|
|
@@ -39,6 +39,7 @@ export interface PrevalenceOverTimeInnerProps {
|
|
|
39
39
|
smoothingWindow: number;
|
|
40
40
|
views: View[];
|
|
41
41
|
confidenceIntervalMethods: ConfidenceIntervalMethod[];
|
|
42
|
+
lapisDateField: string;
|
|
42
43
|
}
|
|
43
44
|
|
|
44
45
|
export const PrevalenceOverTime: FunctionComponent<PrevalenceOverTimeProps> = ({
|
|
@@ -51,6 +52,7 @@ export const PrevalenceOverTime: FunctionComponent<PrevalenceOverTimeProps> = ({
|
|
|
51
52
|
width,
|
|
52
53
|
height,
|
|
53
54
|
headline = 'Prevalence over time',
|
|
55
|
+
lapisDateField,
|
|
54
56
|
}) => {
|
|
55
57
|
const size = { height, width };
|
|
56
58
|
|
|
@@ -65,6 +67,7 @@ export const PrevalenceOverTime: FunctionComponent<PrevalenceOverTimeProps> = ({
|
|
|
65
67
|
smoothingWindow={smoothingWindow}
|
|
66
68
|
views={views}
|
|
67
69
|
confidenceIntervalMethods={confidenceIntervalMethods}
|
|
70
|
+
lapisDateField={lapisDateField}
|
|
68
71
|
/>
|
|
69
72
|
</Headline>
|
|
70
73
|
</ResizeContainer>
|
|
@@ -79,11 +82,12 @@ export const PrevalenceOverTimeInner: FunctionComponent<PrevalenceOverTimeInnerP
|
|
|
79
82
|
smoothingWindow,
|
|
80
83
|
views,
|
|
81
84
|
confidenceIntervalMethods,
|
|
85
|
+
lapisDateField,
|
|
82
86
|
}) => {
|
|
83
87
|
const lapis = useContext(LapisUrlContext);
|
|
84
88
|
|
|
85
89
|
const { data, error, isLoading } = useQuery(
|
|
86
|
-
() => queryPrevalenceOverTime(numerator, denominator, granularity, smoothingWindow, lapis),
|
|
90
|
+
() => queryPrevalenceOverTime(numerator, denominator, granularity, smoothingWindow, lapis, lapisDateField),
|
|
87
91
|
[lapis, numerator, denominator, granularity, smoothingWindow],
|
|
88
92
|
);
|
|
89
93
|
|
|
@@ -226,9 +230,9 @@ const Toolbar: FunctionComponent<ToolbarProps> = ({
|
|
|
226
230
|
);
|
|
227
231
|
};
|
|
228
232
|
|
|
229
|
-
const PrevalenceOverTimeInfo: FunctionComponent = () => {
|
|
233
|
+
const PrevalenceOverTimeInfo: FunctionComponent = ({}) => {
|
|
230
234
|
return (
|
|
231
|
-
<Info
|
|
235
|
+
<Info height={'100px'}>
|
|
232
236
|
<InfoHeadline1>Prevalence over time</InfoHeadline1>
|
|
233
237
|
<InfoParagraph>Prevalence over time info.</InfoParagraph>
|
|
234
238
|
</Info>
|
|
@@ -35,6 +35,7 @@ export const Primary = {
|
|
|
35
35
|
width={args.width}
|
|
36
36
|
height={args.height}
|
|
37
37
|
headline={args.headline}
|
|
38
|
+
lapisDateField={args.lapisDateField}
|
|
38
39
|
/>
|
|
39
40
|
</LapisUrlContext.Provider>
|
|
40
41
|
),
|
|
@@ -46,6 +47,7 @@ export const Primary = {
|
|
|
46
47
|
width: '100%',
|
|
47
48
|
height: '700px',
|
|
48
49
|
headline: 'Relative growth advantage',
|
|
50
|
+
lapisDateField: 'date',
|
|
49
51
|
},
|
|
50
52
|
parameters: {
|
|
51
53
|
fetchMock: {
|
|
@@ -33,6 +33,7 @@ export interface RelativeGrowthAdvantagePropsInner {
|
|
|
33
33
|
denominator: LapisFilter;
|
|
34
34
|
generationTime: number;
|
|
35
35
|
views: View[];
|
|
36
|
+
lapisDateField: string;
|
|
36
37
|
}
|
|
37
38
|
|
|
38
39
|
export const RelativeGrowthAdvantage: FunctionComponent<RelativeGrowthAdvantageProps> = ({
|
|
@@ -43,6 +44,7 @@ export const RelativeGrowthAdvantage: FunctionComponent<RelativeGrowthAdvantageP
|
|
|
43
44
|
denominator,
|
|
44
45
|
generationTime,
|
|
45
46
|
headline = 'Relative growth advantage',
|
|
47
|
+
lapisDateField,
|
|
46
48
|
}) => {
|
|
47
49
|
const size = { height, width };
|
|
48
50
|
|
|
@@ -55,6 +57,7 @@ export const RelativeGrowthAdvantage: FunctionComponent<RelativeGrowthAdvantageP
|
|
|
55
57
|
numerator={numerator}
|
|
56
58
|
denominator={denominator}
|
|
57
59
|
generationTime={generationTime}
|
|
60
|
+
lapisDateField={lapisDateField}
|
|
58
61
|
/>
|
|
59
62
|
</Headline>
|
|
60
63
|
</ResizeContainer>
|
|
@@ -67,12 +70,13 @@ export const RelativeGrowthAdvantageInner: FunctionComponent<RelativeGrowthAdvan
|
|
|
67
70
|
denominator,
|
|
68
71
|
generationTime,
|
|
69
72
|
views,
|
|
73
|
+
lapisDateField,
|
|
70
74
|
}) => {
|
|
71
75
|
const lapis = useContext(LapisUrlContext);
|
|
72
76
|
const [yAxisScaleType, setYAxisScaleType] = useState<ScaleType>('linear');
|
|
73
77
|
|
|
74
78
|
const { data, error, isLoading } = useQuery(
|
|
75
|
-
() => queryRelativeGrowthAdvantage(numerator, denominator, generationTime, lapis),
|
|
79
|
+
() => queryRelativeGrowthAdvantage(numerator, denominator, generationTime, lapis, lapisDateField),
|
|
76
80
|
[lapis, numerator, denominator, generationTime, views],
|
|
77
81
|
);
|
|
78
82
|
|
|
@@ -166,7 +170,7 @@ const RelativeGrowthAdvantageToolbar: FunctionComponent<RelativeGrowthAdvantageT
|
|
|
166
170
|
|
|
167
171
|
const RelativeGrowthAdvantageInfo: FunctionComponent<{ generationTime: number }> = ({ generationTime }) => {
|
|
168
172
|
return (
|
|
169
|
-
<Info
|
|
173
|
+
<Info>
|
|
170
174
|
<InfoHeadline1>Relative growth advantage</InfoHeadline1>
|
|
171
175
|
<InfoParagraph>
|
|
172
176
|
If variants spread pre-dominantly by local transmission across demographic groups, this estimate
|
|
@@ -3,6 +3,7 @@ import { FetchAggregatedOperator } from '../operator/FetchAggregatedOperator';
|
|
|
3
3
|
import { FillMissingOperator } from '../operator/FillMissingOperator';
|
|
4
4
|
import { GroupByAndSumOperator } from '../operator/GroupByAndSumOperator';
|
|
5
5
|
import { MapOperator } from '../operator/MapOperator';
|
|
6
|
+
import { RenameFieldOperator } from '../operator/RenameFieldOperator';
|
|
6
7
|
import { SlidingOperator } from '../operator/SlidingOperator';
|
|
7
8
|
import { SortOperator } from '../operator/SortOperator';
|
|
8
9
|
import { type LapisFilter, type NamedLapisFilter, type TemporalGranularity } from '../types';
|
|
@@ -27,14 +28,15 @@ export function queryPrevalenceOverTime(
|
|
|
27
28
|
granularity: TemporalGranularity,
|
|
28
29
|
smoothingWindow: number,
|
|
29
30
|
lapis: string,
|
|
31
|
+
lapisDateField: string,
|
|
30
32
|
signal?: AbortSignal,
|
|
31
33
|
): Promise<PrevalenceOverTimeData> {
|
|
32
34
|
const numeratorFilters = makeArray(numeratorFilter);
|
|
33
35
|
|
|
34
|
-
const denominatorData = fetchAndPrepare(denominatorFilter, granularity, smoothingWindow);
|
|
36
|
+
const denominatorData = fetchAndPrepare(denominatorFilter, granularity, smoothingWindow, lapisDateField);
|
|
35
37
|
const subQueries = numeratorFilters.map(async (namedLapisFilter) => {
|
|
36
38
|
const { displayName, lapisFilter } = namedLapisFilter;
|
|
37
|
-
const numeratorData = fetchAndPrepare(lapisFilter, granularity, smoothingWindow);
|
|
39
|
+
const numeratorData = fetchAndPrepare(lapisFilter, granularity, smoothingWindow, lapisDateField);
|
|
38
40
|
const divide = new DivisionOperator(
|
|
39
41
|
numeratorData,
|
|
40
42
|
denominatorData,
|
|
@@ -60,11 +62,15 @@ function makeArray<T>(arrayOrSingleItem: T | T[]) {
|
|
|
60
62
|
return [arrayOrSingleItem];
|
|
61
63
|
}
|
|
62
64
|
|
|
63
|
-
function fetchAndPrepare
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
65
|
+
function fetchAndPrepare<LapisDateField extends string>(
|
|
66
|
+
filter: LapisFilter,
|
|
67
|
+
granularity: TemporalGranularity,
|
|
68
|
+
smoothingWindow: number,
|
|
69
|
+
lapisDateField: LapisDateField,
|
|
70
|
+
) {
|
|
71
|
+
const fetchData = new FetchAggregatedOperator<{ [key in LapisDateField]: string | null }>(filter, [lapisDateField]);
|
|
72
|
+
const dataWithFixedDateKey = new RenameFieldOperator(fetchData, lapisDateField, 'date');
|
|
73
|
+
const mapData = new MapOperator(dataWithFixedDateKey, (d) => mapDateToGranularityRange(d, granularity));
|
|
68
74
|
const groupByData = new GroupByAndSumOperator(mapData, 'dateRange', 'count');
|
|
69
75
|
const fillData = new FillMissingOperator(
|
|
70
76
|
groupByData,
|
|
@@ -1,25 +1,29 @@
|
|
|
1
1
|
import { FetchAggregatedOperator } from '../operator/FetchAggregatedOperator';
|
|
2
2
|
import { MapOperator } from '../operator/MapOperator';
|
|
3
|
+
import { RenameFieldOperator } from '../operator/RenameFieldOperator';
|
|
3
4
|
import { type LapisFilter } from '../types';
|
|
4
5
|
import { getMinMaxTemporal, TemporalCache, type YearMonthDay } from '../utils/temporal';
|
|
5
6
|
|
|
6
7
|
export type RelativeGrowthAdvantageData = Awaited<ReturnType<typeof queryRelativeGrowthAdvantage>>;
|
|
7
8
|
|
|
8
|
-
export async function queryRelativeGrowthAdvantage(
|
|
9
|
+
export async function queryRelativeGrowthAdvantage<LapisDateField extends string>(
|
|
9
10
|
numerator: LapisFilter,
|
|
10
11
|
denominator: LapisFilter,
|
|
11
12
|
generationTime: number,
|
|
12
13
|
lapis: string,
|
|
14
|
+
lapisDateField: LapisDateField,
|
|
13
15
|
signal?: AbortSignal,
|
|
14
16
|
) {
|
|
15
17
|
const fetchNumerator = new FetchAggregatedOperator<{
|
|
16
|
-
|
|
17
|
-
}>(numerator, [
|
|
18
|
+
[key in LapisDateField]: string | null;
|
|
19
|
+
}>(numerator, [lapisDateField]);
|
|
18
20
|
const fetchDenominator = new FetchAggregatedOperator<{
|
|
19
|
-
|
|
20
|
-
}>(denominator, [
|
|
21
|
-
const
|
|
22
|
-
const
|
|
21
|
+
[key in LapisDateField]: string | null;
|
|
22
|
+
}>(denominator, [lapisDateField]);
|
|
23
|
+
const mapToFixedDateKeyNumerator = new RenameFieldOperator(fetchNumerator, lapisDateField, 'date');
|
|
24
|
+
const mapToFixedDateKeyDenominator = new RenameFieldOperator(fetchDenominator, lapisDateField, 'date');
|
|
25
|
+
const mapNumerator = new MapOperator(mapToFixedDateKeyNumerator, toYearMonthDay);
|
|
26
|
+
const mapDenominator = new MapOperator(mapToFixedDateKeyDenominator, toYearMonthDay);
|
|
23
27
|
const [numeratorData, denominatorData] = await Promise.all([
|
|
24
28
|
mapNumerator.evaluate(lapis, signal),
|
|
25
29
|
mapDenominator.evaluate(lapis, signal),
|
|
@@ -5,8 +5,11 @@ import { html } from 'lit';
|
|
|
5
5
|
|
|
6
6
|
import { withComponentDocs } from '../../../.storybook/ComponentDocsBlock';
|
|
7
7
|
import { LAPIS_URL } from '../../constants';
|
|
8
|
+
import { type DateRangeSelectorProps } from '../../preact/dateRangeSelector/date-range-selector';
|
|
9
|
+
import './gs-date-range-selector';
|
|
10
|
+
import '../app';
|
|
11
|
+
import { toYYYYMMDD } from '../../preact/dateRangeSelector/dateConversion';
|
|
8
12
|
import {
|
|
9
|
-
type DateRangeSelectorProps,
|
|
10
13
|
PRESET_VALUE_ALL_TIMES,
|
|
11
14
|
PRESET_VALUE_CUSTOM,
|
|
12
15
|
PRESET_VALUE_LAST_2_MONTHS,
|
|
@@ -14,10 +17,7 @@ import {
|
|
|
14
17
|
PRESET_VALUE_LAST_3_MONTHS,
|
|
15
18
|
PRESET_VALUE_LAST_6_MONTHS,
|
|
16
19
|
PRESET_VALUE_LAST_MONTH,
|
|
17
|
-
} from '../../preact/dateRangeSelector/
|
|
18
|
-
import './gs-date-range-selector';
|
|
19
|
-
import '../app';
|
|
20
|
-
import { toYYYYMMDD } from '../../preact/dateRangeSelector/dateConversion';
|
|
20
|
+
} from '../../preact/dateRangeSelector/selectableOptions';
|
|
21
21
|
import { withinShadowRoot } from '../withinShadowRoot.story';
|
|
22
22
|
|
|
23
23
|
const codeExample = String.raw`
|
|
@@ -25,6 +25,8 @@ const codeExample = String.raw`
|
|
|
25
25
|
customSelectOptions='[{ "label": "Year 2021", "dateFrom": "2021-01-01", "dateTo": "2021-12-31" }]'
|
|
26
26
|
earliestDate="1970-01-01"
|
|
27
27
|
initialValue="${PRESET_VALUE_LAST_6_MONTHS}"
|
|
28
|
+
initialDateFrom="2020-01-01"
|
|
29
|
+
initialDateTo="2021-01-01"
|
|
28
30
|
width="100%"
|
|
29
31
|
dateColumn="myDateColumn"
|
|
30
32
|
></gs-date-range-selector>`;
|
|
@@ -82,6 +84,8 @@ const meta: Meta<Required<DateRangeSelectorProps<'CustomDateRange'>>> = {
|
|
|
82
84
|
initialValue: PRESET_VALUE_LAST_6_MONTHS,
|
|
83
85
|
dateColumn: 'aDateColumn',
|
|
84
86
|
width: '100%',
|
|
87
|
+
initialDateFrom: '',
|
|
88
|
+
initialDateTo: '',
|
|
85
89
|
},
|
|
86
90
|
decorators: [withActions],
|
|
87
91
|
tags: ['autodocs'],
|
|
@@ -97,6 +101,8 @@ export const DateRangeSelectorStory: StoryObj<Required<DateRangeSelectorProps<'C
|
|
|
97
101
|
.customSelectOptions=${args.customSelectOptions}
|
|
98
102
|
.earliestDate=${args.earliestDate}
|
|
99
103
|
.initialValue=${args.initialValue}
|
|
104
|
+
.initialDateFrom=${args.initialDateFrom}
|
|
105
|
+
.initialDateTo=${args.initialDateTo}
|
|
100
106
|
.width=${args.width}
|
|
101
107
|
.dateColumn=${args.dateColumn}
|
|
102
108
|
></gs-date-range-selector>
|
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
import { customElement, property } from 'lit/decorators.js';
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
DateRangeSelector,
|
|
6
|
-
type PresetOptionValues,
|
|
7
|
-
} from '../../preact/dateRangeSelector/date-range-selector';
|
|
3
|
+
import { DateRangeSelector } from '../../preact/dateRangeSelector/date-range-selector';
|
|
4
|
+
import { type CustomSelectOption, type PresetOptionValues } from '../../preact/dateRangeSelector/selectableOptions';
|
|
8
5
|
import { type Equals, type Expect } from '../../utils/typeAssertions';
|
|
9
6
|
import { PreactLitAdapter } from '../PreactLitAdapter';
|
|
10
7
|
|
|
@@ -61,6 +58,8 @@ export class DateRangeSelectorComponent extends PreactLitAdapter {
|
|
|
61
58
|
* Must be a valid label from the preset labels or a `label` given in the `customSelectOptions`.
|
|
62
59
|
*
|
|
63
60
|
* If the value is invalid, the component will default to `'last6Months'`.
|
|
61
|
+
*
|
|
62
|
+
* It will be overwritten if `initialDateFrom` or `initialDateTo` is set.
|
|
64
63
|
*/
|
|
65
64
|
@property()
|
|
66
65
|
initialValue:
|
|
@@ -73,6 +72,22 @@ export class DateRangeSelectorComponent extends PreactLitAdapter {
|
|
|
73
72
|
| 'last6Months'
|
|
74
73
|
| string = 'last6Months';
|
|
75
74
|
|
|
75
|
+
/**
|
|
76
|
+
* A date string in the format `YYYY-MM-DD`.
|
|
77
|
+
* If set, the date range selector will be initialized with the given date (overwriting `initialValue` to `custom`).
|
|
78
|
+
* If `initialDateTo` is set, but this is unset, it will default to `earliestDate`.
|
|
79
|
+
*/
|
|
80
|
+
@property()
|
|
81
|
+
initialDateFrom: string = '';
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* A date string in the format `YYYY-MM-DD`.
|
|
85
|
+
* If set, the date range selector will be initialized with the given date (overwriting `initialValue` to `custom`).
|
|
86
|
+
* If `initialDateFrom` is set, but this is unset, it will default to the current date.
|
|
87
|
+
*/
|
|
88
|
+
@property()
|
|
89
|
+
initialDateTo: string = '';
|
|
90
|
+
|
|
76
91
|
/**
|
|
77
92
|
* The width of the component.
|
|
78
93
|
*
|
|
@@ -93,6 +108,8 @@ export class DateRangeSelectorComponent extends PreactLitAdapter {
|
|
|
93
108
|
customSelectOptions={this.customSelectOptions}
|
|
94
109
|
earliestDate={this.earliestDate}
|
|
95
110
|
initialValue={this.initialValue}
|
|
111
|
+
initialDateFrom={this.initialDateFrom}
|
|
112
|
+
initialDateTo={this.initialDateTo}
|
|
96
113
|
dateColumn={this.dateColumn}
|
|
97
114
|
width={this.width}
|
|
98
115
|
/>
|
|
@@ -190,13 +190,12 @@ export const FiresEvent: StoryObj<LocationFilterProps> = {
|
|
|
190
190
|
|
|
191
191
|
await step('Select Asia', async () => {
|
|
192
192
|
await userEvent.type(inputField(), 'Asia');
|
|
193
|
-
await expect(listenerMock).
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
);
|
|
193
|
+
await expect(listenerMock.mock.calls.at(-1)[0].detail).toStrictEqual({
|
|
194
|
+
region: 'Asia',
|
|
195
|
+
country: undefined,
|
|
196
|
+
division: undefined,
|
|
197
|
+
location: undefined,
|
|
198
|
+
});
|
|
200
199
|
});
|
|
201
200
|
|
|
202
201
|
await step('Select Asia / Bangladesh / Rajshahi / Chapainawabgonj', async () => {
|
|
@@ -20,13 +20,14 @@ import { PreactLitAdapter } from '../PreactLitAdapter';
|
|
|
20
20
|
* @fires {CustomEvent<Record<string, string>>} gs-location-changed
|
|
21
21
|
* Fired when a value from the datalist is selected or when a valid value is typed into the field.
|
|
22
22
|
* The `details` of this event contain an object with all `fields` as keys
|
|
23
|
-
* and the corresponding values as values, if they are
|
|
23
|
+
* and the corresponding values as values, even if they are `undefined`.
|
|
24
24
|
* Example:
|
|
25
25
|
* ```
|
|
26
26
|
* {
|
|
27
27
|
* continent: "Asia",
|
|
28
28
|
* country: "China",
|
|
29
|
-
* city: "Beijing"
|
|
29
|
+
* city: "Beijing",
|
|
30
|
+
* district: undefined,
|
|
30
31
|
* }
|
|
31
32
|
* ```
|
|
32
33
|
*/
|
|
@@ -14,7 +14,6 @@ const codeExample = String.raw`
|
|
|
14
14
|
<gs-mutation-filter
|
|
15
15
|
initialValue='["A123T"]'
|
|
16
16
|
width='100%'
|
|
17
|
-
height='6.5rem'
|
|
18
17
|
></gs-mutation-filter>`;
|
|
19
18
|
|
|
20
19
|
const meta: Meta<MutationFilterProps> = {
|
|
@@ -38,7 +37,6 @@ const meta: Meta<MutationFilterProps> = {
|
|
|
38
37
|
},
|
|
39
38
|
},
|
|
40
39
|
width: { control: 'text' },
|
|
41
|
-
height: { control: 'text' },
|
|
42
40
|
},
|
|
43
41
|
decorators: [withActions],
|
|
44
42
|
tags: ['autodocs'],
|
|
@@ -50,18 +48,13 @@ const Template: StoryObj<MutationFilterProps> = {
|
|
|
50
48
|
render: (args) => {
|
|
51
49
|
return html` <gs-app lapis="${LAPIS_URL}">
|
|
52
50
|
<div class="max-w-screen-lg">
|
|
53
|
-
<gs-mutation-filter
|
|
54
|
-
.initialValue=${args.initialValue}
|
|
55
|
-
.width=${args.width}
|
|
56
|
-
.height=${args.height}
|
|
57
|
-
></gs-mutation-filter>
|
|
51
|
+
<gs-mutation-filter .initialValue=${args.initialValue} .width=${args.width}></gs-mutation-filter>
|
|
58
52
|
</div>
|
|
59
53
|
</gs-app>`;
|
|
60
54
|
},
|
|
61
55
|
args: {
|
|
62
56
|
initialValue: [],
|
|
63
57
|
width: '100%',
|
|
64
|
-
height: '3rem',
|
|
65
58
|
},
|
|
66
59
|
};
|
|
67
60
|
|
|
@@ -91,18 +91,10 @@ export class MutationFilterComponent extends PreactLitAdapter {
|
|
|
91
91
|
@property({ type: String })
|
|
92
92
|
width: string = '100%';
|
|
93
93
|
|
|
94
|
-
/**
|
|
95
|
-
* The height of the component.
|
|
96
|
-
*
|
|
97
|
-
* Visit https://genspectrum.github.io/dashboards/?path=/docs/components-size-of-components--docs for more information.
|
|
98
|
-
*/
|
|
99
|
-
@property({ type: String })
|
|
100
|
-
height: string = '6.5rem';
|
|
101
|
-
|
|
102
94
|
override render() {
|
|
103
95
|
return (
|
|
104
96
|
<ReferenceGenomesAwaiter>
|
|
105
|
-
<MutationFilter initialValue={this.initialValue} width={this.width}
|
|
97
|
+
<MutationFilter initialValue={this.initialValue} width={this.width} />
|
|
106
98
|
</ReferenceGenomesAwaiter>
|
|
107
99
|
);
|
|
108
100
|
}
|
|
@@ -23,8 +23,9 @@ const codeExample = String.raw`
|
|
|
23
23
|
views='["bar", "line", "bubble", "table"]'
|
|
24
24
|
confidenceIntervalMethods='["wilson"]'
|
|
25
25
|
headline="Prevalence over time"
|
|
26
|
-
width=
|
|
27
|
-
height=
|
|
26
|
+
width="100%"
|
|
27
|
+
height="700px"
|
|
28
|
+
lapisDateField="date"
|
|
28
29
|
></gs-prevalence-over-time>`;
|
|
29
30
|
|
|
30
31
|
const meta: Meta<Required<PrevalenceOverTimeProps>> = {
|
|
@@ -75,6 +76,7 @@ const Template: StoryObj<Required<PrevalenceOverTimeProps>> = {
|
|
|
75
76
|
.width=${args.width}
|
|
76
77
|
.height=${args.height}
|
|
77
78
|
.headline=${args.headline}
|
|
79
|
+
.lapisDateField=${args.lapisDateField}
|
|
78
80
|
></gs-prevalence-over-time>
|
|
79
81
|
</gs-app>
|
|
80
82
|
`,
|
|
@@ -95,6 +97,7 @@ export const TwoVariants: StoryObj<Required<PrevalenceOverTimeProps>> = {
|
|
|
95
97
|
width: '100%',
|
|
96
98
|
height: '700px',
|
|
97
99
|
headline: 'Prevalence over time',
|
|
100
|
+
lapisDateField: 'date',
|
|
98
101
|
},
|
|
99
102
|
parameters: {
|
|
100
103
|
fetchMock: {
|
|
@@ -166,6 +169,7 @@ export const OneVariant: StoryObj<Required<PrevalenceOverTimeProps>> = {
|
|
|
166
169
|
width: '100%',
|
|
167
170
|
height: '700px',
|
|
168
171
|
headline: 'Prevalence over time',
|
|
172
|
+
lapisDateField: 'date',
|
|
169
173
|
},
|
|
170
174
|
parameters: {
|
|
171
175
|
fetchMock: {
|