Skip to content

Commit

Permalink
feat(web): re-add readme and changelog to the dataset screen
Browse files Browse the repository at this point in the history
  • Loading branch information
ivan-aksamentov committed Sep 28, 2023
1 parent eb566e1 commit 64e5578
Show file tree
Hide file tree
Showing 12 changed files with 403 additions and 205 deletions.
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
@@ -1,14 +1,13 @@
import classnames from 'classnames'
import React, { PropsWithChildren, useCallback, useMemo, useRef, useState } from 'react'
import React, { PropsWithChildren, useCallback, useState } from 'react'
import styled from 'styled-components'
import {
Nav as NavBase,
NavItem as NavItemBase,
NavLink as NavLinkBase,
TabPane,
TabPane as TabPaneBase,
TabContent as TabContentBase,
NavItemProps,
TabPaneProps,
} from 'reactstrap'
import { useRecoilValue } from 'recoil'
import { MarkdownRemote } from 'src/components/Common/Markdown'
Expand All @@ -27,14 +26,13 @@ export function DatasetContentSection() {
{'CHANGELOG.md'}
</TabLabel>
</Nav>

<TabContent activeTab={activeTabId}>
<TabContentPane tabId={0} activeTabId={activeTabId}>
<TabPane tabId={0}>
{currentDataset?.files.readme && <MarkdownRemote url={currentDataset?.files.readme} />}
</TabContentPane>
<TabContentPane tabId={1} activeTabId={activeTabId}>
</TabPane>
<TabPane tabId={1}>
{currentDataset?.files.changelog && <MarkdownRemote url={currentDataset?.files.changelog} />}
</TabContentPane>
</TabPane>
</TabContent>
</ContentSection>
)
Expand All @@ -58,40 +56,34 @@ export function TabLabel({ tabId, activeTabId, setActiveTabId, children, ...rest
)
}

export interface TabContentPaneProps extends PropsWithChildren<TabPaneProps> {
tabId: number
activeTabId: number
}
const ContentSection = styled.div`
display: flex;
flex: 1;
flex-direction: column;
overflow: hidden;
`

export function TabContentPane({ tabId, activeTabId, children, ...rest }: TabContentPaneProps) {
const active = activeTabId === tabId
return (
<LazyRender visible={active}>
<TabPane tabId={0} {...rest}>
{children}
</TabPane>
</LazyRender>
)
}
const TabContent = styled(TabContentBase)`
display: flex;
flex: 1;
flex-direction: column;
overflow: hidden;
`

const TabPane = styled(TabPaneBase)`
display: flex;
flex: 1;
flex-direction: column;
overflow: auto;
border: 1px #ccc9 solid;
border-radius: 5px;
padding: 1rem;
`

export interface LazyProps {
visible: boolean
}

export function LazyRender({ visible, children }: PropsWithChildren<LazyProps>) {
const rendered = useRef(visible)
const style = useMemo(() => ({ display: visible ? 'block' : 'none' }), [visible])
if (visible && !rendered.current) {
rendered.current = true
}
if (!rendered.current) return null
return <div style={style}>{children}</div>
}

const ContentSection = styled.div`
max-width: 100%;
`

const Nav = styled(NavBase)`
border-bottom: 0 !important;
`
Expand Down Expand Up @@ -135,8 +127,3 @@ const NavItem = styled(NavItemBase)`
const NavLink = styled(NavLinkBase)`
color: ${(props) => props.theme.bodyColor};
`

const TabContent = styled(TabContentBase)`
border: #ddd 1px solid;
margin-top: -1px;
`
191 changes: 78 additions & 113 deletions packages_rs/nextclade-web/src/components/Main/DatasetCurrent.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,13 @@
import { isNil } from 'lodash'
import React, { useCallback, useMemo, useState } from 'react'
import { Button, Col, Collapse, Row, UncontrolledAlert } from 'reactstrap'
import { useRecoilState, useRecoilValue, useResetRecoilState, useSetRecoilState } from 'recoil'
import React, { useCallback } from 'react'
import { Button, Col, Row } from 'reactstrap'
import { useRecoilValue, useResetRecoilState } from 'recoil'
import { DatasetContentSection } from 'src/components/Main/DatasetContentSection'
import styled from 'styled-components'
import { useUpdatedDataset } from 'src/io/fetchDatasets'
import { datasetCurrentAtom, datasetUpdatedAtom } from 'src/state/dataset.state'
import { datasetCurrentAtom } from 'src/state/dataset.state'
import { useTranslationSafe } from 'src/helpers/useTranslationSafe'
import { ButtonCustomize } from 'src/components/Main/ButtonCustomize'
import { FilePickerAdvanced } from 'src/components/FilePicker/FilePickerAdvanced'
import { LinkExternal } from 'src/components/Link/LinkExternal'
import { DatasetInfo } from './DatasetInfo'
import AdvancedModeExplanationContent from './AdvancedModeExplanation.mdx'
import { DatasetInfo } from 'src/components/Main/DatasetInfo'
import { DatasetCurrentUpdateNotification } from 'src/components/Main/DatasetCurrentUpdateNotification'

export const CurrentDatasetInfoContainer = styled.div`
display: flex;
Expand All @@ -27,31 +24,45 @@ export const CurrentDatasetInfoHeader = styled.section`
const DatasetInfoH4 = styled.h4`
flex: 1;
margin: auto 0;
margin-top: 12px;
`

export const CurrentDatasetInfoBody = styled.section`
display: flex;
flex-direction: column;
margin: 0;
padding: 12px;
border: 1px #ccc9 solid;
border-radius: 5px;
height: 100%;
`

export const Left = styled.section`
flex: 1 1 auto;
const Container = styled.div`
display: flex;
flex: 1;
flex-direction: column;
overflow: hidden;
`

