@genspectrum/dashboard-components 0.14.2 → 0.16.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 +281 -105
- package/dist/{LineageFilterChangedEvent-C9dXOxt6.js → LineageFilterChangedEvent-COWV-Y0k.js} +6 -6
- package/dist/LineageFilterChangedEvent-COWV-Y0k.js.map +1 -0
- package/dist/assets/{mutationOverTimeWorker-Dxnxrfe0.js.map → mutationOverTimeWorker-BL50C-yi.js.map} +1 -1
- package/dist/components.d.ts +40 -40
- package/dist/components.js +373 -243
- package/dist/components.js.map +1 -1
- package/dist/style.css +9 -0
- package/dist/util.d.ts +49 -49
- package/dist/util.js +2 -2
- package/package.json +1 -1
- package/src/preact/ReferenceGenomeContext.ts +14 -1
- package/src/preact/aggregatedData/aggregate-bar-chart.tsx +26 -5
- package/src/preact/aggregatedData/aggregate.stories.tsx +0 -1
- package/src/preact/aggregatedData/aggregate.tsx +5 -1
- package/src/preact/components/ReferenceGenomesAwaiter.tsx +1 -6
- package/src/preact/components/resize-container.tsx +1 -1
- package/src/preact/{dateRangeSelector/date-range-selector.stories.tsx → dateRangeFilter/date-range-filter.stories.tsx} +15 -15
- package/src/preact/{dateRangeSelector/date-range-selector.tsx → dateRangeFilter/date-range-filter.tsx} +9 -9
- package/src/preact/{dateRangeSelector → dateRangeFilter}/dateRangeOption.ts +2 -2
- package/src/preact/mutationComparison/mutation-comparison-venn.tsx +4 -2
- package/src/preact/mutationComparison/mutation-comparison.stories.tsx +0 -1
- package/src/preact/mutationComparison/mutation-comparison.tsx +5 -1
- package/src/preact/mutationFilter/mutation-filter.stories.tsx +17 -1
- package/src/preact/mutationFilter/mutation-filter.tsx +8 -0
- package/src/preact/mutations/mutations.stories.tsx +0 -1
- package/src/preact/mutations/mutations.tsx +1 -1
- package/src/preact/mutationsOverTime/mutations-over-time.stories.tsx +0 -2
- package/src/preact/mutationsOverTime/mutations-over-time.tsx +1 -1
- package/src/preact/numberSequencesOverTime/number-sequences-over-time-bar-chart.tsx +8 -3
- package/src/preact/numberSequencesOverTime/number-sequences-over-time-line-chart.tsx +8 -3
- package/src/preact/numberSequencesOverTime/number-sequences-over-time.stories.tsx +3 -1
- package/src/preact/numberSequencesOverTime/number-sequences-over-time.tsx +18 -3
- package/src/preact/prevalenceOverTime/prevalence-over-time-bar-chart.tsx +48 -35
- package/src/preact/prevalenceOverTime/prevalence-over-time-bubble-chart.tsx +83 -70
- package/src/preact/prevalenceOverTime/prevalence-over-time-line-chart.tsx +48 -37
- package/src/preact/prevalenceOverTime/prevalence-over-time.stories.tsx +0 -3
- package/src/preact/prevalenceOverTime/prevalence-over-time.tsx +6 -1
- package/src/preact/relativeGrowthAdvantage/relative-growth-advantage-chart.tsx +31 -23
- package/src/preact/relativeGrowthAdvantage/relative-growth-advantage.stories.tsx +0 -1
- package/src/preact/relativeGrowthAdvantage/relative-growth-advantage.tsx +5 -1
- package/src/preact/sequencesByLocation/__mockData__/worldAtlas.json +1 -0
- package/src/preact/{map → sequencesByLocation}/sequences-by-location-map.tsx +6 -3
- package/src/preact/{map → sequencesByLocation}/sequences-by-location-table.tsx +1 -1
- package/src/preact/{map → sequencesByLocation}/sequences-by-location.stories.tsx +58 -1
- package/src/preact/{map → sequencesByLocation}/sequences-by-location.tsx +10 -1
- package/src/preact/shared/aspectRatio/AspectRatio.tsx +13 -0
- package/src/preact/shared/charts/getMaintainAspectRatio.ts +3 -0
- package/src/preact/statistic/statistics.stories.tsx +0 -1
- package/src/preact/statistic/statistics.tsx +4 -4
- package/src/preact/{textInput/TextInputChangedEvent.ts → textFilter/TextFilterChangedEvent.ts} +2 -2
- package/src/preact/{textInput/text-input.stories.tsx → textFilter/text-filter.stories.tsx} +10 -10
- package/src/preact/{textInput/text-input.tsx → textFilter/text-filter.tsx} +10 -10
- package/src/preact/wastewater/mutationsOverTime/wastewater-mutations-over-time.stories.tsx +0 -1
- package/src/preact/wastewater/mutationsOverTime/wastewater-mutations-over-time.tsx +1 -1
- package/src/query/computeMapLocationData.spec.ts +1 -1
- package/src/query/computeMapLocationData.ts +1 -1
- package/src/query/querySequencesByLocationData.ts +1 -1
- package/src/utilEntrypoint.ts +3 -3
- package/src/web-components/ResizeContainer.mdx +4 -1
- package/src/web-components/input/{gs-date-range-selector.stories.ts → gs-date-range-filter.stories.ts} +59 -14
- package/src/web-components/input/{gs-date-range-selector.tsx → gs-date-range-filter.tsx} +28 -13
- package/src/web-components/input/{gs-text-input.stories.ts → gs-text-filter.stories.ts} +15 -15
- package/src/web-components/input/{gs-text-input.tsx → gs-text-filter.tsx} +16 -16
- package/src/web-components/input/index.ts +2 -2
- package/src/web-components/visualization/gs-aggregate.stories.ts +13 -6
- package/src/web-components/visualization/gs-aggregate.tsx +1 -1
- package/src/web-components/visualization/gs-mutation-comparison.stories.ts +8 -1
- package/src/web-components/visualization/gs-mutation-comparison.tsx +1 -1
- package/src/web-components/visualization/gs-mutations-over-time.stories.ts +9 -1
- package/src/web-components/visualization/gs-mutations-over-time.tsx +1 -1
- package/src/web-components/visualization/gs-mutations.stories.ts +8 -1
- package/src/web-components/visualization/gs-mutations.tsx +1 -1
- package/src/web-components/visualization/gs-number-sequences-over-time.stories.ts +11 -1
- package/src/web-components/visualization/gs-number-sequences-over-time.tsx +1 -1
- package/src/web-components/visualization/gs-prevalence-over-time.stories.ts +8 -2
- package/src/web-components/visualization/gs-prevalence-over-time.tsx +1 -1
- package/src/web-components/visualization/gs-relative-growth-advantage.stories.ts +8 -1
- package/src/web-components/visualization/gs-relative-growth-advantage.tsx +1 -1
- package/src/web-components/visualization/gs-sequences-by-location.stories.ts +13 -7
- package/src/web-components/visualization/gs-sequences-by-location.tsx +6 -3
- package/src/web-components/visualization/gs-statistics.stories.ts +0 -1
- package/src/web-components/visualization/gs-statistics.tsx +1 -1
- package/src/web-components/wastewaterVisualization/gs-wastewater-mutations-over-time.stories.ts +9 -1
- package/src/web-components/wastewaterVisualization/gs-wastewater-mutations-over-time.tsx +1 -1
- package/standalone-bundle/assets/{mutationOverTimeWorker-CmSrq4SZ.js.map → mutationOverTimeWorker-CFB5-Mdk.js.map} +1 -1
- package/standalone-bundle/dashboard-components.js +6032 -5937
- package/standalone-bundle/dashboard-components.js.map +1 -1
- package/standalone-bundle/style.css +1 -1
- package/dist/LineageFilterChangedEvent-C9dXOxt6.js.map +0 -1
- package/src/preact/map/__mockData__/worldAtlas.json +0 -497127
- /package/src/preact/{dateRangeSelector → dateRangeFilter}/computeInitialValues.spec.ts +0 -0
- /package/src/preact/{dateRangeSelector → dateRangeFilter}/computeInitialValues.ts +0 -0
- /package/src/preact/{dateRangeSelector → dateRangeFilter}/dateConversion.ts +0 -0
- /package/src/preact/{dateRangeSelector → dateRangeFilter}/selectableOptions.ts +0 -0
- /package/src/preact/{map → sequencesByLocation}/__mockData__/aggregatedGermany.json +0 -0
- /package/src/preact/{map → sequencesByLocation}/__mockData__/aggregatedWorld.json +0 -0
- /package/src/preact/{map → sequencesByLocation}/__mockData__/germanyMap.json +0 -0
- /package/src/preact/{map → sequencesByLocation}/__mockData__/howToGenerateWorldMap.md +0 -0
- /package/src/preact/{map → sequencesByLocation}/leafletStyleModifications.css +0 -0
- /package/src/preact/{map → sequencesByLocation}/loadMapSource.tsx +0 -0
- /package/src/preact/{textInput → textFilter}/__mockData__/aggregated_hosts.json +0 -0
- /package/src/preact/{textInput → textFilter}/fetchStringAutocompleteList.spec.ts +0 -0
- /package/src/preact/{textInput → textFilter}/fetchStringAutocompleteList.ts +0 -0
|
@@ -4,7 +4,7 @@ import type { StepFunction } from '@storybook/types';
|
|
|
4
4
|
import dayjs from 'dayjs/esm';
|
|
5
5
|
import { useEffect, useRef, useState } from 'preact/hooks';
|
|
6
6
|
|
|
7
|
-
import {
|
|
7
|
+
import { DateRangeFilter, type DateRangeFilterProps } from './date-range-filter';
|
|
8
8
|
import { previewHandles } from '../../../.storybook/preview';
|
|
9
9
|
import { LAPIS_URL } from '../../constants';
|
|
10
10
|
import { LapisUrlContextProvider } from '../LapisUrlContext';
|
|
@@ -19,9 +19,9 @@ const customDateRange = {
|
|
|
19
19
|
dateTo: '2021-12-31',
|
|
20
20
|
};
|
|
21
21
|
|
|
22
|
-
const meta: Meta<
|
|
23
|
-
title: 'Input/
|
|
24
|
-
component:
|
|
22
|
+
const meta: Meta<DateRangeFilterProps> = {
|
|
23
|
+
title: 'Input/DateRangeFilter',
|
|
24
|
+
component: DateRangeFilter,
|
|
25
25
|
parameters: {
|
|
26
26
|
actions: {
|
|
27
27
|
handles: ['gs-date-range-filter-changed', 'gs-date-range-option-changed', ...previewHandles],
|
|
@@ -61,15 +61,15 @@ const meta: Meta<DateRangeSelectorProps> = {
|
|
|
61
61
|
|
|
62
62
|
export default meta;
|
|
63
63
|
|
|
64
|
-
const Primary: StoryObj<
|
|
64
|
+
const Primary: StoryObj<DateRangeFilterProps> = {
|
|
65
65
|
render: (args) => (
|
|
66
66
|
<LapisUrlContextProvider value={LAPIS_URL}>
|
|
67
|
-
<
|
|
67
|
+
<DateRangeFilter {...args} />
|
|
68
68
|
</LapisUrlContextProvider>
|
|
69
69
|
),
|
|
70
70
|
};
|
|
71
71
|
|
|
72
|
-
export const SetCorrectInitialValues: StoryObj<
|
|
72
|
+
export const SetCorrectInitialValues: StoryObj<DateRangeFilterProps> = {
|
|
73
73
|
...Primary,
|
|
74
74
|
args: {
|
|
75
75
|
...Primary.args,
|
|
@@ -88,7 +88,7 @@ export const SetCorrectInitialValues: StoryObj<DateRangeSelectorProps> = {
|
|
|
88
88
|
|
|
89
89
|
const initialDateFrom = '2000-01-01';
|
|
90
90
|
|
|
91
|
-
export const SetCorrectInitialDateFrom: StoryObj<
|
|
91
|
+
export const SetCorrectInitialDateFrom: StoryObj<DateRangeFilterProps> = {
|
|
92
92
|
...Primary,
|
|
93
93
|
args: {
|
|
94
94
|
...Primary.args,
|
|
@@ -107,7 +107,7 @@ export const SetCorrectInitialDateFrom: StoryObj<DateRangeSelectorProps> = {
|
|
|
107
107
|
|
|
108
108
|
const initialDateTo = '2000-01-01';
|
|
109
109
|
|
|
110
|
-
export const SetCorrectInitialDateTo: StoryObj<
|
|
110
|
+
export const SetCorrectInitialDateTo: StoryObj<DateRangeFilterProps> = {
|
|
111
111
|
...Primary,
|
|
112
112
|
args: {
|
|
113
113
|
...Primary.args,
|
|
@@ -124,7 +124,7 @@ export const SetCorrectInitialDateTo: StoryObj<DateRangeSelectorProps> = {
|
|
|
124
124
|
},
|
|
125
125
|
};
|
|
126
126
|
|
|
127
|
-
export const ChangingDateSetsOptionToCustom: StoryObj<
|
|
127
|
+
export const ChangingDateSetsOptionToCustom: StoryObj<DateRangeFilterProps> = {
|
|
128
128
|
...Primary,
|
|
129
129
|
args: {
|
|
130
130
|
...Primary.args,
|
|
@@ -167,7 +167,7 @@ export const ChangingDateSetsOptionToCustom: StoryObj<DateRangeSelectorProps> =
|
|
|
167
167
|
},
|
|
168
168
|
};
|
|
169
169
|
|
|
170
|
-
export const ChangingTheValueProgrammatically: StoryObj<
|
|
170
|
+
export const ChangingTheValueProgrammatically: StoryObj<DateRangeFilterProps> = {
|
|
171
171
|
...Primary,
|
|
172
172
|
render: (args) => {
|
|
173
173
|
const StatefulWrapper = () => {
|
|
@@ -183,7 +183,7 @@ export const ChangingTheValueProgrammatically: StoryObj<DateRangeSelectorProps>
|
|
|
183
183
|
return (
|
|
184
184
|
<div ref={ref}>
|
|
185
185
|
<LapisUrlContextProvider value={LAPIS_URL}>
|
|
186
|
-
<
|
|
186
|
+
<DateRangeFilter {...args} value={value} />
|
|
187
187
|
</LapisUrlContextProvider>
|
|
188
188
|
<button className='btn' onClick={() => setValue(customDateRange.label)}>
|
|
189
189
|
Set to Custom
|
|
@@ -230,7 +230,7 @@ export const ChangingTheValueProgrammatically: StoryObj<DateRangeSelectorProps>
|
|
|
230
230
|
},
|
|
231
231
|
};
|
|
232
232
|
|
|
233
|
-
export const ChangingDateOption: StoryObj<
|
|
233
|
+
export const ChangingDateOption: StoryObj<DateRangeFilterProps> = {
|
|
234
234
|
...Primary,
|
|
235
235
|
play: async ({ canvasElement, step }) => {
|
|
236
236
|
const { canvas, filterChangedListenerMock, optionChangedListenerMock } = await prepare(canvasElement, step);
|
|
@@ -263,7 +263,7 @@ export const ChangingDateOption: StoryObj<DateRangeSelectorProps> = {
|
|
|
263
263
|
},
|
|
264
264
|
};
|
|
265
265
|
|
|
266
|
-
export const HandlesInvalidInitialDateFrom: StoryObj<
|
|
266
|
+
export const HandlesInvalidInitialDateFrom: StoryObj<DateRangeFilterProps> = {
|
|
267
267
|
...Primary,
|
|
268
268
|
args: {
|
|
269
269
|
...Primary.args,
|
|
@@ -278,7 +278,7 @@ export const HandlesInvalidInitialDateFrom: StoryObj<DateRangeSelectorProps> = {
|
|
|
278
278
|
},
|
|
279
279
|
};
|
|
280
280
|
|
|
281
|
-
export const WithNoDateColumn: StoryObj<
|
|
281
|
+
export const WithNoDateColumn: StoryObj<DateRangeFilterProps> = {
|
|
282
282
|
...Primary,
|
|
283
283
|
args: {
|
|
284
284
|
...Primary.args,
|
|
@@ -18,39 +18,39 @@ import type { ScaleType } from '../shared/charts/getYAxisScale';
|
|
|
18
18
|
|
|
19
19
|
const customOption = 'Custom';
|
|
20
20
|
|
|
21
|
-
const
|
|
21
|
+
const dateRangeFilterInnerPropsSchema = z.object({
|
|
22
22
|
dateRangeOptions: z.array(dateRangeOptionSchema),
|
|
23
23
|
earliestDate: z.string().date(),
|
|
24
24
|
value: dateRangeValueSchema.optional(),
|
|
25
25
|
lapisDateField: z.string().min(1),
|
|
26
26
|
});
|
|
27
27
|
|
|
28
|
-
const
|
|
28
|
+
const dateRangeFilterPropsSchema = dateRangeFilterInnerPropsSchema.extend({
|
|
29
29
|
width: z.string(),
|
|
30
30
|
});
|
|
31
31
|
|
|
32
|
-
export type
|
|
33
|
-
export type
|
|
32
|
+
export type DateRangeFilterProps = z.infer<typeof dateRangeFilterPropsSchema>;
|
|
33
|
+
export type DateRangeFilterInnerProps = z.infer<typeof dateRangeFilterInnerPropsSchema>;
|
|
34
34
|
|
|
35
|
-
export const
|
|
35
|
+
export const DateRangeFilter = (props: DateRangeFilterProps) => {
|
|
36
36
|
const { width, ...innerProps } = props;
|
|
37
37
|
const size = { width, height: '3rem' };
|
|
38
38
|
|
|
39
39
|
return (
|
|
40
|
-
<ErrorBoundary size={size} layout='horizontal' componentProps={props} schema={
|
|
40
|
+
<ErrorBoundary size={size} layout='horizontal' componentProps={props} schema={dateRangeFilterPropsSchema}>
|
|
41
41
|
<div style={{ width }}>
|
|
42
|
-
<
|
|
42
|
+
<DateRangeFilterInner {...innerProps} />
|
|
43
43
|
</div>
|
|
44
44
|
</ErrorBoundary>
|
|
45
45
|
);
|
|
46
46
|
};
|
|
47
47
|
|
|
48
|
-
export const
|
|
48
|
+
export const DateRangeFilterInner = ({
|
|
49
49
|
dateRangeOptions,
|
|
50
50
|
earliestDate = '1900-01-01',
|
|
51
51
|
value,
|
|
52
52
|
lapisDateField,
|
|
53
|
-
}:
|
|
53
|
+
}: DateRangeFilterInnerProps) => {
|
|
54
54
|
const initialValues = useMemo(
|
|
55
55
|
() => computeInitialValues(value, earliestDate, dateRangeOptions),
|
|
56
56
|
[value, earliestDate, dateRangeOptions],
|
|
@@ -3,7 +3,7 @@ import z from 'zod';
|
|
|
3
3
|
import { toYYYYMMDD } from './dateConversion';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
-
* A date range option that can be used in the `gs-date-range-
|
|
6
|
+
* A date range option that can be used in the `gs-date-range-filter` component.
|
|
7
7
|
*/
|
|
8
8
|
export const dateRangeOptionSchema = z.object({
|
|
9
9
|
/** The label of the date range option that will be shown to the user */
|
|
@@ -65,7 +65,7 @@ const lastYear = new Date(today);
|
|
|
65
65
|
lastYear.setFullYear(today.getFullYear() - 1);
|
|
66
66
|
|
|
67
67
|
/**
|
|
68
|
-
* Presets for the `gs-date-range-
|
|
68
|
+
* Presets for the `gs-date-range-filter` component that can be used as `dateRangeOptions`.
|
|
69
69
|
*/
|
|
70
70
|
export const dateRangeOptionPresets = {
|
|
71
71
|
last2Weeks: {
|
|
@@ -13,11 +13,13 @@ Chart.register(...registerables, VennDiagramController, ArcSlice);
|
|
|
13
13
|
export interface MutationComparisonVennProps {
|
|
14
14
|
data: Dataset<MutationData>;
|
|
15
15
|
proportionInterval: ProportionInterval;
|
|
16
|
+
maintainAspectRatio: boolean;
|
|
16
17
|
}
|
|
17
18
|
|
|
18
19
|
export const MutationComparisonVenn: FunctionComponent<MutationComparisonVennProps> = ({
|
|
19
20
|
data,
|
|
20
21
|
proportionInterval,
|
|
22
|
+
maintainAspectRatio,
|
|
21
23
|
}) => {
|
|
22
24
|
const [selectedDatasetIndex, setSelectedDatasetIndex] = useState<null | number>(null);
|
|
23
25
|
|
|
@@ -48,7 +50,7 @@ export const MutationComparisonVenn: FunctionComponent<MutationComparisonVennPro
|
|
|
48
50
|
type: 'venn',
|
|
49
51
|
data: sets,
|
|
50
52
|
options: {
|
|
51
|
-
maintainAspectRatio
|
|
53
|
+
maintainAspectRatio,
|
|
52
54
|
scales: {
|
|
53
55
|
x: {
|
|
54
56
|
ticks: {
|
|
@@ -91,7 +93,7 @@ export const MutationComparisonVenn: FunctionComponent<MutationComparisonVennPro
|
|
|
91
93
|
},
|
|
92
94
|
},
|
|
93
95
|
}),
|
|
94
|
-
[sets],
|
|
96
|
+
[maintainAspectRatio, sets],
|
|
95
97
|
);
|
|
96
98
|
|
|
97
99
|
if (data.content.length > 5) {
|
|
@@ -21,6 +21,7 @@ import { ProportionSelectorDropdown } from '../components/proportion-selector-dr
|
|
|
21
21
|
import { ResizeContainer } from '../components/resize-container';
|
|
22
22
|
import { type DisplayedSegment, SegmentSelector, useDisplayedSegments } from '../components/segment-selector';
|
|
23
23
|
import Tabs from '../components/tabs';
|
|
24
|
+
import { getMaintainAspectRatio } from '../shared/charts/getMaintainAspectRatio';
|
|
24
25
|
import { useQuery } from '../useQuery';
|
|
25
26
|
|
|
26
27
|
export const mutationComparisonViewSchema = z.union([z.literal(views.table), z.literal(views.venn)]);
|
|
@@ -28,7 +29,7 @@ export type MutationComparisonView = z.infer<typeof mutationComparisonViewSchema
|
|
|
28
29
|
|
|
29
30
|
const mutationComparisonPropsSchema = z.object({
|
|
30
31
|
width: z.string(),
|
|
31
|
-
height: z.string(),
|
|
32
|
+
height: z.string().optional(),
|
|
32
33
|
lapisFilters: z.array(namedLapisFilterSchema).min(1),
|
|
33
34
|
sequenceType: sequenceTypeSchema,
|
|
34
35
|
views: z.array(mutationComparisonViewSchema),
|
|
@@ -91,6 +92,8 @@ const MutationComparisonTabs: FunctionComponent<MutationComparisonTabsProps> = (
|
|
|
91
92
|
[data, displayedSegments, displayedMutationTypes],
|
|
92
93
|
);
|
|
93
94
|
|
|
95
|
+
const maintainAspectRatio = getMaintainAspectRatio(originalComponentProps.height);
|
|
96
|
+
|
|
94
97
|
const getTab = (view: MutationComparisonView) => {
|
|
95
98
|
switch (view) {
|
|
96
99
|
case 'table':
|
|
@@ -111,6 +114,7 @@ const MutationComparisonTabs: FunctionComponent<MutationComparisonTabsProps> = (
|
|
|
111
114
|
<MutationComparisonVenn
|
|
112
115
|
data={{ content: filteredData }}
|
|
113
116
|
proportionInterval={proportionInterval}
|
|
117
|
+
maintainAspectRatio={maintainAspectRatio}
|
|
114
118
|
/>
|
|
115
119
|
),
|
|
116
120
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type Meta, type PreactRenderer, type StoryObj } from '@storybook/preact';
|
|
2
2
|
import { expect, fireEvent, fn, userEvent, waitFor, within } from '@storybook/test';
|
|
3
3
|
import { type StepFunction } from '@storybook/types';
|
|
4
4
|
|
|
@@ -8,6 +8,7 @@ import { LAPIS_URL } from '../../constants';
|
|
|
8
8
|
import referenceGenome from '../../lapisApi/__mockData__/referenceGenome.json';
|
|
9
9
|
import { LapisUrlContextProvider } from '../LapisUrlContext';
|
|
10
10
|
import { ReferenceGenomeContext } from '../ReferenceGenomeContext';
|
|
11
|
+
import { playThatExpectsErrorMessage } from '../shared/stories/expectErrorMessage';
|
|
11
12
|
|
|
12
13
|
const meta: Meta<MutationFilterProps> = {
|
|
13
14
|
title: 'Input/MutationFilter',
|
|
@@ -219,6 +220,21 @@ export const WithInitialValue: StoryObj<MutationFilterProps> = {
|
|
|
219
220
|
},
|
|
220
221
|
};
|
|
221
222
|
|
|
223
|
+
export const WithNoReferenceSequencesDefined: StoryObj<MutationFilterProps> = {
|
|
224
|
+
...Default,
|
|
225
|
+
render: (args) => (
|
|
226
|
+
<LapisUrlContextProvider value={LAPIS_URL}>
|
|
227
|
+
<ReferenceGenomeContext.Provider value={{ nucleotideSequences: [], genes: [] }}>
|
|
228
|
+
<MutationFilter {...args} />
|
|
229
|
+
</ReferenceGenomeContext.Provider>
|
|
230
|
+
</LapisUrlContextProvider>
|
|
231
|
+
),
|
|
232
|
+
play: playThatExpectsErrorMessage(
|
|
233
|
+
'Error - No reference sequences available',
|
|
234
|
+
'This organism has neither nucleotide nor amino acid sequences',
|
|
235
|
+
),
|
|
236
|
+
};
|
|
237
|
+
|
|
222
238
|
async function prepare(canvasElement: HTMLElement, step: StepFunction<PreactRenderer, unknown>) {
|
|
223
239
|
const canvas = within(canvasElement);
|
|
224
240
|
|
|
@@ -10,6 +10,7 @@ import { type MutationsFilter, mutationsFilterSchema } from '../../types';
|
|
|
10
10
|
import { type DeletionClass, type InsertionClass, type SubstitutionClass } from '../../utils/mutations';
|
|
11
11
|
import { ReferenceGenomeContext } from '../ReferenceGenomeContext';
|
|
12
12
|
import { ErrorBoundary } from '../components/error-boundary';
|
|
13
|
+
import { UserFacingError } from '../components/error-display';
|
|
13
14
|
import { singleGraphColorRGBByName } from '../shared/charts/colors';
|
|
14
15
|
|
|
15
16
|
const mutationFilterInnerPropsSchema = z.object({
|
|
@@ -54,6 +55,13 @@ export const MutationFilterInner: FunctionComponent<MutationFilterInnerProps> =
|
|
|
54
55
|
|
|
55
56
|
const filterRef = useRef<HTMLDivElement>(null);
|
|
56
57
|
|
|
58
|
+
if (referenceGenome.nucleotideSequences.length === 0 && referenceGenome.genes.length === 0) {
|
|
59
|
+
throw new UserFacingError(
|
|
60
|
+
'No reference sequences available',
|
|
61
|
+
'This organism has neither nucleotide nor amino acid sequences configured in its reference genome. You cannot filter by mutations.',
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
|
|
57
65
|
const handleRemoveValue = (option: ParsedMutationFilter) => {
|
|
58
66
|
const newSelectedFilters = {
|
|
59
67
|
...selectedFilters,
|
|
@@ -41,7 +41,7 @@ const mutationsPropsSchema = z.object({
|
|
|
41
41
|
views: mutationsViewSchema.array(),
|
|
42
42
|
pageSize: z.union([z.boolean(), z.number()]),
|
|
43
43
|
width: z.string(),
|
|
44
|
-
height: z.string(),
|
|
44
|
+
height: z.string().optional(),
|
|
45
45
|
});
|
|
46
46
|
export type MutationsProps = z.infer<typeof mutationsPropsSchema>;
|
|
47
47
|
|
|
@@ -62,7 +62,6 @@ export const Default: StoryObj<MutationsOverTimeProps> = {
|
|
|
62
62
|
sequenceType: 'nucleotide',
|
|
63
63
|
views: ['grid'],
|
|
64
64
|
width: '100%',
|
|
65
|
-
height: '700px',
|
|
66
65
|
granularity: 'month',
|
|
67
66
|
lapisDateField: 'date',
|
|
68
67
|
},
|
|
@@ -76,7 +75,6 @@ export const ShowsMessageWhenTooManyMutations: StoryObj<MutationsOverTimeProps>
|
|
|
76
75
|
sequenceType: 'nucleotide',
|
|
77
76
|
views: ['grid'],
|
|
78
77
|
width: '100%',
|
|
79
|
-
height: '700px',
|
|
80
78
|
granularity: 'year',
|
|
81
79
|
lapisDateField: 'date',
|
|
82
80
|
},
|
|
@@ -45,7 +45,7 @@ const mutationOverTimeSchema = z.object({
|
|
|
45
45
|
granularity: temporalGranularitySchema,
|
|
46
46
|
lapisDateField: z.string().min(1),
|
|
47
47
|
width: z.string(),
|
|
48
|
-
height: z.string(),
|
|
48
|
+
height: z.string().optional(),
|
|
49
49
|
});
|
|
50
50
|
export type MutationsOverTimeProps = z.infer<typeof mutationOverTimeSchema>;
|
|
51
51
|
|
|
@@ -10,11 +10,16 @@ import { getYAxisScale, type ScaleType } from '../shared/charts/getYAxisScale';
|
|
|
10
10
|
interface NumberSequencesOverBarChartProps {
|
|
11
11
|
data: NumberOfSequencesDatasets;
|
|
12
12
|
yAxisScaleType: ScaleType;
|
|
13
|
+
maintainAspectRatio: boolean;
|
|
13
14
|
}
|
|
14
15
|
|
|
15
16
|
Chart.register(...registerables);
|
|
16
17
|
|
|
17
|
-
export const NumberSequencesOverTimeBarChart = ({
|
|
18
|
+
export const NumberSequencesOverTimeBarChart = ({
|
|
19
|
+
data,
|
|
20
|
+
yAxisScaleType,
|
|
21
|
+
maintainAspectRatio,
|
|
22
|
+
}: NumberSequencesOverBarChartProps) => {
|
|
18
23
|
const config: ChartConfiguration = useMemo(
|
|
19
24
|
() => ({
|
|
20
25
|
type: 'bar',
|
|
@@ -22,7 +27,7 @@ export const NumberSequencesOverTimeBarChart = ({ data, yAxisScaleType }: Number
|
|
|
22
27
|
datasets: getDatasets(data),
|
|
23
28
|
},
|
|
24
29
|
options: {
|
|
25
|
-
maintainAspectRatio
|
|
30
|
+
maintainAspectRatio,
|
|
26
31
|
animation: false,
|
|
27
32
|
scales: {
|
|
28
33
|
y: {
|
|
@@ -40,7 +45,7 @@ export const NumberSequencesOverTimeBarChart = ({ data, yAxisScaleType }: Number
|
|
|
40
45
|
},
|
|
41
46
|
},
|
|
42
47
|
}),
|
|
43
|
-
[data, yAxisScaleType],
|
|
48
|
+
[data, maintainAspectRatio, yAxisScaleType],
|
|
44
49
|
);
|
|
45
50
|
|
|
46
51
|
return <GsChart configuration={config} />;
|
|
@@ -10,11 +10,16 @@ import { getYAxisScale, type ScaleType } from '../shared/charts/getYAxisScale';
|
|
|
10
10
|
interface NumberSequencesOverBarChartProps {
|
|
11
11
|
data: NumberOfSequencesDatasets;
|
|
12
12
|
yAxisScaleType: ScaleType;
|
|
13
|
+
maintainAspectRatio: boolean;
|
|
13
14
|
}
|
|
14
15
|
|
|
15
16
|
Chart.register(...registerables);
|
|
16
17
|
|
|
17
|
-
export const NumberSequencesOverTimeLineChart = ({
|
|
18
|
+
export const NumberSequencesOverTimeLineChart = ({
|
|
19
|
+
data,
|
|
20
|
+
yAxisScaleType,
|
|
21
|
+
maintainAspectRatio,
|
|
22
|
+
}: NumberSequencesOverBarChartProps) => {
|
|
18
23
|
const config: ChartConfiguration = useMemo(
|
|
19
24
|
() => ({
|
|
20
25
|
type: 'line',
|
|
@@ -22,7 +27,7 @@ export const NumberSequencesOverTimeLineChart = ({ data, yAxisScaleType }: Numbe
|
|
|
22
27
|
datasets: getDatasets(data),
|
|
23
28
|
},
|
|
24
29
|
options: {
|
|
25
|
-
maintainAspectRatio
|
|
30
|
+
maintainAspectRatio,
|
|
26
31
|
animation: false,
|
|
27
32
|
scales: {
|
|
28
33
|
y: {
|
|
@@ -40,7 +45,7 @@ export const NumberSequencesOverTimeLineChart = ({ data, yAxisScaleType }: Numbe
|
|
|
40
45
|
},
|
|
41
46
|
},
|
|
42
47
|
}),
|
|
43
|
-
[data, yAxisScaleType],
|
|
48
|
+
[data, maintainAspectRatio, yAxisScaleType],
|
|
44
49
|
);
|
|
45
50
|
|
|
46
51
|
return <GsChart configuration={config} />;
|
|
@@ -24,6 +24,9 @@ export default {
|
|
|
24
24
|
control: { type: 'check' },
|
|
25
25
|
},
|
|
26
26
|
pageSize: { control: 'object' },
|
|
27
|
+
height: {
|
|
28
|
+
control: 'text',
|
|
29
|
+
},
|
|
27
30
|
},
|
|
28
31
|
};
|
|
29
32
|
|
|
@@ -40,7 +43,6 @@ const Template: StoryObj<NumberSequencesOverTimeProps> = {
|
|
|
40
43
|
],
|
|
41
44
|
lapisDateField: 'date',
|
|
42
45
|
width: '100%',
|
|
43
|
-
height: '700px',
|
|
44
46
|
smoothingWindow: 0,
|
|
45
47
|
granularity: 'month',
|
|
46
48
|
pageSize: 10,
|
|
@@ -21,6 +21,7 @@ import { NoDataDisplay } from '../components/no-data-display';
|
|
|
21
21
|
import { ResizeContainer } from '../components/resize-container';
|
|
22
22
|
import { ScalingSelector } from '../components/scaling-selector';
|
|
23
23
|
import Tabs from '../components/tabs';
|
|
24
|
+
import { getMaintainAspectRatio } from '../shared/charts/getMaintainAspectRatio';
|
|
24
25
|
import type { ScaleType } from '../shared/charts/getYAxisScale';
|
|
25
26
|
import { useQuery } from '../useQuery';
|
|
26
27
|
|
|
@@ -33,7 +34,7 @@ export type NumberSequencesOverTimeView = z.infer<typeof numberSequencesOverTime
|
|
|
33
34
|
|
|
34
35
|
const numberSequencesOverTimePropsSchema = z.object({
|
|
35
36
|
width: z.string(),
|
|
36
|
-
height: z.string(),
|
|
37
|
+
height: z.string().optional(),
|
|
37
38
|
lapisFilters: z.array(namedLapisFilterSchema).min(1),
|
|
38
39
|
lapisDateField: z.string().min(1),
|
|
39
40
|
views: z.array(numberSequencesOverTimeViewSchema),
|
|
@@ -89,17 +90,31 @@ interface NumberSequencesOverTimeTabsProps {
|
|
|
89
90
|
const NumberSequencesOverTimeTabs = ({ data, originalComponentProps }: NumberSequencesOverTimeTabsProps) => {
|
|
90
91
|
const [yAxisScaleType, setYAxisScaleType] = useState<ScaleType>('linear');
|
|
91
92
|
|
|
93
|
+
const maintainAspectRatio = getMaintainAspectRatio(originalComponentProps.height);
|
|
94
|
+
|
|
92
95
|
const getTab = (view: NumberSequencesOverTimeView) => {
|
|
93
96
|
switch (view) {
|
|
94
97
|
case 'bar':
|
|
95
98
|
return {
|
|
96
99
|
title: 'Bar',
|
|
97
|
-
content:
|
|
100
|
+
content: (
|
|
101
|
+
<NumberSequencesOverTimeBarChart
|
|
102
|
+
data={data}
|
|
103
|
+
yAxisScaleType={yAxisScaleType}
|
|
104
|
+
maintainAspectRatio={maintainAspectRatio}
|
|
105
|
+
/>
|
|
106
|
+
),
|
|
98
107
|
};
|
|
99
108
|
case 'line':
|
|
100
109
|
return {
|
|
101
110
|
title: 'Line',
|
|
102
|
-
content:
|
|
111
|
+
content: (
|
|
112
|
+
<NumberSequencesOverTimeLineChart
|
|
113
|
+
data={data}
|
|
114
|
+
yAxisScaleType={yAxisScaleType}
|
|
115
|
+
maintainAspectRatio={maintainAspectRatio}
|
|
116
|
+
/>
|
|
117
|
+
),
|
|
103
118
|
};
|
|
104
119
|
case 'table':
|
|
105
120
|
return {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Chart, type ChartConfiguration, type ChartDataset, registerables, type TooltipItem } from 'chart.js';
|
|
2
2
|
import { BarWithErrorBar, BarWithErrorBarsController } from 'chartjs-chart-error-bars';
|
|
3
|
+
import { useMemo } from 'preact/hooks';
|
|
3
4
|
|
|
4
5
|
import { maxInData } from './prevalence-over-time';
|
|
5
6
|
import {
|
|
@@ -21,55 +22,67 @@ interface PrevalenceOverTimeBarChartProps {
|
|
|
21
22
|
yAxisScaleType: ScaleType;
|
|
22
23
|
confidenceIntervalMethod: ConfidenceIntervalMethod;
|
|
23
24
|
yAxisMaxConfig: YAxisMaxConfig;
|
|
25
|
+
maintainAspectRatio: boolean;
|
|
24
26
|
}
|
|
25
27
|
|
|
26
28
|
Chart.register(...registerables, LogitScale, BarWithErrorBarsController, BarWithErrorBar);
|
|
27
29
|
|
|
30
|
+
const NO_DATA = 'noData';
|
|
31
|
+
|
|
28
32
|
const PrevalenceOverTimeBarChart = ({
|
|
29
33
|
data,
|
|
30
34
|
yAxisScaleType,
|
|
31
35
|
confidenceIntervalMethod,
|
|
32
36
|
yAxisMaxConfig,
|
|
37
|
+
maintainAspectRatio,
|
|
33
38
|
}: PrevalenceOverTimeBarChartProps) => {
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
39
|
+
const config = useMemo<ChartConfiguration | typeof NO_DATA>(() => {
|
|
40
|
+
const nullFirstData = data
|
|
41
|
+
.filter((prevalenceOverTimeData) => prevalenceOverTimeData.content.length > 0)
|
|
42
|
+
.map((variantData) => {
|
|
43
|
+
return {
|
|
44
|
+
content: variantData.content.sort(sortNullToBeginningThenByDate),
|
|
45
|
+
displayName: variantData.displayName,
|
|
46
|
+
};
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
if (nullFirstData.length === 0) {
|
|
50
|
+
return NO_DATA;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const datasets = nullFirstData.map((graphData, index) =>
|
|
54
|
+
getDataset(graphData, index, confidenceIntervalMethod),
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
const maxY =
|
|
58
|
+
yAxisScaleType !== 'logit'
|
|
59
|
+
? getYAxisMax(maxInData(nullFirstData), yAxisMaxConfig?.[yAxisScaleType])
|
|
60
|
+
: undefined;
|
|
42
61
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
const datasets = nullFirstData.map((graphData, index) => getDataset(graphData, index, confidenceIntervalMethod));
|
|
48
|
-
|
|
49
|
-
const maxY =
|
|
50
|
-
yAxisScaleType !== 'logit'
|
|
51
|
-
? getYAxisMax(maxInData(nullFirstData), yAxisMaxConfig?.[yAxisScaleType])
|
|
52
|
-
: undefined;
|
|
53
|
-
|
|
54
|
-
const config: ChartConfiguration = {
|
|
55
|
-
type: BarWithErrorBarsController.id,
|
|
56
|
-
data: {
|
|
57
|
-
datasets,
|
|
58
|
-
},
|
|
59
|
-
options: {
|
|
60
|
-
maintainAspectRatio: false,
|
|
61
|
-
animation: false,
|
|
62
|
-
scales: {
|
|
63
|
-
y: { ...getYAxisScale(yAxisScaleType), max: maxY },
|
|
62
|
+
return {
|
|
63
|
+
type: BarWithErrorBarsController.id,
|
|
64
|
+
data: {
|
|
65
|
+
datasets,
|
|
64
66
|
},
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
67
|
+
options: {
|
|
68
|
+
maintainAspectRatio,
|
|
69
|
+
animation: false,
|
|
70
|
+
scales: {
|
|
71
|
+
y: { ...getYAxisScale(yAxisScaleType), max: maxY },
|
|
72
|
+
},
|
|
73
|
+
plugins: {
|
|
74
|
+
legend: {
|
|
75
|
+
display: false,
|
|
76
|
+
},
|
|
77
|
+
tooltip: tooltip(confidenceIntervalMethod),
|
|
68
78
|
},
|
|
69
|
-
tooltip: tooltip(confidenceIntervalMethod),
|
|
70
79
|
},
|
|
71
|
-
}
|
|
72
|
-
};
|
|
80
|
+
};
|
|
81
|
+
}, [data, yAxisScaleType, confidenceIntervalMethod, yAxisMaxConfig, maintainAspectRatio]);
|
|
82
|
+
|
|
83
|
+
if (config === NO_DATA) {
|
|
84
|
+
return <NoDataDisplay />;
|
|
85
|
+
}
|
|
73
86
|
|
|
74
87
|
return <GsChart configuration={config} />;
|
|
75
88
|
};
|