@genspectrum/dashboard-components 0.1.5 → 0.2.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 +1013 -931
- package/dist/dashboard-components.js +350 -171
- package/dist/dashboard-components.js.map +1 -1
- package/dist/genspectrum-components.d.ts +48 -57
- package/dist/style.css +74 -23
- package/package.json +2 -2
- package/src/preact/aggregatedData/aggregate.tsx +28 -28
- package/src/preact/components/error-boundary.stories.tsx +62 -0
- package/src/preact/components/error-boundary.tsx +31 -0
- package/src/preact/components/error-display.stories.tsx +24 -3
- package/src/preact/components/error-display.tsx +14 -1
- package/src/preact/components/loading-display.stories.tsx +6 -6
- package/src/preact/components/loading-display.tsx +1 -1
- package/src/preact/components/no-data-display.tsx +5 -1
- package/src/preact/dateRangeSelector/date-range-selector.stories.tsx +17 -0
- package/src/preact/dateRangeSelector/date-range-selector.tsx +33 -5
- package/src/preact/locationFilter/location-filter.stories.tsx +23 -6
- package/src/preact/locationFilter/location-filter.tsx +29 -18
- package/src/preact/mutationComparison/mutation-comparison.tsx +29 -25
- package/src/preact/mutationFilter/mutation-filter.stories.tsx +17 -2
- package/src/preact/mutationFilter/mutation-filter.tsx +25 -7
- package/src/preact/mutations/mutations.tsx +23 -23
- package/src/preact/prevalenceOverTime/prevalence-over-time.tsx +44 -28
- package/src/preact/relativeGrowthAdvantage/relative-growth-advantage.tsx +43 -31
- package/src/preact/textInput/text-input.tsx +26 -3
- package/src/web-components/app.stories.ts +1 -2
- package/src/web-components/app.ts +4 -2
- package/src/web-components/index.ts +1 -1
- package/src/web-components/input/{date-range-selector-component.stories.ts → gs-date-range-selector.stories.ts} +19 -2
- package/src/web-components/input/{date-range-selector-component.tsx → gs-date-range-selector.tsx} +12 -0
- package/src/web-components/input/{location-filter-component.stories.ts → gs-location-filter.stories.ts} +29 -4
- package/src/web-components/input/{location-filter-component.tsx → gs-location-filter.tsx} +12 -1
- package/src/web-components/input/{mutation-filter-component.stories.ts → gs-mutation-filter.stories.ts} +20 -4
- package/src/web-components/input/{mutation-filter-component.tsx → gs-mutation-filter.tsx} +36 -5
- package/src/web-components/input/{text-input-component.stories.ts → gs-text-input.stories.ts} +31 -3
- package/src/web-components/input/{text-input-component.tsx → gs-text-input.tsx} +12 -0
- package/src/web-components/input/index.ts +4 -4
- package/src/web-components/visualization/data_visualization_statistical_analysis.mdx +26 -0
- package/src/web-components/{display/aggregate-component.stories.ts → visualization/gs-aggregate.stories.ts} +5 -6
- package/src/web-components/{display/aggregate-component.tsx → visualization/gs-aggregate.tsx} +1 -1
- package/src/web-components/{display/mutation-comparison-component.stories.ts → visualization/gs-mutation-comparison.stories.ts} +8 -9
- package/src/web-components/{display/mutation-comparison-component.tsx → visualization/gs-mutation-comparison.tsx} +1 -1
- package/src/web-components/{display/mutations-component.stories.ts → visualization/gs-mutations.stories.ts} +6 -7
- package/src/web-components/{display/mutations-component.tsx → visualization/gs-mutations.tsx} +2 -2
- package/src/web-components/{display/prevalence-over-time-component.stories.ts → visualization/gs-prevalence-over-time.stories.ts} +1 -2
- package/src/web-components/{display/prevalence-over-time-component.tsx → visualization/gs-prevalence-over-time.tsx} +3 -1
- package/src/web-components/{display/relative-growth-advantage-component.stories.ts → visualization/gs-relative-growth-advantage.stories.ts} +1 -2
- package/src/web-components/visualization/index.ts +5 -0
- package/src/web-components/display/index.ts +0 -5
- /package/src/web-components/{display/relative-growth-advantage-component.tsx → visualization/gs-relative-growth-advantage.tsx} +0 -0
|
@@ -3,12 +3,18 @@ import 'flatpickr/dist/flatpickr.min.css';
|
|
|
3
3
|
import { useEffect, useRef, useState } from 'preact/hooks';
|
|
4
4
|
|
|
5
5
|
import { toYYYYMMDD } from './dateConversion';
|
|
6
|
+
import { ErrorBoundary } from '../components/error-boundary';
|
|
7
|
+
import { ResizeContainer } from '../components/resize-container';
|
|
6
8
|
import { Select } from '../components/select';
|
|
7
9
|
import type { ScaleType } from '../shared/charts/getYAxisScale';
|
|
8
10
|
|
|
9
11
|
export type CustomSelectOption<CustomLabel extends string> = { label: CustomLabel; dateFrom: string; dateTo: string };
|
|
10
12
|
|
|
11
|
-
export interface DateRangeSelectorProps<CustomLabel extends string> {
|
|
13
|
+
export interface DateRangeSelectorProps<CustomLabel extends string> extends DateRangeSelectorPropsInner<CustomLabel> {
|
|
14
|
+
width?: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface DateRangeSelectorPropsInner<CustomLabel extends string> {
|
|
12
18
|
customSelectOptions: CustomSelectOption<CustomLabel>[];
|
|
13
19
|
earliestDate?: string;
|
|
14
20
|
initialValue?: PresetOptionValues | CustomLabel;
|
|
@@ -38,6 +44,28 @@ export const DateRangeSelector = <CustomLabel extends string>({
|
|
|
38
44
|
customSelectOptions,
|
|
39
45
|
earliestDate = '1900-01-01',
|
|
40
46
|
initialValue,
|
|
47
|
+
width,
|
|
48
|
+
}: DateRangeSelectorProps<CustomLabel>) => {
|
|
49
|
+
const defaultSize = { width: '100%', height: '3rem' };
|
|
50
|
+
const size = width === undefined ? undefined : { width, height: defaultSize.height };
|
|
51
|
+
|
|
52
|
+
return (
|
|
53
|
+
<ErrorBoundary defaultSize={defaultSize} size={size}>
|
|
54
|
+
<ResizeContainer defaultSize={defaultSize} size={size}>
|
|
55
|
+
<DateRangeSelectorInner
|
|
56
|
+
customSelectOptions={customSelectOptions}
|
|
57
|
+
earliestDate={earliestDate}
|
|
58
|
+
initialValue={initialValue}
|
|
59
|
+
/>
|
|
60
|
+
</ResizeContainer>
|
|
61
|
+
</ErrorBoundary>
|
|
62
|
+
);
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
export const DateRangeSelectorInner = <CustomLabel extends string>({
|
|
66
|
+
customSelectOptions,
|
|
67
|
+
earliestDate = '1900-01-01',
|
|
68
|
+
initialValue,
|
|
41
69
|
}: DateRangeSelectorProps<CustomLabel>) => {
|
|
42
70
|
const fromDatePickerRef = useRef<HTMLInputElement>(null);
|
|
43
71
|
const toDatePickerRef = useRef<HTMLInputElement>(null);
|
|
@@ -151,11 +179,11 @@ export const DateRangeSelector = <CustomLabel extends string>({
|
|
|
151
179
|
};
|
|
152
180
|
|
|
153
181
|
return (
|
|
154
|
-
<div class='join' ref={divRef}>
|
|
182
|
+
<div class='join w-full' ref={divRef}>
|
|
155
183
|
<Select
|
|
156
184
|
items={selectableOptions}
|
|
157
185
|
selected={selectedDateRange}
|
|
158
|
-
selectStyle='select-bordered rounded-none join-item'
|
|
186
|
+
selectStyle='select-bordered rounded-none join-item grow'
|
|
159
187
|
onChange={(event: Event) => {
|
|
160
188
|
event.preventDefault();
|
|
161
189
|
const select = event.target as HTMLSelectElement;
|
|
@@ -164,7 +192,7 @@ export const DateRangeSelector = <CustomLabel extends string>({
|
|
|
164
192
|
}}
|
|
165
193
|
/>
|
|
166
194
|
<input
|
|
167
|
-
class='input input-bordered rounded-none join-item'
|
|
195
|
+
class='input input-bordered rounded-none join-item grow'
|
|
168
196
|
type='text'
|
|
169
197
|
placeholder='Date from'
|
|
170
198
|
ref={fromDatePickerRef}
|
|
@@ -172,7 +200,7 @@ export const DateRangeSelector = <CustomLabel extends string>({
|
|
|
172
200
|
onBlur={onChangeDateFrom}
|
|
173
201
|
/>
|
|
174
202
|
<input
|
|
175
|
-
class='input input-bordered rounded-none join-item'
|
|
203
|
+
class='input input-bordered rounded-none join-item grow'
|
|
176
204
|
type='text'
|
|
177
205
|
placeholder='Date to'
|
|
178
206
|
ref={toDatePickerRef}
|
|
@@ -6,7 +6,7 @@ import { LocationFilter, type LocationFilterProps } from './location-filter';
|
|
|
6
6
|
import { AGGREGATED_ENDPOINT, LAPIS_URL } from '../../constants';
|
|
7
7
|
import { LapisUrlContext } from '../LapisUrlContext';
|
|
8
8
|
|
|
9
|
-
const meta: Meta<
|
|
9
|
+
const meta: Meta<LocationFilterProps> = {
|
|
10
10
|
title: 'Input/LocationFilter',
|
|
11
11
|
component: LocationFilter,
|
|
12
12
|
parameters: {
|
|
@@ -32,7 +32,26 @@ const meta: Meta<typeof LocationFilter> = {
|
|
|
32
32
|
},
|
|
33
33
|
},
|
|
34
34
|
args: {
|
|
35
|
+
width: '100%',
|
|
35
36
|
fields: ['region', 'country', 'division', 'location'],
|
|
37
|
+
initialValue: 'United States',
|
|
38
|
+
},
|
|
39
|
+
argTypes: {
|
|
40
|
+
fields: {
|
|
41
|
+
control: {
|
|
42
|
+
type: 'object',
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
initialValue: {
|
|
46
|
+
control: {
|
|
47
|
+
type: 'text',
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
width: {
|
|
51
|
+
control: {
|
|
52
|
+
type: 'text',
|
|
53
|
+
},
|
|
54
|
+
},
|
|
36
55
|
},
|
|
37
56
|
decorators: [withActions],
|
|
38
57
|
};
|
|
@@ -41,10 +60,8 @@ export default meta;
|
|
|
41
60
|
|
|
42
61
|
export const Primary: StoryObj<LocationFilterProps> = {
|
|
43
62
|
render: (args) => (
|
|
44
|
-
<
|
|
45
|
-
<
|
|
46
|
-
|
|
47
|
-
</LapisUrlContext.Provider>
|
|
48
|
-
</div>
|
|
63
|
+
<LapisUrlContext.Provider value={LAPIS_URL}>
|
|
64
|
+
<LocationFilter fields={args.fields} initialValue={args.initialValue} width={args.width} />
|
|
65
|
+
</LapisUrlContext.Provider>
|
|
49
66
|
),
|
|
50
67
|
};
|
|
@@ -1,15 +1,37 @@
|
|
|
1
|
+
import { type FunctionComponent } from 'preact';
|
|
1
2
|
import { useContext, useRef, useState } from 'preact/hooks';
|
|
2
3
|
|
|
3
4
|
import { fetchAutocompletionList } from './fetchAutocompletionList';
|
|
4
5
|
import { LapisUrlContext } from '../LapisUrlContext';
|
|
6
|
+
import { ErrorBoundary } from '../components/error-boundary';
|
|
7
|
+
import { ErrorDisplay } from '../components/error-display';
|
|
8
|
+
import { LoadingDisplay } from '../components/loading-display';
|
|
9
|
+
import { ResizeContainer } from '../components/resize-container';
|
|
5
10
|
import { useQuery } from '../useQuery';
|
|
6
11
|
|
|
7
|
-
export
|
|
12
|
+
export interface LocationFilterInnerProps {
|
|
8
13
|
initialValue?: string;
|
|
9
14
|
fields: string[];
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface LocationFilterProps extends LocationFilterInnerProps {
|
|
18
|
+
width?: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export const LocationFilter: FunctionComponent<LocationFilterProps> = ({ width, initialValue, fields }) => {
|
|
22
|
+
const defaultSize = { width: '100%', height: '3rem' };
|
|
23
|
+
const size = width === undefined ? undefined : { width, height: defaultSize.height };
|
|
24
|
+
|
|
25
|
+
return (
|
|
26
|
+
<ErrorBoundary defaultSize={defaultSize} size={size}>
|
|
27
|
+
<ResizeContainer size={size} defaultSize={defaultSize}>
|
|
28
|
+
<LocationFilterInner initialValue={initialValue} fields={fields} />
|
|
29
|
+
</ResizeContainer>
|
|
30
|
+
</ErrorBoundary>
|
|
31
|
+
);
|
|
10
32
|
};
|
|
11
33
|
|
|
12
|
-
export const
|
|
34
|
+
export const LocationFilterInner = ({ initialValue, fields }: LocationFilterInnerProps) => {
|
|
13
35
|
const lapis = useContext(LapisUrlContext);
|
|
14
36
|
|
|
15
37
|
const [value, setValue] = useState(initialValue ?? '');
|
|
@@ -19,22 +41,11 @@ export const LocationFilter = ({ initialValue, fields }: LocationFilterProps) =>
|
|
|
19
41
|
|
|
20
42
|
const { data, error, isLoading } = useQuery(() => fetchAutocompletionList(fields, lapis), [fields, lapis]);
|
|
21
43
|
|
|
22
|
-
if (isLoading)
|
|
23
|
-
return
|
|
24
|
-
|
|
25
|
-
<input type='text' class='input input-bordered grow' value={value} disabled />
|
|
26
|
-
<button class='btn ml-1' disabled type='submit'>
|
|
27
|
-
Loading...
|
|
28
|
-
</button>
|
|
29
|
-
</form>
|
|
30
|
-
);
|
|
31
|
-
|
|
44
|
+
if (isLoading) {
|
|
45
|
+
return <LoadingDisplay />;
|
|
46
|
+
}
|
|
32
47
|
if (error) {
|
|
33
|
-
return
|
|
34
|
-
<p>
|
|
35
|
-
Error: {error.name} {error.message} {error.stack}
|
|
36
|
-
</p>
|
|
37
|
-
);
|
|
48
|
+
return <ErrorDisplay error={error} />;
|
|
38
49
|
}
|
|
39
50
|
|
|
40
51
|
const onInput = (event: InputEvent) => {
|
|
@@ -70,7 +81,7 @@ export const LocationFilter = ({ initialValue, fields }: LocationFilterProps) =>
|
|
|
70
81
|
};
|
|
71
82
|
|
|
72
83
|
return (
|
|
73
|
-
<form class='flex' onSubmit={submit} ref={formRef}>
|
|
84
|
+
<form class='flex w-full' onSubmit={submit} ref={formRef}>
|
|
74
85
|
<input
|
|
75
86
|
type='text'
|
|
76
87
|
class={`input input-bordered grow ${unknownLocation ? 'border-2 border-error' : ''}`}
|
|
@@ -9,6 +9,7 @@ import { type LapisFilter, 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';
|
|
12
|
+
import { ErrorBoundary } from '../components/error-boundary';
|
|
12
13
|
import { ErrorDisplay } from '../components/error-display';
|
|
13
14
|
import Headline from '../components/headline';
|
|
14
15
|
import Info from '../components/info';
|
|
@@ -28,12 +29,15 @@ export interface MutationComparisonVariant {
|
|
|
28
29
|
displayName: string;
|
|
29
30
|
}
|
|
30
31
|
|
|
31
|
-
export interface MutationComparisonProps {
|
|
32
|
+
export interface MutationComparisonProps extends MutationComparisonInnerProps {
|
|
33
|
+
size?: Size;
|
|
34
|
+
headline?: string;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface MutationComparisonInnerProps {
|
|
32
38
|
variants: MutationComparisonVariant[];
|
|
33
39
|
sequenceType: SequenceType;
|
|
34
40
|
views: View[];
|
|
35
|
-
size?: Size;
|
|
36
|
-
headline?: string;
|
|
37
41
|
}
|
|
38
42
|
|
|
39
43
|
export const MutationComparison: FunctionComponent<MutationComparisonProps> = ({
|
|
@@ -42,6 +46,24 @@ export const MutationComparison: FunctionComponent<MutationComparisonProps> = ({
|
|
|
42
46
|
views,
|
|
43
47
|
size,
|
|
44
48
|
headline = 'Mutation comparison',
|
|
49
|
+
}) => {
|
|
50
|
+
const defaultSize = { height: '600px', width: '100%' };
|
|
51
|
+
|
|
52
|
+
return (
|
|
53
|
+
<ErrorBoundary size={size} defaultSize={defaultSize} headline={headline}>
|
|
54
|
+
<ResizeContainer size={size} defaultSize={defaultSize}>
|
|
55
|
+
<Headline heading={headline}>
|
|
56
|
+
<MutationComparisonInner variants={variants} sequenceType={sequenceType} views={views} />
|
|
57
|
+
</Headline>
|
|
58
|
+
</ResizeContainer>
|
|
59
|
+
</ErrorBoundary>
|
|
60
|
+
);
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
export const MutationComparisonInner: FunctionComponent<MutationComparisonInnerProps> = ({
|
|
64
|
+
variants,
|
|
65
|
+
sequenceType,
|
|
66
|
+
views,
|
|
45
67
|
}) => {
|
|
46
68
|
const lapis = useContext(LapisUrlContext);
|
|
47
69
|
|
|
@@ -50,36 +72,18 @@ export const MutationComparison: FunctionComponent<MutationComparisonProps> = ({
|
|
|
50
72
|
}, [variants, sequenceType, lapis]);
|
|
51
73
|
|
|
52
74
|
if (isLoading) {
|
|
53
|
-
return
|
|
54
|
-
<Headline heading={headline}>
|
|
55
|
-
<LoadingDisplay />
|
|
56
|
-
</Headline>
|
|
57
|
-
);
|
|
75
|
+
return <LoadingDisplay />;
|
|
58
76
|
}
|
|
59
77
|
|
|
60
78
|
if (error !== null) {
|
|
61
|
-
return
|
|
62
|
-
<Headline heading={headline}>
|
|
63
|
-
<ErrorDisplay error={error} />
|
|
64
|
-
</Headline>
|
|
65
|
-
);
|
|
79
|
+
return <ErrorDisplay error={error} />;
|
|
66
80
|
}
|
|
67
81
|
|
|
68
82
|
if (data === null) {
|
|
69
|
-
return
|
|
70
|
-
<Headline heading={headline}>
|
|
71
|
-
<NoDataDisplay />
|
|
72
|
-
</Headline>
|
|
73
|
-
);
|
|
83
|
+
return <NoDataDisplay />;
|
|
74
84
|
}
|
|
75
85
|
|
|
76
|
-
return
|
|
77
|
-
<ResizeContainer size={size} defaultSize={{ height: '700px', width: '100%' }}>
|
|
78
|
-
<Headline heading={headline}>
|
|
79
|
-
<MutationComparisonTabs data={data.mutationData} sequenceType={sequenceType} views={views} />
|
|
80
|
-
</Headline>
|
|
81
|
-
</ResizeContainer>
|
|
82
|
-
);
|
|
86
|
+
return <MutationComparisonTabs data={data.mutationData} sequenceType={sequenceType} views={views} />;
|
|
83
87
|
};
|
|
84
88
|
|
|
85
89
|
type MutationComparisonTabsProps = {
|
|
@@ -18,19 +18,34 @@ const meta: Meta<MutationFilterProps> = {
|
|
|
18
18
|
},
|
|
19
19
|
fetchMock: {},
|
|
20
20
|
},
|
|
21
|
+
argTypes: {
|
|
22
|
+
size: {
|
|
23
|
+
control: {
|
|
24
|
+
type: 'object',
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
initialValue: {
|
|
28
|
+
control: {
|
|
29
|
+
type: 'object',
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
},
|
|
21
33
|
decorators: [withActions],
|
|
22
34
|
};
|
|
23
35
|
|
|
24
36
|
export default meta;
|
|
25
37
|
|
|
26
38
|
export const Default: StoryObj<MutationFilterProps> = {
|
|
27
|
-
render: () => (
|
|
39
|
+
render: (args) => (
|
|
28
40
|
<LapisUrlContext.Provider value={LAPIS_URL}>
|
|
29
41
|
<ReferenceGenomeContext.Provider value={referenceGenome}>
|
|
30
|
-
<MutationFilter />
|
|
42
|
+
<MutationFilter size={args.size} initialValue={args.initialValue} />
|
|
31
43
|
</ReferenceGenomeContext.Provider>
|
|
32
44
|
</LapisUrlContext.Provider>
|
|
33
45
|
),
|
|
46
|
+
args: {
|
|
47
|
+
size: { width: '100%' },
|
|
48
|
+
},
|
|
34
49
|
};
|
|
35
50
|
|
|
36
51
|
export const FiresFilterChangedEvents: StoryObj<MutationFilterProps> = {
|
|
@@ -5,13 +5,19 @@ import { parseAndValidateMutation } from './parseAndValidateMutation';
|
|
|
5
5
|
import { type ReferenceGenome } from '../../lapisApi/ReferenceGenome';
|
|
6
6
|
import { type Deletion, type Insertion, type Mutation, type Substitution } from '../../utils/mutations';
|
|
7
7
|
import { ReferenceGenomeContext } from '../ReferenceGenomeContext';
|
|
8
|
+
import { ErrorBoundary } from '../components/error-boundary';
|
|
8
9
|
import Info from '../components/info';
|
|
10
|
+
import { ResizeContainer, type Size } from '../components/resize-container';
|
|
9
11
|
import { singleGraphColorRGBByName } from '../shared/charts/colors';
|
|
10
12
|
import { DeleteIcon } from '../shared/icons/DeleteIcon';
|
|
11
13
|
|
|
12
|
-
export
|
|
14
|
+
export interface MutationFilterInnerProps {
|
|
13
15
|
initialValue?: SelectedMutationFilterStrings | string[] | undefined;
|
|
14
|
-
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface MutationFilterProps extends MutationFilterInnerProps {
|
|
19
|
+
size?: Size;
|
|
20
|
+
}
|
|
15
21
|
|
|
16
22
|
export type SelectedFilters = {
|
|
17
23
|
nucleotideMutations: (Substitution | Deletion)[];
|
|
@@ -24,7 +30,19 @@ export type SelectedMutationFilterStrings = {
|
|
|
24
30
|
[Key in keyof SelectedFilters]: string[];
|
|
25
31
|
};
|
|
26
32
|
|
|
27
|
-
export const MutationFilter: FunctionComponent<MutationFilterProps> = ({ initialValue }) => {
|
|
33
|
+
export const MutationFilter: FunctionComponent<MutationFilterProps> = ({ initialValue, size }) => {
|
|
34
|
+
const defaultSize = { width: '100%', height: '6.5rem' };
|
|
35
|
+
|
|
36
|
+
return (
|
|
37
|
+
<ErrorBoundary size={size} defaultSize={defaultSize}>
|
|
38
|
+
<ResizeContainer size={size} defaultSize={defaultSize}>
|
|
39
|
+
<MutationFilterInner initialValue={initialValue} />
|
|
40
|
+
</ResizeContainer>
|
|
41
|
+
</ErrorBoundary>
|
|
42
|
+
);
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export const MutationFilterInner: FunctionComponent<MutationFilterInnerProps> = ({ initialValue }) => {
|
|
28
46
|
const referenceGenome = useContext(ReferenceGenomeContext);
|
|
29
47
|
const [selectedFilters, setSelectedFilters] = useState<SelectedFilters>(
|
|
30
48
|
getInitialState(initialValue, referenceGenome),
|
|
@@ -83,7 +101,7 @@ export const MutationFilter: FunctionComponent<MutationFilterProps> = ({ initial
|
|
|
83
101
|
};
|
|
84
102
|
|
|
85
103
|
return (
|
|
86
|
-
<div class={`rounded-lg border border-gray-300 bg-white p-2`}>
|
|
104
|
+
<div class={`h-full w-full rounded-lg border border-gray-300 bg-white p-2 overflow-scroll`}>
|
|
87
105
|
<div class='flex justify-between'>
|
|
88
106
|
<SelectedMutationDisplay
|
|
89
107
|
selectedFilters={selectedFilters}
|
|
@@ -295,11 +313,11 @@ const SelectedFilter = <MutationType extends Mutation>({
|
|
|
295
313
|
}: SelectedFilterProps<MutationType>) => {
|
|
296
314
|
return (
|
|
297
315
|
<div
|
|
298
|
-
class='flex items-center flex-nowrap gap-1 rounded me-1 px-2.5 py-0.5 font-medium text-xs mb-1'
|
|
316
|
+
class='flex items-center flex-nowrap gap-1 rounded me-1 px-2.5 py-0.5 font-medium text-xs mb-1 min-w-max'
|
|
299
317
|
style={{ backgroundColor, color: textColor }}
|
|
300
318
|
>
|
|
301
|
-
<div>{mutation.toString()}</div>
|
|
302
|
-
<button onClick={() => onDelete(mutation)}>
|
|
319
|
+
<div className='whitespace-nowrap min-w-max'>{mutation.toString()}</div>
|
|
320
|
+
<button type='button' onClick={() => onDelete(mutation)}>
|
|
303
321
|
<DeleteIcon />
|
|
304
322
|
</button>
|
|
305
323
|
</div>
|
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
import { LapisUrlContext } from '../LapisUrlContext';
|
|
17
17
|
import { type DisplayedSegment, SegmentSelector, useDisplayedSegments } from '../components/SegmentSelector';
|
|
18
18
|
import { CsvDownloadButton } from '../components/csv-download-button';
|
|
19
|
+
import { ErrorBoundary } from '../components/error-boundary';
|
|
19
20
|
import { ErrorDisplay } from '../components/error-display';
|
|
20
21
|
import Headline from '../components/headline';
|
|
21
22
|
import Info from '../components/info';
|
|
@@ -30,10 +31,13 @@ import { useQuery } from '../useQuery';
|
|
|
30
31
|
|
|
31
32
|
export type View = 'table' | 'grid' | 'insertions';
|
|
32
33
|
|
|
33
|
-
export interface
|
|
34
|
+
export interface MutationsInnerProps {
|
|
34
35
|
variant: LapisFilter;
|
|
35
36
|
sequenceType: SequenceType;
|
|
36
37
|
views: View[];
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface MutationsProps extends MutationsInnerProps {
|
|
37
41
|
size?: Size;
|
|
38
42
|
headline?: string;
|
|
39
43
|
}
|
|
@@ -45,42 +49,38 @@ export const Mutations: FunctionComponent<MutationsProps> = ({
|
|
|
45
49
|
size,
|
|
46
50
|
headline = 'Mutations',
|
|
47
51
|
}) => {
|
|
52
|
+
const defaultSize = { height: '600px', width: '100%' };
|
|
53
|
+
|
|
54
|
+
return (
|
|
55
|
+
<ErrorBoundary size={size} defaultSize={defaultSize} headline={headline}>
|
|
56
|
+
<ResizeContainer size={size} defaultSize={defaultSize}>
|
|
57
|
+
<Headline heading={headline}>
|
|
58
|
+
<MutationsInner variant={variant} sequenceType={sequenceType} views={views} />
|
|
59
|
+
</Headline>
|
|
60
|
+
</ResizeContainer>
|
|
61
|
+
</ErrorBoundary>
|
|
62
|
+
);
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
export const MutationsInner: FunctionComponent<MutationsInnerProps> = ({ variant, sequenceType, views }) => {
|
|
48
66
|
const lapis = useContext(LapisUrlContext);
|
|
49
67
|
const { data, error, isLoading } = useQuery(async () => {
|
|
50
68
|
return queryMutationsData(variant, sequenceType, lapis);
|
|
51
69
|
}, [variant, sequenceType, lapis]);
|
|
52
70
|
|
|
53
71
|
if (isLoading) {
|
|
54
|
-
return
|
|
55
|
-
<Headline heading={headline}>
|
|
56
|
-
<LoadingDisplay />
|
|
57
|
-
</Headline>
|
|
58
|
-
);
|
|
72
|
+
return <LoadingDisplay />;
|
|
59
73
|
}
|
|
60
74
|
|
|
61
75
|
if (error !== null) {
|
|
62
|
-
return
|
|
63
|
-
<Headline heading={headline}>
|
|
64
|
-
<ErrorDisplay error={error} />
|
|
65
|
-
</Headline>
|
|
66
|
-
);
|
|
76
|
+
return <ErrorDisplay error={error} />;
|
|
67
77
|
}
|
|
68
78
|
|
|
69
79
|
if (data === null) {
|
|
70
|
-
return
|
|
71
|
-
<Headline heading={headline}>
|
|
72
|
-
<NoDataDisplay />
|
|
73
|
-
</Headline>
|
|
74
|
-
);
|
|
80
|
+
return <NoDataDisplay />;
|
|
75
81
|
}
|
|
76
82
|
|
|
77
|
-
return
|
|
78
|
-
<ResizeContainer size={size} defaultSize={{ height: '700px', width: '100%' }}>
|
|
79
|
-
<Headline heading={headline}>
|
|
80
|
-
<MutationsTabs mutationsData={data} sequenceType={sequenceType} views={views} />
|
|
81
|
-
</Headline>
|
|
82
|
-
</ResizeContainer>
|
|
83
|
-
);
|
|
83
|
+
return <MutationsTabs mutationsData={data} sequenceType={sequenceType} views={views} />;
|
|
84
84
|
};
|
|
85
85
|
|
|
86
86
|
type MutationTabsProps = {
|
|
@@ -11,6 +11,7 @@ import { 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';
|
|
14
|
+
import { ErrorBoundary } from '../components/error-boundary';
|
|
14
15
|
import { ErrorDisplay } from '../components/error-display';
|
|
15
16
|
import Headline from '../components/headline';
|
|
16
17
|
import Info, { InfoHeadline1, InfoParagraph } from '../components/info';
|
|
@@ -25,15 +26,18 @@ import { useQuery } from '../useQuery';
|
|
|
25
26
|
|
|
26
27
|
export type View = 'bar' | 'line' | 'bubble' | 'table';
|
|
27
28
|
|
|
28
|
-
export interface PrevalenceOverTimeProps {
|
|
29
|
+
export interface PrevalenceOverTimeProps extends PrevalenceOverTimeInnerProps {
|
|
30
|
+
size?: Size;
|
|
31
|
+
headline?: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface PrevalenceOverTimeInnerProps {
|
|
29
35
|
numerator: NamedLapisFilter | NamedLapisFilter[];
|
|
30
36
|
denominator: NamedLapisFilter;
|
|
31
37
|
granularity: TemporalGranularity;
|
|
32
38
|
smoothingWindow: number;
|
|
33
39
|
views: View[];
|
|
34
40
|
confidenceIntervalMethods: ConfidenceIntervalMethod[];
|
|
35
|
-
size?: Size;
|
|
36
|
-
headline?: string;
|
|
37
41
|
}
|
|
38
42
|
|
|
39
43
|
export const PrevalenceOverTime: FunctionComponent<PrevalenceOverTimeProps> = ({
|
|
@@ -45,6 +49,34 @@ export const PrevalenceOverTime: FunctionComponent<PrevalenceOverTimeProps> = ({
|
|
|
45
49
|
confidenceIntervalMethods,
|
|
46
50
|
size,
|
|
47
51
|
headline = 'Prevalence over time',
|
|
52
|
+
}) => {
|
|
53
|
+
const defaultSize = { height: '600px', width: '100%' };
|
|
54
|
+
|
|
55
|
+
return (
|
|
56
|
+
<ErrorBoundary size={size} defaultSize={defaultSize} headline={headline}>
|
|
57
|
+
<ResizeContainer size={size} defaultSize={defaultSize}>
|
|
58
|
+
<Headline heading={headline}>
|
|
59
|
+
<PrevalenceOverTimeInner
|
|
60
|
+
numerator={numerator}
|
|
61
|
+
denominator={denominator}
|
|
62
|
+
granularity={granularity}
|
|
63
|
+
smoothingWindow={smoothingWindow}
|
|
64
|
+
views={views}
|
|
65
|
+
confidenceIntervalMethods={confidenceIntervalMethods}
|
|
66
|
+
/>
|
|
67
|
+
</Headline>
|
|
68
|
+
</ResizeContainer>
|
|
69
|
+
</ErrorBoundary>
|
|
70
|
+
);
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
export const PrevalenceOverTimeInner: FunctionComponent<PrevalenceOverTimeInnerProps> = ({
|
|
74
|
+
numerator,
|
|
75
|
+
denominator,
|
|
76
|
+
granularity,
|
|
77
|
+
smoothingWindow,
|
|
78
|
+
views,
|
|
79
|
+
confidenceIntervalMethods,
|
|
48
80
|
}) => {
|
|
49
81
|
const lapis = useContext(LapisUrlContext);
|
|
50
82
|
|
|
@@ -54,40 +86,24 @@ export const PrevalenceOverTime: FunctionComponent<PrevalenceOverTimeProps> = ({
|
|
|
54
86
|
);
|
|
55
87
|
|
|
56
88
|
if (isLoading) {
|
|
57
|
-
return
|
|
58
|
-
<Headline heading={headline}>
|
|
59
|
-
<LoadingDisplay />
|
|
60
|
-
</Headline>
|
|
61
|
-
);
|
|
89
|
+
return <LoadingDisplay />;
|
|
62
90
|
}
|
|
63
91
|
|
|
64
92
|
if (error !== null) {
|
|
65
|
-
return
|
|
66
|
-
<Headline heading={headline}>
|
|
67
|
-
<ErrorDisplay error={error} />
|
|
68
|
-
</Headline>
|
|
69
|
-
);
|
|
93
|
+
return <ErrorDisplay error={error} />;
|
|
70
94
|
}
|
|
71
95
|
|
|
72
96
|
if (data === null) {
|
|
73
|
-
return
|
|
74
|
-
<Headline heading={headline}>
|
|
75
|
-
<NoDataDisplay />
|
|
76
|
-
</Headline>
|
|
77
|
-
);
|
|
97
|
+
return <NoDataDisplay />;
|
|
78
98
|
}
|
|
79
99
|
|
|
80
100
|
return (
|
|
81
|
-
<
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
confidenceIntervalMethods={confidenceIntervalMethods}
|
|
88
|
-
/>
|
|
89
|
-
</Headline>
|
|
90
|
-
</ResizeContainer>
|
|
101
|
+
<PrevalenceOverTimeTabs
|
|
102
|
+
views={views}
|
|
103
|
+
data={data}
|
|
104
|
+
granularity={granularity}
|
|
105
|
+
confidenceIntervalMethods={confidenceIntervalMethods}
|
|
106
|
+
/>
|
|
91
107
|
);
|
|
92
108
|
};
|
|
93
109
|
|