@genspectrum/dashboard-components 0.1.3 → 0.1.5
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 +488 -117
- package/dist/dashboard-components.js +904 -466
- package/dist/dashboard-components.js.map +1 -1
- package/dist/genspectrum-components.d.ts +473 -67
- package/dist/style.css +273 -153
- package/package.json +11 -7
- package/src/preact/aggregatedData/aggregate.stories.tsx +7 -5
- package/src/preact/aggregatedData/aggregate.tsx +16 -7
- package/src/preact/components/ReferenceGenomesAwaiter.tsx +25 -0
- package/src/preact/components/csv-download-button.tsx +8 -2
- package/src/preact/components/headline.stories.tsx +19 -1
- package/src/preact/components/headline.tsx +25 -5
- package/src/preact/components/info.stories.tsx +24 -3
- package/src/preact/components/info.tsx +49 -5
- package/src/preact/components/min-max-range-slider.tsx +4 -4
- package/src/preact/components/percent-intput.tsx +2 -3
- package/src/preact/components/resize-container.tsx +23 -0
- package/src/preact/components/table.tsx +1 -0
- package/src/preact/components/tabs.stories.tsx +2 -2
- package/src/preact/components/tabs.tsx +47 -24
- package/src/preact/dateRangeSelector/date-range-selector.stories.tsx +36 -4
- package/src/preact/dateRangeSelector/date-range-selector.tsx +67 -53
- package/src/preact/locationFilter/location-filter.tsx +2 -2
- package/src/preact/mutationComparison/getMutationComparisonTableData.spec.ts +5 -5
- package/src/preact/mutationComparison/getMutationComparisonTableData.ts +45 -10
- package/src/preact/mutationComparison/mutation-comparison-table.tsx +20 -22
- package/src/preact/mutationComparison/mutation-comparison-venn.tsx +6 -3
- package/src/preact/mutationComparison/mutation-comparison.stories.tsx +11 -1
- package/src/preact/mutationComparison/mutation-comparison.tsx +16 -7
- package/src/preact/mutationFilter/mutation-filter.stories.tsx +70 -31
- package/src/preact/mutationFilter/mutation-filter.tsx +62 -14
- package/src/preact/mutations/getInsertionsTableData.spec.ts +6 -4
- package/src/preact/mutations/getInsertionsTableData.ts +1 -1
- package/src/preact/mutations/getMutationsTableData.spec.ts +9 -19
- package/src/preact/mutations/getMutationsTableData.ts +1 -1
- package/src/preact/mutations/mutations-insertions-table.tsx +3 -1
- package/src/preact/mutations/mutations-table.tsx +3 -1
- package/src/preact/mutations/mutations.stories.tsx +11 -1
- package/src/preact/mutations/mutations.tsx +24 -7
- package/src/preact/prevalenceOverTime/prevalence-over-time-bar-chart.tsx +1 -0
- package/src/preact/prevalenceOverTime/prevalence-over-time-bubble-chart.tsx +1 -0
- package/src/preact/prevalenceOverTime/prevalence-over-time-line-chart.tsx +1 -0
- package/src/preact/prevalenceOverTime/prevalence-over-time.stories.tsx +8 -0
- package/src/preact/prevalenceOverTime/prevalence-over-time.tsx +31 -13
- package/src/preact/relativeGrowthAdvantage/relative-growth-advantage-chart.tsx +8 -5
- package/src/preact/relativeGrowthAdvantage/relative-growth-advantage.stories.tsx +15 -0
- package/src/preact/relativeGrowthAdvantage/relative-growth-advantage.tsx +62 -12
- package/src/preact/shared/sort/sortInsertions.spec.ts +11 -10
- package/src/preact/shared/sort/sortInsertions.ts +10 -17
- package/src/preact/shared/sort/sortSubstitutionsAndDeletions.spec.ts +19 -10
- package/src/preact/shared/sort/sortSubstitutionsAndDeletions.ts +45 -12
- package/src/preact/textInput/text-input.stories.tsx +22 -1
- package/src/preact/textInput/text-input.tsx +3 -1
- package/src/utils/typeAssertions.spec.ts +31 -0
- package/src/utils/typeAssertions.ts +16 -0
- package/src/web-components/PreactLitAdapter.tsx +0 -1
- package/src/web-components/app.stories.ts +129 -0
- package/src/web-components/app.ts +27 -6
- package/src/web-components/display/aggregate-component.stories.ts +24 -11
- package/src/web-components/display/aggregate-component.tsx +26 -5
- package/src/web-components/display/mutation-comparison-component.stories.ts +32 -11
- package/src/web-components/display/mutation-comparison-component.tsx +79 -4
- package/src/web-components/display/mutations-component.stories.ts +40 -19
- package/src/web-components/display/mutations-component.tsx +71 -4
- package/src/web-components/display/prevalence-over-time-component.stories.ts +44 -18
- package/src/web-components/display/prevalence-over-time-component.tsx +105 -5
- package/src/web-components/display/relative-growth-advantage-component.stories.ts +32 -10
- package/src/web-components/display/relative-growth-advantage-component.tsx +66 -3
- package/src/web-components/input/date-range-selector-component.stories.ts +51 -9
- package/src/web-components/input/date-range-selector-component.tsx +69 -4
- package/src/web-components/input/location-filter-component.stories.ts +15 -4
- package/src/web-components/input/location-filter-component.tsx +2 -6
- package/src/web-components/input/mutation-filter-component.stories.ts +33 -12
- package/src/web-components/input/mutation-filter-component.tsx +60 -4
- package/src/web-components/input/text-input-component.stories.ts +26 -6
- package/src/web-components/input/text-input-component.tsx +34 -3
- package/src/web-components/display/aggregate-component.mdx +0 -25
- package/src/web-components/input/location-filter.mdx +0 -25
|
@@ -2,21 +2,82 @@ import { customElement, property } from 'lit/decorators.js';
|
|
|
2
2
|
|
|
3
3
|
import { Mutations, type View } from '../../preact/mutations/mutations';
|
|
4
4
|
import { type LapisFilter, type SequenceType } from '../../types';
|
|
5
|
+
import type { Equals, Expect } from '../../utils/typeAssertions';
|
|
5
6
|
import { PreactLitAdapterWithGridJsStyles } from '../PreactLitAdapterWithGridJsStyles';
|
|
6
7
|
|
|
8
|
+
/**
|
|
9
|
+
* This component displays mutations (substitutions, deletions and insertions) for a given variant.
|
|
10
|
+
*
|
|
11
|
+
* ## Views
|
|
12
|
+
*
|
|
13
|
+
* ### Table View
|
|
14
|
+
*
|
|
15
|
+
* The table view shows all substitutions and deletions for the given variant.
|
|
16
|
+
* It shows the type (substitution or deletion), the total count of the mutation
|
|
17
|
+
* and the proportion of the mutation in the variant.
|
|
18
|
+
* The proportion is relative to the total number of sequences matching
|
|
19
|
+
* the specified sequence filters with non-ambiguous reads at that position.
|
|
20
|
+
*
|
|
21
|
+
* The proportion interval filter can be used to filter the displayed mutations on client side.
|
|
22
|
+
*
|
|
23
|
+
* ### Grid View
|
|
24
|
+
*
|
|
25
|
+
* The grid view shows the proportion of each sequence symbol (nucleotide or amino acid) for each position that has a mutation.
|
|
26
|
+
* Only positions with at least one mutation in the selected proportion interval are shown.
|
|
27
|
+
*
|
|
28
|
+
* ### Insertions View
|
|
29
|
+
*
|
|
30
|
+
* The insertions view shows the count of all insertions for the given variant.
|
|
31
|
+
*
|
|
32
|
+
*/
|
|
7
33
|
@customElement('gs-mutations-component')
|
|
8
34
|
export class MutationsComponent extends PreactLitAdapterWithGridJsStyles {
|
|
35
|
+
/**
|
|
36
|
+
* The `variant` will be sent as is to LAPIS to filter the mutation data.
|
|
37
|
+
* It must be a valid LAPIS filter object.
|
|
38
|
+
*/
|
|
9
39
|
@property({ type: Object })
|
|
10
|
-
variant:
|
|
40
|
+
variant: Record<string, string | number | null | boolean> = { displayName: '' };
|
|
11
41
|
|
|
42
|
+
/**
|
|
43
|
+
* The type of the sequence for which the mutations should be shown.
|
|
44
|
+
*/
|
|
12
45
|
@property({ type: String })
|
|
13
|
-
sequenceType:
|
|
46
|
+
sequenceType: 'nucleotide' | 'amino acid' = 'nucleotide';
|
|
14
47
|
|
|
48
|
+
/**
|
|
49
|
+
* A list of tabs with views that this component should provide.
|
|
50
|
+
*/
|
|
15
51
|
@property({ type: Array })
|
|
16
|
-
views:
|
|
52
|
+
views: ('table' | 'grid' | 'insertions')[] = ['table', 'grid'];
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* The size of the component.
|
|
56
|
+
*
|
|
57
|
+
* If not set, the component will take the full width of its container with height 700px.
|
|
58
|
+
*
|
|
59
|
+
* The width and height should be a string with a unit in css style, e.g. '100%', '500px' or '50vh'.
|
|
60
|
+
* If the unit is %, the size will be relative to the container of the component.
|
|
61
|
+
*/
|
|
62
|
+
@property({ type: Object })
|
|
63
|
+
size: { width?: string; height?: string } | undefined = undefined;
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* The headline of the component. Set to an empty string to hide the headline.
|
|
67
|
+
*/
|
|
68
|
+
@property({ type: String })
|
|
69
|
+
headline: string | undefined = 'Mutations';
|
|
17
70
|
|
|
18
71
|
override render() {
|
|
19
|
-
return
|
|
72
|
+
return (
|
|
73
|
+
<Mutations
|
|
74
|
+
variant={this.variant}
|
|
75
|
+
sequenceType={this.sequenceType}
|
|
76
|
+
views={this.views}
|
|
77
|
+
size={this.size}
|
|
78
|
+
headline={this.headline}
|
|
79
|
+
/>
|
|
80
|
+
);
|
|
20
81
|
}
|
|
21
82
|
}
|
|
22
83
|
|
|
@@ -25,3 +86,9 @@ declare global {
|
|
|
25
86
|
'gs-mutations-component': MutationsComponent;
|
|
26
87
|
}
|
|
27
88
|
}
|
|
89
|
+
|
|
90
|
+
/* eslint-disable @typescript-eslint/no-unused-vars, no-unused-vars */
|
|
91
|
+
type VariantsMatches = Expect<Equals<typeof MutationsComponent.prototype.variant, LapisFilter>>;
|
|
92
|
+
type SequenceTypeMatches = Expect<Equals<typeof MutationsComponent.prototype.sequenceType, SequenceType>>;
|
|
93
|
+
type ViewsMatches = Expect<Equals<typeof MutationsComponent.prototype.views, View[]>>;
|
|
94
|
+
/* eslint-enable @typescript-eslint/no-unused-vars, no-unused-vars */
|
|
@@ -4,6 +4,7 @@ import { html } from 'lit';
|
|
|
4
4
|
|
|
5
5
|
import '../app';
|
|
6
6
|
import './prevalence-over-time-component';
|
|
7
|
+
import { withComponentDocs } from '../../../.storybook/ComponentDocsBlock';
|
|
7
8
|
import { AGGREGATED_ENDPOINT, LAPIS_URL } from '../../constants';
|
|
8
9
|
import denominator from '../../preact/prevalenceOverTime/__mockData__/denominator.json';
|
|
9
10
|
import denominatorOneVariant from '../../preact/prevalenceOverTime/__mockData__/denominatorOneVariant.json';
|
|
@@ -13,6 +14,16 @@ import numeratorOneVariant from '../../preact/prevalenceOverTime/__mockData__/nu
|
|
|
13
14
|
import { type PrevalenceOverTimeProps } from '../../preact/prevalenceOverTime/prevalence-over-time';
|
|
14
15
|
import { withinShadowRoot } from '../withinShadowRoot.story';
|
|
15
16
|
|
|
17
|
+
const codeExample = String.raw`
|
|
18
|
+
<gs-prevalence-over-time
|
|
19
|
+
numerator='[{ "displayName": "EG", "country": "USA", "pangoLineage": "EG*" }, { "displayName": "JN.1", "country": "USA", "pangoLineage": "JN.1*" }]'
|
|
20
|
+
denominator='{ "country": "USA", "displayName": "All" }'
|
|
21
|
+
granularity="month"
|
|
22
|
+
smoothingWindow="0"
|
|
23
|
+
views='["bar", "line", "bubble", "table"]'
|
|
24
|
+
confidenceIntervalMethods='["wilson"]'
|
|
25
|
+
></gs-prevalence-over-time>`;
|
|
26
|
+
|
|
16
27
|
const meta: Meta<PrevalenceOverTimeProps> = {
|
|
17
28
|
title: 'Visualization/Prevalence over time',
|
|
18
29
|
component: 'gs-prevalence-over-time',
|
|
@@ -32,25 +43,36 @@ const meta: Meta<PrevalenceOverTimeProps> = {
|
|
|
32
43
|
options: ['wilson'],
|
|
33
44
|
control: { type: 'check' },
|
|
34
45
|
},
|
|
46
|
+
size: [{ control: 'object' }],
|
|
47
|
+
headline: { control: 'text' },
|
|
35
48
|
},
|
|
49
|
+
parameters: withComponentDocs({
|
|
50
|
+
componentDocs: {
|
|
51
|
+
tag: 'gs-prevalence-over-time',
|
|
52
|
+
opensShadowDom: true,
|
|
53
|
+
expectsChildren: false,
|
|
54
|
+
codeExample,
|
|
55
|
+
},
|
|
56
|
+
}),
|
|
57
|
+
tags: ['autodocs'],
|
|
36
58
|
};
|
|
37
59
|
|
|
38
60
|
export default meta;
|
|
39
61
|
|
|
40
62
|
const Template: StoryObj<PrevalenceOverTimeProps> = {
|
|
41
63
|
render: (args) => html`
|
|
42
|
-
<
|
|
43
|
-
<gs-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
</
|
|
64
|
+
<gs-app lapis="${LAPIS_URL}">
|
|
65
|
+
<gs-prevalence-over-time
|
|
66
|
+
.numerator=${args.numerator}
|
|
67
|
+
.denominator=${args.denominator}
|
|
68
|
+
.granularity=${args.granularity}
|
|
69
|
+
.smoothingWindow=${args.smoothingWindow}
|
|
70
|
+
.views=${args.views}
|
|
71
|
+
.confidenceIntervalMethods=${args.confidenceIntervalMethods}
|
|
72
|
+
.size=${args.size}
|
|
73
|
+
.headline=${args.headline}
|
|
74
|
+
></gs-prevalence-over-time>
|
|
75
|
+
</gs-app>
|
|
54
76
|
`,
|
|
55
77
|
};
|
|
56
78
|
|
|
@@ -66,6 +88,8 @@ export const TwoVariants: StoryObj<PrevalenceOverTimeProps> = {
|
|
|
66
88
|
smoothingWindow: 0,
|
|
67
89
|
views: ['bar', 'line', 'bubble', 'table'],
|
|
68
90
|
confidenceIntervalMethods: ['wilson'],
|
|
91
|
+
size: { width: '100%', height: '700px' },
|
|
92
|
+
headline: 'Prevalence over time',
|
|
69
93
|
},
|
|
70
94
|
parameters: {
|
|
71
95
|
fetchMock: {
|
|
@@ -131,6 +155,8 @@ export const OneVariant: StoryObj<PrevalenceOverTimeProps> = {
|
|
|
131
155
|
smoothingWindow: 7,
|
|
132
156
|
views: ['bar', 'line', 'bubble', 'table'],
|
|
133
157
|
confidenceIntervalMethods: ['wilson'],
|
|
158
|
+
size: { width: '100%', height: '700px' },
|
|
159
|
+
headline: 'Prevalence over time',
|
|
134
160
|
},
|
|
135
161
|
parameters: {
|
|
136
162
|
fetchMock: {
|
|
@@ -176,9 +202,9 @@ export const OneVariantOnLineTab: StoryObj<PrevalenceOverTimeProps> = {
|
|
|
176
202
|
play: async ({ canvasElement }) => {
|
|
177
203
|
const canvas = await withinShadowRoot(canvasElement, 'gs-prevalence-over-time');
|
|
178
204
|
|
|
179
|
-
await waitFor(() => expect(canvas.
|
|
205
|
+
await waitFor(() => expect(canvas.getByRole('button', { name: 'Line' })).toBeInTheDocument());
|
|
180
206
|
|
|
181
|
-
await fireEvent.click(canvas.
|
|
207
|
+
await fireEvent.click(canvas.getByRole('button', { name: 'Line' }));
|
|
182
208
|
},
|
|
183
209
|
};
|
|
184
210
|
|
|
@@ -187,9 +213,9 @@ export const OneVariantOnBubbleTab: StoryObj<PrevalenceOverTimeProps> = {
|
|
|
187
213
|
play: async ({ canvasElement }) => {
|
|
188
214
|
const canvas = await withinShadowRoot(canvasElement, 'gs-prevalence-over-time');
|
|
189
215
|
|
|
190
|
-
await waitFor(() => expect(canvas.
|
|
216
|
+
await waitFor(() => expect(canvas.getByRole('button', { name: 'Bubble' })).toBeInTheDocument());
|
|
191
217
|
|
|
192
|
-
await fireEvent.click(canvas.
|
|
218
|
+
await fireEvent.click(canvas.getByRole('button', { name: 'Bubble' }));
|
|
193
219
|
},
|
|
194
220
|
};
|
|
195
221
|
|
|
@@ -198,8 +224,8 @@ export const OneVariantOnTableTab: StoryObj<PrevalenceOverTimeProps> = {
|
|
|
198
224
|
play: async ({ canvasElement }) => {
|
|
199
225
|
const canvas = await withinShadowRoot(canvasElement, 'gs-prevalence-over-time');
|
|
200
226
|
|
|
201
|
-
await waitFor(() => expect(canvas.
|
|
227
|
+
await waitFor(() => expect(canvas.getByRole('button', { name: 'Table' })).toBeInTheDocument());
|
|
202
228
|
|
|
203
|
-
await fireEvent.click(canvas.
|
|
229
|
+
await fireEvent.click(canvas.getByRole('button', { name: 'Table' }));
|
|
204
230
|
},
|
|
205
231
|
};
|
|
@@ -3,27 +3,113 @@ import { customElement, property } from 'lit/decorators.js';
|
|
|
3
3
|
import PrevalenceOverTime, { type View } from '../../preact/prevalenceOverTime/prevalence-over-time';
|
|
4
4
|
import { type ConfidenceIntervalMethod } from '../../preact/shared/charts/confideceInterval';
|
|
5
5
|
import { type NamedLapisFilter, type TemporalGranularity } from '../../types';
|
|
6
|
+
import { type Equals, type Expect } from '../../utils/typeAssertions';
|
|
6
7
|
import { PreactLitAdapterWithGridJsStyles } from '../PreactLitAdapterWithGridJsStyles';
|
|
7
8
|
|
|
9
|
+
/**
|
|
10
|
+
* This component displays the prevalence over time of one or more variants.
|
|
11
|
+
* The prevalence is calculated as the ratio of the number of cases of each variant given as `numerator`
|
|
12
|
+
* to the number of cases of the variant given as `denominator`.
|
|
13
|
+
*
|
|
14
|
+
* In the chart views,
|
|
15
|
+
* - the user can select whether to display a confidence interval (not available in the bubble chart).
|
|
16
|
+
* The confidence interval is calculated using [Wilson score interval](https://en.wikipedia.org/wiki/Binomial_proportion_confidence_interval),
|
|
17
|
+
* with a confidence level of 95%.
|
|
18
|
+
* - the x-axis shows time steps in the selected `granularity`.
|
|
19
|
+
* - the user can select the y-axis scale (linear, logistic, logit).
|
|
20
|
+
*
|
|
21
|
+
* ## Views
|
|
22
|
+
*
|
|
23
|
+
* ### Bar View
|
|
24
|
+
*
|
|
25
|
+
* Displays the prevalence over time as a bar chart.
|
|
26
|
+
* Shows a bar for each variant in the `numerator` on every time step.
|
|
27
|
+
*
|
|
28
|
+
* ### Line View
|
|
29
|
+
*
|
|
30
|
+
* Displays the prevalence over time as a line chart.
|
|
31
|
+
* Each data point is connected for better visibility.
|
|
32
|
+
* Shows a line for each variant in the `numerator`.
|
|
33
|
+
*
|
|
34
|
+
* ### Bubble View
|
|
35
|
+
*
|
|
36
|
+
* Displays the prevalence over time as a bubble chart.
|
|
37
|
+
* The size of the bubbles represents the number of cases of the `denominator` variant.
|
|
38
|
+
* The height of the bubbles represents the prevalence of the `numerator` variants.
|
|
39
|
+
*
|
|
40
|
+
* ### Table View
|
|
41
|
+
*
|
|
42
|
+
* Displays the prevalence over time as a table with one row per time point.
|
|
43
|
+
*/
|
|
8
44
|
@customElement('gs-prevalence-over-time')
|
|
9
45
|
export class PrevalenceOverTimeComponent extends PreactLitAdapterWithGridJsStyles {
|
|
46
|
+
/**
|
|
47
|
+
* Either a single variant or an array of variants to compare.
|
|
48
|
+
* This must be a valid LAPIS filter object with an additional `displayName` property
|
|
49
|
+
* which will be used as the label for the variant in the views,
|
|
50
|
+
* or an array of such objects.
|
|
51
|
+
*/
|
|
10
52
|
@property({ type: Object })
|
|
11
|
-
numerator:
|
|
53
|
+
numerator:
|
|
54
|
+
| (Record<string, string | number | null | boolean> & { displayName: string })
|
|
55
|
+
| (Record<string, string | number | null | boolean> & {
|
|
56
|
+
displayName: string;
|
|
57
|
+
})[] = { displayName: '' };
|
|
12
58
|
|
|
59
|
+
/**
|
|
60
|
+
* The variant that the variants in `numerator` are compared to.
|
|
61
|
+
*/
|
|
13
62
|
@property({ type: Object })
|
|
14
|
-
denominator:
|
|
63
|
+
denominator: Record<string, string | number | null | boolean> & { displayName: string } = { displayName: '' };
|
|
15
64
|
|
|
65
|
+
/**
|
|
66
|
+
* The granularity of the time axis.
|
|
67
|
+
*/
|
|
16
68
|
@property({ type: String })
|
|
17
|
-
granularity:
|
|
69
|
+
granularity: 'day' | 'week' | 'month' | 'year' = 'day';
|
|
18
70
|
|
|
71
|
+
/**
|
|
72
|
+
* The number of time steps to use for smoothing the data.
|
|
73
|
+
* `0` means no smoothing.
|
|
74
|
+
* Must be a non-negative integer.
|
|
75
|
+
*
|
|
76
|
+
* For a given time, the shown value is the mean of the neighbouring measured values.
|
|
77
|
+
* The `smoothingWindow` value provides the number of neighbouring values to take into account.
|
|
78
|
+
* The resulting time is computed via `Math.floor(smoothingWindow / 2)`.
|
|
79
|
+
*/
|
|
19
80
|
@property({ type: Number })
|
|
20
81
|
smoothingWindow: number = 0;
|
|
21
82
|
|
|
83
|
+
/**
|
|
84
|
+
* A list of tabs with views that this component should provide.
|
|
85
|
+
*/
|
|
22
86
|
@property({ type: Array })
|
|
23
|
-
views:
|
|
87
|
+
views: ('bar' | 'line' | 'bubble' | 'table')[] = ['bar', 'line', 'bubble', 'table'];
|
|
24
88
|
|
|
89
|
+
/**
|
|
90
|
+
* A list of methods to calculate the confidence interval.
|
|
91
|
+
* The option `none` is always available and disables confidence intervals.
|
|
92
|
+
* Pass an empty array to disable the confidence interval selector.
|
|
93
|
+
*/
|
|
25
94
|
@property({ type: Array })
|
|
26
|
-
confidenceIntervalMethods:
|
|
95
|
+
confidenceIntervalMethods: ('wilson' | 'none')[] = ['wilson'];
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* The headline of the component. Set to an empty string to hide the headline.
|
|
99
|
+
*/
|
|
100
|
+
@property({ type: String })
|
|
101
|
+
headline: string | undefined = 'Prevalence over time';
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* The size of the component.
|
|
105
|
+
*
|
|
106
|
+
* If not set, the component will take the full width of its container with height 700px.
|
|
107
|
+
*
|
|
108
|
+
* The width and height should be a string with a unit in css style, e.g. '100%', '500px' or '50vh'.
|
|
109
|
+
* If the unit is %, the size will be relative to the container of the component.
|
|
110
|
+
*/
|
|
111
|
+
@property({ type: Object })
|
|
112
|
+
size: { width?: string; height?: string } | undefined = undefined;
|
|
27
113
|
|
|
28
114
|
override render() {
|
|
29
115
|
return (
|
|
@@ -34,6 +120,8 @@ export class PrevalenceOverTimeComponent extends PreactLitAdapterWithGridJsStyle
|
|
|
34
120
|
smoothingWindow={this.smoothingWindow}
|
|
35
121
|
views={this.views}
|
|
36
122
|
confidenceIntervalMethods={this.confidenceIntervalMethods}
|
|
123
|
+
size={this.size}
|
|
124
|
+
headline={this.headline}
|
|
37
125
|
/>
|
|
38
126
|
);
|
|
39
127
|
}
|
|
@@ -44,3 +132,15 @@ declare global {
|
|
|
44
132
|
'gs-prevalence-over-time': PrevalenceOverTimeComponent;
|
|
45
133
|
}
|
|
46
134
|
}
|
|
135
|
+
|
|
136
|
+
/* eslint-disable @typescript-eslint/no-unused-vars, no-unused-vars */
|
|
137
|
+
type NumeratorMatches = Expect<
|
|
138
|
+
Equals<typeof PrevalenceOverTimeComponent.prototype.numerator, NamedLapisFilter | NamedLapisFilter[]>
|
|
139
|
+
>;
|
|
140
|
+
type DenominatorMatches = Expect<Equals<typeof PrevalenceOverTimeComponent.prototype.denominator, NamedLapisFilter>>;
|
|
141
|
+
type GranularityMatches = Expect<Equals<typeof PrevalenceOverTimeComponent.prototype.granularity, TemporalGranularity>>;
|
|
142
|
+
type ViewsMatches = Expect<Equals<typeof PrevalenceOverTimeComponent.prototype.views, View[]>>;
|
|
143
|
+
type ConfidenceIntervalMethodsMatches = Expect<
|
|
144
|
+
Equals<typeof PrevalenceOverTimeComponent.prototype.confidenceIntervalMethods, ConfidenceIntervalMethod[]>
|
|
145
|
+
>;
|
|
146
|
+
/* eslint-enable @typescript-eslint/no-unused-vars, no-unused-vars */
|
|
@@ -3,11 +3,20 @@ import { html } from 'lit';
|
|
|
3
3
|
|
|
4
4
|
import './relative-growth-advantage-component';
|
|
5
5
|
import '../app';
|
|
6
|
+
import { withComponentDocs } from '../../../.storybook/ComponentDocsBlock';
|
|
6
7
|
import { AGGREGATED_ENDPOINT, LAPIS_URL } from '../../constants';
|
|
7
8
|
import denominator from '../../preact/relativeGrowthAdvantage/__mockData__/denominator.json';
|
|
8
9
|
import numerator from '../../preact/relativeGrowthAdvantage/__mockData__/numerator.json';
|
|
9
10
|
import { type RelativeGrowthAdvantageProps } from '../../preact/relativeGrowthAdvantage/relative-growth-advantage';
|
|
10
11
|
|
|
12
|
+
const codeExample = String.raw`
|
|
13
|
+
<gs-relative-growth-advantage
|
|
14
|
+
numerator='{ "country": "Switzerland", "pangoLineage": "B.1.1.7", "dateFrom": "2020-12-01" }'
|
|
15
|
+
denominator='{ "country": "Switzerland", "dateFrom": "2020-12-01" }'
|
|
16
|
+
generationTime="7"
|
|
17
|
+
views='["line"]'
|
|
18
|
+
></gs-relative-growth-advantage>`;
|
|
19
|
+
|
|
11
20
|
const meta: Meta<RelativeGrowthAdvantageProps> = {
|
|
12
21
|
title: 'Visualization/Relative growth advantage',
|
|
13
22
|
component: 'gs-relative-growth-advantage',
|
|
@@ -19,23 +28,34 @@ const meta: Meta<RelativeGrowthAdvantageProps> = {
|
|
|
19
28
|
options: ['line'],
|
|
20
29
|
control: { type: 'check' },
|
|
21
30
|
},
|
|
31
|
+
size: [{ control: 'object' }],
|
|
32
|
+
headline: { control: 'text' },
|
|
22
33
|
},
|
|
34
|
+
parameters: withComponentDocs({
|
|
35
|
+
componentDocs: {
|
|
36
|
+
tag: 'gs-relative-growth-advantage',
|
|
37
|
+
opensShadowDom: true,
|
|
38
|
+
expectsChildren: false,
|
|
39
|
+
codeExample,
|
|
40
|
+
},
|
|
41
|
+
}),
|
|
42
|
+
tags: ['autodocs'],
|
|
23
43
|
};
|
|
24
44
|
|
|
25
45
|
export default meta;
|
|
26
46
|
|
|
27
47
|
const Template: StoryObj<RelativeGrowthAdvantageProps> = {
|
|
28
48
|
render: (args) => html`
|
|
29
|
-
<
|
|
30
|
-
<gs-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
</
|
|
49
|
+
<gs-app lapis="${LAPIS_URL}">
|
|
50
|
+
<gs-relative-growth-advantage
|
|
51
|
+
.numerator=${args.numerator}
|
|
52
|
+
.denominator=${args.denominator}
|
|
53
|
+
.generationTime=${args.generationTime}
|
|
54
|
+
.views=${args.views}
|
|
55
|
+
.size=${args.size}
|
|
56
|
+
.headline=${args.headline}
|
|
57
|
+
></gs-relative-growth-advantage>
|
|
58
|
+
</gs-app>
|
|
39
59
|
`,
|
|
40
60
|
};
|
|
41
61
|
|
|
@@ -46,6 +66,8 @@ export const Default: StoryObj<RelativeGrowthAdvantageProps> = {
|
|
|
46
66
|
denominator: { country: 'Switzerland', dateFrom: '2020-12-01', dateTo: '2021-03-01' },
|
|
47
67
|
generationTime: 7,
|
|
48
68
|
views: ['line'],
|
|
69
|
+
size: { width: '100%', height: '700px' },
|
|
70
|
+
headline: 'Relative growth advantage',
|
|
49
71
|
},
|
|
50
72
|
parameters: {
|
|
51
73
|
fetchMock: {
|
|
@@ -2,21 +2,76 @@ import { customElement, property } from 'lit/decorators.js';
|
|
|
2
2
|
|
|
3
3
|
import { RelativeGrowthAdvantage, type View } from '../../preact/relativeGrowthAdvantage/relative-growth-advantage';
|
|
4
4
|
import type { LapisFilter } from '../../types';
|
|
5
|
+
import { type Equals, type Expect } from '../../utils/typeAssertions';
|
|
5
6
|
import { PreactLitAdapter } from '../PreactLitAdapter';
|
|
6
7
|
|
|
8
|
+
/**
|
|
9
|
+
* We assume a discrete time model, where new infections happen exactly every `generationTime` days.
|
|
10
|
+
* This is what we call a "generation".
|
|
11
|
+
*
|
|
12
|
+
* This component estimates the relative growth advantage of a variant by performing a logistic regression.
|
|
13
|
+
* Based on the inferred logistic growth rate, we derive the relative growth advantage (per generation).
|
|
14
|
+
*
|
|
15
|
+
* For details on the scientific method, see:
|
|
16
|
+
* Chen, Chaoran, et al. "Quantification of the spread of SARS-CoV-2 variant B.1.1.7 in Switzerland." Epidemics (2021);
|
|
17
|
+
* doi: [10.1016/j.epidem.2021.100480](https://doi.org/10.1016/j.epidem.2021.100480)
|
|
18
|
+
*
|
|
19
|
+
* This component fetches aggregated data from LAPIS.
|
|
20
|
+
* Then the data is sent to `https://cov-spectrum.org/api/v2/computed/model/chen2021Fitness`
|
|
21
|
+
* which performs the logistic regression and calculates the relative growth advantage.
|
|
22
|
+
*
|
|
23
|
+
* ## Views
|
|
24
|
+
*
|
|
25
|
+
* ### Line View
|
|
26
|
+
*
|
|
27
|
+
* The line view shows the relative growth advantage over time in a line chart.
|
|
28
|
+
* The dots in the plot show the proportions of the focal variant (`numerator`) to the `denominator` variant
|
|
29
|
+
* for every day as observed in the data.
|
|
30
|
+
* The line shows a logistic curve fitted to the data points, including a 95% confidence interval.
|
|
31
|
+
*/
|
|
7
32
|
@customElement('gs-relative-growth-advantage')
|
|
8
33
|
export class RelativeGrowthAdvantageComponent extends PreactLitAdapter {
|
|
34
|
+
/**
|
|
35
|
+
* The LAPIS filter for the focal variant.
|
|
36
|
+
*/
|
|
9
37
|
@property({ type: Object })
|
|
10
|
-
numerator:
|
|
38
|
+
numerator: Record<string, string | number | null | boolean> = {};
|
|
11
39
|
|
|
40
|
+
/**
|
|
41
|
+
* The LAPIS filter for the variant that the focal variant (`numerator`) should be compared to.
|
|
42
|
+
*/
|
|
12
43
|
@property({ type: Object })
|
|
13
|
-
denominator:
|
|
44
|
+
denominator: Record<string, string | number | null | boolean> = {};
|
|
14
45
|
|
|
46
|
+
/**
|
|
47
|
+
* The generation time represents the number of days over which the variant's relative growth advantage is measured.
|
|
48
|
+
* For example, if we set a generation time of 7 days, then we estimate the growth advantage per week.
|
|
49
|
+
*/
|
|
15
50
|
@property({ type: Number })
|
|
16
51
|
generationTime: number = 7;
|
|
17
52
|
|
|
53
|
+
/**
|
|
54
|
+
* A list of tabs with views that this component should provide.
|
|
55
|
+
*/
|
|
18
56
|
@property({ type: Array })
|
|
19
|
-
views:
|
|
57
|
+
views: 'line'[] = ['line'];
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* The headline of the component. Set to an empty string to hide the headline.
|
|
61
|
+
*/
|
|
62
|
+
@property({ type: String })
|
|
63
|
+
headline: string | undefined = 'Relative growth advantage';
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* The size of the component.
|
|
67
|
+
*
|
|
68
|
+
* If not set, the component will take the full width of its container with height 700px.
|
|
69
|
+
*
|
|
70
|
+
* The width and height should be a string with a unit in css style, e.g. '100%', '500px' or '50vh'.
|
|
71
|
+
* If the unit is %, the size will be relative to the container of the component.
|
|
72
|
+
*/
|
|
73
|
+
@property({ type: Object })
|
|
74
|
+
size: { width?: string; height?: string } | undefined = undefined;
|
|
20
75
|
|
|
21
76
|
override render() {
|
|
22
77
|
return (
|
|
@@ -25,6 +80,8 @@ export class RelativeGrowthAdvantageComponent extends PreactLitAdapter {
|
|
|
25
80
|
denominator={this.denominator}
|
|
26
81
|
generationTime={this.generationTime}
|
|
27
82
|
views={this.views}
|
|
83
|
+
size={this.size}
|
|
84
|
+
headline={this.headline}
|
|
28
85
|
/>
|
|
29
86
|
);
|
|
30
87
|
}
|
|
@@ -35,3 +92,9 @@ declare global {
|
|
|
35
92
|
'gs-relative-growth-advantage': RelativeGrowthAdvantageComponent;
|
|
36
93
|
}
|
|
37
94
|
}
|
|
95
|
+
|
|
96
|
+
/* eslint-disable @typescript-eslint/no-unused-vars, no-unused-vars */
|
|
97
|
+
type NumeratorMatches = Expect<Equals<typeof RelativeGrowthAdvantageComponent.prototype.numerator, LapisFilter>>;
|
|
98
|
+
type DenominatorMatches = Expect<Equals<typeof RelativeGrowthAdvantageComponent.prototype.denominator, LapisFilter>>;
|
|
99
|
+
type ViewsMatches = Expect<Equals<typeof RelativeGrowthAdvantageComponent.prototype.views, View[]>>;
|
|
100
|
+
/* eslint-enable @typescript-eslint/no-unused-vars, no-unused-vars */
|
|
@@ -3,38 +3,84 @@ import { expect, waitFor } from '@storybook/test';
|
|
|
3
3
|
import type { Meta, StoryObj } from '@storybook/web-components';
|
|
4
4
|
import { html } from 'lit';
|
|
5
5
|
|
|
6
|
+
import { withComponentDocs } from '../../../.storybook/ComponentDocsBlock';
|
|
6
7
|
import { LAPIS_URL } from '../../constants';
|
|
7
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
type DateRangeSelectorProps,
|
|
10
|
+
PRESET_VALUE_ALL_TIMES,
|
|
11
|
+
PRESET_VALUE_CUSTOM,
|
|
12
|
+
PRESET_VALUE_LAST_2_MONTHS,
|
|
13
|
+
PRESET_VALUE_LAST_2_WEEKS,
|
|
14
|
+
PRESET_VALUE_LAST_3_MONTHS,
|
|
15
|
+
PRESET_VALUE_LAST_6_MONTHS,
|
|
16
|
+
PRESET_VALUE_LAST_MONTH,
|
|
17
|
+
} from '../../preact/dateRangeSelector/date-range-selector';
|
|
8
18
|
import './date-range-selector-component';
|
|
9
19
|
import '../app';
|
|
10
20
|
import { toYYYYMMDD } from '../../preact/dateRangeSelector/dateConversion';
|
|
11
21
|
import { withinShadowRoot } from '../withinShadowRoot.story';
|
|
12
22
|
|
|
13
|
-
const
|
|
23
|
+
const codeExample = String.raw`
|
|
24
|
+
<gs-date-range-selector
|
|
25
|
+
customSelectOptions='[{ "label": "Year 2021", "dateFrom": "2021-01-01", "dateTo": "2021-12-31" }]'
|
|
26
|
+
earliestDate="1970-01-01"
|
|
27
|
+
initialValue="${PRESET_VALUE_LAST_6_MONTHS}"
|
|
28
|
+
></gs-date-range-selector>`;
|
|
29
|
+
|
|
30
|
+
const meta: Meta<DateRangeSelectorProps<'CustomDateRange'>> = {
|
|
14
31
|
title: 'Input/DateRangeSelector',
|
|
15
32
|
component: 'gs-date-range-selector',
|
|
16
|
-
parameters: {
|
|
33
|
+
parameters: withComponentDocs({
|
|
17
34
|
actions: {
|
|
18
35
|
handles: ['gs-date-range-changed'],
|
|
19
36
|
},
|
|
20
37
|
fetchMock: {},
|
|
38
|
+
componentDocs: {
|
|
39
|
+
tag: 'gs-date-range-selector',
|
|
40
|
+
opensShadowDom: true,
|
|
41
|
+
expectsChildren: false,
|
|
42
|
+
codeExample,
|
|
43
|
+
},
|
|
44
|
+
}),
|
|
45
|
+
argTypes: {
|
|
46
|
+
initialValue: {
|
|
47
|
+
control: {
|
|
48
|
+
type: 'select',
|
|
49
|
+
},
|
|
50
|
+
options: [
|
|
51
|
+
PRESET_VALUE_CUSTOM,
|
|
52
|
+
PRESET_VALUE_ALL_TIMES,
|
|
53
|
+
PRESET_VALUE_LAST_2_WEEKS,
|
|
54
|
+
PRESET_VALUE_LAST_MONTH,
|
|
55
|
+
PRESET_VALUE_LAST_2_MONTHS,
|
|
56
|
+
PRESET_VALUE_LAST_3_MONTHS,
|
|
57
|
+
PRESET_VALUE_LAST_6_MONTHS,
|
|
58
|
+
'CustomDateRange',
|
|
59
|
+
],
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
args: {
|
|
63
|
+
customSelectOptions: [{ label: 'CustomDateRange', dateFrom: '2021-01-01', dateTo: '2021-12-31' }],
|
|
64
|
+
earliestDate: '1970-01-01',
|
|
65
|
+
initialValue: PRESET_VALUE_LAST_6_MONTHS,
|
|
21
66
|
},
|
|
22
67
|
decorators: [withActions],
|
|
68
|
+
tags: ['autodocs'],
|
|
23
69
|
};
|
|
24
70
|
|
|
25
71
|
export default meta;
|
|
26
72
|
|
|
27
|
-
export const DateRangeSelectorStory: StoryObj<DateRangeSelectorProps
|
|
73
|
+
export const DateRangeSelectorStory: StoryObj<DateRangeSelectorProps<'CustomDateRange'>> = {
|
|
28
74
|
render: (args) =>
|
|
29
75
|
html` <gs-app lapis="${LAPIS_URL}">
|
|
30
76
|
<div class="max-w-screen-lg">
|
|
31
77
|
<gs-date-range-selector
|
|
32
78
|
.customSelectOptions=${args.customSelectOptions}
|
|
33
79
|
.earliestDate=${args.earliestDate}
|
|
80
|
+
.initialValue=${args.initialValue}
|
|
34
81
|
></gs-date-range-selector>
|
|
35
82
|
</div>
|
|
36
83
|
</gs-app>`,
|
|
37
|
-
|
|
38
84
|
play: async ({ canvasElement, step }) => {
|
|
39
85
|
const canvas = await withinShadowRoot(canvasElement, 'gs-date-range-selector');
|
|
40
86
|
const dateTo = () => canvas.getByPlaceholderText('Date to');
|
|
@@ -46,8 +92,4 @@ export const DateRangeSelectorStory: StoryObj<DateRangeSelectorProps> = {
|
|
|
46
92
|
});
|
|
47
93
|
});
|
|
48
94
|
},
|
|
49
|
-
args: {
|
|
50
|
-
customSelectOptions: [{ label: 'CustomDateRange', dateFrom: '2021-01-01', dateTo: '2021-12-31' }],
|
|
51
|
-
earliestDate: '1970-01-01',
|
|
52
|
-
},
|
|
53
95
|
};
|