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

feat(web): add link to view reference tree on nextstrain org #1544

Merged
merged 4 commits into from
Nov 4, 2024
Merged
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
41 changes: 34 additions & 7 deletions packages/nextclade-web/src/components/Main/ButtonLoadExample.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { Dataset } from '_SchemaRoot'
import { isEmpty } from 'lodash'
import React, { useCallback } from 'react'
import React, { useCallback, useMemo } from 'react'
import { Button } from 'reactstrap'
import { useRecoilValue } from 'recoil'
import { useTranslationSafe } from 'src/helpers/useTranslationSafe'
Expand All @@ -11,6 +10,7 @@ import { datasetCurrentAtom } from 'src/state/dataset.state'
import { hasInputErrorsAtom } from 'src/state/error.state'
import { useQuerySeqInputs } from 'src/state/inputs.state'
import { shouldRunAutomaticallyAtom, shouldSuggestDatasetsOnDatasetPageAtom } from 'src/state/settings.state'
import styled from 'styled-components'

export function useSetExampleSequences() {
const { addQryInputs } = useQuerySeqInputs()
Expand Down Expand Up @@ -45,13 +45,40 @@ export function ButtonLoadExample({ ...rest }) {
setExampleSequences(datasetCurrent)
}, [datasetCurrent, setExampleSequences])

if (isEmpty(datasetCurrent?.files?.examples)) {
return null
}
const title = useMemo(
() =>
datasetCurrent?.files?.examples
? t('Load example sequence data (for demonstration)')
: t('There is no example data in this dataset'),
[datasetCurrent?.files?.examples, t],
)

return (
<Button {...rest} color="link" onClick={onClick} disabled={hasInputErrors || !datasetCurrent}>
<ButtonStyled
{...rest}
color="link"
onClick={onClick}
title={title}
disabled={!datasetCurrent?.files?.examples || hasInputErrors || !datasetCurrent}
>
{t('Load example')}
</Button>
</ButtonStyled>
)
}

const ButtonStyled = styled(Button)`
margin: 0 0.5rem;
max-width: 300px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;

&:disabled {
text-decoration: none !important;
pointer-events: all !important;
cursor: not-allowed !important;
transition: none !important;
}

transition: none !important;
`
11 changes: 11 additions & 0 deletions packages/nextclade-web/src/components/Main/DatasetCurrent.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import React from 'react'
import { Col, Row, Container as ContainerBase } from 'reactstrap'
import { useRecoilValue } from 'recoil'
import { ButtonLoadExample } from 'src/components/Main/ButtonLoadExample'
import { ButtonRun } from 'src/components/Main/ButtonRun'
import { LinkOpenTree } from 'src/components/Main/LinkOpenTree'
import { useRunAnalysis } from 'src/hooks/useRunAnalysis'
import styled from 'styled-components'
import { useUpdatedDataset } from 'src/io/fetchDatasets'
Expand Down Expand Up @@ -87,6 +89,15 @@ export function DatasetCurrent() {
<DatasetInfo dataset={dataset} />
</Col>
</Row>

<Row noGutters className="d-flex w-100">
<Col className="d-flex">
<div className="d-flex ml-auto">
<LinkOpenTree className="my-auto" dataset={dataset} />
<ButtonLoadExample />
</div>
</Col>
</Row>
</CurrentDatasetInfoBody>
</Header>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { MdClear as IconClearBase } from 'react-icons/md'
import { ButtonTransparent } from 'src/components/Common/ButtonTransparent'
import { ButtonLoadExample } from 'src/components/Main/ButtonLoadExample'
import { DatasetCustomizationsIndicatorLink } from 'src/components/Main/DatasetCustomizationIndicator'
import { LinkOpenTree } from 'src/components/Main/LinkOpenTree'
import { useTranslationSafe } from 'src/helpers/useTranslationSafe'
import styled from 'styled-components'
import { useUpdatedDataset } from 'src/io/fetchDatasets'
Expand Down Expand Up @@ -51,9 +52,13 @@ export function DatasetCurrentSummary() {
<DatasetCustomizationsIndicatorLink />
</DatasetCustomizationWrapper>
</Col>

<Col className="d-flex ml-auto mt-2">
<ButtonLoadExample className="ml-auto" />
</Row>
<Row noGutters className="d-flex w-100">
<Col className="d-flex">
<div className="d-flex ml-auto">
<LinkOpenTree className="my-auto" dataset={dataset} />
<ButtonLoadExample />
</div>
</Col>
</Row>
</Col>
Expand Down
63 changes: 63 additions & 0 deletions packages/nextclade-web/src/components/Main/LinkOpenTree.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import classNames from 'classnames'
import type { Dataset } from 'src/types'
import React, { HTMLProps, useMemo } from 'react'
import { LinkExternal } from 'src/components/Link/LinkExternal'
import { useTranslationSafe } from 'src/helpers/useTranslationSafe'
import styled from 'styled-components'
import urljoin from 'url-join'

export interface LinkOpenTreeProps extends HTMLProps<HTMLDivElement> {
dataset: Dataset
}

export function LinkOpenTree({ dataset }: LinkOpenTreeProps) {
const { t } = useTranslationSafe()
const nextstrainOrgTreeUrl = useMemo(() => {
if (!dataset.files?.treeJson) {
return undefined
}
const path = dataset.files.treeJson.replace(/^https?:\/\//, '')
return urljoin('https://nextstrain.org/fetch', path)
}, [dataset.files?.treeJson])

const title = useMemo(
() =>
nextstrainOrgTreeUrl
? t('Open reference tree for this dataset on nextstrain.org')
: t('There is no reference tree in this dataset'),
[nextstrainOrgTreeUrl, t],
)

return (
<LinkContainer className="mx-2">
<LinkExternalStyled
className={classNames(!nextstrainOrgTreeUrl && 'disabled')}
title={title}
href={nextstrainOrgTreeUrl}
>
{t('Open tree')}
</LinkExternalStyled>
</LinkContainer>
)
}

const LinkContainer = styled.div`
margin: auto 0;
max-width: 200px;
overflow: hidden;
text-overflow: ellipsis;
`

const LinkExternalStyled = styled(LinkExternal)`
white-space: nowrap;

&.disabled {
color: #7b838a !important;
opacity: 0.65 !important;
text-decoration: none;
cursor: not-allowed !important;
transition: none !important;
}

transition: none !important;
`