@genspectrum/dashboard-components 0.2.0 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/custom-elements.json +330 -179
- package/dist/dashboard-components.js +379 -187
- package/dist/dashboard-components.js.map +1 -1
- package/dist/genspectrum-components.d.ts +160 -114
- package/dist/style.css +171 -71
- package/package.json +1 -2
- package/src/constants.ts +1 -1
- package/src/lapisApi/lapisApi.ts +46 -2
- package/src/lapisApi/lapisTypes.ts +14 -0
- package/src/preact/aggregatedData/aggregate.stories.tsx +4 -2
- package/src/preact/aggregatedData/aggregate.tsx +8 -6
- package/src/preact/components/error-boundary.stories.tsx +6 -14
- package/src/preact/components/error-boundary.tsx +2 -11
- package/src/preact/components/error-display.stories.tsx +12 -5
- package/src/preact/components/error-display.tsx +37 -3
- package/src/preact/components/loading-display.stories.tsx +1 -1
- package/src/preact/components/resize-container.tsx +5 -14
- package/src/preact/dateRangeSelector/date-range-selector.stories.tsx +2 -0
- package/src/preact/dateRangeSelector/date-range-selector.tsx +11 -8
- package/src/preact/locationFilter/fetchAutocompletionList.ts +15 -1
- package/src/preact/locationFilter/location-filter.stories.tsx +1 -1
- package/src/preact/locationFilter/location-filter.tsx +16 -30
- package/src/preact/mutationComparison/mutation-comparison.stories.tsx +6 -3
- package/src/preact/mutationComparison/mutation-comparison.tsx +10 -13
- package/src/preact/mutationComparison/queryMutationData.ts +2 -3
- package/src/preact/mutationFilter/mutation-filter.stories.tsx +8 -8
- package/src/preact/mutationFilter/mutation-filter.tsx +7 -6
- package/src/preact/mutations/mutations.stories.tsx +6 -3
- package/src/preact/mutations/mutations.tsx +8 -6
- package/src/preact/prevalenceOverTime/prevalence-over-time.stories.tsx +14 -7
- package/src/preact/prevalenceOverTime/prevalence-over-time.tsx +10 -8
- package/src/preact/relativeGrowthAdvantage/relative-growth-advantage.stories.tsx +6 -3
- package/src/preact/relativeGrowthAdvantage/relative-growth-advantage.tsx +9 -7
- package/src/preact/textInput/text-input.stories.tsx +26 -0
- package/src/preact/textInput/text-input.tsx +4 -5
- package/src/query/queryPrevalenceOverTime.ts +4 -10
- package/src/types.ts +4 -1
- package/src/web-components/ResizeContainer.mdx +13 -0
- package/src/web-components/app.ts +3 -1
- package/src/web-components/input/gs-date-range-selector.stories.ts +10 -2
- package/src/web-components/input/gs-date-range-selector.tsx +26 -16
- package/src/web-components/input/gs-location-filter.stories.ts +5 -7
- package/src/web-components/input/gs-location-filter.tsx +6 -7
- package/src/web-components/input/gs-mutation-filter.stories.ts +11 -8
- package/src/web-components/input/gs-mutation-filter.tsx +38 -26
- package/src/web-components/input/gs-text-input.stories.ts +3 -3
- package/src/web-components/input/gs-text-input.tsx +10 -10
- package/src/web-components/input/introduction.mdx +11 -0
- package/src/web-components/introduction.mdx +15 -0
- package/src/web-components/visualization/gs-aggregate.stories.ts +19 -6
- package/src/web-components/visualization/gs-aggregate.tsx +31 -15
- package/src/web-components/visualization/gs-mutation-comparison.stories.ts +13 -7
- package/src/web-components/visualization/gs-mutation-comparison.tsx +26 -17
- package/src/web-components/visualization/gs-mutations.stories.ts +14 -8
- package/src/web-components/visualization/gs-mutations.tsx +18 -8
- package/src/web-components/visualization/gs-prevalence-over-time.stories.ts +28 -18
- package/src/web-components/visualization/gs-prevalence-over-time.tsx +45 -22
- package/src/web-components/visualization/gs-relative-growth-advantage.stories.ts +11 -5
- package/src/web-components/visualization/gs-relative-growth-advantage.tsx +21 -9
|
@@ -1,23 +1,14 @@
|
|
|
1
1
|
import { type FunctionComponent } from 'preact';
|
|
2
2
|
|
|
3
3
|
export type Size = {
|
|
4
|
-
width
|
|
5
|
-
height
|
|
4
|
+
width: string;
|
|
5
|
+
height: string;
|
|
6
6
|
};
|
|
7
7
|
|
|
8
8
|
export interface ResizeContainerProps {
|
|
9
|
-
size
|
|
10
|
-
defaultSize: Size;
|
|
9
|
+
size: Size;
|
|
11
10
|
}
|
|
12
11
|
|
|
13
|
-
export const ResizeContainer: FunctionComponent<ResizeContainerProps> = ({ children, size
|
|
14
|
-
return <div style={
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
const extendByDefault = (size: Size | undefined, defaultSize: Size) => {
|
|
18
|
-
if (size === undefined) {
|
|
19
|
-
return defaultSize;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
return { ...defaultSize, ...size };
|
|
12
|
+
export const ResizeContainer: FunctionComponent<ResizeContainerProps> = ({ children, size }) => {
|
|
13
|
+
return <div style={size}>{children}</div>;
|
|
23
14
|
};
|
|
@@ -60,6 +60,7 @@ const meta: Meta<DateRangeSelectorProps<'CustomDateRange'>> = {
|
|
|
60
60
|
customSelectOptions: [{ label: 'CustomDateRange', dateFrom: '2021-01-01', dateTo: '2021-12-31' }],
|
|
61
61
|
earliestDate: '1970-01-01',
|
|
62
62
|
initialValue: PRESET_VALUE_LAST_3_MONTHS,
|
|
63
|
+
dateColumn: 'aDateColumn',
|
|
63
64
|
width: '100%',
|
|
64
65
|
},
|
|
65
66
|
decorators: [withActions],
|
|
@@ -75,6 +76,7 @@ export const Primary: StoryObj<DateRangeSelectorProps<'CustomDateRange'>> = {
|
|
|
75
76
|
earliestDate={args.earliestDate}
|
|
76
77
|
initialValue={args.initialValue}
|
|
77
78
|
width={args.width}
|
|
79
|
+
dateColumn={args.dateColumn}
|
|
78
80
|
/>
|
|
79
81
|
</LapisUrlContext.Provider>
|
|
80
82
|
),
|
|
@@ -11,13 +11,14 @@ import type { ScaleType } from '../shared/charts/getYAxisScale';
|
|
|
11
11
|
export type CustomSelectOption<CustomLabel extends string> = { label: CustomLabel; dateFrom: string; dateTo: string };
|
|
12
12
|
|
|
13
13
|
export interface DateRangeSelectorProps<CustomLabel extends string> extends DateRangeSelectorPropsInner<CustomLabel> {
|
|
14
|
-
width
|
|
14
|
+
width: string;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
export interface DateRangeSelectorPropsInner<CustomLabel extends string> {
|
|
18
18
|
customSelectOptions: CustomSelectOption<CustomLabel>[];
|
|
19
19
|
earliestDate?: string;
|
|
20
20
|
initialValue?: PresetOptionValues | CustomLabel;
|
|
21
|
+
dateColumn: string;
|
|
21
22
|
}
|
|
22
23
|
|
|
23
24
|
export const PRESET_VALUE_CUSTOM = 'custom';
|
|
@@ -45,17 +46,18 @@ export const DateRangeSelector = <CustomLabel extends string>({
|
|
|
45
46
|
earliestDate = '1900-01-01',
|
|
46
47
|
initialValue,
|
|
47
48
|
width,
|
|
49
|
+
dateColumn,
|
|
48
50
|
}: DateRangeSelectorProps<CustomLabel>) => {
|
|
49
|
-
const
|
|
50
|
-
const size = width === undefined ? undefined : { width, height: defaultSize.height };
|
|
51
|
+
const size = { width, height: '3rem' };
|
|
51
52
|
|
|
52
53
|
return (
|
|
53
|
-
<ErrorBoundary
|
|
54
|
-
<ResizeContainer
|
|
54
|
+
<ErrorBoundary size={size}>
|
|
55
|
+
<ResizeContainer size={size}>
|
|
55
56
|
<DateRangeSelectorInner
|
|
56
57
|
customSelectOptions={customSelectOptions}
|
|
57
58
|
earliestDate={earliestDate}
|
|
58
59
|
initialValue={initialValue}
|
|
60
|
+
dateColumn={dateColumn}
|
|
59
61
|
/>
|
|
60
62
|
</ResizeContainer>
|
|
61
63
|
</ErrorBoundary>
|
|
@@ -66,7 +68,8 @@ export const DateRangeSelectorInner = <CustomLabel extends string>({
|
|
|
66
68
|
customSelectOptions,
|
|
67
69
|
earliestDate = '1900-01-01',
|
|
68
70
|
initialValue,
|
|
69
|
-
|
|
71
|
+
dateColumn,
|
|
72
|
+
}: DateRangeSelectorPropsInner<CustomLabel>) => {
|
|
70
73
|
const fromDatePickerRef = useRef<HTMLInputElement>(null);
|
|
71
74
|
const toDatePickerRef = useRef<HTMLInputElement>(null);
|
|
72
75
|
const divRef = useRef<HTMLDivElement>(null);
|
|
@@ -165,8 +168,8 @@ export const DateRangeSelectorInner = <CustomLabel extends string>({
|
|
|
165
168
|
const dateTo = toYYYYMMDD(dateToPicker?.selectedDates[0]);
|
|
166
169
|
|
|
167
170
|
const detail = {
|
|
168
|
-
...(dateFrom !== undefined && { dateFrom }),
|
|
169
|
-
...(dateTo !== undefined && { dateTo }),
|
|
171
|
+
...(dateFrom !== undefined && { [`${dateColumn}From`]: dateFrom }),
|
|
172
|
+
...(dateTo !== undefined && { [`${dateColumn}To`]: dateTo }),
|
|
170
173
|
};
|
|
171
174
|
|
|
172
175
|
divRef.current?.dispatchEvent(
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import { LapisError } from '../../lapisApi/lapisApi';
|
|
1
2
|
import { FetchAggregatedOperator } from '../../operator/FetchAggregatedOperator';
|
|
3
|
+
import { UserFacingError } from '../components/error-display';
|
|
2
4
|
|
|
3
5
|
export async function fetchAutocompletionList(fields: string[], lapis: string, signal?: AbortSignal) {
|
|
4
6
|
const toAncestorInHierarchyOverwriteValues = Array(fields.length - 1)
|
|
@@ -8,7 +10,19 @@ export async function fetchAutocompletionList(fields: string[], lapis: string, s
|
|
|
8
10
|
|
|
9
11
|
const fetchAggregatedOperator = new FetchAggregatedOperator<Record<string, string | null>>({}, fields);
|
|
10
12
|
|
|
11
|
-
|
|
13
|
+
let data;
|
|
14
|
+
try {
|
|
15
|
+
data = (await fetchAggregatedOperator.evaluate(lapis, signal)).content;
|
|
16
|
+
} catch (error) {
|
|
17
|
+
if (error instanceof LapisError) {
|
|
18
|
+
throw new UserFacingError(
|
|
19
|
+
`Failed to fetch autocomplete list from LAPIS: ${error.problemDetail.status} ${error.problemDetail.title ?? ''}`,
|
|
20
|
+
error.problemDetail.detail ?? error.message,
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
throw error;
|
|
24
|
+
}
|
|
25
|
+
|
|
12
26
|
const locationValues = data
|
|
13
27
|
.map((entry) => fields.reduce((acc, field) => ({ ...acc, [field]: entry[field] }), {}))
|
|
14
28
|
.reduce<Set<string>>((setOfAllHierarchies, entry) => {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type FunctionComponent } from 'preact';
|
|
2
2
|
import { useContext, useRef, useState } from 'preact/hooks';
|
|
3
|
+
import { type JSXInternal } from 'preact/src/jsx';
|
|
3
4
|
|
|
4
5
|
import { fetchAutocompletionList } from './fetchAutocompletionList';
|
|
5
6
|
import { LapisUrlContext } from '../LapisUrlContext';
|
|
@@ -15,16 +16,15 @@ export interface LocationFilterInnerProps {
|
|
|
15
16
|
}
|
|
16
17
|
|
|
17
18
|
export interface LocationFilterProps extends LocationFilterInnerProps {
|
|
18
|
-
width
|
|
19
|
+
width: string;
|
|
19
20
|
}
|
|
20
21
|
|
|
21
22
|
export const LocationFilter: FunctionComponent<LocationFilterProps> = ({ width, initialValue, fields }) => {
|
|
22
|
-
const
|
|
23
|
-
const size = width === undefined ? undefined : { width, height: defaultSize.height };
|
|
23
|
+
const size = { width, height: '3rem' };
|
|
24
24
|
|
|
25
25
|
return (
|
|
26
|
-
<ErrorBoundary
|
|
27
|
-
<ResizeContainer size={size}
|
|
26
|
+
<ErrorBoundary size={size}>
|
|
27
|
+
<ResizeContainer size={size}>
|
|
28
28
|
<LocationFilterInner initialValue={initialValue} fields={fields} />
|
|
29
29
|
</ResizeContainer>
|
|
30
30
|
</ErrorBoundary>
|
|
@@ -37,7 +37,7 @@ export const LocationFilterInner = ({ initialValue, fields }: LocationFilterInne
|
|
|
37
37
|
const [value, setValue] = useState(initialValue ?? '');
|
|
38
38
|
const [unknownLocation, setUnknownLocation] = useState(false);
|
|
39
39
|
|
|
40
|
-
const
|
|
40
|
+
const divRef = useRef<HTMLDivElement>(null);
|
|
41
41
|
|
|
42
42
|
const { data, error, isLoading } = useQuery(() => fetchAutocompletionList(fields, lapis), [fields, lapis]);
|
|
43
43
|
|
|
@@ -48,40 +48,29 @@ export const LocationFilterInner = ({ initialValue, fields }: LocationFilterInne
|
|
|
48
48
|
return <ErrorDisplay error={error} />;
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
-
const onInput = (event:
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
const eventDetail = parseLocation(inputValue, fields);
|
|
57
|
-
if (hasMatchingEntry(data, eventDetail)) {
|
|
58
|
-
setUnknownLocation(false);
|
|
59
|
-
}
|
|
60
|
-
}
|
|
51
|
+
const onInput = (event: JSXInternal.TargetedInputEvent<HTMLInputElement>) => {
|
|
52
|
+
const inputValue = event.currentTarget.value;
|
|
53
|
+
setValue(inputValue);
|
|
54
|
+
if (inputValue.trim() === value.trim()) {
|
|
55
|
+
return;
|
|
61
56
|
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
const submit = (event: SubmitEvent) => {
|
|
65
|
-
event.preventDefault();
|
|
66
|
-
const eventDetail = parseLocation(value, fields);
|
|
67
|
-
|
|
57
|
+
const eventDetail = parseLocation(inputValue, fields);
|
|
68
58
|
if (hasMatchingEntry(data, eventDetail)) {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
formRef.current?.dispatchEvent(
|
|
59
|
+
divRef.current?.dispatchEvent(
|
|
72
60
|
new CustomEvent('gs-location-changed', {
|
|
73
61
|
detail: eventDetail,
|
|
74
62
|
bubbles: true,
|
|
75
63
|
composed: true,
|
|
76
64
|
}),
|
|
77
65
|
);
|
|
66
|
+
setUnknownLocation(false);
|
|
78
67
|
} else {
|
|
79
68
|
setUnknownLocation(true);
|
|
80
69
|
}
|
|
81
70
|
};
|
|
82
71
|
|
|
83
72
|
return (
|
|
84
|
-
<
|
|
73
|
+
<div class='flex w-full' ref={divRef}>
|
|
85
74
|
<input
|
|
86
75
|
type='text'
|
|
87
76
|
class={`input input-bordered grow ${unknownLocation ? 'border-2 border-error' : ''}`}
|
|
@@ -98,10 +87,7 @@ export const LocationFilterInner = ({ initialValue, fields }: LocationFilterInne
|
|
|
98
87
|
return <option key={value} value={value} />;
|
|
99
88
|
})}
|
|
100
89
|
</datalist>
|
|
101
|
-
|
|
102
|
-
Submit
|
|
103
|
-
</button>
|
|
104
|
-
</form>
|
|
90
|
+
</div>
|
|
105
91
|
);
|
|
106
92
|
};
|
|
107
93
|
|
|
@@ -27,7 +27,8 @@ const meta: Meta<MutationComparisonProps> = {
|
|
|
27
27
|
options: ['table', 'venn'],
|
|
28
28
|
control: { type: 'check' },
|
|
29
29
|
},
|
|
30
|
-
|
|
30
|
+
width: { control: 'text' },
|
|
31
|
+
height: { control: 'text' },
|
|
31
32
|
headline: { control: 'text' },
|
|
32
33
|
},
|
|
33
34
|
parameters: {
|
|
@@ -81,7 +82,8 @@ const Template: StoryObj<MutationComparisonProps> = {
|
|
|
81
82
|
variants={args.variants}
|
|
82
83
|
sequenceType={args.sequenceType}
|
|
83
84
|
views={args.views}
|
|
84
|
-
|
|
85
|
+
width={args.width}
|
|
86
|
+
height={args.height}
|
|
85
87
|
headline={args.headline}
|
|
86
88
|
/>
|
|
87
89
|
</ReferenceGenomeContext.Provider>
|
|
@@ -109,7 +111,8 @@ export const TwoVariants: StoryObj<MutationComparisonProps> = {
|
|
|
109
111
|
],
|
|
110
112
|
sequenceType: 'nucleotide',
|
|
111
113
|
views: ['table', 'venn'],
|
|
112
|
-
|
|
114
|
+
width: '100%',
|
|
115
|
+
height: '700px',
|
|
113
116
|
headline: 'Mutation comparison',
|
|
114
117
|
},
|
|
115
118
|
};
|
|
@@ -5,7 +5,7 @@ import { getMutationComparisonTableData } from './getMutationComparisonTableData
|
|
|
5
5
|
import { MutationComparisonTable } from './mutation-comparison-table';
|
|
6
6
|
import { MutationComparisonVenn } from './mutation-comparison-venn';
|
|
7
7
|
import { filterMutationData, type MutationData, queryMutationData } from './queryMutationData';
|
|
8
|
-
import { type
|
|
8
|
+
import { type NamedLapisFilter, type SequenceType } from '../../types';
|
|
9
9
|
import { LapisUrlContext } from '../LapisUrlContext';
|
|
10
10
|
import { type DisplayedSegment, SegmentSelector, useDisplayedSegments } from '../components/SegmentSelector';
|
|
11
11
|
import { CsvDownloadButton } from '../components/csv-download-button';
|
|
@@ -18,24 +18,20 @@ import { type DisplayedMutationType, MutationTypeSelector } from '../components/
|
|
|
18
18
|
import { NoDataDisplay } from '../components/no-data-display';
|
|
19
19
|
import { type ProportionInterval } from '../components/proportion-selector';
|
|
20
20
|
import { ProportionSelectorDropdown } from '../components/proportion-selector-dropdown';
|
|
21
|
-
import { ResizeContainer
|
|
21
|
+
import { ResizeContainer } from '../components/resize-container';
|
|
22
22
|
import Tabs from '../components/tabs';
|
|
23
23
|
import { useQuery } from '../useQuery';
|
|
24
24
|
|
|
25
25
|
export type View = 'table' | 'venn';
|
|
26
26
|
|
|
27
|
-
export interface MutationComparisonVariant {
|
|
28
|
-
lapisFilter: LapisFilter;
|
|
29
|
-
displayName: string;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
27
|
export interface MutationComparisonProps extends MutationComparisonInnerProps {
|
|
33
|
-
|
|
28
|
+
width: string;
|
|
29
|
+
height: string;
|
|
34
30
|
headline?: string;
|
|
35
31
|
}
|
|
36
32
|
|
|
37
33
|
export interface MutationComparisonInnerProps {
|
|
38
|
-
variants:
|
|
34
|
+
variants: NamedLapisFilter[];
|
|
39
35
|
sequenceType: SequenceType;
|
|
40
36
|
views: View[];
|
|
41
37
|
}
|
|
@@ -44,14 +40,15 @@ export const MutationComparison: FunctionComponent<MutationComparisonProps> = ({
|
|
|
44
40
|
variants,
|
|
45
41
|
sequenceType,
|
|
46
42
|
views,
|
|
47
|
-
|
|
43
|
+
width,
|
|
44
|
+
height,
|
|
48
45
|
headline = 'Mutation comparison',
|
|
49
46
|
}) => {
|
|
50
|
-
const
|
|
47
|
+
const size = { height, width };
|
|
51
48
|
|
|
52
49
|
return (
|
|
53
|
-
<ErrorBoundary size={size}
|
|
54
|
-
<ResizeContainer size={size}
|
|
50
|
+
<ErrorBoundary size={size} headline={headline}>
|
|
51
|
+
<ResizeContainer size={size}>
|
|
55
52
|
<Headline heading={headline}>
|
|
56
53
|
<MutationComparisonInner variants={variants} sequenceType={sequenceType} views={views} />
|
|
57
54
|
</Headline>
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { type MutationComparisonVariant } from './mutation-comparison';
|
|
2
1
|
import { querySubstitutionsOrDeletions } from '../../query/querySubstitutionsOrDeletions';
|
|
3
|
-
import { type SubstitutionOrDeletionEntry } from '../../types';
|
|
2
|
+
import { type NamedLapisFilter, type SubstitutionOrDeletionEntry } from '../../types';
|
|
4
3
|
import { type DisplayedSegment } from '../components/SegmentSelector';
|
|
5
4
|
import { type DisplayedMutationType } from '../components/mutation-type-selector';
|
|
6
5
|
|
|
@@ -10,7 +9,7 @@ export type MutationData = {
|
|
|
10
9
|
};
|
|
11
10
|
|
|
12
11
|
export async function queryMutationData(
|
|
13
|
-
variants:
|
|
12
|
+
variants: NamedLapisFilter[],
|
|
14
13
|
sequenceType: 'nucleotide' | 'amino acid',
|
|
15
14
|
lapis: string,
|
|
16
15
|
) {
|
|
@@ -19,11 +19,8 @@ const meta: Meta<MutationFilterProps> = {
|
|
|
19
19
|
fetchMock: {},
|
|
20
20
|
},
|
|
21
21
|
argTypes: {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
type: 'object',
|
|
25
|
-
},
|
|
26
|
-
},
|
|
22
|
+
width: { control: 'text' },
|
|
23
|
+
height: { control: 'text' },
|
|
27
24
|
initialValue: {
|
|
28
25
|
control: {
|
|
29
26
|
type: 'object',
|
|
@@ -39,12 +36,13 @@ export const Default: StoryObj<MutationFilterProps> = {
|
|
|
39
36
|
render: (args) => (
|
|
40
37
|
<LapisUrlContext.Provider value={LAPIS_URL}>
|
|
41
38
|
<ReferenceGenomeContext.Provider value={referenceGenome}>
|
|
42
|
-
<MutationFilter
|
|
39
|
+
<MutationFilter width={args.width} height={args.height} initialValue={args.initialValue} />
|
|
43
40
|
</ReferenceGenomeContext.Provider>
|
|
44
41
|
</LapisUrlContext.Provider>
|
|
45
42
|
),
|
|
46
43
|
args: {
|
|
47
|
-
|
|
44
|
+
width: '100%',
|
|
45
|
+
height: '700px',
|
|
48
46
|
},
|
|
49
47
|
};
|
|
50
48
|
|
|
@@ -155,7 +153,7 @@ export const WithInitialValue: StoryObj<MutationFilterProps> = {
|
|
|
155
153
|
render: (args) => (
|
|
156
154
|
<LapisUrlContext.Provider value={LAPIS_URL}>
|
|
157
155
|
<ReferenceGenomeContext.Provider value={referenceGenome}>
|
|
158
|
-
<MutationFilter initialValue={args.initialValue} />
|
|
156
|
+
<MutationFilter initialValue={args.initialValue} width={args.width} height={args.height} />
|
|
159
157
|
</ReferenceGenomeContext.Provider>
|
|
160
158
|
</LapisUrlContext.Provider>
|
|
161
159
|
),
|
|
@@ -166,6 +164,8 @@ export const WithInitialValue: StoryObj<MutationFilterProps> = {
|
|
|
166
164
|
nucleotideInsertions: ['ins_123:AAA'],
|
|
167
165
|
aminoAcidInsertions: ['ins_S:123:AAA'],
|
|
168
166
|
},
|
|
167
|
+
width: '100%',
|
|
168
|
+
height: '700px',
|
|
169
169
|
},
|
|
170
170
|
play: async ({ canvasElement, step }) => {
|
|
171
171
|
const { canvas, onBlurListenerMock } = await prepare(canvasElement, step);
|
|
@@ -7,7 +7,7 @@ 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
|
|
10
|
+
import { ResizeContainer } from '../components/resize-container';
|
|
11
11
|
import { singleGraphColorRGBByName } from '../shared/charts/colors';
|
|
12
12
|
import { DeleteIcon } from '../shared/icons/DeleteIcon';
|
|
13
13
|
|
|
@@ -16,7 +16,8 @@ export interface MutationFilterInnerProps {
|
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
export interface MutationFilterProps extends MutationFilterInnerProps {
|
|
19
|
-
|
|
19
|
+
width: string;
|
|
20
|
+
height: string;
|
|
20
21
|
}
|
|
21
22
|
|
|
22
23
|
export type SelectedFilters = {
|
|
@@ -30,12 +31,12 @@ export type SelectedMutationFilterStrings = {
|
|
|
30
31
|
[Key in keyof SelectedFilters]: string[];
|
|
31
32
|
};
|
|
32
33
|
|
|
33
|
-
export const MutationFilter: FunctionComponent<MutationFilterProps> = ({ initialValue,
|
|
34
|
-
const
|
|
34
|
+
export const MutationFilter: FunctionComponent<MutationFilterProps> = ({ initialValue, width, height }) => {
|
|
35
|
+
const size = { height, width };
|
|
35
36
|
|
|
36
37
|
return (
|
|
37
|
-
<ErrorBoundary size={size}
|
|
38
|
-
<ResizeContainer size={size}
|
|
38
|
+
<ErrorBoundary size={size}>
|
|
39
|
+
<ResizeContainer size={size}>
|
|
39
40
|
<MutationFilterInner initialValue={initialValue} />
|
|
40
41
|
</ResizeContainer>
|
|
41
42
|
</ErrorBoundary>
|
|
@@ -22,7 +22,8 @@ const meta: Meta<MutationsProps> = {
|
|
|
22
22
|
options: ['table', 'grid', 'insertions'],
|
|
23
23
|
control: { type: 'check' },
|
|
24
24
|
},
|
|
25
|
-
|
|
25
|
+
width: { control: 'text' },
|
|
26
|
+
height: { control: 'text' },
|
|
26
27
|
headline: { control: 'text' },
|
|
27
28
|
},
|
|
28
29
|
};
|
|
@@ -37,7 +38,8 @@ const Template = {
|
|
|
37
38
|
variant={args.variant}
|
|
38
39
|
sequenceType={args.sequenceType}
|
|
39
40
|
views={args.views}
|
|
40
|
-
|
|
41
|
+
width={args.width}
|
|
42
|
+
height={args.height}
|
|
41
43
|
headline={args.headline}
|
|
42
44
|
/>
|
|
43
45
|
</ReferenceGenomeContext.Provider>
|
|
@@ -51,7 +53,8 @@ export const Default: StoryObj<MutationsProps> = {
|
|
|
51
53
|
variant: { country: 'Switzerland', pangoLineage: 'B.1.1.7', dateTo: '2022-01-01' },
|
|
52
54
|
sequenceType: 'nucleotide',
|
|
53
55
|
views: ['grid', 'table', 'insertions'],
|
|
54
|
-
|
|
56
|
+
width: '100%',
|
|
57
|
+
height: '700px',
|
|
55
58
|
headline: 'Mutations',
|
|
56
59
|
},
|
|
57
60
|
parameters: {
|
|
@@ -25,7 +25,7 @@ import { type DisplayedMutationType, MutationTypeSelector } from '../components/
|
|
|
25
25
|
import { NoDataDisplay } from '../components/no-data-display';
|
|
26
26
|
import type { ProportionInterval } from '../components/proportion-selector';
|
|
27
27
|
import { ProportionSelectorDropdown } from '../components/proportion-selector-dropdown';
|
|
28
|
-
import { ResizeContainer
|
|
28
|
+
import { ResizeContainer } from '../components/resize-container';
|
|
29
29
|
import Tabs from '../components/tabs';
|
|
30
30
|
import { useQuery } from '../useQuery';
|
|
31
31
|
|
|
@@ -38,7 +38,8 @@ export interface MutationsInnerProps {
|
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
export interface MutationsProps extends MutationsInnerProps {
|
|
41
|
-
|
|
41
|
+
width: string;
|
|
42
|
+
height: string;
|
|
42
43
|
headline?: string;
|
|
43
44
|
}
|
|
44
45
|
|
|
@@ -46,14 +47,15 @@ export const Mutations: FunctionComponent<MutationsProps> = ({
|
|
|
46
47
|
variant,
|
|
47
48
|
sequenceType,
|
|
48
49
|
views,
|
|
49
|
-
|
|
50
|
+
width,
|
|
51
|
+
height,
|
|
50
52
|
headline = 'Mutations',
|
|
51
53
|
}) => {
|
|
52
|
-
const
|
|
54
|
+
const size = { height, width };
|
|
53
55
|
|
|
54
56
|
return (
|
|
55
|
-
<ErrorBoundary size={size}
|
|
56
|
-
<ResizeContainer size={size}
|
|
57
|
+
<ErrorBoundary size={size} headline={headline}>
|
|
58
|
+
<ResizeContainer size={size}>
|
|
57
59
|
<Headline heading={headline}>
|
|
58
60
|
<MutationsInner variant={variant} sequenceType={sequenceType} views={views} />
|
|
59
61
|
</Headline>
|
|
@@ -29,7 +29,8 @@ export default {
|
|
|
29
29
|
options: ['wilson'],
|
|
30
30
|
control: { type: 'check' },
|
|
31
31
|
},
|
|
32
|
-
|
|
32
|
+
width: { control: 'text' },
|
|
33
|
+
height: { control: 'text' },
|
|
33
34
|
headline: { control: 'text' },
|
|
34
35
|
},
|
|
35
36
|
};
|
|
@@ -44,7 +45,8 @@ const Template = {
|
|
|
44
45
|
smoothingWindow={args.smoothingWindow}
|
|
45
46
|
views={args.views}
|
|
46
47
|
confidenceIntervalMethods={args.confidenceIntervalMethods}
|
|
47
|
-
|
|
48
|
+
width={args.width}
|
|
49
|
+
height={args.height}
|
|
48
50
|
headline={args.headline}
|
|
49
51
|
/>
|
|
50
52
|
</LapisUrlContext.Provider>
|
|
@@ -55,15 +57,16 @@ export const TwoVariants = {
|
|
|
55
57
|
...Template,
|
|
56
58
|
args: {
|
|
57
59
|
numerator: [
|
|
58
|
-
{ displayName: 'EG', country: 'USA', pangoLineage: 'EG*', dateFrom: '2023-01-01' },
|
|
59
|
-
{ displayName: 'JN.1', country: 'USA', pangoLineage: 'JN.1*', dateFrom: '2023-01-01' },
|
|
60
|
+
{ displayName: 'EG', lapisFilter: { country: 'USA', pangoLineage: 'EG*', dateFrom: '2023-01-01' } },
|
|
61
|
+
{ displayName: 'JN.1', lapisFilter: { country: 'USA', pangoLineage: 'JN.1*', dateFrom: '2023-01-01' } },
|
|
60
62
|
],
|
|
61
63
|
denominator: { country: 'USA', dateFrom: '2023-01-01' },
|
|
62
64
|
granularity: 'month',
|
|
63
65
|
smoothingWindow: 0,
|
|
64
66
|
views: ['bar', 'line', 'bubble', 'table'],
|
|
65
67
|
confidenceIntervalMethods: ['wilson'],
|
|
66
|
-
|
|
68
|
+
width: '100%',
|
|
69
|
+
height: '700px',
|
|
67
70
|
headline: 'Prevalence over time',
|
|
68
71
|
},
|
|
69
72
|
parameters: {
|
|
@@ -124,13 +127,17 @@ export const TwoVariants = {
|
|
|
124
127
|
export const OneVariant = {
|
|
125
128
|
...Template,
|
|
126
129
|
args: {
|
|
127
|
-
numerator: {
|
|
130
|
+
numerator: {
|
|
131
|
+
displayName: 'EG',
|
|
132
|
+
lapisFilter: { country: 'USA', pangoLineage: 'BA.2.86*', dateFrom: '2023-10-01' },
|
|
133
|
+
},
|
|
128
134
|
denominator: { country: 'USA', dateFrom: '2023-10-01' },
|
|
129
135
|
granularity: 'day',
|
|
130
136
|
smoothingWindow: 7,
|
|
131
137
|
views: ['bar', 'line', 'bubble', 'table'],
|
|
132
138
|
confidenceIntervalMethods: ['wilson'],
|
|
133
|
-
|
|
139
|
+
width: '100%',
|
|
140
|
+
height: '700px',
|
|
134
141
|
headline: 'Prevalence over time',
|
|
135
142
|
},
|
|
136
143
|
parameters: {
|
|
@@ -7,7 +7,7 @@ import PrevalenceOverTimeBubbleChart from './prevalence-over-time-bubble-chart';
|
|
|
7
7
|
import PrevalenceOverTimeLineChart from './prevalence-over-time-line-chart';
|
|
8
8
|
import PrevalenceOverTimeTable from './prevalence-over-time-table';
|
|
9
9
|
import { type PrevalenceOverTimeData, queryPrevalenceOverTime } from '../../query/queryPrevalenceOverTime';
|
|
10
|
-
import { type NamedLapisFilter, type TemporalGranularity } from '../../types';
|
|
10
|
+
import { type LapisFilter, type NamedLapisFilter, type TemporalGranularity } from '../../types';
|
|
11
11
|
import { LapisUrlContext } from '../LapisUrlContext';
|
|
12
12
|
import { ConfidenceIntervalSelector } from '../components/confidence-interval-selector';
|
|
13
13
|
import { CsvDownloadButton } from '../components/csv-download-button';
|
|
@@ -17,7 +17,7 @@ import Headline from '../components/headline';
|
|
|
17
17
|
import Info, { InfoHeadline1, InfoParagraph } from '../components/info';
|
|
18
18
|
import { LoadingDisplay } from '../components/loading-display';
|
|
19
19
|
import { NoDataDisplay } from '../components/no-data-display';
|
|
20
|
-
import { ResizeContainer
|
|
20
|
+
import { ResizeContainer } from '../components/resize-container';
|
|
21
21
|
import { ScalingSelector } from '../components/scaling-selector';
|
|
22
22
|
import Tabs from '../components/tabs';
|
|
23
23
|
import { type ConfidenceIntervalMethod } from '../shared/charts/confideceInterval';
|
|
@@ -27,13 +27,14 @@ import { useQuery } from '../useQuery';
|
|
|
27
27
|
export type View = 'bar' | 'line' | 'bubble' | 'table';
|
|
28
28
|
|
|
29
29
|
export interface PrevalenceOverTimeProps extends PrevalenceOverTimeInnerProps {
|
|
30
|
-
|
|
30
|
+
width: string;
|
|
31
|
+
height: string;
|
|
31
32
|
headline?: string;
|
|
32
33
|
}
|
|
33
34
|
|
|
34
35
|
export interface PrevalenceOverTimeInnerProps {
|
|
35
36
|
numerator: NamedLapisFilter | NamedLapisFilter[];
|
|
36
|
-
denominator:
|
|
37
|
+
denominator: LapisFilter;
|
|
37
38
|
granularity: TemporalGranularity;
|
|
38
39
|
smoothingWindow: number;
|
|
39
40
|
views: View[];
|
|
@@ -47,14 +48,15 @@ export const PrevalenceOverTime: FunctionComponent<PrevalenceOverTimeProps> = ({
|
|
|
47
48
|
smoothingWindow,
|
|
48
49
|
views,
|
|
49
50
|
confidenceIntervalMethods,
|
|
50
|
-
|
|
51
|
+
width,
|
|
52
|
+
height,
|
|
51
53
|
headline = 'Prevalence over time',
|
|
52
54
|
}) => {
|
|
53
|
-
const
|
|
55
|
+
const size = { height, width };
|
|
54
56
|
|
|
55
57
|
return (
|
|
56
|
-
<ErrorBoundary size={size}
|
|
57
|
-
<ResizeContainer size={size}
|
|
58
|
+
<ErrorBoundary size={size} headline={headline}>
|
|
59
|
+
<ResizeContainer size={size}>
|
|
58
60
|
<Headline heading={headline}>
|
|
59
61
|
<PrevalenceOverTimeInner
|
|
60
62
|
numerator={numerator}
|
|
@@ -18,7 +18,8 @@ export default {
|
|
|
18
18
|
options: ['line'],
|
|
19
19
|
control: { type: 'check' },
|
|
20
20
|
},
|
|
21
|
-
|
|
21
|
+
width: { control: 'text' },
|
|
22
|
+
height: { control: 'text' },
|
|
22
23
|
headline: { control: 'text' },
|
|
23
24
|
},
|
|
24
25
|
};
|
|
@@ -31,7 +32,8 @@ export const Primary = {
|
|
|
31
32
|
denominator={args.denominator}
|
|
32
33
|
generationTime={args.generationTime}
|
|
33
34
|
views={args.views}
|
|
34
|
-
|
|
35
|
+
width={args.width}
|
|
36
|
+
height={args.height}
|
|
35
37
|
headline={args.headline}
|
|
36
38
|
/>
|
|
37
39
|
</LapisUrlContext.Provider>
|
|
@@ -41,7 +43,8 @@ export const Primary = {
|
|
|
41
43
|
denominator: { country: 'Switzerland', dateFrom: '2020-12-01', dateTo: '2021-03-01' },
|
|
42
44
|
generationTime: 7,
|
|
43
45
|
views: ['line'],
|
|
44
|
-
|
|
46
|
+
width: '100%',
|
|
47
|
+
height: '700px',
|
|
45
48
|
headline: 'Relative growth advantage',
|
|
46
49
|
},
|
|
47
50
|
parameters: {
|