Skip to content

Commit

Permalink
add helper panel, add publication/creation date logic, add tooltips t…
Browse files Browse the repository at this point in the history
…o analysis details, add section descriptions, ensure hash links work with `document.querySelector`, add preliminary logic for scrolling to lazy loaded pieces,
  • Loading branch information
NickAkhmetov committed Aug 1, 2024
1 parent dea3cb2 commit 4d88066
Show file tree
Hide file tree
Showing 22 changed files with 416 additions and 85 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,20 @@ function AnalysisDetails({ dagListData }: AnalysisDetails) {

return (
<div>
{ingestPipelines.length > 0 && <AnalysisDetailsList pipelines={ingestPipelines} pipelineType="Ingest" />}
{cwlPipelines.length > 0 && <AnalysisDetailsList pipelines={cwlPipelines} pipelineType="CWL" />}
{ingestPipelines.length > 0 && (
<AnalysisDetailsList
pipelines={ingestPipelines}
pipelineType="Ingest"
tooltip="Supplementary links for the data ingestion pipelines for this dataset"
/>
)}
{cwlPipelines.length > 0 && (
<AnalysisDetailsList
pipelines={cwlPipelines}
pipelineType="CWL"
tooltip="Supplementary links for the CWL (Common Workflow Language) pipelines for this dataset"
/>
)}
</div>
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React from 'react';
import List from '@mui/material/List';
import Typography from '@mui/material/Typography';

import InfoTooltipIcon from 'js/shared-styles/icons/TooltipIcon';
import ProvAnalysisDetailsLink from './AnalysisDetailsLink';

interface ProvAnalysisDetailsListProps {
Expand All @@ -11,12 +12,15 @@ interface ProvAnalysisDetailsListProps {
origin: string;
}[];
pipelineType: string;
tooltip?: string;
}

function ProvAnalysisDetailsList({ pipelines, pipelineType }: ProvAnalysisDetailsListProps) {
function ProvAnalysisDetailsList({ pipelines, pipelineType, tooltip }: ProvAnalysisDetailsListProps) {
return (
<>
<Typography variant="subtitle2">{`${pipelineType} Pipelines`}</Typography>
<Typography variant="subtitle2">
{`${pipelineType} Pipelines`} <InfoTooltipIcon iconTooltipText={tooltip} />
</Typography>
<List data-testid={pipelineType}>
{pipelines.map((item) => (
<ProvAnalysisDetailsLink
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,20 @@ import React from 'react';

import SectionHeader from 'js/shared-styles/sections/SectionHeader';
import { DetailPageSection } from 'js/components/detailPage/style';
import { useFlaskDataContext } from 'js/components/Contexts';
import { FilesContextProvider } from 'js/components/detailPage/files/FilesContext';
import BulkDataTransferPanels from './BulkDataTransferPanels';
import { StyledContainer } from './style';
import { SectionDescription } from '../ProcessedData/ProcessedDataset/SectionDescription';

function BulkDataTransfer() {
const {
entity: { entity_type },
} = useFlaskDataContext();

return (
<FilesContextProvider>
<DetailPageSection id="bulk-data-transfer" data-testid="bulk-data-transfer">
<SectionHeader
iconTooltipText={`Information about how to bulk download all files related to this ${entity_type?.toLowerCase()}.`}
>
Bulk Data Transfer
</SectionHeader>
<SectionHeader>Bulk Data Transfer</SectionHeader>
<SectionDescription>
This section explains how to download data in bulk from raw and processed datasets. Processed datasets have
separate download directories in Globus or dbGaP, distinct from the raw dataset.
</SectionDescription>
<StyledContainer>
<BulkDataTransferPanels />
</StyledContainer>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ import React from 'react';

import SectionHeader from 'js/shared-styles/sections/SectionHeader';
import PanelList from 'js/shared-styles/panels/PanelList';
import { useFlaskDataContext } from 'js/components/Contexts';
import { DetailPageSection } from 'js/components/detailPage/style';
import { buildCollectionsPanelsProps } from 'js/pages/Collections/utils';
import { CollectionHit } from 'js/pages/Collections/types';
import { SectionDescription } from '../ProcessedData/ProcessedDataset/SectionDescription';

interface CollectionsSectionProps {
collectionsData: CollectionHit[];
Expand All @@ -14,15 +14,13 @@ interface CollectionsSectionProps {
function CollectionsSection({ collectionsData }: CollectionsSectionProps) {
const panelsProps = buildCollectionsPanelsProps(collectionsData);

const {
entity: { entity_type },
} = useFlaskDataContext();

return (
<DetailPageSection id="collections">
<SectionHeader iconTooltipText={`List of collections that contain this ${entity_type?.toLowerCase()}.`}>
Collections
</SectionHeader>
<SectionHeader>Collections</SectionHeader>
<SectionDescription>
Collections may contain references to either raw or processed datasets. If a processed dataset is not included
in any collection, there will be no corresponding tabs in the table below.
</SectionDescription>
<PanelList panelsProps={panelsProps} />
</DetailPageSection>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import Typography from '@mui/material/Typography';
import { AccountTreeRounded, ExtensionRounded, SvgIconComponent } from '@mui/icons-material';
import Skeleton from '@mui/material/Skeleton';
import { Box } from '@mui/system';
import { formatSectionHash } from 'js/shared-styles/sections/TableOfContents/utils';
import StatusIcon from '../StatusIcon';
import { usePipelineInfo } from './hooks';

Expand Down Expand Up @@ -126,7 +127,7 @@ function ProcessedDatasetNode({ data }: NodeProps<ProcessedDatasetNodeProps>) {
<NodeTemplate
rounded
target
href={`#${data.name}-section`}
href={formatSectionHash(`#section-${data.name}`)}
icon={nodeIcons.processedDataset}
bgColor={nodeColors.processedDataset}
{...data}
Expand All @@ -144,7 +145,7 @@ function ComponentDatasetNode({ data }: NodeProps<ComponentDatasetNodeProps>) {
rounded
target
icon={nodeIcons.componentDataset}
href={`#${data.name}-section`}
href={formatSectionHash(`#section-${data.name}`)}
bgColor={nodeColors.componentDataset}
{...data}
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import Stack from '@mui/material/Stack';
import useEntityStore, { savedAlertStatus, editedAlertStatus, EntityStore } from 'js/stores/useEntityStore';
import TableOfContents from 'js/shared-styles/sections/TableOfContents';
import { TableOfContentsItems } from 'js/shared-styles/sections/TableOfContents/types';
import { leftRouteBoundaryID } from 'js/components/Routes/Route/Route';
import { leftRouteBoundaryID, rightRouteBoundaryID } from 'js/components/Routes/Route/Route';
import { SectionOrder, getSections } from 'js/shared-styles/sections/TableOfContents/utils';
import { StyledAlert } from './style';

Expand Down Expand Up @@ -33,6 +33,14 @@ function TableOfContentsPortal({ items, isLoading = false }: { items: TableOfCon
);
}

export function HelperPanelPortal({ children }: PropsWithChildren) {
const element = document.getElementById(rightRouteBoundaryID);
if (!element) {
return null;
}
return createPortal(children, element);
}

function DetailAlert() {
const { shouldDisplaySavedOrEditedAlert, setShouldDisplaySavedOrEditedAlert } = useEntityStore(entityStoreSelector);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
import React, { PropsWithChildren } from 'react';
import Stack from '@mui/material/Stack';
import Typography from '@mui/material/Typography';
import { formatDate } from 'date-fns/format';
import Divider from '@mui/material/Divider';
import Box from '@mui/system/Box';
import { useIsDesktop } from 'js/hooks/media-queries';
import SchemaRounded from '@mui/icons-material/SchemaRounded';
import { WorkspacesIcon } from 'js/shared-styles/icons';
import { CloudDownloadRounded } from '@mui/icons-material';
import { useAppContext } from 'js/components/Contexts';
import { formatSectionHash } from 'js/shared-styles/sections/TableOfContents/utils';
import { HelperPanelPortal } from '../../DetailLayout/DetailLayout';
import useProcessedDataStore from '../store';
import StatusIcon from '../../StatusIcon';
import { getDateLabelAndValue } from '../../utils';
import { HelperPanelButton } from './styles';

function useCurrentDataset() {
return useProcessedDataStore((state) => state.currentDataset);
}

function HelperPanelHeader() {
const currentDataset = useCurrentDataset();
return (
<Typography variant="subtitle2" display="flex" alignItems="center" gap={0.5} whiteSpace="nowrap">
<SchemaRounded fontSize="small" />
<a href={formatSectionHash(`#section-${currentDataset?.hubmap_id}`)}>{currentDataset?.hubmap_id}</a>
</Typography>
);
}

function HelperPanelStatus() {
const currentDataset = useCurrentDataset();
if (!currentDataset) {
return null;
}
return (
<Stack direction="row" alignItems="center">
<StatusIcon status={currentDataset.status} />
<Typography variant="body2">{currentDataset.status}</Typography>
</Stack>
);
}

interface HelperPanelBodyItemProps extends PropsWithChildren {
label: string;
noWrap?: boolean;
}

const noWrapStyles = {
overflow: 'hidden',
textOverflow: 'ellipsis',
display: '-webkit-box',
WebkitLineClamp: 2,
WebkitBoxOrient: 'vertical',
};

function HelperPanelBodyItem({ label, children, noWrap }: HelperPanelBodyItemProps) {
const valueStyles = noWrap ? noWrapStyles : {};
return (
<Stack direction="column">
<Typography variant="overline">{label}</Typography>
<Typography variant="body2" sx={valueStyles}>
{children}
</Typography>
</Stack>
);
}

function HelperPanelBody() {
const currentDataset = useCurrentDataset();
if (!currentDataset) {
return null;
}
const [dateLabel, date] = getDateLabelAndValue(currentDataset);
return (
<>
{currentDataset.title && <HelperPanelBodyItem label="Title">{currentDataset.title}</HelperPanelBodyItem>}
{currentDataset.description && (
<HelperPanelBodyItem label="Description" noWrap>
{currentDataset.description}
</HelperPanelBodyItem>
)}
<HelperPanelBodyItem label="Pipeline">{currentDataset.pipeline}</HelperPanelBodyItem>
<HelperPanelBodyItem label="Consortium">{currentDataset.group_name}</HelperPanelBodyItem>
<HelperPanelBodyItem label={dateLabel}>{date && formatDate(date, 'yyyy-MM-dd')}</HelperPanelBodyItem>
</>
);
}

function HelperPanelActions() {
const currentDataset = useCurrentDataset();
// TODO: Add workspace actions/dropdown menu
const { isWorkspacesUser } = useAppContext();
return (
<>
{isWorkspacesUser && <HelperPanelButton startIcon={<WorkspacesIcon />}>Workspace</HelperPanelButton>}
<HelperPanelButton
startIcon={<CloudDownloadRounded />}
href={`#bulk-data-transfer?bulk-data=${currentDataset?.hubmap_id}`}
>
Bulk Download
</HelperPanelButton>
</>
);
}

export default function HelperPanel() {
const currentDataset = useCurrentDataset();
// const panelOffset = useProcessedDataStore((state) => state.currentDatasetOffset);
const isDesktop = useIsDesktop();
if (!currentDataset || !isDesktop) {
return null;
}
return (
<HelperPanelPortal>
<Box position="relative" height="100%">
<Stack
position="fixed"
bottom="50%"
direction="column"
maxWidth="12rem"
padding={1}
gap={1}
bgcolor="secondaryContainer.main"
boxShadow={2}
>
<HelperPanelHeader />
<Divider />
<HelperPanelStatus />
<HelperPanelBody />
<HelperPanelActions />
</Stack>
</Box>
</HelperPanelPortal>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import HelperPanel from './HelperPanel';

export default HelperPanel;
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import React from 'react';

import { styled } from '@mui/material/styles';
import Button, { ButtonProps } from '@mui/material/Button';

export const HelperPanelButton = styled((props: ButtonProps) => <Button {...props} variant="outlined" />)(
({ theme }) => ({
backgroundColor: 'white',
borderRadius: theme.spacing(0.5),
whiteSpace: 'nowrap',
}),
);
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
import React from 'react';
import { useProcessedDatasets } from 'js/pages/Dataset/hooks';
import SectionHeader from 'js/shared-styles/sections/SectionHeader';
import { DetailSectionPaper } from 'js/shared-styles/surfaces';
import Stack from '@mui/material/Stack';
import Typography from '@mui/material/Typography';
import { InfoIcon } from 'js/shared-styles/icons';
import LabelledSectionText from 'js/shared-styles/sections/LabelledSectionText';
import { DetailPageSection } from '../style';
import ProcessedDataset from './ProcessedDataset';
import { SectionDescription } from './ProcessedDataset/SectionDescription';
import HelperPanel from './HelperPanel';

function ProcessedDataSection() {
const processedDatasets = useProcessedDatasets();
Expand All @@ -18,20 +16,14 @@ function ProcessedDataSection() {
return (
<DetailPageSection id="processed-data" data-testid="processed-data">
<SectionHeader>Processed Data</SectionHeader>
<DetailSectionPaper>
<Stack direction="column" gap={1}>
<Stack direction="row" gap={0.5}>
<InfoIcon color="primary" fontSize="1.5rem" />
<Typography variant="body1">
This section contains the results of any additional analyses performed on this dataset. Additional data
may include visualizations and essential data files (data products). Analysis results could be generated
from consortium standardized pipelines or by external groups, and may have been generated independently
from the primary data submitted by the original group.
</Typography>
</Stack>
<LabelledSectionText label={pipelinesText}>{pipelines.join(', ')}</LabelledSectionText>
</Stack>
</DetailSectionPaper>
<SectionDescription
addendum={<LabelledSectionText label={pipelinesText}>{pipelines.join(', ')}</LabelledSectionText>}
>
This section contains the results of any additional analyses performed on this dataset. Additional data may
include visualizations and essential data files (data products). Analysis results could be generated from
consortium standardized pipelines or by external groups, and may have been generated independently from the
primary data submitted by the original group.
</SectionDescription>
{processedDatasets.searchHits.map((dataset) => (
<ProcessedDataset
dataset={dataset._source}
Expand All @@ -40,6 +32,7 @@ function ProcessedDataSection() {
isLoading={processedDatasets.isLoading}
/>
))}
<HelperPanel />
</DetailPageSection>
);
}
Expand Down
Loading

0 comments on commit 4d88066

Please sign in to comment.