Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

UI 3 #1260

Closed
wants to merge 22 commits into from
Closed

UI 3 #1260

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
d67bb2f
feat(web): scaffold multi-step wizard on main page
ivan-aksamentov Sep 21, 2023
2a83eaa
refactor: split dataset list into subcomponents
ivan-aksamentov Sep 22, 2023
194a2e2
refactor: turn dataset selector input state inside out
ivan-aksamentov Sep 26, 2023
eb566e1
feat(web): cleanup widgets for auto and manual dataset selection flow
ivan-aksamentov Sep 27, 2023
64e5578
feat(web): re-add readme and changelog to the dataset screen
ivan-aksamentov Sep 28, 2023
9b385f2
feat(web): re-add dataset customization
ivan-aksamentov Sep 28, 2023
32fd71f
fix(web): prevent preview alert from adding scroll to the page layout
ivan-aksamentov Sep 28, 2023
bcfa812
feat: split main wizzard into actual web pages
ivan-aksamentov Oct 17, 2023
c2b2350
feat: move dataset badges back to right side
ivan-aksamentov Oct 18, 2023
f16b4c0
feat: prettify autosuggestion list entry
ivan-aksamentov Oct 18, 2023
7b0f1c0
Merge branch 'master' into ui-3
ivan-aksamentov Oct 20, 2023
3f7844d
fix(web): width of settings page
ivan-aksamentov Oct 20, 2023
e07924a
Merge branch 'master' into ui-3
ivan-aksamentov Oct 20, 2023
ca16412
Merge branch 'master' into ui-3
ivan-aksamentov Oct 20, 2023
009c8b0
Merge branch 'master' into ui-3
ivan-aksamentov Oct 20, 2023
cefb5b3
Merge branch 'master' into ui-3
ivan-aksamentov Oct 20, 2023
1f8c26f
feat(web): ui-3: redirect to dataset page if there is a saved dataset
ivan-aksamentov Nov 7, 2023
5c8814d
Revert "feat(web): ui-3: redirect to dataset page if there is a saved…
ivan-aksamentov Nov 7, 2023
59e66a1
feat(web): redirect to dataset page when there is a saved dataset
ivan-aksamentov Nov 8, 2023
1cf7afe
feat(web): allow autosuggest page as entry point
ivan-aksamentov Nov 8, 2023
dc847f8
chore: remove vercel rewrites
ivan-aksamentov Nov 8, 2023
e270cbc
feat(web): add search box autofocus
ivan-aksamentov Nov 8, 2023
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
1 change: 1 addition & 0 deletions packages_rs/nextclade-web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@
"react-resize-detector": "7.0.0",
"react-select": "5.3.0",
"react-toggle": "4.1.2",
"react-use-wizard": "2.2.3",
"react-virtualized-auto-sizer": "1.0.6",
"react-virtualized-select": "3.1.3",
"react-window": "1.8.7",
Expand Down
7 changes: 7 additions & 0 deletions packages_rs/nextclade-web/src/components/About/About.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import React from 'react'

import AboutContent from './AboutContent.mdx'

export function About() {
return <AboutContent />
}
43 changes: 43 additions & 0 deletions packages_rs/nextclade-web/src/components/About/AboutContent.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { CladeSchema } from 'src/components/Main/CladeSchema.tsx'

## What is Nextclade?

Nextclade is a tool that performs genetic sequence alignment, clade assignment, mutation calling, phylogenetic placement, and quality checks for SARS-CoV-2, Influenza (Flu), Mpox (Monkeypox), Respiratory Syncytial Virus (RSV) and other pathogens.

Nextclade identifies differences between your sequences and a reference sequence, uses these differences to assign your sequences to clades, reports potential sequence quality issues in your data, and shows how the sequences are related to each other by placing them into an existing phylogenetic tree (we call it "phylogenetic placement"). You can use the tool to analyze sequences before you upload them to a database, or if you want to assign Nextstrain clades to a set of sequences.

To analyze your data, drag a fasta file onto the upload box or paste sequences into the text box. These sequences will then be analyzed in your browser - data never leave your computer. Since your computer is doing the work rather than a server, it is advisable to analyze at most a few hundred sequences at a time.

