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
2 changes: 1 addition & 1 deletion .github/workflows/commitlint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ jobs:
with:
node-version: lts/*

- uses: actions/cache@v4
- uses: actions/cache@v5
with:
path: ~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('package-lock.json') }}
Expand Down
11 changes: 8 additions & 3 deletions .github/workflows/e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ jobs:
with:
node-version: lts/*
- name: Cache .npm
uses: actions/cache@v4
uses: actions/cache@v5
with:
path: ~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('lapis-e2e/**/package-lock.json') }}
Expand Down Expand Up @@ -57,10 +57,15 @@ jobs:
WEBSITE_TAG: ${{ steps.branchTag.outputs.branchTag }}
BACKEND_TAG: ${{ steps.branchTag.outputs.branchTag }}

- name: Run Playwright tests
- name: Run Playwright tests (all browsers)
if: github.ref == 'refs/heads/main'
run: npm run e2e

- uses: actions/upload-artifact@v4
- name: Run Playwright tests (chromium only)
if: github.ref != 'refs/heads/main'
run: npm run e2e:chromium

- uses: actions/upload-artifact@v6
if: ${{ !cancelled() }}
with:
name: playwright-report
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/website.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:
- uses: actions/setup-node@v6
with:
node-version: lts/*
- uses: actions/cache@v4
- uses: actions/cache@v5
with:
path: ~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('package-lock.json') }}
Expand All @@ -37,8 +37,8 @@ jobs:
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
with:
node-version: 22
- uses: actions/cache@v4
node-version: lts/*
- uses: actions/cache@v5
with:
path: ~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('package-lock.json') }}
Expand Down
2 changes: 1 addition & 1 deletion Dockerfile_website
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Dockerfile for the website
# This is here so that the build context can access the backend config files
FROM node:22-alpine AS base
FROM node:24-alpine AS base
WORKDIR /app

COPY website/package.json website/package-lock.json website/patches ./
Expand Down
2,473 changes: 1,130 additions & 1,343 deletions website/package-lock.json

Large diffs are not rendered by default.

61 changes: 31 additions & 30 deletions website/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,67 +20,68 @@
"test:headed": "vitest --browser.headless false",
"test:browser": "vitest --project browser",
"test:browser:headed": "vitest --project browser --browser.headless false",
"e2e": "playwright test"
"e2e": "playwright test",
"e2e:chromium": "playwright test --project=chromium"
},
"dependencies": {
"@astrojs/node": "^9.4.4",
"@astrojs/node": "^9.5.1",
"@auth/core": "^0.37.4",
"@genspectrum/dashboard-components": "^1.11.1",
"@tanstack/react-query": "^5.90.1",
"@genspectrum/dashboard-components": "^1.12.0",
"@tanstack/react-query": "^5.90.12",
"astro": "^5.13.10",
"auth-astro": "^4.2.0",
"axios": "^1.12.2",
"cookie": "^1.0.2",
"dayjs": "^1.11.18",
"katex": "^0.16.25",
"patch-package": "^8.0.0",
"axios": "^1.13.2",
"cookie": "^1.1.1",
"dayjs": "^1.11.19",
"katex": "^0.16.27",
"patch-package": "^8.0.1",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-katex": "^3.1.0",
"react-toastify": "^11.0.5",
"uuid": "^11.1.0",
"winston": "^3.17.0",
"winston": "^3.19.0",
"winston-daily-rotate-file": "^5.0.0",
"yaml": "^2.8.1"
"yaml": "^2.8.2"
},
"devDependencies": {
"@astrojs/check": "^0.9.4",
"@astrojs/react": "^4.3.1",
"@eslint/js": "^9.36.0",
"@astrojs/check": "^0.9.6",
"@astrojs/react": "^4.4.2",
"@eslint/js": "^9.39.2",
"@iconify-json/mdi": "^1.2.3",
"@iconify-json/mdi-light": "^1.2.2",
"@iconify/tailwind4": "^1.0.6",
"@playwright/test": "^1.55.0",
"@tailwindcss/vite": "^4.1.13",
"@tanstack/eslint-plugin-query": "^5.90.1",
"@iconify/tailwind4": "^1.2.0",
"@playwright/test": "^1.57.0",
"@tailwindcss/vite": "^4.1.18",
"@tanstack/eslint-plugin-query": "^5.91.2",
"@testing-library/dom": "^10.4.1",
"@testing-library/jest-dom": "^6.8.0",
"@testing-library/react": "^16.3.0",
"@testing-library/jest-dom": "^6.9.1",
"@testing-library/react": "^16.3.1",
"@testing-library/user-event": "^14.6.1",
"@types/node": "^24.5.2",
"@types/react": "^18.3.12",
"@types/react-dom": "^18.3.1",
"@types/react-katex": "^3.0.4",
"@types/topojson-specification": "^1.0.5",
"@typescript-eslint/eslint-plugin": "^8.44.1",
"@typescript-eslint/parser": "^8.44.1",
"@typescript-eslint/eslint-plugin": "^8.50.0",
"@typescript-eslint/parser": "^8.50.0",
"@vitest/browser": "^3.2.4",
"astro-eslint-parser": "^1.2.2",
"daisyui": "^5.1.14",
"daisyui": "^5.5.14",
"dotenv": "^16.5.0",
"eslint": "^9.36.0",
"eslint-plugin-astro": "^1.3.1",
"eslint": "^9.39.2",
"eslint-plugin-astro": "^1.5.0",
"eslint-plugin-import": "^2.32.0",
"eslint-plugin-react": "^7.37.5",
"eslint-plugin-react-hooks": "^5.2.0",
"msw": "^2.11.3",
"msw": "^2.12.4",
"playwright": "^1.55.0",
"prettier": "^3.6.2",
"prettier": "^3.7.4",
"prettier-plugin-astro": "^0.14.1",
"prettier-plugin-tailwindcss": "^0.6.14",
"prettier-plugin-tailwindcss": "^0.7.2",
"tailwindcss": "^4.0.9",
"typescript": "^5.9.2",
"typescript-eslint": "^8.44.1",
"typescript": "^5.9.3",
"typescript-eslint": "^8.50.0",
"vitest": "^3.2.4",
"vitest-browser-react": "^1.0.1",
"zod": "^3.25.74"
Expand Down
25 changes: 0 additions & 25 deletions website/src/components/genspectrum/GsMutationComparison.astro

This file was deleted.

35 changes: 35 additions & 0 deletions website/src/components/genspectrum/GsMutationComparison.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import type { NamedLapisFilter, SequenceType } from '@genspectrum/dashboard-components/util';
import type { FC } from 'react';

import { defaultTablePageSize } from '../../views/View';
import { ComponentWrapper } from '../ComponentWrapper.tsx';

export type GsMutationComparisonProps = {
lapisFilters: NamedLapisFilter[];
sequenceType: SequenceType;
height?: string;
pageSize?: number;
};

export const GsMutationComparison: FC<GsMutationComparisonProps> = ({
lapisFilters,
sequenceType,
height,
pageSize,
}) => {
return (
<ComponentWrapper
title={sequenceType === 'nucleotide' ? 'Nucleotide changes' : 'Amino acid changes'}
height={height}
>
<gs-mutation-comparison
lapisFilters={JSON.stringify(lapisFilters)}
sequenceType={sequenceType}
views='["venn", "table"]'
pageSize={pageSize ?? defaultTablePageSize}
width='100%'
height={height ? '100%' : undefined}
></gs-mutation-comparison>
</ComponentWrapper>
);
};
2 changes: 1 addition & 1 deletion website/src/components/genspectrum/GsMutationFilter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export function GsMutationFilter({
initialValue,
width,
enabledMutationTypes,
onMutationChange = () => {},
onMutationChange,
}: {
width?: string;
initialValue?: MutationFilter | string[] | undefined;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ export function DynamicDateFilter({
// When the value has a "Custom" label, try to match it back to one of the generated options
// by comparing dateFrom and dateTo. If there's a match, use that option's label instead of "Custom".
const normalizedValue = useMemo(() => {
if (value === undefined || value.label !== CustomDateRangeLabel) {
if (value?.label !== CustomDateRangeLabel) {
return value;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,13 @@ export function DefineClinicalSignatureInfo() {
indicate that either not all sequences of the variant have the mutation or not all sequences with the
mutation belong to the variant (or both).
</p>

<h2 className='mt-4 mb-2 text-base font-semibold'>Time Frame</h2>
<p className='text-gray-700'>
Time frame for sequence selection. Shorter windows reduce dominance of well-sequenced historical
variants, yielding higher Jaccard values for emerging variants with fewer clinical sequences. Use this
filter to exclude variants no longer in circulation when assessing significance of current mutations.
</p>
</InfoBlock>
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ describe('VariantExplorerFilter', () => {
minProportion: 0.05,
minCount: 10,
minJaccard: 0.5,
timeFrame: 'all',
};

it('calls setPageState when changing sequence type', async ({ routeMockers: { lapis } }) => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import { Inset } from '../../../../styles/Inset';
import { GsLineageFilter } from '../../../genspectrum/GsLineageFilter';
import type { WasapVariantFilter } from '../../../views/wasap/wasapPageConfig';
import {
VARIANT_TIME_FRAME,
type VariantTimeFrame,
type WasapVariantFilter,
} from '../../../views/wasap/wasapPageConfig';
import { SelectorHeadline } from '../../SelectorHeadline';
import { DefineClinicalSignatureInfo } from '../InfoBlocks';
import { LabeledField } from '../utils/LabeledField';
Expand Down Expand Up @@ -70,6 +74,17 @@ export function VariantExplorerFilter({
step={0.01}
onChange={(v) => setPageState({ ...pageState, minJaccard: v })}
/>
<LabeledField label='Time frame'>
<select
className='select select-bordered'
value={pageState.timeFrame}
onChange={(e) => setPageState({ ...pageState, timeFrame: e.target.value as VariantTimeFrame })}
>
<option value={VARIANT_TIME_FRAME.all}>All</option>
<option value={VARIANT_TIME_FRAME.sixMonths}>Past 6 months</option>
<option value={VARIANT_TIME_FRAME.threeMonths}>Past 3 months</option>
</select>
</LabeledField>
</Inset>
</>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { type FC, useMemo } from 'react';

import { SelectBaseline } from './SelectBaseline.tsx';
import type { OrganismsConfig } from '../../../config.ts';
import { chooseGranularityBasedOnDateRange } from '../../../util/chooseGranularityBasedOnDateRange.ts';
import { ComponentHeight } from '../../../views/OrganismConstants.ts';
import type { CompareToBaselineData } from '../../../views/View.ts';
import { type OrganismWithViewKey, Routing } from '../../../views/routing.ts';
import { compareToBaselineViewKey } from '../../../views/viewKeys.ts';
import { ComponentsGrid } from '../../ComponentsGrid.tsx';
import { GsPrevalenceOverTime } from '../../genspectrum/GsPrevalenceOverTime.tsx';

export type GenericCompareToBaselineDisplayProps = {
organismViewKey: `${OrganismWithViewKey<typeof compareToBaselineViewKey>}.${typeof compareToBaselineViewKey}`;
organismsConfig: OrganismsConfig;
pageState: CompareToBaselineData;
};

export const GenericCompareToBaselineDataDisplay: FC<GenericCompareToBaselineDisplayProps> = ({
organismViewKey,
organismsConfig,
pageState,
}) => {
const view = useMemo(
() => new Routing(organismsConfig).getOrganismView(organismViewKey),
[organismsConfig, organismViewKey],
);

const baselineLapisFilter = view.pageStateHandler.baselineFilterToLapisFilter(pageState);
const timeGranularity = chooseGranularityBasedOnDateRange({
earliestDate: new Date(view.organismConstants.earliestDate),
dateRange: pageState.datasetFilter.dateFilters[view.organismConstants.mainDateField],
});

const numeratorLapisFilters = view.pageStateHandler.variantFiltersToNamedLapisFilters(pageState);
const noVariantSelected = pageState.variants.size < 1;

return noVariantSelected ? (
<SelectBaseline />
) : (
<ComponentsGrid>
<GsPrevalenceOverTime
numeratorFilters={numeratorLapisFilters}
denominatorFilter={baselineLapisFilter}
lapisDateField={view.organismConstants.mainDateField}
granularity={timeGranularity}
views={['line', 'table', 'bar']}
height={ComponentHeight.large}
pageSize={12}
/>
</ComponentsGrid>
);
};
Loading