export const Right = styled.section`
flex: 0 0 250px;
const Header = styled.div`
flex: 0;
`

const Main = styled.div`
display: flex;
flex: 1;
flex-direction: column;
height: 100%;
overflow: auto;
width: 100%;
margin-top: 1rem;
`

export const ChangeButton = styled(Button)`
export const FlexLeft = styled.div`
flex: 1;
`

export const FlexRight = styled.div``

const ChangeButton = styled(Button)`
flex: 0 0 auto;
height: 2.1rem;
min-width: 100px;
Expand All @@ -72,37 +83,37 @@ export function DatasetCurrent() {
useUpdatedDataset()

const { t } = useTranslationSafe()
const [advancedOpen, setAdvancedOpen] = useState(false)
const datasetCurrent = useRecoilValue(datasetCurrentAtom)
const resetDatasetCurrent = useResetRecoilState(datasetCurrentAtom)

const onChangeClicked = useCallback(() => {
resetDatasetCurrent()
}, [resetDatasetCurrent])

const onCustomizeClicked = useCallback(() => setAdvancedOpen((advancedOpen) => !advancedOpen), [])

const customize = useMemo(() => {
if (datasetCurrent?.path === 'autodetect') {
return null
}

return (
<Row noGutters>
<Col>
<ButtonCustomize isOpen={advancedOpen} onClick={onCustomizeClicked} />

<Collapse isOpen={advancedOpen}>
<AdvancedModeExplanationWrapper>
<AdvancedModeExplanationContent />
</AdvancedModeExplanationWrapper>

<FilePickerAdvanced />
</Collapse>
</Col>
</Row>
)
}, [advancedOpen, datasetCurrent?.path, onCustomizeClicked])
// const [advancedOpen, setAdvancedOpen] = useState(false)
// const onCustomizeClicked = useCallback(() => setAdvancedOpen((advancedOpen) => !advancedOpen), [])

// const customize = useMemo(() => {
// if (datasetCurrent?.path === 'autodetect') {
// return null
// }
//
// return (
// <Row noGutters>
// <Col>
// <ButtonCustomize isOpen={advancedOpen} onClick={onCustomizeClicked} />
//
// <Collapse isOpen={advancedOpen}>
// <AdvancedModeExplanationWrapper>
// <AdvancedModeExplanationContent />
// </AdvancedModeExplanationWrapper>
//
// <FilePickerAdvanced />
// </Collapse>
// </Col>
// </Row>
// )
// }, [advancedOpen, datasetCurrent?.path, onCustomizeClicked])

if (!datasetCurrent) {
return null
Expand All @@ -113,77 +124,31 @@ export function DatasetCurrent() {
<CurrentDatasetInfoHeader>
<DatasetInfoH4>{t('Selected pathogen')}</DatasetInfoH4>
</CurrentDatasetInfoHeader>

<CurrentDatasetInfoBody>
<DatasetCurrentUpdateNotification />

<Row noGutters>
<Col className="d-flex flex-row">
<Left>
<DatasetInfo dataset={datasetCurrent} />
</Left>

<Right>
<ChangeButton type="button" color="secondary" onClick={onChangeClicked}>
{t('Change')}
</ChangeButton>
</Right>
</Col>
</Row>

{customize}
</CurrentDatasetInfoBody>
<Container>
<Header>
<CurrentDatasetInfoBody>
<DatasetCurrentUpdateNotification />

<Row noGutters className="w-100">
<Col className="d-flex w-100">
<FlexLeft>
<DatasetInfo dataset={datasetCurrent} />
</FlexLeft>

<FlexRight>
<ChangeButton type="button" color="secondary" onClick={onChangeClicked}>
{t('Change')}
</ChangeButton>
</FlexRight>
</Col>
</Row>
</CurrentDatasetInfoBody>
</Header>

<Main>
<DatasetContentSection />
</Main>
</Container>
</CurrentDatasetInfoContainer>
)
}

function DatasetCurrentUpdateNotification() {
const { t } = useTranslationSafe()
const [datasetUpdated, setDatasetUpdated] = useRecoilState(datasetUpdatedAtom)
const setDatasetCurrent = useSetRecoilState(datasetCurrentAtom)

const onDatasetUpdateClicked = useCallback(() => {
setDatasetCurrent(datasetUpdated)
setDatasetUpdated(undefined)
}, [datasetUpdated, setDatasetCurrent, setDatasetUpdated])

if (isNil(datasetUpdated)) {
return null
}

return (
<Row noGutters>
<Col>
<UncontrolledAlert closeClassName="d-none" fade={false} color="info" className="mx-1 py-2 px-2 d-flex w-100">
<AlertTextWrapper>
<p className="my-0">{t('A new version of this dataset is available.')}</p>
<p className="my-0">
<LinkExternal href="https://github.com/nextstrain/nextclade_data/blob/release/CHANGELOG.md">
{"What's new?"}
</LinkExternal>
</p>
</AlertTextWrapper>

<AlertButtonWrapper>
<ChangeButton
type="button"
color="info"
title={t('Accept the updated dataset')}
onClick={onDatasetUpdateClicked}
>
{t('Update')}
</ChangeButton>
</AlertButtonWrapper>
</UncontrolledAlert>
</Col>
</Row>
)
}

const AlertTextWrapper = styled.div`
flex: 1;
`

const AlertButtonWrapper = styled.div`
flex: 0;
`
Loading

0 comments on commit 64e5578

Please sign in to comment.