The Nextclade app and algorithms are opensource. The code is available on [GitHub](https://github.com/nextstrain/nextclade). The user manual is available at [docs.nextstrain.org/projects/nextclade](https://docs.nextstrain.org/projects/nextclade).


### What are the SARS-CoV-2 clades?

Nextclade was originally developed during COVID-19 pandemic, primarily focused on SARS-CoV-2. This section describes clades with application to SARS-CoV-2, but Nextclade can analyse other pathogens too.

<CladeSchema />

Since its emergence in late 2019, SARS-CoV-2 has diversified into several different co-circulating variants. To facilitate discussion of these variants, we have grouped them into __clades__ which are defined by specific signature mutations.

We currently define more than 30 clades (see [this blog post](https://nextstrain.org/blog/2021-01-06-updated-SARS-CoV-2-clade-naming) for details):

- 19A and 19B emerged in Wuhan and have dominated the early outbreak
- 20A emerged from 19A out of dominated the European outbreak in March and has since spread globally
- 20B and 20C are large genetically distinct subclades 20A emerged in early 2020
- 20D to 20J have emerged over the summer of 2020 and include three "Variants of Concern" (VoC).
- 21A to 21F include the VoC __delta__ and several Variants of Interest (VoI).
- 21K onwards are different clades within the diverse VoC __omicron__.

Within Nextstrain, we define each clade by its combination of signature mutations. You can find the exact clade definition in [github.com/nextstrain/ncov/defaults/clades.tsv](https://github.com/nextstrain/ncov/blob/master/defaults/clades.tsv). When available, we will include [WHO labels for VoCs and VoIs](https://www.who.int/en/activities/tracking-SARS-CoV-2-variants/).

Learn more about how Nextclade assigns clades in the [documentation](https://docs.nextstrain.org/projects/nextclade/en/stable/user/algorithm/).

### Other pathogens

Besides SARS-CoV-2, we provide Nextclade datasets to analyze the following other pathogens:

* Seasonal Influenza viruses (HA and NA for A/H3N2, A/H1N1pdm, B/Vic, and B/Yam)
* Mpox virus (the overall clade structure, as well as fine-grained lineages within the recent sustained human-to-human transmission)
* Respiratory Syncytial Virus (RSV) (subtypes A and B)

You can also put together your own dataset to analyse other pathogens.
8 changes: 2 additions & 6 deletions packages_rs/nextclade-web/src/components/Common/Markdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,21 @@ import ReactMarkdown from 'react-markdown'
import remarkGfm from 'remark-gfm'
import rehypeRaw from 'rehype-raw'
import rehypeSanitize from 'rehype-sanitize'
import { LinkExternal } from 'src/components/Link/LinkExternal'
import { useAxiosQuery } from 'src/helpers/useAxiosQuery'
import { LOADING } from 'src/components/Loading/Loading'
import { mdxComponents } from 'src/mdx-components'

const REMARK_PLUGINS = [remarkGfm]

const REHYPE_PLUGINS = [rehypeRaw, rehypeSanitize]

const MD_COMPONENTS = {
a: LinkExternal,
}

export interface MarkdownProps {
content: string
}

export function Markdown({ content }: MarkdownProps) {
return (
<ReactMarkdown rehypePlugins={REHYPE_PLUGINS} remarkPlugins={REMARK_PLUGINS} components={MD_COMPONENTS}>
<ReactMarkdown rehypePlugins={REHYPE_PLUGINS} remarkPlugins={REMARK_PLUGINS} components={mdxComponents}>
{content}
</ReactMarkdown>
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ export function SearchBox({ searchTitle, searchTerm, onSearchTermChange, ...rest
<IconSearch size={25} />
</IconSearchWrapper>
<Input
autoFocus
type="text"
title={searchTitle}
placeholder={searchTitle}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export function FilePickerAdvanced() {
<Row noGutters>
<Col>
<FilePicker
className="my-3"
className="mb-2"
compact
icon={iconJson}
title={t('Reference tree')}
Expand Down
26 changes: 23 additions & 3 deletions packages_rs/nextclade-web/src/components/Layout/NavigationBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import { BsCaretRightFill as ArrowRight } from 'react-icons/bs'
import { Link } from 'src/components/Link/Link'
import { FaDocker, FaGithub, FaXTwitter, FaDiscourse } from 'react-icons/fa6'
import { LinkSmart } from 'src/components/Link/LinkSmart'
import { isInSuggestModeAtom } from 'src/state/autodetect.state'
import { datasetCurrentAtom } from 'src/state/dataset.state'
import { ResultsStatus } from 'src/components/Results/ResultsStatus'
import { canDownloadAtom, hasRanAtom, hasTreeAtom } from 'src/state/results.state'
import styled, { useTheme } from 'styled-components'
Expand Down Expand Up @@ -133,35 +135,53 @@ export function NavigationBar() {

const hasTree = useRecoilValue(hasTreeAtom)
const hasRan = useRecoilValue(hasRanAtom)
const isInSuggestMode = useRecoilValue(isInSuggestModeAtom)
const dataset = useRecoilValue(datasetCurrentAtom)
const canDownload = useRecoilValue(canDownloadAtom)

const linksLeft = useMemo(() => {
return [
{ url: '/', content: t('Start'), title: t('Show start page') },
{
url: '/dataset-suggest',
content: t('Suggest'),
title: t('Show dataset suggestion page'),
hidden: !isInSuggestMode,
},
{
url: dataset ? '/dataset' : undefined,
content: t('Dataset'),
title: dataset ? t('Show dataset selection page') : t('Please select dataset first'),
},
{
url: hasRan ? '/results' : undefined,
content: t('Results'),
title: hasRan ? t('Show analysis results table') : t('Please run the analysis first'),
title: hasRan ? t('Show analysis results table') : t('Please select a dataset and run the analysis first'),
},
{
url: hasTree ? '/tree' : undefined,
content: t('Tree'),
title: hasTree ? t('Show phylogenetic tree') : t('Please run the analysis on a dataset with reference tree'),
title: hasTree
? t('Show phylogenetic tree')
: t('Please select a dataset with reference tree and run the analysis first'),
},
{
url: canDownload ? '/export' : undefined,
content: t('Export'),
title: canDownload ? t('Export results') : t('Please run the analysis first.'),
},
].map((desc, i) => {
if (desc.hidden) {
return null
}
const link = <NavLinkBreadcrumb key={desc.url ?? desc.title} desc={desc} active={pathname === desc.url} />
if (i === 0) {
return [link]
}
const arrow = <BreadcrumbArrow key={`arrow-${desc.url ?? desc.title}`} disabled={!desc.url} />
return [arrow, link]
})
}, [canDownload, hasRan, hasTree, pathname, t])
}, [canDownload, dataset, hasRan, hasTree, isInSuggestMode, pathname, t])

const linksRight = useMemo(() => {
return [
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import React, { useCallback, useMemo, useState } from 'react'
import styled from 'styled-components'
import { Layout } from 'src/components/Layout/Layout'
import { Dataset } from '_SchemaRoot'
import { useRouter } from 'next/router'
import { useRecoilState, useRecoilValue } from 'recoil'
import { FaChevronLeft as IconLeft, FaChevronRight as IconRight } from 'react-icons/fa6'
import {
FlexCol,
FlexRow,
Footer,
WizardContainer,
WizardMain,
WizardNavigationButton,
WizardNavigationForm,
} from 'src/components/Main/Wizard'
import { QuerySequenceFilePicker } from 'src/components/Main/QuerySequenceFilePicker'
import { useTranslationSafe } from 'src/helpers/useTranslationSafe'
import { hasAutodetectResultsAtom } from 'src/state/autodetect.state'
import { datasetCurrentAtom } from 'src/state/dataset.state'
import { globalErrorAtom } from 'src/state/error.state'
import { DatasetAutosuggestionResultsList } from 'src/components/Main/DatasetSelector'

const Main = styled.div`
display: flex;
flex: 1 1 100%;
overflow: hidden;
padding: 0;
margin: 0 auto;

width: 100%;
max-width: 1400px;
`

export function DatasetAutodetectPage() {
return (
<Layout>
<Main>
<WizardAuto />
</Main>
</Layout>
)
}

export function WizardAuto() {
const { t } = useTranslationSafe()
const { push } = useRouter()
const [dataset, setDataset] = useRecoilState(datasetCurrentAtom)
const [datasetHighlighted, setDatasetHighlighted] = useState<Dataset | undefined>(dataset)

const onPrevious = useCallback(() => {
void push('/') // eslint-disable-line no-void
}, [push])

const onNext = useCallback(() => {
setDataset(datasetHighlighted)
void push('/dataset') // eslint-disable-line no-void
}, [datasetHighlighted, push, setDataset])

const hasAutodetectResults = useRecoilValue(hasAutodetectResultsAtom)
const hasErrors = !!useRecoilValue(globalErrorAtom)

const { isRunButtonDisabled, runButtonColor, runButtonTooltip } = useMemo(() => {
const hasDatasetHighlighted = !!datasetHighlighted
const isRunButtonDisabled = !hasAutodetectResults || !hasDatasetHighlighted || hasErrors
return {
isRunButtonDisabled,
runButtonColor: isRunButtonDisabled ? 'secondary' : 'success',
runButtonTooltip: isRunButtonDisabled
? t('Please provide sequence data and select one of the datasets')
: t('Go to the next step!'),
}
}, [datasetHighlighted, hasAutodetectResults, hasErrors, t])

return (
<WizardContainer>
<WizardMain>
<FlexRow noGutters>
<FlexCol lg={6}>
<DatasetAutosuggestionResultsList
datasetHighlighted={datasetHighlighted}
onDatasetHighlighted={setDatasetHighlighted}
/>
</FlexCol>
<FlexCol lg={6}>
<QuerySequenceFilePicker />
</FlexCol>
</FlexRow>
</WizardMain>
<Footer>
<WizardNavigationForm>
<WizardNavigationButton color="danger" className="mr-auto" onClick={onPrevious}>
<IconLeft size={15} className="mr-1" />
{t('Previous')}
</WizardNavigationButton>

<WizardNavigationButton
className="ml-auto"
disabled={isRunButtonDisabled}
color={runButtonColor}
onClick={onNext}
title={runButtonTooltip}
>
{t('Next')}
<IconRight size={15} className="ml-1" />
</WizardNavigationButton>
</WizardNavigationForm>
</Footer>
</WizardContainer>
)
}
Loading