From c0ab4602f61cc63385bd7b9284dcf9c89379653e Mon Sep 17 00:00:00 2001 From: Fabian Engelniederhammer Date: Wed, 17 Dec 2025 12:08:22 +0100 Subject: [PATCH 1/3] 979 also render the layouts in React --- ...sUnreachableWrapperClient.browser.spec.tsx | 2 +- .../LapisUnreachableWrapperClient.tsx | 7 +- .../pageStateSelectors/FallbackElement.tsx | 38 ------- .../CovidSingleVariantReactPage.tsx | 78 ++++++++++++++ .../GenericAnalyseSingleVariantReactPage.tsx | 76 +++++++++++++ .../GenericAnalyzeSingleVariantPage.astro | 70 +++--------- .../GenericCompareSideBySidePage.astro | 82 +++----------- .../GenericCompareSideBySideReactPage.tsx | 87 +++++++++++++++ .../GenericCompareToBaselinePage.astro | 55 +++------- .../GenericCompareToBaselineReactPage.tsx | 63 +++++++++++ .../GenericCompareVariantsPage.astro | 54 +++------- .../GenericCompareVariantsReactPage.tsx | 63 +++++++++++ .../GenericSequencingEffortsPage.astro | 55 +++------- .../GenericSequencingEffortsReactPage.tsx | 61 +++++++++++ .../src/components/views/wasap/Wasap.astro | 28 +---- .../src/components/views/wasap/WasapPage.tsx | 100 +++++++++++------- website/src/lapis/getLastUpdatedDate.ts | 10 +- .../AccessionsDownloadButton.astro | 53 ---------- .../OrganismPage/AccessionsDownloadButton.tsx | 63 +++++++++++ .../layouts/OrganismPage/DataPageLayout.astro | 48 --------- .../layouts/OrganismPage/DataPageLayout.tsx | 50 +++++++++ .../OrganismPage/LastUpdatedInfo.astro | 13 --- .../layouts/OrganismPage/LastUpdatedInfo.tsx | 18 ++++ .../OrganismPage/OrganismViewPageLayout.astro | 33 ------ .../OrganismPage/OrganismViewPageLayout.tsx | 34 ++++++ .../SingleVariantOrganismPageLayout.astro | 25 ----- .../SingleVariantOrganismPageLayout.tsx | 37 +++++++ .../src/layouts/base/footer/DataInfo.astro | 26 ----- website/src/layouts/base/footer/DataInfo.tsx | 38 +++++++ .../layouts/base/footer/DataOriginLink.astro | 12 --- website/src/pages/covid/single-variant.astro | 76 ++----------- website/src/pages/index.astro | 2 +- website/src/pages/swiss-wastewater/flu.astro | 90 ++++++++-------- website/src/pages/swiss-wastewater/rsv.astro | 92 ++++++++-------- 34 files changed, 908 insertions(+), 731 deletions(-) delete mode 100644 website/src/components/pageStateSelectors/FallbackElement.tsx create mode 100644 website/src/components/views/analyzeSingleVariant/CovidSingleVariantReactPage.tsx create mode 100644 website/src/components/views/analyzeSingleVariant/GenericAnalyseSingleVariantReactPage.tsx create mode 100644 website/src/components/views/compareSideBySide/GenericCompareSideBySideReactPage.tsx create mode 100644 website/src/components/views/compareToBaseline/GenericCompareToBaselineReactPage.tsx create mode 100644 website/src/components/views/compareVariants/GenericCompareVariantsReactPage.tsx create mode 100644 website/src/components/views/sequencingEfforts/GenericSequencingEffortsReactPage.tsx delete mode 100644 website/src/layouts/OrganismPage/AccessionsDownloadButton.astro create mode 100644 website/src/layouts/OrganismPage/AccessionsDownloadButton.tsx delete mode 100644 website/src/layouts/OrganismPage/DataPageLayout.astro create mode 100644 website/src/layouts/OrganismPage/DataPageLayout.tsx delete mode 100644 website/src/layouts/OrganismPage/LastUpdatedInfo.astro create mode 100644 website/src/layouts/OrganismPage/LastUpdatedInfo.tsx delete mode 100644 website/src/layouts/OrganismPage/OrganismViewPageLayout.astro create mode 100644 website/src/layouts/OrganismPage/OrganismViewPageLayout.tsx delete mode 100644 website/src/layouts/OrganismPage/SingleVariantOrganismPageLayout.astro create mode 100644 website/src/layouts/OrganismPage/SingleVariantOrganismPageLayout.tsx delete mode 100644 website/src/layouts/base/footer/DataInfo.astro create mode 100644 website/src/layouts/base/footer/DataInfo.tsx delete mode 100644 website/src/layouts/base/footer/DataOriginLink.astro diff --git a/website/src/components/LapisUnreachableWrapperClient.browser.spec.tsx b/website/src/components/LapisUnreachableWrapperClient.browser.spec.tsx index 3108cf7b0..bd66c9c8a 100644 --- a/website/src/components/LapisUnreachableWrapperClient.browser.spec.tsx +++ b/website/src/components/LapisUnreachableWrapperClient.browser.spec.tsx @@ -1,7 +1,7 @@ import { describe, expect } from 'vitest'; import { render } from 'vitest-browser-react'; -import LapisUnreachableWrapperClient from './LapisUnreachableWrapperClient'; +import { LapisUnreachableWrapperClient } from './LapisUnreachableWrapperClient'; import { DUMMY_LAPIS_URL } from '../../routeMocker'; import { it } from '../../test-extend'; diff --git a/website/src/components/LapisUnreachableWrapperClient.tsx b/website/src/components/LapisUnreachableWrapperClient.tsx index 18307c761..ddec59b3a 100644 --- a/website/src/components/LapisUnreachableWrapperClient.tsx +++ b/website/src/components/LapisUnreachableWrapperClient.tsx @@ -2,14 +2,13 @@ import { useQuery } from '@tanstack/react-query'; import { type FC, type ReactNode } from 'react'; import { checkLapisHealth } from '../lapis/checkLapisHealth'; -import { withQueryProvider } from './subscriptions/backendApi/withQueryProvider'; type LapisUnreachableWrapperClientProps = { lapisUrl: string; children: ReactNode; }; -const LapisUnreachableWrapperClientInner: FC = ({ lapisUrl, children }) => { +export const LapisUnreachableWrapperClient: FC = ({ lapisUrl, children }) => { const { data: isReachable, isLoading } = useQuery({ queryKey: ['lapis-reachable', lapisUrl], queryFn: () => checkLapisHealth(lapisUrl), @@ -37,7 +36,3 @@ const LapisUnreachableWrapperClientInner: FC return <>{children}; }; - -const LapisUnreachableWrapperClient = withQueryProvider(LapisUnreachableWrapperClientInner); - -export default LapisUnreachableWrapperClient; diff --git a/website/src/components/pageStateSelectors/FallbackElement.tsx b/website/src/components/pageStateSelectors/FallbackElement.tsx deleted file mode 100644 index ed5d3090e..000000000 --- a/website/src/components/pageStateSelectors/FallbackElement.tsx +++ /dev/null @@ -1,38 +0,0 @@ -export function SequencingEffortsSelectorFallback() { - return
; -} - -export function AnalyzeSingleVariantSelectorFallback() { - return
; -} - -export function CompareSideBySideSelectorFallback({ numFilters }: { numFilters: number }) { - return ( -
- {numFilters > 2 &&
} - {numFilters <= 2 &&
} -
- ); -} - -export function CompareToBaselineSelectorFallback({ numFilters }: { numFilters: number }) { - return ( - <> -
- {Array.from({ length: numFilters }).map((_, index) => ( -
- ))} - - ); -} - -export function CompareVariantsSelectorFallback({ numFilters }: { numFilters: number }) { - return ( - <> -
- {Array.from({ length: numFilters }).map((_, index) => ( -
- ))} - - ); -} diff --git a/website/src/components/views/analyzeSingleVariant/CovidSingleVariantReactPage.tsx b/website/src/components/views/analyzeSingleVariant/CovidSingleVariantReactPage.tsx new file mode 100644 index 000000000..35b12bad5 --- /dev/null +++ b/website/src/components/views/analyzeSingleVariant/CovidSingleVariantReactPage.tsx @@ -0,0 +1,78 @@ +import { type FC, useMemo } from 'react'; + +import { CollectionsList } from './CollectionsList.tsx'; +import { CovidSingleVariantDataDisplay } from './CovidSingleVariantDataDisplay.tsx'; +import type { OrganismsConfig } from '../../../config.ts'; +import { SingleVariantOrganismPageLayout } from '../../../layouts/OrganismPage/SingleVariantOrganismPageLayout.tsx'; +import { hasOnlyUndefinedValues } from '../../../util/hasOnlyUndefinedValues.ts'; +import { toDisplayName } from '../../../views/pageStateHandlers/PageStateHandler.ts'; +import { type OrganismViewKey, Routing } from '../../../views/routing.ts'; +import { SelectorHeadline } from '../../pageStateSelectors/SelectorHeadline.tsx'; +import { SingleVariantPageStateSelector } from '../../pageStateSelectors/SingleVariantPageStateSelector.tsx'; +import { sanitizeForFilename } from '../compareSideBySide/toDownloadLink.ts'; + +export type CovidSingleVariantReactPageProps = { + organismsConfig: OrganismsConfig; + isStaging: boolean; +}; + +export const CovidSingleVariantReactPage: FC = ({ organismsConfig, isStaging }) => { + const organismViewKey: OrganismViewKey = 'covid.singleVariantView'; + const view = useMemo(() => new Routing(organismsConfig).getOrganismView(organismViewKey), [organismsConfig]); + + const pageState = view.pageStateHandler.parsePageStateFromUrl(new URL(window.location.href)); + + const variantFilter = view.pageStateHandler.toLapisFilter(pageState); + + const noVariantSelected = hasOnlyUndefinedValues(pageState.variantFilter); + + const displayName = toDisplayName(pageState.variantFilter); + const downloadLinks = noVariantSelected + ? [ + { + label: 'Download all accessions', + filter: variantFilter, + downloadFileBasename: `${view.organismConstants.organism}_accessions`, + }, + ] + : [ + { + label: `Download accessions of ${displayName}`, + filter: variantFilter, + downloadFileBasename: `${view.organismConstants.organism}_${sanitizeForFilename(displayName)}_accessions`, + }, + ]; + + return ( + + +
+
+ Collections + +
+ + } + dataDisplay={ + + } + /> + ); +}; diff --git a/website/src/components/views/analyzeSingleVariant/GenericAnalyseSingleVariantReactPage.tsx b/website/src/components/views/analyzeSingleVariant/GenericAnalyseSingleVariantReactPage.tsx new file mode 100644 index 000000000..3a9c770f8 --- /dev/null +++ b/website/src/components/views/analyzeSingleVariant/GenericAnalyseSingleVariantReactPage.tsx @@ -0,0 +1,76 @@ +import { type FC, useMemo } from 'react'; + +import { GenericAnalyseSingleVariantDataDisplay } from './GenericAnalyseSingleVariantDataDisplay'; +import { type OrganismsConfig } from '../../../config'; +import { SingleVariantOrganismPageLayout } from '../../../layouts/OrganismPage/SingleVariantOrganismPageLayout.tsx'; +import { Organisms } from '../../../types/Organism'; +import { hasOnlyUndefinedValues } from '../../../util/hasOnlyUndefinedValues'; +import { toDisplayName } from '../../../views/pageStateHandlers/PageStateHandler'; +import { type OrganismViewKey, type OrganismWithViewKey, Routing } from '../../../views/routing'; +import { singleVariantViewKey } from '../../../views/viewKeys'; +import { SingleVariantPageStateSelector } from '../../pageStateSelectors/SingleVariantPageStateSelector'; +import { sanitizeForFilename } from '../compareSideBySide/toDownloadLink'; + +export type GenericAnalyseSingleVariantReactPageProps = { + organism: Exclude, typeof Organisms.covid>; + organismsConfig: OrganismsConfig; + isStaging: boolean; +}; + +export const GenericAnalyseSingleVariantReactPage: FC = ({ + organism, + organismsConfig, + isStaging, +}) => { + const organismViewKey = `${organism}.${singleVariantViewKey}` satisfies OrganismViewKey; + const view = useMemo( + () => new Routing(organismsConfig).getOrganismView(organismViewKey), + [organismsConfig, organismViewKey], + ); + + const pageState = view.pageStateHandler.parsePageStateFromUrl(new URL(window.location.href)); + + const variantLapisFilter = view.pageStateHandler.toLapisFilter(pageState); + + const noVariantSelected = hasOnlyUndefinedValues(pageState.variantFilter); + + const displayName = toDisplayName(pageState.variantFilter); + const downloadLinks = noVariantSelected + ? [ + { + label: 'Download all accessions', + filter: variantLapisFilter, + downloadFileBasename: `${view.organismConstants.organism}_accessions`, + }, + ] + : [ + { + label: `Download accessions of "${displayName}"`, + filter: variantLapisFilter, + downloadFileBasename: `${organism}_${sanitizeForFilename(displayName)}_accessions`, + }, + ]; + + return ( + + } + dataDisplay={ + + } + /> + ); +}; diff --git a/website/src/components/views/analyzeSingleVariant/GenericAnalyzeSingleVariantPage.astro b/website/src/components/views/analyzeSingleVariant/GenericAnalyzeSingleVariantPage.astro index 256b7079a..7c978a5fb 100644 --- a/website/src/components/views/analyzeSingleVariant/GenericAnalyzeSingleVariantPage.astro +++ b/website/src/components/views/analyzeSingleVariant/GenericAnalyzeSingleVariantPage.astro @@ -1,70 +1,26 @@ --- -import { GenericAnalyseSingleVariantDataDisplay } from './GenericAnalyseSingleVariantDataDisplay'; import { isStaging, getDashboardsConfig } from '../../../config'; -import SingleVariantOrganismPageLayout from '../../../layouts/OrganismPage/SingleVariantOrganismPageLayout.astro'; -import { Organisms } from '../../../types/Organism'; -import { hasOnlyUndefinedValues } from '../../../util/hasOnlyUndefinedValues'; -import { toDisplayName } from '../../../views/pageStateHandlers/PageStateHandler'; -import { type OrganismViewKey, type OrganismWithViewKey } from '../../../views/routing'; +import { type OrganismWithViewKey } from '../../../views/routing'; import { ServerSide } from '../../../views/serverSideRouting'; import { singleVariantViewKey } from '../../../views/viewKeys'; -import { AnalyzeSingleVariantSelectorFallback } from '../../pageStateSelectors/FallbackElement'; -import { SingleVariantPageStateSelector } from '../../pageStateSelectors/SingleVariantPageStateSelector'; -import { sanitizeForFilename } from '../compareSideBySide/toDownloadLink'; +import BaseLayout from '../../../layouts/base/BaseLayout.astro'; +import { GenericAnalyseSingleVariantReactPage } from './GenericAnalyseSingleVariantReactPage'; +import { Organisms } from '../../../types/Organism'; -type OrganismViewCompareVariant = OrganismWithViewKey; interface Props { - organism: Exclude; + organism: Exclude, typeof Organisms.covid>; } const { organism } = Astro.props; -const organismViewKey = `${organism}.${singleVariantViewKey}` satisfies OrganismViewKey; -const view = ServerSide.routing.getOrganismView(organismViewKey); - -const pageState = view.pageStateHandler.parsePageStateFromUrl(Astro.url); - -const variantLapisFilter = view.pageStateHandler.toLapisFilter(pageState); -const noVariantSelected = hasOnlyUndefinedValues(pageState.variantFilter); - -const organismConfig = getDashboardsConfig().dashboards.organisms; - -const displayName = toDisplayName(pageState.variantFilter); -const downloadLinks = noVariantSelected - ? [ - { - label: 'Download all accessions', - filter: variantLapisFilter, - downloadFileBasename: `${view.organismConstants.organism}_accessions`, - }, - ] - : [ - { - label: `Download accessions of "${displayName}"`, - filter: variantLapisFilter, - downloadFileBasename: `${organism}_${sanitizeForFilename(displayName)}_accessions`, - }, - ]; +const view = ServerSide.routing.getOrganismView(`${organism}.${singleVariantViewKey}`); --- - - + - - - -
- -
-
+ /> + diff --git a/website/src/components/views/compareSideBySide/GenericCompareSideBySidePage.astro b/website/src/components/views/compareSideBySide/GenericCompareSideBySidePage.astro index fe397ea9d..01235b527 100644 --- a/website/src/components/views/compareSideBySide/GenericCompareSideBySidePage.astro +++ b/website/src/components/views/compareSideBySide/GenericCompareSideBySidePage.astro @@ -1,80 +1,28 @@ --- -import { GenericCompareSideBySideDataDisplay } from './GenericCompareSideBySideDataDisplay.tsx'; -import { toDownloadLink } from './toDownloadLink'; import { isStaging, getDashboardsConfig } from '../../../config'; -import OrganismViewPageLayout from '../../../layouts/OrganismPage/OrganismViewPageLayout.astro'; -import { type OrganismViewKey, type OrganismWithViewKey } from '../../../views/routing'; +import { type OrganismWithViewKey } from '../../../views/routing'; import { ServerSide } from '../../../views/serverSideRouting'; import { compareSideBySideViewKey } from '../../../views/viewKeys'; -import { CompareSideBySidePageStateSelector } from '../../pageStateSelectors/CompareSideBySidePageStateSelector'; -import { CompareSideBySideSelectorFallback } from '../../pageStateSelectors/FallbackElement'; +import BaseLayout from '../../../layouts/base/BaseLayout.astro'; +import { GenericCompareSideBySideReactPage } from './GenericCompareSideBySideReactPage'; -type OrganismViewCompareVariant = OrganismWithViewKey; interface Props { - organism: OrganismViewCompareVariant; + organism: OrganismWithViewKey; hideMutationComponents?: boolean; } const { organism, hideMutationComponents } = Astro.props; -const organismViewKey = `${organism}.${compareSideBySideViewKey}` satisfies OrganismViewKey; -const view = ServerSide.routing.getOrganismView(organismViewKey); -const pageState = view.pageStateHandler.parsePageStateFromUrl(Astro.url); - -const organismConfig = getDashboardsConfig().dashboards.organisms; - -const downloadLinks = [...pageState.filters.entries()].map(toDownloadLink(view.pageStateHandler, organism)); +const view = ServerSide.routing.getOrganismView(`${organism}.${compareSideBySideViewKey}`); +const organismsConfig = getDashboardsConfig().dashboards.organisms; --- - -
- { - Array.from(pageState.filters).map(([id, datasetAndVariantData]) => { - return ( -
-
- {pageState.filters.size > 1 && ( - - Remove column - - )} - - - -
- - -
- ); - }) - } - - Add column - -
-
+ + + diff --git a/website/src/components/views/compareSideBySide/GenericCompareSideBySideReactPage.tsx b/website/src/components/views/compareSideBySide/GenericCompareSideBySideReactPage.tsx new file mode 100644 index 000000000..20e72eede --- /dev/null +++ b/website/src/components/views/compareSideBySide/GenericCompareSideBySideReactPage.tsx @@ -0,0 +1,87 @@ +import { type FC, useMemo } from 'react'; + +import { GenericCompareSideBySideDataDisplay } from './GenericCompareSideBySideDataDisplay.tsx'; +import { toDownloadLink } from './toDownloadLink'; +import { type OrganismsConfig } from '../../../config'; +import { OrganismViewPageLayout } from '../../../layouts/OrganismPage/OrganismViewPageLayout.tsx'; +import { type OrganismViewKey, type OrganismWithViewKey, Routing } from '../../../views/routing'; +import { compareSideBySideViewKey } from '../../../views/viewKeys'; +import { CompareSideBySidePageStateSelector } from '../../pageStateSelectors/CompareSideBySidePageStateSelector'; + +export type GenericCompareSideBySideReactPageProps = { + organism: OrganismWithViewKey; + hideMutationComponents?: boolean; + organismsConfig: OrganismsConfig; + isStaging: boolean; +}; + +export const GenericCompareSideBySideReactPage: FC = ({ + organism, + hideMutationComponents, + organismsConfig, + isStaging, +}) => { + const organismViewKey = `${organism}.${compareSideBySideViewKey}` satisfies OrganismViewKey; + + const view = useMemo( + () => new Routing(organismsConfig).getOrganismView(organismViewKey), + [organismsConfig, organismViewKey], + ); + + const pageState = view.pageStateHandler.parsePageStateFromUrl(new URL(window.location.href)); + + const downloadLinks = [...pageState.filters.entries()].map(toDownloadLink(view.pageStateHandler, organism)); + + return ( + +
+ {Array.from(pageState.filters).map(([id, datasetAndVariantData]) => { + return ( +
+
+ {pageState.filters.size > 1 && ( + + Remove column + + )} + +
+ + +
+ ); + })} + + Add column + +
+
+ ); +}; diff --git a/website/src/components/views/compareToBaseline/GenericCompareToBaselinePage.astro b/website/src/components/views/compareToBaseline/GenericCompareToBaselinePage.astro index 5095db10d..df14581d0 100644 --- a/website/src/components/views/compareToBaseline/GenericCompareToBaselinePage.astro +++ b/website/src/components/views/compareToBaseline/GenericCompareToBaselinePage.astro @@ -1,56 +1,25 @@ --- -import { GenericCompareToBaselineDataDisplay } from './GenericCompareToBaselineDataDisplay'; import { isStaging, getDashboardsConfig } from '../../../config'; -import SingleVariantOrganismPageLayout from '../../../layouts/OrganismPage/SingleVariantOrganismPageLayout.astro'; -import { type OrganismViewKey, type OrganismWithViewKey } from '../../../views/routing'; +import { type OrganismWithViewKey } from '../../../views/routing'; import { ServerSide } from '../../../views/serverSideRouting'; import { compareToBaselineViewKey } from '../../../views/viewKeys'; -import { CompareVariantsToBaselineStateSelector } from '../../pageStateSelectors/CompareVariantsToBaselineStateSelector'; -import { CompareToBaselineSelectorFallback } from '../../pageStateSelectors/FallbackElement'; -import { sanitizeForFilename } from '../compareSideBySide/toDownloadLink'; +import BaseLayout from '../../../layouts/base/BaseLayout.astro'; +import { GenericCompareToBaselineReactPage } from './GenericCompareToBaselineReactPage'; -type OrganismViewCompareVariant = OrganismWithViewKey; interface Props { - organism: OrganismViewCompareVariant; + organism: OrganismWithViewKey; } const { organism } = Astro.props; -const organismViewKey = `${organism}.${compareToBaselineViewKey}` satisfies OrganismViewKey; -const view = ServerSide.routing.getOrganismView(organismViewKey); -const pageState = view.pageStateHandler.parsePageStateFromUrl(Astro.url); - -const numeratorLapisFilters = view.pageStateHandler.variantFiltersToNamedLapisFilters(pageState); -const noVariantSelected = pageState.variants.size < 1; - -const downloadLinks = noVariantSelected - ? [] - : numeratorLapisFilters.map(({ lapisFilter, displayName }) => ({ - label: `Download accessions of "${displayName}"`, - filter: lapisFilter, - downloadFileBasename: `${organism}_${sanitizeForFilename(displayName)}_accessions`, - })); - -const organismConfig = getDashboardsConfig().dashboards.organisms; +const view = ServerSide.routing.getOrganismView(`${organism}.${compareToBaselineViewKey}`); --- - - + - - -
- -
-
+ /> + diff --git a/website/src/components/views/compareToBaseline/GenericCompareToBaselineReactPage.tsx b/website/src/components/views/compareToBaseline/GenericCompareToBaselineReactPage.tsx new file mode 100644 index 000000000..b1afb1c0b --- /dev/null +++ b/website/src/components/views/compareToBaseline/GenericCompareToBaselineReactPage.tsx @@ -0,0 +1,63 @@ +import { type FC, useMemo } from 'react'; + +import { GenericCompareToBaselineDataDisplay } from './GenericCompareToBaselineDataDisplay'; +import { type OrganismsConfig } from '../../../config'; +import { SingleVariantOrganismPageLayout } from '../../../layouts/OrganismPage/SingleVariantOrganismPageLayout.tsx'; +import { type OrganismViewKey, type OrganismWithViewKey, Routing } from '../../../views/routing'; +import { compareToBaselineViewKey } from '../../../views/viewKeys'; +import { CompareVariantsToBaselineStateSelector } from '../../pageStateSelectors/CompareVariantsToBaselineStateSelector'; +import { sanitizeForFilename } from '../compareSideBySide/toDownloadLink'; + +export type GenericCompareToBaselineReactPageProps = { + organism: OrganismWithViewKey; + organismsConfig: OrganismsConfig; + isStaging: boolean; +}; + +export const GenericCompareToBaselineReactPage: FC = ({ + organism, + organismsConfig, + isStaging, +}) => { + const organismViewKey = `${organism}.${compareToBaselineViewKey}` satisfies OrganismViewKey; + const view = useMemo( + () => new Routing(organismsConfig).getOrganismView(organismViewKey), + [organismsConfig, organismViewKey], + ); + + const pageState = view.pageStateHandler.parsePageStateFromUrl(new URL(window.location.href)); + + const numeratorLapisFilters = view.pageStateHandler.variantFiltersToNamedLapisFilters(pageState); + const noVariantSelected = pageState.variants.size < 1; + + const downloadLinks = noVariantSelected + ? [] + : numeratorLapisFilters.map(({ lapisFilter, displayName }) => ({ + label: `Download accessions of "${displayName}"`, + filter: lapisFilter, + downloadFileBasename: `${organism}_${sanitizeForFilename(displayName)}_accessions`, + })); + + return ( + + } + dataDisplay={ + + } + /> + ); +}; diff --git a/website/src/components/views/compareVariants/GenericCompareVariantsPage.astro b/website/src/components/views/compareVariants/GenericCompareVariantsPage.astro index 76727768d..60feafa19 100644 --- a/website/src/components/views/compareVariants/GenericCompareVariantsPage.astro +++ b/website/src/components/views/compareVariants/GenericCompareVariantsPage.astro @@ -1,55 +1,25 @@ --- -import { GenericCompareVariantsDataDisplay } from './GenericCompareVariantsDataDisplay'; import { isStaging, getDashboardsConfig } from '../../../config'; -import SingleVariantOrganismPageLayout from '../../../layouts/OrganismPage/SingleVariantOrganismPageLayout.astro'; -import { type OrganismViewKey, type OrganismWithViewKey } from '../../../views/routing'; +import { type OrganismWithViewKey } from '../../../views/routing'; import { ServerSide } from '../../../views/serverSideRouting'; import { compareVariantsViewKey } from '../../../views/viewKeys'; -import { CompareVariantsPageStateSelector } from '../../pageStateSelectors/CompareVariantsPageStateSelector'; -import { CompareVariantsSelectorFallback } from '../../pageStateSelectors/FallbackElement'; -import { sanitizeForFilename } from '../compareSideBySide/toDownloadLink'; +import BaseLayout from '../../../layouts/base/BaseLayout.astro'; +import { GenericCompareVariantsReactPage } from './GenericCompareVariantsReactPage'; -type OrganismViewCompareVariant = OrganismWithViewKey; interface Props { - organism: OrganismViewCompareVariant; + organism: OrganismWithViewKey; } const { organism } = Astro.props; -const organismViewKey = `${organism}.${compareVariantsViewKey}` satisfies OrganismViewKey; -const view = ServerSide.routing.getOrganismView(organismViewKey); -const pageState = view.pageStateHandler.parsePageStateFromUrl(Astro.url); - -const numeratorLapisFilters = view.pageStateHandler.variantFiltersToNamedLapisFilters(pageState); -const notEnoughVariantsSelected = pageState.variants.size < 2; -const downloadLinks = notEnoughVariantsSelected - ? [] - : numeratorLapisFilters.map(({ lapisFilter, displayName }) => ({ - label: `Download accessions of "${displayName}"`, - filter: lapisFilter, - downloadFileBasename: `${organism}_${sanitizeForFilename(displayName)}_accessions`, - })); - -const organismConfig = getDashboardsConfig().dashboards.organisms; +const view = ServerSide.routing.getOrganismView(`${organism}.${compareVariantsViewKey}`); --- - - + - - -
- -
-
+ /> + diff --git a/website/src/components/views/compareVariants/GenericCompareVariantsReactPage.tsx b/website/src/components/views/compareVariants/GenericCompareVariantsReactPage.tsx new file mode 100644 index 000000000..d1b970481 --- /dev/null +++ b/website/src/components/views/compareVariants/GenericCompareVariantsReactPage.tsx @@ -0,0 +1,63 @@ +import { type FC, useMemo } from 'react'; + +import { GenericCompareVariantsDataDisplay } from './GenericCompareVariantsDataDisplay'; +import { type OrganismsConfig } from '../../../config'; +import { SingleVariantOrganismPageLayout } from '../../../layouts/OrganismPage/SingleVariantOrganismPageLayout.tsx'; +import { type OrganismViewKey, type OrganismWithViewKey, Routing } from '../../../views/routing'; +import { compareVariantsViewKey } from '../../../views/viewKeys'; +import { CompareVariantsPageStateSelector } from '../../pageStateSelectors/CompareVariantsPageStateSelector'; +import { sanitizeForFilename } from '../compareSideBySide/toDownloadLink'; + +export type GenericCompareVariantsReactPageProps = { + organism: OrganismWithViewKey; + organismsConfig: OrganismsConfig; + isStaging: boolean; +}; + +export const GenericCompareVariantsReactPage: FC = ({ + organism, + organismsConfig, + isStaging, +}) => { + const organismViewKey = `${organism}.${compareVariantsViewKey}` satisfies OrganismViewKey; + const view = useMemo( + () => new Routing(organismsConfig).getOrganismView(organismViewKey), + [organismsConfig, organismViewKey], + ); + + const pageState = view.pageStateHandler.parsePageStateFromUrl(new URL(window.location.href)); + + const numeratorLapisFilters = view.pageStateHandler.variantFiltersToNamedLapisFilters(pageState); + const notEnoughVariantsSelected = pageState.variants.size < 2; + + const downloadLinks = notEnoughVariantsSelected + ? [] + : numeratorLapisFilters.map(({ lapisFilter, displayName }) => ({ + label: `Download accessions of "${displayName}"`, + filter: lapisFilter, + downloadFileBasename: `${organism}_${sanitizeForFilename(displayName)}_accessions`, + })); + + return ( + + } + dataDisplay={ + + } + /> + ); +}; diff --git a/website/src/components/views/sequencingEfforts/GenericSequencingEffortsPage.astro b/website/src/components/views/sequencingEfforts/GenericSequencingEffortsPage.astro index 45418e0a1..9b152b834 100644 --- a/website/src/components/views/sequencingEfforts/GenericSequencingEffortsPage.astro +++ b/website/src/components/views/sequencingEfforts/GenericSequencingEffortsPage.astro @@ -1,56 +1,25 @@ --- -import { GenericSequencingEffortsDataDisplay } from './GenericSequencingEffortsDataDisplay'; import { isStaging, getDashboardsConfig } from '../../../config'; -import SingleVariantOrganismPageLayout from '../../../layouts/OrganismPage/SingleVariantOrganismPageLayout.astro'; -import { type OrganismViewKey, type OrganismWithViewKey } from '../../../views/routing'; +import { type OrganismWithViewKey } from '../../../views/routing'; import { ServerSide } from '../../../views/serverSideRouting'; import { sequencingEffortsViewKey } from '../../../views/viewKeys'; -import { SequencingEffortsSelectorFallback } from '../../pageStateSelectors/FallbackElement'; -import { SequencingEffortsPageStateSelector } from '../../pageStateSelectors/SequencingEffortsPageStateSelector'; +import BaseLayout from '../../../layouts/base/BaseLayout.astro'; +import { GenericSequencingEffortsReactPage } from './GenericSequencingEffortsReactPage'; -type OrganismViewCompareVariant = OrganismWithViewKey; interface Props { - organism: OrganismViewCompareVariant; + organism: OrganismWithViewKey; } const { organism } = Astro.props; -const organismViewKey: OrganismViewKey = `${organism}.${sequencingEffortsViewKey}` satisfies OrganismViewKey; -const view = ServerSide.routing.getOrganismView(organismViewKey); -const pageState = view.pageStateHandler.parsePageStateFromUrl(Astro.url); - -const lapisFilter = view.pageStateHandler.toLapisFilter(pageState); - -const organismConfig = getDashboardsConfig().dashboards.organisms; +const view = ServerSide.routing.getOrganismView(`${organism}.${sequencingEffortsViewKey}`); --- - - + - - - -
- -
-
+ /> + diff --git a/website/src/components/views/sequencingEfforts/GenericSequencingEffortsReactPage.tsx b/website/src/components/views/sequencingEfforts/GenericSequencingEffortsReactPage.tsx new file mode 100644 index 000000000..8dea17a2e --- /dev/null +++ b/website/src/components/views/sequencingEfforts/GenericSequencingEffortsReactPage.tsx @@ -0,0 +1,61 @@ +import { type FC, useMemo } from 'react'; + +import { GenericSequencingEffortsDataDisplay } from './GenericSequencingEffortsDataDisplay'; +import { type OrganismsConfig } from '../../../config'; +import { SingleVariantOrganismPageLayout } from '../../../layouts/OrganismPage/SingleVariantOrganismPageLayout.tsx'; +import { type OrganismViewKey, type OrganismWithViewKey, Routing } from '../../../views/routing'; +import { sequencingEffortsViewKey } from '../../../views/viewKeys'; +import { SequencingEffortsPageStateSelector } from '../../pageStateSelectors/SequencingEffortsPageStateSelector'; + +export type GenericSequencingEffortsReactPageProps = { + organism: OrganismWithViewKey; + organismsConfig: OrganismsConfig; + isStaging: boolean; +}; + +export const GenericSequencingEffortsReactPage: FC = ({ + organism, + organismsConfig, + isStaging, +}) => { + const organismViewKey = `${organism}.${sequencingEffortsViewKey}` satisfies OrganismViewKey; + const view = useMemo( + () => new Routing(organismsConfig).getOrganismView(organismViewKey), + [organismsConfig, organismViewKey], + ); + + const pageState = view.pageStateHandler.parsePageStateFromUrl(new URL(window.location.href)); + + const lapisFilter = view.pageStateHandler.toLapisFilter(pageState); + + const downloadLinks = [ + { + label: 'Download accessions', + filter: lapisFilter, + downloadFileBasename: `${organism}_sequencing_efforts_accessions`, + }, + ]; + + return ( + + } + dataDisplay={ + + } + /> + ); +}; diff --git a/website/src/components/views/wasap/Wasap.astro b/website/src/components/views/wasap/Wasap.astro index 2a9c7196f..c689c8569 100644 --- a/website/src/components/views/wasap/Wasap.astro +++ b/website/src/components/views/wasap/Wasap.astro @@ -1,36 +1,18 @@ --- import { WasapPage } from './WasapPage'; -import { defaultBreadcrumbs } from '../../../layouts/Breadcrumbs'; -import DataPageLayout from '../../../layouts/OrganismPage/DataPageLayout.astro'; -import { dataOrigins } from '../../../types/dataOrigins'; -import { - wastewaterBreadcrumb, - wastewaterOrganismConfigs, - type WastewaterOrganismName, -} from '../../../types/wastewaterConfig'; +import { wastewaterOrganismConfigs, type WastewaterOrganismName } from '../../../types/wastewaterConfig'; +import BaseLayout from '../../../layouts/base/BaseLayout.astro'; type Props = { wastewaterOrganism: WastewaterOrganismName; }; const { wastewaterOrganism } = Astro.props; -const { name, path: href, lapisBaseUrl } = wastewaterOrganismConfigs[wastewaterOrganism]; +const { name } = wastewaterOrganismConfigs[wastewaterOrganism]; const url = Astro.url; --- - + - + diff --git a/website/src/components/views/wasap/WasapPage.tsx b/website/src/components/views/wasap/WasapPage.tsx index 52f94c8c7..03f93c723 100644 --- a/website/src/components/views/wasap/WasapPage.tsx +++ b/website/src/components/views/wasap/WasapPage.tsx @@ -8,7 +8,14 @@ import { useWasapPageData } from './useWasapPageData'; import type { WasapAnalysisFilter, WasapPageConfig } from './wasapPageConfig'; import { getDateRange } from '../../../lapis/getDateRange'; import { getTotalCount } from '../../../lapis/getTotalCount'; -import { wastewaterOrganismConfigs, type WastewaterOrganismName } from '../../../types/wastewaterConfig'; +import { defaultBreadcrumbs } from '../../../layouts/Breadcrumbs.tsx'; +import { DataPageLayout } from '../../../layouts/OrganismPage/DataPageLayout.tsx'; +import { dataOrigins } from '../../../types/dataOrigins.ts'; +import { + wastewaterBreadcrumb, + wastewaterOrganismConfigs, + type WastewaterOrganismName, +} from '../../../types/wastewaterConfig'; import { Loading } from '../../../util/Loading'; import { WasapPageStateHandler } from '../../../views/pageStateHandlers/WasapPageStateHandler'; import { GsMutationsOverTime } from '../../genspectrum/GsMutationsOverTime'; @@ -59,47 +66,60 @@ export const WasapPageInner: FC = ({ wastewaterOrganism, current const memoizedLinkTemplate = useMemo(() => JSON.stringify(config.linkTemplate), [config.linkTemplate]); return ( - -
-
- -
- {isError ? ( - There was an error fetching the mutations to display. - ) : isPending ? ( - - ) : ( -
- {displayMutations?.length === 0 ? ( - - ) : ( - - )} - + +
+
+
- )} -
-
+ {isError ? ( + There was an error fetching the mutations to display. + ) : isPending ? ( + + ) : ( +
+ {displayMutations?.length === 0 ? ( + + ) : ( + + )} + +
+ )} +
+ + ); }; diff --git a/website/src/lapis/getLastUpdatedDate.ts b/website/src/lapis/getLastUpdatedDate.ts index 4fcdac581..013724881 100644 --- a/website/src/lapis/getLastUpdatedDate.ts +++ b/website/src/lapis/getLastUpdatedDate.ts @@ -2,14 +2,14 @@ import axios from 'axios'; import dayjs from 'dayjs'; import { z } from 'zod'; -import { getInstanceLogger } from '../logger.ts'; +import { getClientLogger } from '../clientLogger.ts'; const lapisInfoSchema = z.object({ dataVersion: z.string(), }); export type LapisInfo = z.infer; -const logger = getInstanceLogger('getLastUpdatedDate'); +const logger = getClientLogger('getLastUpdatedDate'); export async function getLastUpdatedDate(lapisUrl: string) { try { @@ -20,7 +20,7 @@ export async function getLastUpdatedDate(lapisUrl: string) { const timestamp = Number(parsedLapisInfo.data.dataVersion); if (isNaN(timestamp)) { logger.error(`Got invalid timestamp from lapis info: '${parsedLapisInfo.data.dataVersion}'`); - return undefined; + return null; } return dayjs.unix(timestamp).locale('de').calendar(null, { sameElse: '[on] YYYY-MM-DD' }); } @@ -28,9 +28,9 @@ export async function getLastUpdatedDate(lapisUrl: string) { logger.error( `Failed to parse lapis info: ${JSON.stringify(parsedLapisInfo)} (was ${JSON.stringify(response.data)})`, ); - return undefined; + return null; } catch (error) { logger.error(`Failed to fetch lapis info: ${JSON.stringify(error)}`); - return undefined; + return null; } } diff --git a/website/src/layouts/OrganismPage/AccessionsDownloadButton.astro b/website/src/layouts/OrganismPage/AccessionsDownloadButton.astro deleted file mode 100644 index 3457d3132..000000000 --- a/website/src/layouts/OrganismPage/AccessionsDownloadButton.astro +++ /dev/null @@ -1,53 +0,0 @@ ---- -import type { LapisFilter } from '@genspectrum/dashboard-components/util'; - -import { assembleDownloadUrl } from './assembleDownloadUrl'; -import { Modal } from '../../styles/containers/Modal'; -import { ModalContent } from '../../styles/containers/ModalContent'; -import { ModalHeader } from '../../styles/containers/ModalHeader'; - -type DownloadLink = { - label: string; - filter: LapisFilter; - downloadFileBasename: string; -}; - -interface Props { - accessionDownloadFields: string[]; - downloadLinks: DownloadLink[]; - lapisUrl: string; -} - -const { accessionDownloadFields, downloadLinks, lapisUrl } = Astro.props; - -const modalId = 'accessionDownloadModal'; ---- - - - - - -

You can download a list of accessions that are used for the visualizations on this page:

-
- { - downloadLinks - .map(({ label, filter, downloadFileBasename }) => ({ - label, - url: assembleDownloadUrl(accessionDownloadFields, filter, downloadFileBasename, lapisUrl), - })) - .map(({ label, url }) => ( - - - {label} - - )) - } -
-
- -
-
-
diff --git a/website/src/layouts/OrganismPage/AccessionsDownloadButton.tsx b/website/src/layouts/OrganismPage/AccessionsDownloadButton.tsx new file mode 100644 index 000000000..4bb1fd7a3 --- /dev/null +++ b/website/src/layouts/OrganismPage/AccessionsDownloadButton.tsx @@ -0,0 +1,63 @@ +import type { LapisFilter } from '@genspectrum/dashboard-components/util'; +import type { FC } from 'react'; + +import { assembleDownloadUrl } from './assembleDownloadUrl'; +import { Modal, useModalRef } from '../../styles/containers/Modal'; +import { ModalContent } from '../../styles/containers/ModalContent'; +import { ModalHeader } from '../../styles/containers/ModalHeader'; + +export type DownloadLink = { + label: string; + filter: LapisFilter; + downloadFileBasename: string; +}; + +export type AccessionsDownloadButtonProps = { + accessionDownloadFields: string[]; + downloadLinks: DownloadLink[]; + lapisUrl: string; +}; + +export const AccessionsDownloadButton: FC = ({ + accessionDownloadFields, + downloadLinks, + lapisUrl, +}) => { + const modalRef = useModalRef(); + + return ( + <> + + + + +

You can download a list of accessions that are used for the visualizations on this page:

+
+ {downloadLinks + .map(({ label, filter, downloadFileBasename }) => ({ + label, + url: assembleDownloadUrl( + accessionDownloadFields, + filter, + downloadFileBasename, + lapisUrl, + ), + })) + .map(({ label, url }) => ( + + + {label} + + ))} +
+
+ +
+
+
+ + ); +}; diff --git a/website/src/layouts/OrganismPage/DataPageLayout.astro b/website/src/layouts/OrganismPage/DataPageLayout.astro deleted file mode 100644 index fbd92a1cf..000000000 --- a/website/src/layouts/OrganismPage/DataPageLayout.astro +++ /dev/null @@ -1,48 +0,0 @@ ---- -import type { ComponentProps } from 'astro/types'; - -import LapisUnreachableWrapperClient from '../../components/LapisUnreachableWrapperClient'; -import { type BreadcrumbElement, Breadcrumbs } from '../Breadcrumbs'; -import AccessionsDownloadButton from './AccessionsDownloadButton.astro'; -import LastUpdatedInfo from './LastUpdatedInfo.astro'; -import type { DataOrigin } from '../../types/dataOrigins.ts'; -import BaseLayout from '../base/BaseLayout.astro'; -import DataInfo from '../base/footer/DataInfo.astro'; - -interface Props { - title: string; - breadcrumbs: BreadcrumbElement[]; - dataOrigins: DataOrigin[]; - lapisUrl: string; - downloadLinks?: ComponentProps['downloadLinks']; - accessionDownloadFields?: string[]; -} - -const { title, breadcrumbs, dataOrigins, lapisUrl, downloadLinks = [], accessionDownloadFields = [] } = Astro.props; ---- - - -
-
- -
-
- - - -
-
- { - downloadLinks.length > 0 && ( - - ) - } - - -
-
-
diff --git a/website/src/layouts/OrganismPage/DataPageLayout.tsx b/website/src/layouts/OrganismPage/DataPageLayout.tsx new file mode 100644 index 000000000..969d87802 --- /dev/null +++ b/website/src/layouts/OrganismPage/DataPageLayout.tsx @@ -0,0 +1,50 @@ +import type { FC, PropsWithChildren } from 'react'; + +import { LapisUnreachableWrapperClient } from '../../components/LapisUnreachableWrapperClient.tsx'; +import type { DataOrigin } from '../../types/dataOrigins.ts'; +import { type BreadcrumbElement, Breadcrumbs } from '../Breadcrumbs.tsx'; +import { AccessionsDownloadButton, type DownloadLink } from './AccessionsDownloadButton.tsx'; +import { LastUpdatedInfo } from './LastUpdatedInfo.tsx'; +import { withQueryProvider } from '../../components/subscriptions/backendApi/withQueryProvider.tsx'; +import { DataInfo } from '../base/footer/DataInfo.tsx'; + +export type DataPageLayoutProps = PropsWithChildren<{ + breadcrumbs: BreadcrumbElement[]; + dataOrigins: DataOrigin[]; + lapisUrl: string; + downloadLinks?: DownloadLink[]; + accessionDownloadFields?: string[]; +}>; + +export const DataPageLayoutInner: FC = ({ + breadcrumbs, + dataOrigins, + lapisUrl, + downloadLinks = [], + accessionDownloadFields = [], + children, +}) => { + return ( +
+
+ +
+
+ {children} +
+
+ {downloadLinks.length > 0 && ( + + )} + + +
+
+ ); +}; + +export const DataPageLayout = withQueryProvider(DataPageLayoutInner); diff --git a/website/src/layouts/OrganismPage/LastUpdatedInfo.astro b/website/src/layouts/OrganismPage/LastUpdatedInfo.astro deleted file mode 100644 index 1d29acab1..000000000 --- a/website/src/layouts/OrganismPage/LastUpdatedInfo.astro +++ /dev/null @@ -1,13 +0,0 @@ ---- -import { getLastUpdatedDate } from '../../lapis/getLastUpdatedDate'; - -interface Props { - lapisUrl: string; -} - -const { lapisUrl } = Astro.props; - -const lastUpdatedDate = await getLastUpdatedDate(lapisUrl); ---- - -Data last updated: {lastUpdatedDate ?? 'unknown'} diff --git a/website/src/layouts/OrganismPage/LastUpdatedInfo.tsx b/website/src/layouts/OrganismPage/LastUpdatedInfo.tsx new file mode 100644 index 000000000..67eeeae5e --- /dev/null +++ b/website/src/layouts/OrganismPage/LastUpdatedInfo.tsx @@ -0,0 +1,18 @@ +import { useQuery } from '@tanstack/react-query'; +import type { FC } from 'react'; + +import { getLastUpdatedDate } from '../../lapis/getLastUpdatedDate'; + +interface LastUpdatedInfoProps { + lapisUrl: string; +} + +export const LastUpdatedInfo: FC = ({ lapisUrl }) => { + const { data: lastUpdatedDate } = useQuery({ + queryKey: ['lastUpdatedDate', lapisUrl], + queryFn: () => getLastUpdatedDate(lapisUrl), + throwOnError: true, + }); + + return Data last updated: {lastUpdatedDate ?? 'unknown'}; +}; diff --git a/website/src/layouts/OrganismPage/OrganismViewPageLayout.astro b/website/src/layouts/OrganismPage/OrganismViewPageLayout.astro deleted file mode 100644 index 11d51ab7b..000000000 --- a/website/src/layouts/OrganismPage/OrganismViewPageLayout.astro +++ /dev/null @@ -1,33 +0,0 @@ ---- -import type { ComponentProps } from 'astro/types'; - -import AccessionsDownloadButton from './AccessionsDownloadButton.astro'; -import DataPageLayout from './DataPageLayout.astro'; -import { getLapisUrl } from '../../config'; -import type { OrganismConstants } from '../../views/OrganismConstants'; -import { type View } from '../../views/View'; -import type { PageStateHandler } from '../../views/pageStateHandlers/PageStateHandler'; - -interface Props { - view: View>; - downloadLinks: ComponentProps['downloadLinks']; -} - -const { view, downloadLinks } = Astro.props; ---- - - - - - - diff --git a/website/src/layouts/OrganismPage/OrganismViewPageLayout.tsx b/website/src/layouts/OrganismPage/OrganismViewPageLayout.tsx new file mode 100644 index 000000000..d14a6f850 --- /dev/null +++ b/website/src/layouts/OrganismPage/OrganismViewPageLayout.tsx @@ -0,0 +1,34 @@ +import type { FC, PropsWithChildren } from 'react'; + +import { type DownloadLink } from './AccessionsDownloadButton.tsx'; +import { DataPageLayout } from './DataPageLayout.tsx'; +import type { OrganismConstants } from '../../views/OrganismConstants'; +import { type View } from '../../views/View'; +import type { PageStateHandler } from '../../views/pageStateHandlers/PageStateHandler'; + +export type OrganismViewPageLayoutProps = PropsWithChildren<{ + view: View>; + downloadLinks: DownloadLink[]; + lapisUrl: string; +}>; + +export const OrganismViewPageLayout: FC = ({ + view, + downloadLinks, + lapisUrl, + children, +}) => { + return ( + + + {children} + + + ); +}; diff --git a/website/src/layouts/OrganismPage/SingleVariantOrganismPageLayout.astro b/website/src/layouts/OrganismPage/SingleVariantOrganismPageLayout.astro deleted file mode 100644 index 514a91a78..000000000 --- a/website/src/layouts/OrganismPage/SingleVariantOrganismPageLayout.astro +++ /dev/null @@ -1,25 +0,0 @@ ---- -import type { ComponentProps } from 'astro/types'; - -import AccessionsDownloadButton from './AccessionsDownloadButton.astro'; -import OrganismViewPageLayout from './OrganismViewPageLayout.astro'; -import type { OrganismConstants } from '../../views/OrganismConstants'; -import type { View } from '../../views/View'; -import { type PageStateHandler } from '../../views/pageStateHandlers/PageStateHandler'; - -interface Props { - view: View>; - downloadLinks: ComponentProps['downloadLinks']; -} - -const { view, downloadLinks } = Astro.props; ---- - - -
-
- -
- -
-
diff --git a/website/src/layouts/OrganismPage/SingleVariantOrganismPageLayout.tsx b/website/src/layouts/OrganismPage/SingleVariantOrganismPageLayout.tsx new file mode 100644 index 000000000..ceebf3a44 --- /dev/null +++ b/website/src/layouts/OrganismPage/SingleVariantOrganismPageLayout.tsx @@ -0,0 +1,37 @@ +import type { FC, ReactNode } from 'react'; + +import { type DownloadLink } from './AccessionsDownloadButton.tsx'; +import { OrganismViewPageLayout } from './OrganismViewPageLayout.tsx'; +import type { OrganismsConfig } from '../../config.ts'; +import type { OrganismConstants } from '../../views/OrganismConstants'; +import type { View } from '../../views/View'; +import { type PageStateHandler } from '../../views/pageStateHandlers/PageStateHandler'; + +export type SingleVariantOrganismPageLayoutProps = { + view: View>; + downloadLinks: DownloadLink[]; + organismsConfig: OrganismsConfig; + filters: ReactNode; + dataDisplay: ReactNode; +}; + +export const SingleVariantOrganismPageLayout: FC = ({ + view, + downloadLinks, + organismsConfig, + filters, + dataDisplay, +}) => { + return ( + +
+
{filters}
+ {dataDisplay} +
+
+ ); +}; diff --git a/website/src/layouts/base/footer/DataInfo.astro b/website/src/layouts/base/footer/DataInfo.astro deleted file mode 100644 index 93cc13254..000000000 --- a/website/src/layouts/base/footer/DataInfo.astro +++ /dev/null @@ -1,26 +0,0 @@ ---- -import DataOriginLink from './DataOriginLink.astro'; -import { type DataOrigin, dataOriginConfig } from '../../../types/dataOrigins'; - -interface Props { - dataOrigins: DataOrigin[]; -} - -const { dataOrigins } = Astro.props; ---- - -
-
- Enabled by data from: - { - dataOrigins.map((dataOrigin, index) => ( - - {index > 0 && (index === dataOrigins.length - 1 ? ' and ' : ', ')} - - {dataOriginConfig[dataOrigin].name} - - - )) - } -
-
diff --git a/website/src/layouts/base/footer/DataInfo.tsx b/website/src/layouts/base/footer/DataInfo.tsx new file mode 100644 index 000000000..ae7b3bd6b --- /dev/null +++ b/website/src/layouts/base/footer/DataInfo.tsx @@ -0,0 +1,38 @@ +import { type FC, type PropsWithChildren } from 'react'; + +import { externalLinkIconCss } from '../../../components/iconCss.ts'; +import { type DataOrigin, dataOriginConfig } from '../../../types/dataOrigins'; + +export type DataInfoProps = { + dataOrigins: DataOrigin[]; +}; + +export const DataInfo: FC = ({ dataOrigins }) => { + return ( +
+
+ Enabled by data from: + {dataOrigins.map((dataOrigin, index) => ( + + {index > 0 && (index === dataOrigins.length - 1 ? ' and ' : ', ')} + + {dataOriginConfig[dataOrigin].name} + + + ))} +
+
+ ); +}; + +type DataOriginLinkProps = PropsWithChildren<{ + href: string; +}>; + +const DataOriginLink: FC = ({ href, children }) => { + return ( + + {children} + + ); +}; diff --git a/website/src/layouts/base/footer/DataOriginLink.astro b/website/src/layouts/base/footer/DataOriginLink.astro deleted file mode 100644 index 3e0700e55..000000000 --- a/website/src/layouts/base/footer/DataOriginLink.astro +++ /dev/null @@ -1,12 +0,0 @@ ---- -import { externalLinkIconCss } from '../../../components/iconCss'; -interface Props { - href: string; -} - -const { href } = Astro.props; ---- - - - - diff --git a/website/src/pages/covid/single-variant.astro b/website/src/pages/covid/single-variant.astro index 0a56d0eaa..51c42f572 100644 --- a/website/src/pages/covid/single-variant.astro +++ b/website/src/pages/covid/single-variant.astro @@ -1,72 +1,16 @@ --- -import { AnalyzeSingleVariantSelectorFallback } from '../../components/pageStateSelectors/FallbackElement'; -import { SelectorHeadline } from '../../components/pageStateSelectors/SelectorHeadline'; -import { SingleVariantPageStateSelector } from '../../components/pageStateSelectors/SingleVariantPageStateSelector'; -import { CollectionsList } from '../../components/views/analyzeSingleVariant/CollectionsList'; -import { CovidSingleVariantDataDisplay } from '../../components/views/analyzeSingleVariant/CovidSingleVariantDataDisplay'; -import { sanitizeForFilename } from '../../components/views/compareSideBySide/toDownloadLink'; import { isStaging, getDashboardsConfig } from '../../config'; -import SingleVariantOrganismPageLayout from '../../layouts/OrganismPage/SingleVariantOrganismPageLayout.astro'; -import { hasOnlyUndefinedValues } from '../../util/hasOnlyUndefinedValues'; -import { toDisplayName } from '../../views/pageStateHandlers/PageStateHandler'; -import { type OrganismViewKey } from '../../views/routing'; import { ServerSide } from '../../views/serverSideRouting'; +import BaseLayout from '../../layouts/base/BaseLayout.astro'; +import { CovidSingleVariantReactPage } from '../../components/views/analyzeSingleVariant/CovidSingleVariantReactPage'; -const organismViewKey: OrganismViewKey = 'covid.singleVariantView'; -const view = ServerSide.routing.getOrganismView(organismViewKey); -const pageState = view.pageStateHandler.parsePageStateFromUrl(Astro.url); - -const variantFilter = view.pageStateHandler.toLapisFilter(pageState); - -const noVariantSelected = hasOnlyUndefinedValues(pageState.variantFilter); - -const displayName = toDisplayName(pageState.variantFilter); -const downloadLinks = noVariantSelected - ? [ - { - label: 'Download all accessions', - filter: variantFilter, - downloadFileBasename: `${view.organismConstants.organism}_accessions`, - }, - ] - : [ - { - label: `Download accessions of ${displayName}`, - filter: variantFilter, - downloadFileBasename: `${view.organismConstants.organism}_${sanitizeForFilename(displayName)}_accessions`, - }, - ]; -const organismConfig = getDashboardsConfig().dashboards.organisms; +const view = ServerSide.routing.getOrganismView('covid.singleVariantView'); --- - -
- - - -
-
- Collections - -
-
- -
- -
-
+ + + diff --git a/website/src/pages/index.astro b/website/src/pages/index.astro index be85c87ee..78faaf238 100644 --- a/website/src/pages/index.astro +++ b/website/src/pages/index.astro @@ -2,7 +2,7 @@ import Link from '../components/Link.astro'; import MainMenu from '../components/MainMenu.astro'; import BaseLayout from '../layouts/base/BaseLayout.astro'; -import DataInfo from '../layouts/base/footer/DataInfo.astro'; +import DataInfo from '../layouts/base/footer/DataInfo.tsx'; import { BrandName } from '../layouts/base/header/Brand'; import { getPathogenMegaMenuSections } from '../layouts/base/header/getPathogenMegaMenuSections'; import { dataOrigins } from '../types/dataOrigins'; diff --git a/website/src/pages/swiss-wastewater/flu.astro b/website/src/pages/swiss-wastewater/flu.astro index 9395dd487..01fa6a30b 100644 --- a/website/src/pages/swiss-wastewater/flu.astro +++ b/website/src/pages/swiss-wastewater/flu.astro @@ -4,7 +4,7 @@ import type { SequenceType } from '@genspectrum/dashboard-components/util'; import InfluenzaWastewaterInfo from '../../components/InfluenzaWastewaterInfo.astro'; import GsWastewaterMutationsOverTime from '../../components/genspectrum/GsWastewaterMutationsOverTime.astro'; import { defaultBreadcrumbs } from '../../layouts/Breadcrumbs'; -import DataPageLayout from '../../layouts/OrganismPage/DataPageLayout.astro'; +import { DataPageLayout } from '../../layouts/OrganismPage/DataPageLayout.tsx'; import { dataOrigins } from '../../types/dataOrigins'; import { getMutationAnnotation, @@ -12,52 +12,54 @@ import { wastewaterBreadcrumb, wastewaterConfig, } from '../../types/wastewaterConfig'; +import BaseLayout from '../../layouts/base/BaseLayout.astro'; const lapisUrl = `${wastewaterConfig.lapisBaseUrl}/influenza`; --- - + { - name: 'Influenza', - href: wastewaterConfig.pages.influenza.path, - }, - ]} - dataOrigins={[dataOrigins.wise]} - lapisUrl={lapisUrl} -> - { - InfluenzaTypes.map((reference) => { - const mutationAnnotations = getMutationAnnotation(reference); - return ( - -
-

{reference}

- {(['nucleotide', 'amino acid'] satisfies SequenceType[]).map((sequenceType) => ( - - { + const mutationAnnotations = getMutationAnnotation(reference); + return ( + +
+

{reference}

+ {(['nucleotide', 'amino acid'] satisfies SequenceType[]).map((sequenceType) => ( + - - ))} -
-
- ); - }) - } - + collapsible={sequenceType === 'nucleotide'} + linkSuffix={reference} + > + +
+ ))} +
+
+ ); + }) + } +
+ diff --git a/website/src/pages/swiss-wastewater/rsv.astro b/website/src/pages/swiss-wastewater/rsv.astro index b8ca93065..54e0524c3 100644 --- a/website/src/pages/swiss-wastewater/rsv.astro +++ b/website/src/pages/swiss-wastewater/rsv.astro @@ -4,56 +4,58 @@ import type { SequenceType } from '@genspectrum/dashboard-components/util'; import RsvWastewaterInfo from '../../components/RsvWastewaterInfo.astro'; import GsWastewaterMutationsOverTime from '../../components/genspectrum/GsWastewaterMutationsOverTime.astro'; import { defaultBreadcrumbs } from '../../layouts/Breadcrumbs'; -import DataPageLayout from '../../layouts/OrganismPage/DataPageLayout.astro'; import { dataOrigins } from '../../types/dataOrigins'; import { wastewaterConfig, RSVTypes, wastewaterBreadcrumb } from '../../types/wastewaterConfig'; +import BaseLayout from '../../layouts/base/BaseLayout.astro'; +import { DataPageLayout } from '../../layouts/OrganismPage/DataPageLayout.tsx'; const lapisUrl = `${wastewaterConfig.lapisBaseUrl}/rsv`; --- - - -
+ + ( - <> -
-

{reference}

- {(['nucleotide', 'amino acid'] satisfies SequenceType[]).map((sequenceType) => ( - - + +
+ { + RSVTypes.map((reference) => ( + <> +
+

{reference}

+ {(['nucleotide', 'amino acid'] satisfies SequenceType[]).map((sequenceType) => ( + - - ))} -
- - )) - } -
-
- + collapsible={sequenceType === 'nucleotide'} + linkSuffix={reference} + > + +
+ ))} +
+ + )) + } +
+
+
+ From 6fb27c28286a590e4e211c64644ee8d70168b922 Mon Sep 17 00:00:00 2001 From: Fabian Engelniederhammer Date: Fri, 19 Dec 2025 11:07:31 +0100 Subject: [PATCH 2/3] 979 fix stuff --- .../GenericAnalyzeSingleVariantPage.astro | 6 +++--- .../compareSideBySide/GenericCompareSideBySidePage.astro | 4 ++-- .../compareToBaseline/GenericCompareToBaselinePage.astro | 4 ++-- .../views/compareVariants/GenericCompareVariantsPage.astro | 4 ++-- .../sequencingEfforts/GenericSequencingEffortsPage.astro | 4 ++-- website/src/components/views/wasap/Wasap.astro | 2 +- website/src/pages/covid/single-variant.astro | 4 ++-- website/src/pages/index.astro | 2 +- website/src/pages/swiss-wastewater/flu.astro | 2 +- website/src/pages/swiss-wastewater/rsv.astro | 4 ++-- 10 files changed, 18 insertions(+), 18 deletions(-) diff --git a/website/src/components/views/analyzeSingleVariant/GenericAnalyzeSingleVariantPage.astro b/website/src/components/views/analyzeSingleVariant/GenericAnalyzeSingleVariantPage.astro index 7c978a5fb..83fe543bf 100644 --- a/website/src/components/views/analyzeSingleVariant/GenericAnalyzeSingleVariantPage.astro +++ b/website/src/components/views/analyzeSingleVariant/GenericAnalyzeSingleVariantPage.astro @@ -1,11 +1,11 @@ --- +import { GenericAnalyseSingleVariantReactPage } from './GenericAnalyseSingleVariantReactPage'; import { isStaging, getDashboardsConfig } from '../../../config'; +import BaseLayout from '../../../layouts/base/BaseLayout.astro'; +import { Organisms } from '../../../types/Organism'; import { type OrganismWithViewKey } from '../../../views/routing'; import { ServerSide } from '../../../views/serverSideRouting'; import { singleVariantViewKey } from '../../../views/viewKeys'; -import BaseLayout from '../../../layouts/base/BaseLayout.astro'; -import { GenericAnalyseSingleVariantReactPage } from './GenericAnalyseSingleVariantReactPage'; -import { Organisms } from '../../../types/Organism'; interface Props { organism: Exclude, typeof Organisms.covid>; diff --git a/website/src/components/views/compareSideBySide/GenericCompareSideBySidePage.astro b/website/src/components/views/compareSideBySide/GenericCompareSideBySidePage.astro index 01235b527..e6216e72e 100644 --- a/website/src/components/views/compareSideBySide/GenericCompareSideBySidePage.astro +++ b/website/src/components/views/compareSideBySide/GenericCompareSideBySidePage.astro @@ -1,10 +1,10 @@ --- +import { GenericCompareSideBySideReactPage } from './GenericCompareSideBySideReactPage'; import { isStaging, getDashboardsConfig } from '../../../config'; +import BaseLayout from '../../../layouts/base/BaseLayout.astro'; import { type OrganismWithViewKey } from '../../../views/routing'; import { ServerSide } from '../../../views/serverSideRouting'; import { compareSideBySideViewKey } from '../../../views/viewKeys'; -import BaseLayout from '../../../layouts/base/BaseLayout.astro'; -import { GenericCompareSideBySideReactPage } from './GenericCompareSideBySideReactPage'; interface Props { organism: OrganismWithViewKey; diff --git a/website/src/components/views/compareToBaseline/GenericCompareToBaselinePage.astro b/website/src/components/views/compareToBaseline/GenericCompareToBaselinePage.astro index df14581d0..650f35d58 100644 --- a/website/src/components/views/compareToBaseline/GenericCompareToBaselinePage.astro +++ b/website/src/components/views/compareToBaseline/GenericCompareToBaselinePage.astro @@ -1,10 +1,10 @@ --- +import { GenericCompareToBaselineReactPage } from './GenericCompareToBaselineReactPage'; import { isStaging, getDashboardsConfig } from '../../../config'; +import BaseLayout from '../../../layouts/base/BaseLayout.astro'; import { type OrganismWithViewKey } from '../../../views/routing'; import { ServerSide } from '../../../views/serverSideRouting'; import { compareToBaselineViewKey } from '../../../views/viewKeys'; -import BaseLayout from '../../../layouts/base/BaseLayout.astro'; -import { GenericCompareToBaselineReactPage } from './GenericCompareToBaselineReactPage'; interface Props { organism: OrganismWithViewKey; diff --git a/website/src/components/views/compareVariants/GenericCompareVariantsPage.astro b/website/src/components/views/compareVariants/GenericCompareVariantsPage.astro index 60feafa19..4b5a062e1 100644 --- a/website/src/components/views/compareVariants/GenericCompareVariantsPage.astro +++ b/website/src/components/views/compareVariants/GenericCompareVariantsPage.astro @@ -1,10 +1,10 @@ --- +import { GenericCompareVariantsReactPage } from './GenericCompareVariantsReactPage'; import { isStaging, getDashboardsConfig } from '../../../config'; +import BaseLayout from '../../../layouts/base/BaseLayout.astro'; import { type OrganismWithViewKey } from '../../../views/routing'; import { ServerSide } from '../../../views/serverSideRouting'; import { compareVariantsViewKey } from '../../../views/viewKeys'; -import BaseLayout from '../../../layouts/base/BaseLayout.astro'; -import { GenericCompareVariantsReactPage } from './GenericCompareVariantsReactPage'; interface Props { organism: OrganismWithViewKey; diff --git a/website/src/components/views/sequencingEfforts/GenericSequencingEffortsPage.astro b/website/src/components/views/sequencingEfforts/GenericSequencingEffortsPage.astro index 9b152b834..b23520e33 100644 --- a/website/src/components/views/sequencingEfforts/GenericSequencingEffortsPage.astro +++ b/website/src/components/views/sequencingEfforts/GenericSequencingEffortsPage.astro @@ -1,10 +1,10 @@ --- +import { GenericSequencingEffortsReactPage } from './GenericSequencingEffortsReactPage'; import { isStaging, getDashboardsConfig } from '../../../config'; +import BaseLayout from '../../../layouts/base/BaseLayout.astro'; import { type OrganismWithViewKey } from '../../../views/routing'; import { ServerSide } from '../../../views/serverSideRouting'; import { sequencingEffortsViewKey } from '../../../views/viewKeys'; -import BaseLayout from '../../../layouts/base/BaseLayout.astro'; -import { GenericSequencingEffortsReactPage } from './GenericSequencingEffortsReactPage'; interface Props { organism: OrganismWithViewKey; diff --git a/website/src/components/views/wasap/Wasap.astro b/website/src/components/views/wasap/Wasap.astro index c689c8569..4c24593d2 100644 --- a/website/src/components/views/wasap/Wasap.astro +++ b/website/src/components/views/wasap/Wasap.astro @@ -1,7 +1,7 @@ --- import { WasapPage } from './WasapPage'; -import { wastewaterOrganismConfigs, type WastewaterOrganismName } from '../../../types/wastewaterConfig'; import BaseLayout from '../../../layouts/base/BaseLayout.astro'; +import { wastewaterOrganismConfigs, type WastewaterOrganismName } from '../../../types/wastewaterConfig'; type Props = { wastewaterOrganism: WastewaterOrganismName; diff --git a/website/src/pages/covid/single-variant.astro b/website/src/pages/covid/single-variant.astro index 51c42f572..fa95aaefb 100644 --- a/website/src/pages/covid/single-variant.astro +++ b/website/src/pages/covid/single-variant.astro @@ -1,8 +1,8 @@ --- +import { CovidSingleVariantReactPage } from '../../components/views/analyzeSingleVariant/CovidSingleVariantReactPage'; import { isStaging, getDashboardsConfig } from '../../config'; -import { ServerSide } from '../../views/serverSideRouting'; import BaseLayout from '../../layouts/base/BaseLayout.astro'; -import { CovidSingleVariantReactPage } from '../../components/views/analyzeSingleVariant/CovidSingleVariantReactPage'; +import { ServerSide } from '../../views/serverSideRouting'; const view = ServerSide.routing.getOrganismView('covid.singleVariantView'); --- diff --git a/website/src/pages/index.astro b/website/src/pages/index.astro index 78faaf238..1f93fad8f 100644 --- a/website/src/pages/index.astro +++ b/website/src/pages/index.astro @@ -2,7 +2,7 @@ import Link from '../components/Link.astro'; import MainMenu from '../components/MainMenu.astro'; import BaseLayout from '../layouts/base/BaseLayout.astro'; -import DataInfo from '../layouts/base/footer/DataInfo.tsx'; +import { DataInfo } from '../layouts/base/footer/DataInfo.tsx'; import { BrandName } from '../layouts/base/header/Brand'; import { getPathogenMegaMenuSections } from '../layouts/base/header/getPathogenMegaMenuSections'; import { dataOrigins } from '../types/dataOrigins'; diff --git a/website/src/pages/swiss-wastewater/flu.astro b/website/src/pages/swiss-wastewater/flu.astro index 01fa6a30b..119324242 100644 --- a/website/src/pages/swiss-wastewater/flu.astro +++ b/website/src/pages/swiss-wastewater/flu.astro @@ -5,6 +5,7 @@ import InfluenzaWastewaterInfo from '../../components/InfluenzaWastewaterInfo.as import GsWastewaterMutationsOverTime from '../../components/genspectrum/GsWastewaterMutationsOverTime.astro'; import { defaultBreadcrumbs } from '../../layouts/Breadcrumbs'; import { DataPageLayout } from '../../layouts/OrganismPage/DataPageLayout.tsx'; +import BaseLayout from '../../layouts/base/BaseLayout.astro'; import { dataOrigins } from '../../types/dataOrigins'; import { getMutationAnnotation, @@ -12,7 +13,6 @@ import { wastewaterBreadcrumb, wastewaterConfig, } from '../../types/wastewaterConfig'; -import BaseLayout from '../../layouts/base/BaseLayout.astro'; const lapisUrl = `${wastewaterConfig.lapisBaseUrl}/influenza`; --- diff --git a/website/src/pages/swiss-wastewater/rsv.astro b/website/src/pages/swiss-wastewater/rsv.astro index 54e0524c3..0a37abb92 100644 --- a/website/src/pages/swiss-wastewater/rsv.astro +++ b/website/src/pages/swiss-wastewater/rsv.astro @@ -4,10 +4,10 @@ import type { SequenceType } from '@genspectrum/dashboard-components/util'; import RsvWastewaterInfo from '../../components/RsvWastewaterInfo.astro'; import GsWastewaterMutationsOverTime from '../../components/genspectrum/GsWastewaterMutationsOverTime.astro'; import { defaultBreadcrumbs } from '../../layouts/Breadcrumbs'; +import { DataPageLayout } from '../../layouts/OrganismPage/DataPageLayout.tsx'; +import BaseLayout from '../../layouts/base/BaseLayout.astro'; import { dataOrigins } from '../../types/dataOrigins'; import { wastewaterConfig, RSVTypes, wastewaterBreadcrumb } from '../../types/wastewaterConfig'; -import BaseLayout from '../../layouts/base/BaseLayout.astro'; -import { DataPageLayout } from '../../layouts/OrganismPage/DataPageLayout.tsx'; const lapisUrl = `${wastewaterConfig.lapisBaseUrl}/rsv`; --- From 50de90d425a84a0576a4a4968c2d35e168be34a2 Mon Sep 17 00:00:00 2001 From: Fabian Engelniederhammer Date: Fri, 19 Dec 2025 11:17:29 +0100 Subject: [PATCH 3/3] 979 fix test --- ...sUnreachableWrapperClient.browser.spec.tsx | 51 +++++++++---------- website/src/lapis/getLastUpdatedDate.spec.ts | 4 +- 2 files changed, 26 insertions(+), 29 deletions(-) diff --git a/website/src/components/LapisUnreachableWrapperClient.browser.spec.tsx b/website/src/components/LapisUnreachableWrapperClient.browser.spec.tsx index bd66c9c8a..c33f73371 100644 --- a/website/src/components/LapisUnreachableWrapperClient.browser.spec.tsx +++ b/website/src/components/LapisUnreachableWrapperClient.browser.spec.tsx @@ -1,3 +1,4 @@ +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { describe, expect } from 'vitest'; import { render } from 'vitest-browser-react'; @@ -5,27 +6,23 @@ import { LapisUnreachableWrapperClient } from './LapisUnreachableWrapperClient'; import { DUMMY_LAPIS_URL } from '../../routeMocker'; import { it } from '../../test-extend'; +const queryClient = new QueryClient(); + describe('LapisUnreachableWrapperClient', () => { it('displays children when LAPIS is reachable', async ({ routeMockers: { lapis } }) => { lapis.mockPostAggregated({}, { data: [{ count: 100 }] }); - const { getByText } = render( - -
Content is visible
-
, - ); + const content = 'Content is visible'; + + const { getByText } = renderWithContent(content); - await expect.element(getByText('Content is visible')).toBeVisible(); + await expect.element(getByText(content)).toBeVisible(); }); it('displays error message when LAPIS is unreachable', async ({ routeMockers: { lapis } }) => { lapis.mockLapisDown(); - const { getByText } = render( - -
Content is visible
-
, - ); + const { getByText } = renderWithContent('Content is visible'); await expect.element(getByText('Data Source Unreachable')).toBeVisible(); await expect.element(getByText('Unable to connect to the data source')).toBeVisible(); @@ -34,23 +31,17 @@ describe('LapisUnreachableWrapperClient', () => { it('does not display children when LAPIS is unreachable', async ({ routeMockers: { lapis } }) => { lapis.mockLapisDown(); - const { getByText } = render( - -
Content should not be visible
-
, - ); + const content = 'Content is visible'; - await expect.poll(() => getByText('Content should not be visible').query()).not.toBeInTheDocument(); + const { getByText } = renderWithContent(content); + + await expect.poll(() => getByText(content).query()).not.toBeInTheDocument(); }); it('displays error when LAPIS returns invalid response', async ({ routeMockers: { lapis } }) => { lapis.mockPostAggregated({}, { data: [] }, 200); - const { getByText } = render( - -
Content is visible
-
, - ); + const { getByText } = renderWithContent('Content is visible'); await expect.element(getByText('Data Source Unreachable')).toBeVisible(); }); @@ -58,12 +49,18 @@ describe('LapisUnreachableWrapperClient', () => { it('displays error when LAPIS returns 500', async ({ routeMockers: { lapis } }) => { lapis.mockPostAggregated({}, { data: [{ count: 100 }] }, 500); - const { getByText } = render( - -
Content is visible
-
, - ); + const { getByText } = renderWithContent('Content is visible'); await expect.element(getByText('Data Source Unreachable')).toBeVisible(); }); }); + +function renderWithContent(content: string) { + return render( + + +
{content}
+
+
, + ); +} diff --git a/website/src/lapis/getLastUpdatedDate.spec.ts b/website/src/lapis/getLastUpdatedDate.spec.ts index 410ee68a8..4dea079e4 100644 --- a/website/src/lapis/getLastUpdatedDate.spec.ts +++ b/website/src/lapis/getLastUpdatedDate.spec.ts @@ -28,7 +28,7 @@ describe('getLastUpdatedDate', () => { const result = await getLastUpdatedDate(DUMMY_LAPIS_URL); - expect(result).toBeUndefined(); + expect(result).toBeNull(); }); test('should handle non-numeric data versions gracefully', async () => { @@ -36,6 +36,6 @@ describe('getLastUpdatedDate', () => { const result = await getLastUpdatedDate(DUMMY_LAPIS_URL); - expect(result).toBeUndefined(); + expect(result).toBeNull(); }); });