Skip to content

Commit 4f600f4

Browse files
refactor(website): migrate generic single variant page to React (#715)
resolves #714
1 parent 1217b90 commit 4f600f4

5 files changed

Lines changed: 168 additions & 131 deletions

File tree

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
import { views } from '@genspectrum/dashboard-components/util';
2+
import { type FC, useMemo } from 'react';
3+
4+
import { QuickstartLinks } from './QuickstartLinks.tsx';
5+
import { SelectVariant } from './SelectVariant.tsx';
6+
import type { OrganismsConfig } from '../../../config.ts';
7+
import { Organisms } from '../../../types/Organism.ts';
8+
import { chooseGranularityBasedOnDateRange } from '../../../util/chooseGranularityBasedOnDateRange.ts';
9+
import { hasOnlyUndefinedValues } from '../../../util/hasOnlyUndefinedValues.ts';
10+
import type { DatasetAndVariantData } from '../../../views/View.ts';
11+
import { getLocationSubdivision } from '../../../views/locationHelpers.ts';
12+
import { locationFieldsToFilterIdentifier } from '../../../views/pageStateHandlers/locationFilterFromToUrl.ts';
13+
import { type OrganismViewKey, Routing, type SingleVariantOrganism } from '../../../views/routing.ts';
14+
import type { singleVariantViewKey } from '../../../views/viewKeys.ts';
15+
import { ComponentsGrid } from '../../ComponentsGrid.tsx';
16+
import { GsAggregate } from '../../genspectrum/GsAggregate.tsx';
17+
import { GsMutations } from '../../genspectrum/GsMutations.tsx';
18+
import { GsMutationsOverTime } from '../../genspectrum/GsMutationsOverTime.tsx';
19+
import { GsPrevalenceOverTime } from '../../genspectrum/GsPrevalenceOverTime.tsx';
20+
import { GsStatistics } from '../../genspectrum/GsStatistics.tsx';
21+
22+
export type GenericAnalyseSingleVariantDataDisplayProps = {
23+
organismViewKey: OrganismViewKey &
24+
`${Exclude<SingleVariantOrganism, typeof Organisms.covid>}.${typeof singleVariantViewKey}`;
25+
organismsConfig: OrganismsConfig;
26+
pageState: DatasetAndVariantData;
27+
};
28+
29+
export const GenericAnalyseSingleVariantDataDisplay: FC<GenericAnalyseSingleVariantDataDisplayProps> = ({
30+
organismViewKey,
31+
organismsConfig,
32+
pageState,
33+
}) => {
34+
const view = useMemo(() => new Routing(organismsConfig), [organismsConfig]).getOrganismView(organismViewKey);
35+
36+
const variantLapisFilter = view.pageStateHandler.toLapisFilter(pageState);
37+
const datasetLapisFilter = view.pageStateHandler.toLapisFilterWithoutVariant(pageState);
38+
const timeGranularity = chooseGranularityBasedOnDateRange({
39+
earliestDate: new Date(view.organismConstants.earliestDate),
40+
dateRange: pageState.datasetFilter.dateFilters[view.organismConstants.mainDateField],
41+
});
42+
43+
const { label: subdivisionLabel, field: subdivisionField } = getLocationSubdivision(
44+
view.organismConstants.locationFields,
45+
pageState.datasetFilter.locationFilters[
46+
locationFieldsToFilterIdentifier(view.organismConstants.locationFields)
47+
],
48+
);
49+
const noVariantSelected = hasOnlyUndefinedValues(pageState.variantFilter);
50+
51+
return (
52+
<div className='mx-[8px] flex flex-col gap-y-6'>
53+
{noVariantSelected && (
54+
<SelectVariant>
55+
<QuickstartLinks view={view} datasetFilter={pageState.datasetFilter} />
56+
</SelectVariant>
57+
)}
58+
<GsStatistics numeratorFilter={variantLapisFilter} denominatorFilter={datasetLapisFilter} />
59+
<ComponentsGrid>
60+
<GsPrevalenceOverTime
61+
numeratorFilters={[
62+
{
63+
displayName: '',
64+
lapisFilter: variantLapisFilter,
65+
},
66+
]}
67+
denominatorFilter={datasetLapisFilter}
68+
lapisDateField={view.organismConstants.mainDateField}
69+
granularity={timeGranularity}
70+
pageSize={10}
71+
/>
72+
<GsMutations
73+
lapisFilter={variantLapisFilter}
74+
baselineLapisFilter={datasetLapisFilter}
75+
sequenceType='nucleotide'
76+
pageSize={10}
77+
/>
78+
<GsMutations
79+
lapisFilter={variantLapisFilter}
80+
baselineLapisFilter={datasetLapisFilter}
81+
sequenceType='amino acid'
82+
pageSize={10}
83+
/>
84+
{subdivisionField !== undefined && (
85+
<GsAggregate
86+
title={subdivisionLabel}
87+
fields={[subdivisionField]}
88+
lapisFilter={variantLapisFilter}
89+
views={[views.table, views.bar]}
90+
pageSize={10}
91+
/>
92+
)}
93+
94+
{view.organismConstants.aggregatedVisualizations.singleVariant.map(({ label, fields, views }) => (
95+
<GsAggregate
96+
key={label}
97+
title={label}
98+
fields={fields}
99+
lapisFilter={variantLapisFilter}
100+
views={views}
101+
pageSize={10}
102+
/>
103+
))}
104+
</ComponentsGrid>
105+
<GsMutationsOverTime
106+
lapisFilter={variantLapisFilter}
107+
sequenceType='nucleotide'
108+
granularity={timeGranularity}
109+
lapisDateField={view.organismConstants.mainDateField}
110+
/>
111+
<GsMutationsOverTime
112+
lapisFilter={variantLapisFilter}
113+
sequenceType='amino acid'
114+
granularity={timeGranularity}
115+
lapisDateField={view.organismConstants.mainDateField}
116+
/>
117+
</div>
118+
);
119+
};
Lines changed: 10 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,13 @@
11
---
2-
import { views } from '@genspectrum/dashboard-components/util';
3-
4-
import QuickstartLinks from './QuickstartLinks.astro';
5-
import { SelectVariant } from './SelectVariant';
2+
import { GenericAnalyseSingleVariantDataDisplay } from './GenericAnalyseSingleVariantDataDisplay';
63
import { getDashboardsConfig } from '../../../config';
74
import SingleVariantOrganismPageLayout from '../../../layouts/OrganismPage/SingleVariantOrganismPageLayout.astro';
85
import { Organisms } from '../../../types/Organism';
9-
import { chooseGranularityBasedOnDateRange } from '../../../util/chooseGranularityBasedOnDateRange';
106
import { hasOnlyUndefinedValues } from '../../../util/hasOnlyUndefinedValues';
11-
import { getLocationSubdivision } from '../../../views/locationHelpers';
127
import { toDisplayName } from '../../../views/pageStateHandlers/PageStateHandler';
13-
import { locationFieldsToFilterIdentifier } from '../../../views/pageStateHandlers/locationFilterFromToUrl';
148
import { type OrganismViewKey, type OrganismWithViewKey } from '../../../views/routing';
159
import { ServerSide } from '../../../views/serverSideRouting';
1610
import { singleVariantViewKey } from '../../../views/viewKeys';
17-
import ComponentsGrid from '../../ComponentsGrid.astro';
18-
import GsAggregate from '../../genspectrum/GsAggregate.astro';
19-
import GsMutations from '../../genspectrum/GsMutations.astro';
20-
import GsMutationsOverTime from '../../genspectrum/GsMutationsOverTime.astro';
21-
import GsPrevalenceOverTime from '../../genspectrum/GsPrevalenceOverTime.astro';
22-
import GsStatistics from '../../genspectrum/GsStatistics.astro';
2311
import { AnalyzeSingleVariantSelectorFallback } from '../../pageStateSelectors/FallbackElement';
2412
import { SingleVariantPageStateSelector } from '../../pageStateSelectors/SingleVariantPageStateSelector';
2513
import { sanitizeForFilename } from '../compareSideBySide/toDownloadLink';
@@ -36,19 +24,11 @@ const view = ServerSide.routing.getOrganismView(organismViewKey);
3624
const pageState = view.pageStateHandler.parsePageStateFromUrl(Astro.url);
3725
3826
const variantLapisFilter = view.pageStateHandler.toLapisFilter(pageState);
39-
const datasetLapisFilter = view.pageStateHandler.toLapisFilterWithoutVariant(pageState);
40-
const timeGranularity = chooseGranularityBasedOnDateRange({
41-
earliestDate: new Date(view.organismConstants.earliestDate),
42-
dateRange: pageState.datasetFilter.dateFilters[view.organismConstants.mainDateField],
43-
});
44-
45-
const { label: subdivisionLabel, field: subdivisionField } = getLocationSubdivision(
46-
view.organismConstants.locationFields,
47-
pageState.datasetFilter.locationFilters[locationFieldsToFilterIdentifier(view.organismConstants.locationFields)],
48-
);
4927
5028
const noVariantSelected = hasOnlyUndefinedValues(pageState.variantFilter);
5129
30+
const organismConfig = getDashboardsConfig().dashboards.organisms;
31+
5232
const displayName = toDisplayName(pageState.variantFilter);
5333
const downloadLinks = noVariantSelected
5434
? []
@@ -65,82 +45,19 @@ const downloadLinks = noVariantSelected
6545
<SingleVariantPageStateSelector
6646
slot='filters'
6747
organismViewKey={organismViewKey}
68-
organismsConfig={getDashboardsConfig().dashboards.organisms}
48+
organismsConfig={organismConfig}
6949
initialPageState={pageState}
7050
client:only='react'
7151
>
7252
<AnalyzeSingleVariantSelectorFallback slot='fallback' />
7353
</SingleVariantPageStateSelector>
7454

75-
<div class='mx-[8px] flex flex-col gap-y-6' slot='dataDisplay'>
76-
{
77-
noVariantSelected && (
78-
<SelectVariant>
79-
<QuickstartLinks view={view} />
80-
</SelectVariant>
81-
)
82-
}
83-
<GsStatistics numeratorFilter={variantLapisFilter} denominatorFilter={datasetLapisFilter} />
84-
<ComponentsGrid>
85-
<GsPrevalenceOverTime
86-
numeratorFilters={[
87-
{
88-
displayName: '',
89-
lapisFilter: variantLapisFilter,
90-
},
91-
]}
92-
denominatorFilter={datasetLapisFilter}
93-
lapisDateField={view.organismConstants.mainDateField}
94-
granularity={timeGranularity}
95-
pageSize={10}
96-
/>
97-
<GsMutations
98-
lapisFilter={variantLapisFilter}
99-
baselineLapisFilter={datasetLapisFilter}
100-
sequenceType='nucleotide'
101-
pageSize={10}
102-
/>
103-
<GsMutations
104-
lapisFilter={variantLapisFilter}
105-
baselineLapisFilter={datasetLapisFilter}
106-
sequenceType='amino acid'
107-
pageSize={10}
108-
/>
109-
{
110-
subdivisionField !== undefined && (
111-
<GsAggregate
112-
title={subdivisionLabel}
113-
fields={[subdivisionField]}
114-
lapisFilter={variantLapisFilter}
115-
views={[views.table, views.bar]}
116-
pageSize={10}
117-
/>
118-
)
119-
}
120-
121-
{
122-
view.organismConstants.aggregatedVisualizations.singleVariant.map(({ label, fields, views }) => (
123-
<GsAggregate
124-
title={label}
125-
fields={fields}
126-
lapisFilter={variantLapisFilter}
127-
views={views}
128-
pageSize={10}
129-
/>
130-
))
131-
}
132-
</ComponentsGrid>
133-
<GsMutationsOverTime
134-
lapisFilter={variantLapisFilter}
135-
sequenceType='nucleotide'
136-
granularity={timeGranularity}
137-
lapisDateField={view.organismConstants.mainDateField}
138-
/>
139-
<GsMutationsOverTime
140-
lapisFilter={variantLapisFilter}
141-
sequenceType='amino acid'
142-
granularity={timeGranularity}
143-
lapisDateField={view.organismConstants.mainDateField}
55+
<div slot='dataDisplay'>
56+
<GenericAnalyseSingleVariantDataDisplay
57+
organismViewKey={organismViewKey}
58+
organismsConfig={organismConfig}
59+
pageState={pageState}
60+
client:load
14461
/>
14562
</div>
14663
</SingleVariantOrganismPageLayout>

website/src/components/views/analyzeSingleVariant/QuickstartLinks.astro

Lines changed: 0 additions & 38 deletions
This file was deleted.
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import type { FC } from 'react';
2+
3+
import { collectionVariantClassName } from './CollectionsList';
4+
import { Organisms } from '../../../types/Organism';
5+
import type { DatasetFilter } from '../../../views/View.ts';
6+
import { toDisplayName } from '../../../views/pageStateHandlers/PageStateHandler';
7+
import type { SingleVariantOrganism, ViewsMap } from '../../../views/routing';
8+
import { singleVariantViewKey } from '../../../views/viewKeys';
9+
10+
export type QuickstartLinksProps = {
11+
view: ViewsMap[Exclude<SingleVariantOrganism, typeof Organisms.covid>][typeof singleVariantViewKey];
12+
datasetFilter: DatasetFilter;
13+
};
14+
15+
export const QuickstartLinks: FC<QuickstartLinksProps> = ({ view, datasetFilter }) => {
16+
const variants = view.organismConstants.predefinedVariants;
17+
18+
return (
19+
<div className='text-center'>
20+
<p className='mt-4 mb-2'>To get started you can choose one of these variants:</p>
21+
22+
<div className='flex max-w-md flex-col'>
23+
{variants.map((variant) => {
24+
const href = view.pageStateHandler.toUrl({
25+
datasetFilter: datasetFilter,
26+
variantFilter: variant,
27+
});
28+
return (
29+
<a key={href} className={collectionVariantClassName} href={href}>
30+
{toDisplayName(variant)}
31+
</a>
32+
);
33+
})}
34+
</div>
35+
</div>
36+
);
37+
};

website/src/views/routing.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,8 @@ export type OrganismViewKey = {
113113
type OrganismExtractor<T, ViewKey extends string> = T extends `${infer O}.${ViewKey}` ? O : never;
114114
export type OrganismWithViewKey<ViewKey extends string> = OrganismExtractor<OrganismViewKey, ViewKey>;
115115

116+
export type SingleVariantOrganism = OrganismWithViewKey<typeof singleVariantViewKey>;
117+
116118
export class Routing {
117119
public readonly views;
118120
public readonly externalPages;

0 commit comments

Comments
 (0)