@genspectrum/dashboard-components 0.4.4 → 0.5.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/README.md +2 -2
- package/custom-elements.json +279 -108
- package/dist/dashboard-components.js +495 -283
- package/dist/dashboard-components.js.map +1 -1
- package/dist/genspectrum-components.d.ts +115 -55
- package/dist/style.css +34 -7
- package/package.json +5 -5
- package/src/preact/aggregatedData/aggregate-table.tsx +3 -2
- package/src/preact/aggregatedData/aggregate.stories.tsx +2 -0
- package/src/preact/aggregatedData/aggregate.tsx +8 -3
- package/src/preact/components/table.stories.tsx +51 -1
- package/src/preact/components/table.tsx +4 -3
- package/src/preact/locationFilter/location-filter.stories.tsx +12 -1
- package/src/preact/locationFilter/location-filter.tsx +10 -3
- package/src/preact/mutationComparison/mutation-comparison-table.tsx +7 -2
- package/src/preact/mutationComparison/mutation-comparison-venn.tsx +1 -1
- package/src/preact/mutationComparison/mutation-comparison.stories.tsx +21 -18
- package/src/preact/mutationComparison/mutation-comparison.tsx +30 -8
- package/src/preact/mutationComparison/queryMutationData.ts +4 -4
- package/src/preact/mutations/mutations-grid.tsx +8 -2
- package/src/preact/mutations/mutations-insertions-table.tsx +3 -2
- package/src/preact/mutations/mutations-table.tsx +3 -2
- package/src/preact/mutations/mutations.stories.tsx +6 -3
- package/src/preact/mutations/mutations.tsx +30 -10
- package/src/preact/mutations/queryMutations.ts +3 -3
- package/src/preact/prevalenceOverTime/__mockData__/{denominatorOneVariant.json → denominatorFilterOneDataset.json} +1 -1
- package/src/preact/prevalenceOverTime/__mockData__/{numeratorOneVariant.json → numeratorFilterOneDataset.json} +1 -1
- package/src/preact/prevalenceOverTime/prevalence-over-time-bar-chart.tsx +42 -5
- package/src/preact/prevalenceOverTime/prevalence-over-time-bubble-chart.tsx +26 -7
- package/src/preact/prevalenceOverTime/prevalence-over-time-line-chart.tsx +62 -28
- package/src/preact/prevalenceOverTime/prevalence-over-time-table.tsx +3 -2
- package/src/preact/prevalenceOverTime/prevalence-over-time.stories.tsx +30 -16
- package/src/preact/prevalenceOverTime/prevalence-over-time.tsx +46 -12
- package/src/preact/relativeGrowthAdvantage/relative-growth-advantage-chart.tsx +39 -7
- package/src/preact/relativeGrowthAdvantage/relative-growth-advantage.stories.tsx +10 -4
- package/src/preact/relativeGrowthAdvantage/relative-growth-advantage.tsx +19 -10
- package/src/preact/shared/charts/confideceInterval.ts +7 -2
- package/src/preact/shared/charts/getYAxisMax.ts +24 -0
- package/src/preact/shared/charts/getYAxisScale.ts +1 -3
- package/src/query/queryAggregateData.ts +2 -2
- package/src/query/queryInsertions.ts +7 -2
- package/src/query/querySubstitutionsOrDeletions.ts +2 -2
- package/src/web-components/input/gs-date-range-selector.tsx +1 -1
- package/src/web-components/input/gs-location-filter.stories.ts +11 -0
- package/src/web-components/input/gs-location-filter.tsx +15 -2
- package/src/web-components/input/gs-mutation-filter.tsx +1 -1
- package/src/web-components/input/gs-text-input.tsx +1 -1
- package/src/web-components/visualization/gs-aggregate.stories.ts +4 -0
- package/src/web-components/visualization/gs-aggregate.tsx +10 -2
- package/src/web-components/visualization/gs-mutation-comparison.stories.ts +16 -12
- package/src/web-components/visualization/gs-mutation-comparison.tsx +26 -19
- package/src/web-components/visualization/gs-mutations.stories.ts +8 -4
- package/src/web-components/visualization/gs-mutations.tsx +18 -11
- package/src/web-components/visualization/gs-prevalence-over-time.stories.ts +51 -35
- package/src/web-components/visualization/gs-prevalence-over-time.tsx +66 -24
- package/src/web-components/visualization/gs-relative-growth-advantage.stories.ts +32 -18
- package/src/web-components/visualization/gs-relative-growth-advantage.tsx +51 -13
- /package/src/preact/mutationComparison/__mockData__/{nucleotideMutationsOtherVariant.json → nucleotideMutationsOtherDataset.json} +0 -0
- /package/src/preact/mutationComparison/__mockData__/{nucleotideMutationsSomeVariant.json → nucleotideMutationsSomeDataset.json} +0 -0
- /package/src/preact/prevalenceOverTime/__mockData__/{denominator.json → denominatorFilter.json} +0 -0
- /package/src/preact/prevalenceOverTime/__mockData__/{numeratorEG.json → numeratorFilterEG.json} +0 -0
- /package/src/preact/prevalenceOverTime/__mockData__/{numeratorJN1.json → numeratorFilterJN1.json} +0 -0
- /package/src/preact/relativeGrowthAdvantage/__mockData__/{denominator.json → denominatorFilter.json} +0 -0
- /package/src/preact/relativeGrowthAdvantage/__mockData__/{numerator.json → numeratorFilter.json} +0 -0
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type Meta, type StoryObj } from '@storybook/preact';
|
|
2
|
+
import { expect, userEvent, waitFor, within } from '@storybook/test';
|
|
2
3
|
|
|
3
4
|
import { Table } from './table';
|
|
4
5
|
|
|
@@ -6,13 +7,51 @@ const meta: Meta = {
|
|
|
6
7
|
title: 'Component/Table',
|
|
7
8
|
component: Table,
|
|
8
9
|
parameters: { fetchMock: {} },
|
|
10
|
+
argTypes: {
|
|
11
|
+
data: { control: 'object' },
|
|
12
|
+
columns: { control: 'object' },
|
|
13
|
+
pageSize: { control: 'object' },
|
|
14
|
+
},
|
|
9
15
|
};
|
|
10
16
|
|
|
11
17
|
export default meta;
|
|
12
18
|
|
|
13
19
|
export const TableStory: StoryObj = {
|
|
14
20
|
render: (args) => {
|
|
15
|
-
return <Table data={args.data} columns={args.columns}
|
|
21
|
+
return <Table data={args.data} columns={args.columns} pageSize={args.pageSize} />;
|
|
22
|
+
},
|
|
23
|
+
args: {
|
|
24
|
+
data: [
|
|
25
|
+
['John Do', 'john@example.com', '123-456-7890'],
|
|
26
|
+
['Jane Doe', 'jane@example.com', '098-765-4321'],
|
|
27
|
+
],
|
|
28
|
+
columns: [{ name: 'Name' }, { name: 'Email', sort: true }, { name: 'Phone Number' }],
|
|
29
|
+
pageSize: 1,
|
|
30
|
+
},
|
|
31
|
+
play: async ({ canvasElement, step }) => {
|
|
32
|
+
const canvas = within(canvasElement);
|
|
33
|
+
|
|
34
|
+
const firstRow = () => canvas.queryByText('John Do', { exact: true });
|
|
35
|
+
const secondRow = () => canvas.queryByText('Jane Doe', { exact: true });
|
|
36
|
+
|
|
37
|
+
await step('Expect first row to be visible and not second row', async () => {
|
|
38
|
+
await waitFor(() => expect(firstRow()).toBeVisible());
|
|
39
|
+
await expect(secondRow()).toBeNull();
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
await step('Expect second row to be visible and not first row after clicking next', async () => {
|
|
43
|
+
const nextButton = canvas.getByRole('button', { name: 'Next' });
|
|
44
|
+
await userEvent.click(nextButton);
|
|
45
|
+
|
|
46
|
+
await waitFor(() => expect(secondRow()).toBeVisible());
|
|
47
|
+
await expect(firstRow()).toBeNull();
|
|
48
|
+
});
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
export const TableStoryNoPagination: StoryObj = {
|
|
53
|
+
render: (args) => {
|
|
54
|
+
return <Table data={args.data} columns={args.columns} pageSize={args.pageSize} />;
|
|
16
55
|
},
|
|
17
56
|
args: {
|
|
18
57
|
data: [
|
|
@@ -20,5 +59,16 @@ export const TableStory: StoryObj = {
|
|
|
20
59
|
['Jane Doe', 'jane@example.com', '098-765-4321'],
|
|
21
60
|
],
|
|
22
61
|
columns: [{ name: 'Name' }, { name: 'Email', sort: true }, { name: 'Phone Number' }],
|
|
62
|
+
pageSize: false,
|
|
63
|
+
},
|
|
64
|
+
play: async ({ canvasElement }) => {
|
|
65
|
+
const canvas = within(canvasElement);
|
|
66
|
+
|
|
67
|
+
const firstRow = () => canvas.queryByText('John Do', { exact: true });
|
|
68
|
+
const secondRow = () => canvas.queryByText('Jane Doe', { exact: true });
|
|
69
|
+
|
|
70
|
+
await waitFor(() => expect(firstRow()).toBeVisible());
|
|
71
|
+
await expect(secondRow()).toBeVisible();
|
|
72
|
+
await waitFor(() => expect(canvas.queryByText('Next')).toBeNull());
|
|
23
73
|
},
|
|
24
74
|
};
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { Grid } from 'gridjs';
|
|
2
2
|
import { type OneDArray, type TColumn, type TData } from 'gridjs/dist/src/types';
|
|
3
|
-
import { type PaginationConfig } from 'gridjs/dist/src/view/plugin/pagination';
|
|
4
3
|
import { type ComponentChild } from 'preact';
|
|
5
4
|
import { useEffect, useRef } from 'preact/hooks';
|
|
6
5
|
|
|
@@ -26,10 +25,12 @@ export const tableStyle = {
|
|
|
26
25
|
interface TableProps {
|
|
27
26
|
data: TData;
|
|
28
27
|
columns: OneDArray<TColumn | string | ComponentChild>;
|
|
29
|
-
|
|
28
|
+
pageSize: number | boolean;
|
|
30
29
|
}
|
|
31
30
|
|
|
32
|
-
export const Table = ({ data, columns,
|
|
31
|
+
export const Table = ({ data, columns, pageSize }: TableProps) => {
|
|
32
|
+
const pagination = typeof pageSize === 'number' ? { limit: pageSize } : pageSize;
|
|
33
|
+
|
|
33
34
|
const wrapper = useRef(null);
|
|
34
35
|
|
|
35
36
|
useEffect(() => {
|
|
@@ -35,6 +35,7 @@ const meta: Meta<LocationFilterProps> = {
|
|
|
35
35
|
width: '100%',
|
|
36
36
|
fields: ['region', 'country', 'division', 'location'],
|
|
37
37
|
initialValue: 'Europe',
|
|
38
|
+
placeholderText: 'Enter a location',
|
|
38
39
|
},
|
|
39
40
|
argTypes: {
|
|
40
41
|
fields: {
|
|
@@ -52,6 +53,11 @@ const meta: Meta<LocationFilterProps> = {
|
|
|
52
53
|
type: 'text',
|
|
53
54
|
},
|
|
54
55
|
},
|
|
56
|
+
placeholderText: {
|
|
57
|
+
control: {
|
|
58
|
+
type: 'text',
|
|
59
|
+
},
|
|
60
|
+
},
|
|
55
61
|
},
|
|
56
62
|
decorators: [withActions],
|
|
57
63
|
};
|
|
@@ -61,7 +67,12 @@ export default meta;
|
|
|
61
67
|
export const Primary: StoryObj<LocationFilterProps> = {
|
|
62
68
|
render: (args) => (
|
|
63
69
|
<LapisUrlContext.Provider value={LAPIS_URL}>
|
|
64
|
-
<LocationFilter
|
|
70
|
+
<LocationFilter
|
|
71
|
+
fields={args.fields}
|
|
72
|
+
initialValue={args.initialValue}
|
|
73
|
+
width={args.width}
|
|
74
|
+
placeholderText={args.placeholderText}
|
|
75
|
+
/>
|
|
65
76
|
</LapisUrlContext.Provider>
|
|
66
77
|
),
|
|
67
78
|
};
|
|
@@ -12,6 +12,7 @@ import { useQuery } from '../useQuery';
|
|
|
12
12
|
|
|
13
13
|
export interface LocationFilterInnerProps {
|
|
14
14
|
initialValue?: string;
|
|
15
|
+
placeholderText?: string;
|
|
15
16
|
fields: string[];
|
|
16
17
|
}
|
|
17
18
|
|
|
@@ -19,19 +20,24 @@ export interface LocationFilterProps extends LocationFilterInnerProps {
|
|
|
19
20
|
width: string;
|
|
20
21
|
}
|
|
21
22
|
|
|
22
|
-
export const LocationFilter: FunctionComponent<LocationFilterProps> = ({
|
|
23
|
+
export const LocationFilter: FunctionComponent<LocationFilterProps> = ({
|
|
24
|
+
width,
|
|
25
|
+
initialValue,
|
|
26
|
+
fields,
|
|
27
|
+
placeholderText,
|
|
28
|
+
}) => {
|
|
23
29
|
const size = { width, height: '3rem' };
|
|
24
30
|
|
|
25
31
|
return (
|
|
26
32
|
<ErrorBoundary size={size}>
|
|
27
33
|
<ResizeContainer size={size}>
|
|
28
|
-
<LocationFilterInner initialValue={initialValue} fields={fields} />
|
|
34
|
+
<LocationFilterInner initialValue={initialValue} fields={fields} placeholderText={placeholderText} />
|
|
29
35
|
</ResizeContainer>
|
|
30
36
|
</ErrorBoundary>
|
|
31
37
|
);
|
|
32
38
|
};
|
|
33
39
|
|
|
34
|
-
export const LocationFilterInner = ({ initialValue, fields }: LocationFilterInnerProps) => {
|
|
40
|
+
export const LocationFilterInner = ({ initialValue, fields, placeholderText }: LocationFilterInnerProps) => {
|
|
35
41
|
const lapis = useContext(LapisUrlContext);
|
|
36
42
|
|
|
37
43
|
const [value, setValue] = useState(initialValue ?? '');
|
|
@@ -77,6 +83,7 @@ export const LocationFilterInner = ({ initialValue, fields }: LocationFilterInne
|
|
|
77
83
|
value={value}
|
|
78
84
|
onInput={onInput}
|
|
79
85
|
list='countries'
|
|
86
|
+
placeholder={placeholderText}
|
|
80
87
|
/>
|
|
81
88
|
<datalist id='countries'>
|
|
82
89
|
{data?.map((v) => {
|
|
@@ -12,9 +12,14 @@ import { formatProportion } from '../shared/table/formatProportion';
|
|
|
12
12
|
export interface MutationsTableProps {
|
|
13
13
|
data: Dataset<MutationData>;
|
|
14
14
|
proportionInterval: ProportionInterval;
|
|
15
|
+
pageSize: boolean | number;
|
|
15
16
|
}
|
|
16
17
|
|
|
17
|
-
export const MutationComparisonTable: FunctionComponent<MutationsTableProps> = ({
|
|
18
|
+
export const MutationComparisonTable: FunctionComponent<MutationsTableProps> = ({
|
|
19
|
+
data,
|
|
20
|
+
proportionInterval,
|
|
21
|
+
pageSize,
|
|
22
|
+
}) => {
|
|
18
23
|
const headers = [
|
|
19
24
|
{
|
|
20
25
|
name: 'Mutation',
|
|
@@ -37,5 +42,5 @@ export const MutationComparisonTable: FunctionComponent<MutationsTableProps> = (
|
|
|
37
42
|
|
|
38
43
|
const tableData = getMutationComparisonTableData(data, proportionInterval).map((row) => Object.values(row));
|
|
39
44
|
|
|
40
|
-
return <Table data={tableData} columns={headers}
|
|
45
|
+
return <Table data={tableData} columns={headers} pageSize={pageSize} />;
|
|
41
46
|
};
|
|
@@ -111,7 +111,7 @@ export const MutationComparisonVenn: FunctionComponent<MutationComparisonVennPro
|
|
|
111
111
|
);
|
|
112
112
|
|
|
113
113
|
if (data.content.length > 5) {
|
|
114
|
-
return <div>Too many
|
|
114
|
+
return <div>Too many datasets to display. Maximum are five. </div>;
|
|
115
115
|
}
|
|
116
116
|
|
|
117
117
|
return (
|
|
@@ -1,24 +1,24 @@
|
|
|
1
1
|
import { type Meta, type StoryObj } from '@storybook/preact';
|
|
2
2
|
import { expect, userEvent, waitFor, within } from '@storybook/test';
|
|
3
3
|
|
|
4
|
-
import
|
|
5
|
-
import
|
|
4
|
+
import nucleotideMutationsOtherDataset from './__mockData__/nucleotideMutationsOtherDataset.json';
|
|
5
|
+
import nucleotideMutationsSomeDataset from './__mockData__/nucleotideMutationsSomeDataset.json';
|
|
6
6
|
import { MutationComparison, type MutationComparisonProps } from './mutation-comparison';
|
|
7
7
|
import { LAPIS_URL, NUCLEOTIDE_MUTATIONS_ENDPOINT } from '../../constants';
|
|
8
8
|
import referenceGenome from '../../lapisApi/__mockData__/referenceGenome.json';
|
|
9
9
|
import { LapisUrlContext } from '../LapisUrlContext';
|
|
10
10
|
import { ReferenceGenomeContext } from '../ReferenceGenomeContext';
|
|
11
11
|
|
|
12
|
-
const
|
|
12
|
+
const dateToSomeDataset = '2022-01-01';
|
|
13
13
|
|
|
14
|
-
const
|
|
15
|
-
const
|
|
14
|
+
const dateFromOtherDataset = '2021-01-01';
|
|
15
|
+
const dateToOtherDataset = '2022-01-02';
|
|
16
16
|
|
|
17
17
|
const meta: Meta<MutationComparisonProps> = {
|
|
18
18
|
title: 'Visualization/Mutation comparison',
|
|
19
19
|
component: MutationComparison,
|
|
20
20
|
argTypes: {
|
|
21
|
-
|
|
21
|
+
lapisFilters: [{ control: 'object' }],
|
|
22
22
|
sequenceType: {
|
|
23
23
|
options: ['nucleotide', 'amino acid'],
|
|
24
24
|
control: { type: 'radio' },
|
|
@@ -30,6 +30,7 @@ const meta: Meta<MutationComparisonProps> = {
|
|
|
30
30
|
width: { control: 'text' },
|
|
31
31
|
height: { control: 'text' },
|
|
32
32
|
headline: { control: 'text' },
|
|
33
|
+
pageSize: { control: 'object' },
|
|
33
34
|
},
|
|
34
35
|
parameters: {
|
|
35
36
|
fetchMock: {
|
|
@@ -41,13 +42,13 @@ const meta: Meta<MutationComparisonProps> = {
|
|
|
41
42
|
body: {
|
|
42
43
|
country: 'Switzerland',
|
|
43
44
|
pangoLineage: 'B.1.1.7',
|
|
44
|
-
dateTo:
|
|
45
|
+
dateTo: dateToSomeDataset,
|
|
45
46
|
minProportion: 0,
|
|
46
47
|
},
|
|
47
48
|
},
|
|
48
49
|
response: {
|
|
49
50
|
status: 200,
|
|
50
|
-
body:
|
|
51
|
+
body: nucleotideMutationsSomeDataset,
|
|
51
52
|
},
|
|
52
53
|
},
|
|
53
54
|
{
|
|
@@ -57,14 +58,14 @@ const meta: Meta<MutationComparisonProps> = {
|
|
|
57
58
|
body: {
|
|
58
59
|
country: 'Switzerland',
|
|
59
60
|
pangoLineage: 'B.1.1.7',
|
|
60
|
-
dateFrom:
|
|
61
|
-
dateTo:
|
|
61
|
+
dateFrom: dateFromOtherDataset,
|
|
62
|
+
dateTo: dateToOtherDataset,
|
|
62
63
|
minProportion: 0,
|
|
63
64
|
},
|
|
64
65
|
},
|
|
65
66
|
response: {
|
|
66
67
|
status: 200,
|
|
67
|
-
body:
|
|
68
|
+
body: nucleotideMutationsOtherDataset,
|
|
68
69
|
},
|
|
69
70
|
},
|
|
70
71
|
],
|
|
@@ -79,12 +80,13 @@ const Template: StoryObj<MutationComparisonProps> = {
|
|
|
79
80
|
<LapisUrlContext.Provider value={LAPIS_URL}>
|
|
80
81
|
<ReferenceGenomeContext.Provider value={referenceGenome}>
|
|
81
82
|
<MutationComparison
|
|
82
|
-
|
|
83
|
+
lapisFilters={args.lapisFilters}
|
|
83
84
|
sequenceType={args.sequenceType}
|
|
84
85
|
views={args.views}
|
|
85
86
|
width={args.width}
|
|
86
87
|
height={args.height}
|
|
87
88
|
headline={args.headline}
|
|
89
|
+
pageSize={args.pageSize}
|
|
88
90
|
/>
|
|
89
91
|
</ReferenceGenomeContext.Provider>
|
|
90
92
|
</LapisUrlContext.Provider>
|
|
@@ -94,18 +96,18 @@ const Template: StoryObj<MutationComparisonProps> = {
|
|
|
94
96
|
export const TwoVariants: StoryObj<MutationComparisonProps> = {
|
|
95
97
|
...Template,
|
|
96
98
|
args: {
|
|
97
|
-
|
|
99
|
+
lapisFilters: [
|
|
98
100
|
{
|
|
99
|
-
displayName: 'Some
|
|
100
|
-
lapisFilter: { country: 'Switzerland', pangoLineage: 'B.1.1.7', dateTo:
|
|
101
|
+
displayName: 'Some dataset',
|
|
102
|
+
lapisFilter: { country: 'Switzerland', pangoLineage: 'B.1.1.7', dateTo: dateToSomeDataset },
|
|
101
103
|
},
|
|
102
104
|
{
|
|
103
|
-
displayName: 'Other
|
|
105
|
+
displayName: 'Other dataset',
|
|
104
106
|
lapisFilter: {
|
|
105
107
|
country: 'Switzerland',
|
|
106
108
|
pangoLineage: 'B.1.1.7',
|
|
107
|
-
dateFrom:
|
|
108
|
-
dateTo:
|
|
109
|
+
dateFrom: dateFromOtherDataset,
|
|
110
|
+
dateTo: dateToOtherDataset,
|
|
109
111
|
},
|
|
110
112
|
},
|
|
111
113
|
],
|
|
@@ -114,6 +116,7 @@ export const TwoVariants: StoryObj<MutationComparisonProps> = {
|
|
|
114
116
|
width: '100%',
|
|
115
117
|
height: '700px',
|
|
116
118
|
headline: 'Mutation comparison',
|
|
119
|
+
pageSize: 10,
|
|
117
120
|
},
|
|
118
121
|
};
|
|
119
122
|
|
|
@@ -31,18 +31,20 @@ export interface MutationComparisonProps extends MutationComparisonInnerProps {
|
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
export interface MutationComparisonInnerProps {
|
|
34
|
-
|
|
34
|
+
lapisFilters: NamedLapisFilter[];
|
|
35
35
|
sequenceType: SequenceType;
|
|
36
36
|
views: View[];
|
|
37
|
+
pageSize: boolean | number;
|
|
37
38
|
}
|
|
38
39
|
|
|
39
40
|
export const MutationComparison: FunctionComponent<MutationComparisonProps> = ({
|
|
40
|
-
|
|
41
|
+
lapisFilters,
|
|
41
42
|
sequenceType,
|
|
42
43
|
views,
|
|
43
44
|
width,
|
|
44
45
|
height,
|
|
45
46
|
headline = 'Mutation comparison',
|
|
47
|
+
pageSize,
|
|
46
48
|
}) => {
|
|
47
49
|
const size = { height, width };
|
|
48
50
|
|
|
@@ -50,7 +52,12 @@ export const MutationComparison: FunctionComponent<MutationComparisonProps> = ({
|
|
|
50
52
|
<ErrorBoundary size={size} headline={headline}>
|
|
51
53
|
<ResizeContainer size={size}>
|
|
52
54
|
<Headline heading={headline}>
|
|
53
|
-
<MutationComparisonInner
|
|
55
|
+
<MutationComparisonInner
|
|
56
|
+
lapisFilters={lapisFilters}
|
|
57
|
+
sequenceType={sequenceType}
|
|
58
|
+
views={views}
|
|
59
|
+
pageSize={pageSize}
|
|
60
|
+
/>
|
|
54
61
|
</Headline>
|
|
55
62
|
</ResizeContainer>
|
|
56
63
|
</ErrorBoundary>
|
|
@@ -58,15 +65,16 @@ export const MutationComparison: FunctionComponent<MutationComparisonProps> = ({
|
|
|
58
65
|
};
|
|
59
66
|
|
|
60
67
|
export const MutationComparisonInner: FunctionComponent<MutationComparisonInnerProps> = ({
|
|
61
|
-
|
|
68
|
+
lapisFilters,
|
|
62
69
|
sequenceType,
|
|
63
70
|
views,
|
|
71
|
+
pageSize,
|
|
64
72
|
}) => {
|
|
65
73
|
const lapis = useContext(LapisUrlContext);
|
|
66
74
|
|
|
67
75
|
const { data, error, isLoading } = useQuery(async () => {
|
|
68
|
-
return queryMutationData(
|
|
69
|
-
}, [
|
|
76
|
+
return queryMutationData(lapisFilters, sequenceType, lapis);
|
|
77
|
+
}, [lapisFilters, sequenceType, lapis]);
|
|
70
78
|
|
|
71
79
|
if (isLoading) {
|
|
72
80
|
return <LoadingDisplay />;
|
|
@@ -80,16 +88,29 @@ export const MutationComparisonInner: FunctionComponent<MutationComparisonInnerP
|
|
|
80
88
|
return <NoDataDisplay />;
|
|
81
89
|
}
|
|
82
90
|
|
|
83
|
-
return
|
|
91
|
+
return (
|
|
92
|
+
<MutationComparisonTabs
|
|
93
|
+
data={data.mutationData}
|
|
94
|
+
sequenceType={sequenceType}
|
|
95
|
+
views={views}
|
|
96
|
+
pageSize={pageSize}
|
|
97
|
+
/>
|
|
98
|
+
);
|
|
84
99
|
};
|
|
85
100
|
|
|
86
101
|
type MutationComparisonTabsProps = {
|
|
87
102
|
data: MutationData[];
|
|
88
103
|
views: View[];
|
|
89
104
|
sequenceType: SequenceType;
|
|
105
|
+
pageSize: boolean | number;
|
|
90
106
|
};
|
|
91
107
|
|
|
92
|
-
const MutationComparisonTabs: FunctionComponent<MutationComparisonTabsProps> = ({
|
|
108
|
+
const MutationComparisonTabs: FunctionComponent<MutationComparisonTabsProps> = ({
|
|
109
|
+
data,
|
|
110
|
+
views,
|
|
111
|
+
sequenceType,
|
|
112
|
+
pageSize,
|
|
113
|
+
}) => {
|
|
93
114
|
const [proportionInterval, setProportionInterval] = useState({ min: 0.5, max: 1 });
|
|
94
115
|
const [displayedMutationTypes, setDisplayedMutationTypes] = useState<DisplayedMutationType[]>([
|
|
95
116
|
{ label: 'Substitutions', checked: true, type: 'substitution' },
|
|
@@ -111,6 +132,7 @@ const MutationComparisonTabs: FunctionComponent<MutationComparisonTabsProps> = (
|
|
|
111
132
|
<MutationComparisonTable
|
|
112
133
|
data={{ content: filteredData }}
|
|
113
134
|
proportionInterval={proportionInterval}
|
|
135
|
+
pageSize={pageSize}
|
|
114
136
|
/>
|
|
115
137
|
),
|
|
116
138
|
};
|
|
@@ -9,15 +9,15 @@ export type MutationData = {
|
|
|
9
9
|
};
|
|
10
10
|
|
|
11
11
|
export async function queryMutationData(
|
|
12
|
-
|
|
12
|
+
lapisFilters: NamedLapisFilter[],
|
|
13
13
|
sequenceType: 'nucleotide' | 'amino acid',
|
|
14
14
|
lapis: string,
|
|
15
15
|
) {
|
|
16
16
|
const mutationData = await Promise.all(
|
|
17
|
-
|
|
17
|
+
lapisFilters.map(async (filter) => {
|
|
18
18
|
return {
|
|
19
|
-
displayName:
|
|
20
|
-
data: (await querySubstitutionsOrDeletions(
|
|
19
|
+
displayName: filter.displayName,
|
|
20
|
+
data: (await querySubstitutionsOrDeletions(filter.lapisFilter, sequenceType, lapis)).content,
|
|
21
21
|
};
|
|
22
22
|
}),
|
|
23
23
|
);
|
|
@@ -13,6 +13,7 @@ interface MutationsGridProps {
|
|
|
13
13
|
data: SubstitutionOrDeletionEntry[];
|
|
14
14
|
sequenceType: SequenceType;
|
|
15
15
|
proportionInterval: ProportionInterval;
|
|
16
|
+
pageSize: boolean | number;
|
|
16
17
|
}
|
|
17
18
|
|
|
18
19
|
export type BaseCell = {
|
|
@@ -20,7 +21,12 @@ export type BaseCell = {
|
|
|
20
21
|
isReference: boolean;
|
|
21
22
|
};
|
|
22
23
|
|
|
23
|
-
export const MutationsGrid: FunctionComponent<MutationsGridProps> = ({
|
|
24
|
+
export const MutationsGrid: FunctionComponent<MutationsGridProps> = ({
|
|
25
|
+
data,
|
|
26
|
+
sequenceType,
|
|
27
|
+
proportionInterval,
|
|
28
|
+
pageSize,
|
|
29
|
+
}) => {
|
|
24
30
|
const getHeaders = () => {
|
|
25
31
|
return [
|
|
26
32
|
{
|
|
@@ -80,5 +86,5 @@ export const MutationsGrid: FunctionComponent<MutationsGridProps> = ({ data, seq
|
|
|
80
86
|
|
|
81
87
|
const tableData = getMutationsGridData(data, sequenceType, proportionInterval).map((row) => Object.values(row));
|
|
82
88
|
|
|
83
|
-
return <Table data={tableData} columns={getHeaders()}
|
|
89
|
+
return <Table data={tableData} columns={getHeaders()} pageSize={pageSize} />;
|
|
84
90
|
};
|
|
@@ -8,9 +8,10 @@ import { sortInsertions } from '../shared/sort/sortInsertions';
|
|
|
8
8
|
|
|
9
9
|
export interface InsertionsTableProps {
|
|
10
10
|
data: InsertionEntry[];
|
|
11
|
+
pageSize: boolean | number;
|
|
11
12
|
}
|
|
12
13
|
|
|
13
|
-
export const InsertionsTable: FunctionComponent<InsertionsTableProps> = ({ data }) => {
|
|
14
|
+
export const InsertionsTable: FunctionComponent<InsertionsTableProps> = ({ data, pageSize }) => {
|
|
14
15
|
const getHeaders = () => {
|
|
15
16
|
return [
|
|
16
17
|
{
|
|
@@ -31,5 +32,5 @@ export const InsertionsTable: FunctionComponent<InsertionsTableProps> = ({ data
|
|
|
31
32
|
|
|
32
33
|
const tableData = getInsertionsTableData(data).map((row) => Object.values(row));
|
|
33
34
|
|
|
34
|
-
return <Table data={tableData} columns={getHeaders()}
|
|
35
|
+
return <Table data={tableData} columns={getHeaders()} pageSize={pageSize} />;
|
|
35
36
|
};
|
|
@@ -11,9 +11,10 @@ import { formatProportion } from '../shared/table/formatProportion';
|
|
|
11
11
|
export interface MutationsTableProps {
|
|
12
12
|
data: SubstitutionOrDeletionEntry[];
|
|
13
13
|
proportionInterval: ProportionInterval;
|
|
14
|
+
pageSize: boolean | number;
|
|
14
15
|
}
|
|
15
16
|
|
|
16
|
-
const MutationsTable: FunctionComponent<MutationsTableProps> = ({ data, proportionInterval }) => {
|
|
17
|
+
const MutationsTable: FunctionComponent<MutationsTableProps> = ({ data, proportionInterval, pageSize }) => {
|
|
17
18
|
const getHeaders = () => {
|
|
18
19
|
return [
|
|
19
20
|
{
|
|
@@ -43,7 +44,7 @@ const MutationsTable: FunctionComponent<MutationsTableProps> = ({ data, proporti
|
|
|
43
44
|
|
|
44
45
|
const tableData = getMutationsTableData(data, proportionInterval).map((row) => Object.values(row));
|
|
45
46
|
|
|
46
|
-
return <Table data={tableData} columns={getHeaders()}
|
|
47
|
+
return <Table data={tableData} columns={getHeaders()} pageSize={pageSize} />;
|
|
47
48
|
};
|
|
48
49
|
|
|
49
50
|
export default MutationsTable;
|
|
@@ -13,7 +13,7 @@ const meta: Meta<MutationsProps> = {
|
|
|
13
13
|
title: 'Visualization/Mutations',
|
|
14
14
|
component: Mutations,
|
|
15
15
|
argTypes: {
|
|
16
|
-
|
|
16
|
+
lapisFilter: { control: 'object' },
|
|
17
17
|
sequenceType: {
|
|
18
18
|
options: ['nucleotide', 'amino acid'],
|
|
19
19
|
control: { type: 'radio' },
|
|
@@ -25,6 +25,7 @@ const meta: Meta<MutationsProps> = {
|
|
|
25
25
|
width: { control: 'text' },
|
|
26
26
|
height: { control: 'text' },
|
|
27
27
|
headline: { control: 'text' },
|
|
28
|
+
pageSize: { control: 'object' },
|
|
28
29
|
},
|
|
29
30
|
};
|
|
30
31
|
|
|
@@ -35,12 +36,13 @@ const Template = {
|
|
|
35
36
|
<LapisUrlContext.Provider value={LAPIS_URL}>
|
|
36
37
|
<ReferenceGenomeContext.Provider value={referenceGenome}>
|
|
37
38
|
<Mutations
|
|
38
|
-
|
|
39
|
+
lapisFilter={args.lapisFilter}
|
|
39
40
|
sequenceType={args.sequenceType}
|
|
40
41
|
views={args.views}
|
|
41
42
|
width={args.width}
|
|
42
43
|
height={args.height}
|
|
43
44
|
headline={args.headline}
|
|
45
|
+
pageSize={args.pageSize}
|
|
44
46
|
/>
|
|
45
47
|
</ReferenceGenomeContext.Provider>
|
|
46
48
|
</LapisUrlContext.Provider>
|
|
@@ -50,12 +52,13 @@ const Template = {
|
|
|
50
52
|
export const Default: StoryObj<MutationsProps> = {
|
|
51
53
|
...Template,
|
|
52
54
|
args: {
|
|
53
|
-
|
|
55
|
+
lapisFilter: { country: 'Switzerland', pangoLineage: 'B.1.1.7', dateTo: '2022-01-01' },
|
|
54
56
|
sequenceType: 'nucleotide',
|
|
55
57
|
views: ['grid', 'table', 'insertions'],
|
|
56
58
|
width: '100%',
|
|
57
59
|
height: '700px',
|
|
58
60
|
headline: 'Mutations',
|
|
61
|
+
pageSize: 10,
|
|
59
62
|
},
|
|
60
63
|
parameters: {
|
|
61
64
|
fetchMock: {
|
|
@@ -32,9 +32,10 @@ import { useQuery } from '../useQuery';
|
|
|
32
32
|
export type View = 'table' | 'grid' | 'insertions';
|
|
33
33
|
|
|
34
34
|
export interface MutationsInnerProps {
|
|
35
|
-
|
|
35
|
+
lapisFilter: LapisFilter;
|
|
36
36
|
sequenceType: SequenceType;
|
|
37
37
|
views: View[];
|
|
38
|
+
pageSize: boolean | number;
|
|
38
39
|
}
|
|
39
40
|
|
|
40
41
|
export interface MutationsProps extends MutationsInnerProps {
|
|
@@ -44,12 +45,13 @@ export interface MutationsProps extends MutationsInnerProps {
|
|
|
44
45
|
}
|
|
45
46
|
|
|
46
47
|
export const Mutations: FunctionComponent<MutationsProps> = ({
|
|
47
|
-
|
|
48
|
+
lapisFilter,
|
|
48
49
|
sequenceType,
|
|
49
50
|
views,
|
|
50
51
|
width,
|
|
51
52
|
height,
|
|
52
53
|
headline = 'Mutations',
|
|
54
|
+
pageSize,
|
|
53
55
|
}) => {
|
|
54
56
|
const size = { height, width };
|
|
55
57
|
|
|
@@ -57,18 +59,28 @@ export const Mutations: FunctionComponent<MutationsProps> = ({
|
|
|
57
59
|
<ErrorBoundary size={size} headline={headline}>
|
|
58
60
|
<ResizeContainer size={size}>
|
|
59
61
|
<Headline heading={headline}>
|
|
60
|
-
<MutationsInner
|
|
62
|
+
<MutationsInner
|
|
63
|
+
lapisFilter={lapisFilter}
|
|
64
|
+
sequenceType={sequenceType}
|
|
65
|
+
views={views}
|
|
66
|
+
pageSize={pageSize}
|
|
67
|
+
/>
|
|
61
68
|
</Headline>
|
|
62
69
|
</ResizeContainer>
|
|
63
70
|
</ErrorBoundary>
|
|
64
71
|
);
|
|
65
72
|
};
|
|
66
73
|
|
|
67
|
-
export const MutationsInner: FunctionComponent<MutationsInnerProps> = ({
|
|
74
|
+
export const MutationsInner: FunctionComponent<MutationsInnerProps> = ({
|
|
75
|
+
lapisFilter,
|
|
76
|
+
sequenceType,
|
|
77
|
+
views,
|
|
78
|
+
pageSize,
|
|
79
|
+
}) => {
|
|
68
80
|
const lapis = useContext(LapisUrlContext);
|
|
69
81
|
const { data, error, isLoading } = useQuery(async () => {
|
|
70
|
-
return queryMutationsData(
|
|
71
|
-
}, [
|
|
82
|
+
return queryMutationsData(lapisFilter, sequenceType, lapis);
|
|
83
|
+
}, [lapisFilter, sequenceType, lapis]);
|
|
72
84
|
|
|
73
85
|
if (isLoading) {
|
|
74
86
|
return <LoadingDisplay />;
|
|
@@ -82,16 +94,17 @@ export const MutationsInner: FunctionComponent<MutationsInnerProps> = ({ variant
|
|
|
82
94
|
return <NoDataDisplay />;
|
|
83
95
|
}
|
|
84
96
|
|
|
85
|
-
return <MutationsTabs mutationsData={data} sequenceType={sequenceType} views={views} />;
|
|
97
|
+
return <MutationsTabs mutationsData={data} sequenceType={sequenceType} views={views} pageSize={pageSize} />;
|
|
86
98
|
};
|
|
87
99
|
|
|
88
100
|
type MutationTabsProps = {
|
|
89
101
|
mutationsData: { insertions: InsertionEntry[]; substitutionsOrDeletions: SubstitutionOrDeletionEntry[] };
|
|
90
102
|
sequenceType: SequenceType;
|
|
91
103
|
views: View[];
|
|
104
|
+
pageSize: boolean | number;
|
|
92
105
|
};
|
|
93
106
|
|
|
94
|
-
const MutationsTabs: FunctionComponent<MutationTabsProps> = ({ mutationsData, sequenceType, views }) => {
|
|
107
|
+
const MutationsTabs: FunctionComponent<MutationTabsProps> = ({ mutationsData, sequenceType, views, pageSize }) => {
|
|
95
108
|
const [proportionInterval, setProportionInterval] = useState({ min: 0.05, max: 1 });
|
|
96
109
|
|
|
97
110
|
const [displayedSegments, setDisplayedSegments] = useDisplayedSegments(sequenceType);
|
|
@@ -107,7 +120,13 @@ const MutationsTabs: FunctionComponent<MutationTabsProps> = ({ mutationsData, se
|
|
|
107
120
|
case 'table':
|
|
108
121
|
return {
|
|
109
122
|
title: 'Table',
|
|
110
|
-
content:
|
|
123
|
+
content: (
|
|
124
|
+
<MutationsTable
|
|
125
|
+
data={filteredData.tableData}
|
|
126
|
+
proportionInterval={proportionInterval}
|
|
127
|
+
pageSize={pageSize}
|
|
128
|
+
/>
|
|
129
|
+
),
|
|
111
130
|
};
|
|
112
131
|
case 'grid':
|
|
113
132
|
return {
|
|
@@ -117,13 +136,14 @@ const MutationsTabs: FunctionComponent<MutationTabsProps> = ({ mutationsData, se
|
|
|
117
136
|
data={filteredData.gridData}
|
|
118
137
|
sequenceType={sequenceType}
|
|
119
138
|
proportionInterval={proportionInterval}
|
|
139
|
+
pageSize={pageSize}
|
|
120
140
|
/>
|
|
121
141
|
),
|
|
122
142
|
};
|
|
123
143
|
case 'insertions':
|
|
124
144
|
return {
|
|
125
145
|
title: 'Insertions',
|
|
126
|
-
content: <InsertionsTable data={filteredData.insertions} />,
|
|
146
|
+
content: <InsertionsTable data={filteredData.insertions} pageSize={pageSize} />,
|
|
127
147
|
};
|
|
128
148
|
}
|
|
129
149
|
};
|