Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,31 +1,28 @@
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
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';

const queryClient = new QueryClient();

describe('LapisUnreachableWrapperClient', () => {
it('displays children when LAPIS is reachable', async ({ routeMockers: { lapis } }) => {
lapis.mockPostAggregated({}, { data: [{ count: 100 }] });

const { getByText } = render(
<LapisUnreachableWrapperClient lapisUrl={DUMMY_LAPIS_URL}>
<div>Content is visible</div>
</LapisUnreachableWrapperClient>,
);
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(
<LapisUnreachableWrapperClient lapisUrl={DUMMY_LAPIS_URL}>
<div>Content is visible</div>
</LapisUnreachableWrapperClient>,
);
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();
Expand All @@ -34,36 +31,36 @@ describe('LapisUnreachableWrapperClient', () => {
it('does not display children when LAPIS is unreachable', async ({ routeMockers: { lapis } }) => {
lapis.mockLapisDown();

const { getByText } = render(
<LapisUnreachableWrapperClient lapisUrl={DUMMY_LAPIS_URL}>
<div>Content should not be visible</div>
</LapisUnreachableWrapperClient>,
);
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(
<LapisUnreachableWrapperClient lapisUrl={DUMMY_LAPIS_URL}>
<div>Content is visible</div>
</LapisUnreachableWrapperClient>,
);
const { getByText } = renderWithContent('Content is visible');

await expect.element(getByText('Data Source Unreachable')).toBeVisible();
});

it('displays error when LAPIS returns 500', async ({ routeMockers: { lapis } }) => {
lapis.mockPostAggregated({}, { data: [{ count: 100 }] }, 500);

const { getByText } = render(
<LapisUnreachableWrapperClient lapisUrl={DUMMY_LAPIS_URL}>
<div>Content is visible</div>
</LapisUnreachableWrapperClient>,
);
const { getByText } = renderWithContent('Content is visible');

await expect.element(getByText('Data Source Unreachable')).toBeVisible();
});
});

function renderWithContent(content: string) {
return render(
<QueryClientProvider client={queryClient}>
<LapisUnreachableWrapperClient lapisUrl={DUMMY_LAPIS_URL}>
<div>{content}</div>
</LapisUnreachableWrapperClient>
</QueryClientProvider>,
);
}
7 changes: 1 addition & 6 deletions website/src/components/LapisUnreachableWrapperClient.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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<LapisUnreachableWrapperClientProps> = ({ lapisUrl, children }) => {
export const LapisUnreachableWrapperClient: FC<LapisUnreachableWrapperClientProps> = ({ lapisUrl, children }) => {
const { data: isReachable, isLoading } = useQuery({
queryKey: ['lapis-reachable', lapisUrl],
queryFn: () => checkLapisHealth(lapisUrl),
Expand Down Expand Up @@ -37,7 +36,3 @@ const LapisUnreachableWrapperClientInner: FC<LapisUnreachableWrapperClientProps>

return <>{children}</>;
};

const LapisUnreachableWrapperClient = withQueryProvider(LapisUnreachableWrapperClientInner);

export default LapisUnreachableWrapperClient;
38 changes: 0 additions & 38 deletions website/src/components/pageStateSelectors/FallbackElement.tsx

This file was deleted.

Original file line number Diff line number Diff line change
@@ -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<CovidSingleVariantReactPageProps> = ({ 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 (
<SingleVariantOrganismPageLayout
view={view}
downloadLinks={downloadLinks}
organismsConfig={organismsConfig}
filters={
<>
<SingleVariantPageStateSelector
organismViewKey={organismViewKey}
organismsConfig={organismsConfig}
initialPageState={pageState}
enableAdvancedQueryFilter={isStaging}
/>
<hr className='my-4 border-gray-200' />
<div className='mt-4'>
<SelectorHeadline>Collections</SelectorHeadline>
<CollectionsList
initialCollectionId={pageState.collectionId}
organismsConfig={organismsConfig}
/>
</div>
</>
}
dataDisplay={
<CovidSingleVariantDataDisplay
organismViewKey={organismViewKey}
organismsConfig={organismsConfig}
pageState={pageState}
/>
}
/>
);
};
Original file line number Diff line number Diff line change
@@ -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<OrganismWithViewKey<typeof singleVariantViewKey>, typeof Organisms.covid>;
organismsConfig: OrganismsConfig;
isStaging: boolean;
};

export const GenericAnalyseSingleVariantReactPage: FC<GenericAnalyseSingleVariantReactPageProps> = ({
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 (
<SingleVariantOrganismPageLayout
view={view}
downloadLinks={downloadLinks}
organismsConfig={organismsConfig}
filters={
<SingleVariantPageStateSelector
organismViewKey={organismViewKey}
organismsConfig={organismsConfig}
initialPageState={pageState}
enableAdvancedQueryFilter={isStaging}
/>
}
dataDisplay={
<GenericAnalyseSingleVariantDataDisplay
organismViewKey={organismViewKey}
organismsConfig={organismsConfig}
pageState={pageState}
/>
}
/>
);
};
Original file line number Diff line number Diff line change
@@ -1,70 +1,26 @@
---
import { GenericAnalyseSingleVariantDataDisplay } from './GenericAnalyseSingleVariantDataDisplay';
import { GenericAnalyseSingleVariantReactPage } from './GenericAnalyseSingleVariantReactPage';
import { isStaging, getDashboardsConfig } from '../../../config';
import SingleVariantOrganismPageLayout from '../../../layouts/OrganismPage/SingleVariantOrganismPageLayout.astro';
import BaseLayout from '../../../layouts/base/BaseLayout.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';

type OrganismViewCompareVariant = OrganismWithViewKey<typeof singleVariantViewKey>;
interface Props {
organism: Exclude<OrganismViewCompareVariant, typeof Organisms.covid>;
organism: Exclude<OrganismWithViewKey<typeof singleVariantViewKey>, 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}`);
---

<SingleVariantOrganismPageLayout view={view} downloadLinks={downloadLinks}>
<SingleVariantPageStateSelector
slot='filters'
organismViewKey={organismViewKey}
organismsConfig={organismConfig}
initialPageState={pageState}
enableAdvancedQueryFilter={isStaging()}
<BaseLayout title={view.viewTitle}>
<GenericAnalyseSingleVariantReactPage
organism={organism}
organismsConfig={getDashboardsConfig().dashboards.organisms}
isStaging={isStaging()}
client:only='react'
>
<AnalyzeSingleVariantSelectorFallback slot='fallback' />
</SingleVariantPageStateSelector>

<div slot='dataDisplay'>
<GenericAnalyseSingleVariantDataDisplay
organismViewKey={organismViewKey}
organismsConfig={organismConfig}
pageState={pageState}
client:load
/>
</div>
</SingleVariantOrganismPageLayout>
/>
</BaseLayout>
Loading
Loading