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

NickAkhmetov/CAT-777 Processed Data section #3495

Merged
merged 52 commits into from
Aug 20, 2024

Conversation

NickAkhmetov
Copy link
Collaborator

@NickAkhmetov NickAkhmetov commented Aug 1, 2024

Summary

This PR implements the Processed Data section for unified views.

TODOs

  • Handle vislifted datasets by treating supports as processed datasets
  • Sort processed datasets - internal datasets on top, then sort by publication status
  • Handle height difference between data products and files to avoid layout shift - since this shift only occurs on user interaction, let's leave it alone for now and correct if needed.
  • Handle versioned datasets - may require further discussion
  • Make helper panel use the same logic as table of contents for positioning
  • Make non-published datasets' accordions collapsed by default
  • Functional workspace menu options
  • Tooltips for status icons I believe this was done upstream and just needs to be merged in; if not, it's a finishing touch.

Future improvements not in scope of this PR

  • Unified protocols section (tabbed view with any protocols from datasets)
  • Improved analysis details section (cards or flowchart instead of wall of links)
  • Combined metadata table
  • Redirect from processed dataset pages

Design Documentation/Original Tickets

Subtask: https://hms-dbmi.atlassian.net/browse/CAT-777
Task: https://hms-dbmi.atlassian.net/browse/CAT-678
Designs: https://www.figma.com/design/GUz7Z37Q3AAMiRyDy7YzWd/Unified-Detail-Pages?node-id=334-42649&m=dev

Testing

See screenshots in accordions below.

Screenshots/Video

CODEX

image

H&E Stained Microscopy

![image](https://github.com/user-attachments/assets/cfb6938e-0789-4af1-a0eb-04417c96b1ab)

Visium

image

Donor

image

Sample

image

Organ

image

Publication

image

Collapsible section demo with disappearing actions:

Screen.Recording.2024-08-16.122217.mp4

Checklist

  • Code follows the project's coding standards
    • Lint checks pass locally
    • New CHANGELOG-your-feature-name-here.md is present in the root directory, describing the change(s) in full sentences.
  • Unit tests covering the new feature have been added
  • All existing tests pass
  • Any relevant documentation in JIRA/Confluence has been updated to reflect the new feature
  • Any new functionalities have appropriate analytics functionalities added

Additional Notes

I'm open to ideas re: unit tests to add.

@NickAkhmetov NickAkhmetov marked this pull request as ready for review August 7, 2024 21:14
Copy link
Collaborator

@austenem austenem left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks great! Most of these are just questions/small stylistic suggestions.

return (
<NodeTemplate
rounded
target
icon={nodeIcons.componentDataset}
href={`#${data.name}-section`}
href={datasetDetails ? `#${datasetSectionId(datasetDetails, 'section')}` : undefined}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could this be

href={datasetDetails && `#${datasetSectionId(datasetDetails, 'section')}`}`

?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I prefer to explicitly pass undefined in props when I can to be safe in case the false boolean value that the expression false && "string" gets toString()ed to "false" down the render chain. With that said, since this is repeated throughout this file, I'll make a helper function for this logic instead.

const PrimaryTextDivider = styled(Divider)(({ theme }) => ({
marginLeft: theme.spacing(0.5),
marginRight: theme.spacing(0.5),
height: '15px',
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could this height val be 1rem?

<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>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This text is longer and might be nice to have as a const.

<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>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here ^


export default function HelperPanel() {
const currentDataset = useCurrentDataset();
// const panelOffset = useProcessedDataStore((state) => state.currentDatasetOffset);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be removed

) =>
styled(IconComponent)(({ theme, $noColor }) => ({
color: $noColor ? 'inherit' : theme.palette[status].main,
fontSize: '1em',
marginRight: 3,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could marginRight come from the theme?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This appears to be the value we have on main as well

{shouldDisplaySection.files && <Files files={files} />}
{shouldDisplaySection['bulk-data-transfer'] && <BulkDataTransfer />}
{shouldDisplaySection.collections && <CollectionsSection collectionsData={collectionsData} />}
{shouldDisplaySection.contributors && <ContributorsTable contributors={contributors} title="Contributors" />}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Destructuring shouldDisplaySection might make this TSX slightly more readable

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call; this has always presented a bit of a maintainability nightmare. I'm currently building out the collapsible detail page sections, which will have a shouldDisplay prop that will clean this up somewhat.

};

const sectionsToDisplay = Object.entries(shouldDisplaySection).filter(([_k, v]) => v === true);

return {
// TODO: Improve the lookup for descendants to exclude anything with a missing pipeline name
...getSectionFromString(pipeline ?? hubmap_id, `${hubmap_id}-section`),
items: sectionsToDisplay.map(([s]) => ({ ...getSectionFromString(s), hash: `${s}-${hubmap_id}` })),
...getSectionFromString(pipeline ?? hubmap_id, datasetSectionId(hit._source, 'section')),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the naming convention behind variables _likeThis?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this scenario - Search API responses return field-level data inside a _source field for each hit, since each hit also includes _id (the entity's UUID), _score (how closely the entity matches query parameters if using looser matching than must), _index (which search index the response came from, a constant value depending on the environment in our usecase), and _type ("doc" in our case).

In non-search API contexts, including the _ prefix for _first indicates that the variable is not used, and keeps ESLint from yelling at us for unused variables. For example, if you want to drop the first item in a list, you can destructure it like so:

const [_first, rest] = [1, 2, 3, 4, 5]

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ahh got it! Thanks for the explanation.

// - original link pointed to older version of the dataset and the user needs to select it
toastError(
'Could not find the section you were looking for. The dataset you wish to view may have been updated to a new version.',
);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe remove the language about the new version? I got this when I pasted a dataset link into a new page and it had a section hash appended to it, and the wording about new versions was a bit confusing. I think the first sentence accounts for all of these cases.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call, thanks for pointing it out!

I am planning to iterate on this by having each section independently check if its ID matches the hash of the window on initial load, but haven't yet gotten around to revisiting this functionality.

export { useThrottledOnScroll, useFindActiveIndex };
function useAnimatedSidebarPosition() {
const { summaryInView } = useEntityStore((state: EntityStore) => state.summaryComponentObserver);
const initialHeightOffset = headerHeight + 16;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the 16 referring to here - where does this unit come from?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was adding 1rem of padding to the top of the sidebar to keep it from touching the entity header; now that John's entity header work is merged in, I was able to replace this.

@NickAkhmetov
Copy link
Collaborator Author

This should be ready for re-review now; the remaining changes are related to fixing broken specs, which I'll be able to fix in the morning. I'll update the PR description with all necessary documentation tomorrow as well.

@NickAkhmetov NickAkhmetov marked this pull request as ready for review August 15, 2024 20:55
Copy link
Collaborator

@john-conroy john-conroy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A lot to sift through, but nothing really stands out as potentially problematic. Amazing work!

Comment on lines 5 to 18
interface CWLPipelineLink {
hash: string;
name: string;
origin: string;
}

interface IngestPipelineLink {
hash: string;
origin: string;
}

interface ProvAnalysisDetailsLinkProps {
data: CWLPipelineLink | IngestPipelineLink;
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we move this into the Dataset type?

Comment on lines 46 to 48
if (result.assaytype === 'image_pyramid') {
return 'Image Pyramid Generation';
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a difference between this statement and checking for is_pyramid in the vitessce-hints?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There isn't, so I've changed this condition to match what we use in portal-visualization.

import { sectionIconMap } from 'js/shared-styles/icons/sectionIconMap';
import BulkDataTransferPanels from './BulkDataTransferPanels';

// Workaround for publication pages
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe a more descriptive comment? Not sure what caused the workaround.

const { status: globusURLStatus, isLoading: globusURLIsLoading } = useFetchProtectedFile(uuid);
const hasNoAccess = globusURLStatus === 403;
const isNonConsortium = !isHubmapUser;

if (accessType !== 'Protected') {
if (!accessType || accessType !== 'Protected') {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are there situations we don't expect accessType or is this just for type safety?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was added to handle the initial loading state for accessType; since this was previously provided by flask data, it was never expected to be undefined, but since each descendant's bulk data transfer logic now gets handled independently, this is now loaded clientside for any processed descendants.


import { createContext, useContext } from 'js/helpers/context';

interface CollectionsSectionContextType {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could it make more sense to have a generic section provider?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The logic in this provider is specific to the collections section:

  • If no collections are found for any of the datasets, the section should not be displayed
  • If the primary dataset is not found in any collections but the processed dataset is, the section should be displayed
  • If the primary dataset is found in a collection, but none of the processed datasets are, the section should be displayed as a single tab

I'm adding this explanation to the context as a comment, thanks for catching this!

Comment on lines 60 to 66
const noWrapStyles = {
overflow: 'hidden',
textOverflow: 'ellipsis',
display: '-webkit-box',
WebkitLineClamp: 3,
WebkitBoxOrient: 'vertical',
};
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we reuse the LineClamp component?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Genuinely forgot about that already existing despite being the one to add it 😆

Comment on lines 158 to 180
onClick={() => {
track({
action: 'Start Creating Workspace',
label: currentDataset?.hubmap_id,
});
setOpenCreateWorkspace(true);
handleClose();
}}
>
<ListItemIcon>
<WorkspacesIcon color="primary" fontSize="1.5rem" />
</ListItemIcon>
Launch New Workspace
</MenuItem>
<MenuItem
onClick={() => {
track({
action: 'Start Adding Dataset to Existing Workspace',
label: currentDataset?.hubmap_id,
});
openEditWorkspaceDialog();
handleClose();
}}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Leverage useCallback for these onclicks? I feel like we've been consistent with that recently.

{hubmap_id}
<SecondaryBackgroundTooltip title="Copy HuBMAP ID">
<IconButton
onClick={() => {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we also use useCallback here? I'm fine with either, but we should be consistent.

Comment on lines +1 to +12
import React from 'react';
import Typography from '@mui/material/Typography';
import { useHandleCopyClick } from 'js/hooks/useCopyText';
import { SecondaryBackgroundTooltip } from 'js/shared-styles/tooltips';
import IconButton from '@mui/material/IconButton';
import ContentCopyIcon from '@mui/icons-material/ContentCopyRounded';
import Stack from '@mui/material/Stack';
import StatusIcon from '../../StatusIcon';
import { useProcessedDatasetContext } from './ProcessedDatasetContext';
import VersionSelect from '../../VersionSelect';
import { useTrackEntityPageEvent } from '../../useTrackEntityPageEvent';
import SummaryJSONButton from '../../summary/SummaryJSONButton';
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not for here, but should we add an eslint rule for import order?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I definitely want to get to the bottom of why that's not autofixing right now 😓

Comment on lines 1 to 12
import { useMemo } from 'react';
import { ProcessedDatasetInfo, useProcessedDatasets } from 'js/pages/Dataset/hooks';

function createdByCentralProcess(dataset: ProcessedDatasetInfo) {
return dataset.creation_action === 'Central Process';
}

function datasetIsPublished(dataset: ProcessedDatasetInfo) {
return dataset.status === 'Published';
}

export function useSortedSearchHits(datasets: ReturnType<typeof useProcessedDatasets>['searchHits']) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A spec would be helpful here.

@NickAkhmetov NickAkhmetov merged commit dda7257 into unified-datasets Aug 20, 2024
5 checks passed
@NickAkhmetov NickAkhmetov deleted the nickakhmetov/processed-datasets branch August 20, 2024 18:10
john-conroy added a commit that referenced this pull request Aug 21, 2024
* Add changelog

* NickAkhmetov/CAT-677 Unified Prov Graph (#3458)

Co-authored-by: John Conroy <[email protected]>
Co-authored-by: Tabassum Kakar <[email protected]>
Co-authored-by: tkakar <[email protected]>
Co-authored-by: Austen Money <[email protected]>
fix table tabs (#3466)
Fix incorrect URL structure in gene search results (#3467)

* John conroy/update table of contents CAT-755 (#3473)

* Convert table of contents to typescript

* Convert route to ts and add boundaries

* Fix styling so top works

* Add icons map

* Fix responsiveness

* Handle sub links

* Update marker color

* Style table of contents

* Add icons to table of contents

* Add processed datasets for table of contents

* Add loading state

* Adjust padding per Tiffany feedback

* Update style per Tiffany's feedback

* Open toc accordions by default

* Add visualization to processed data sections in toc

* Add processed sections and refactor

* Update changelog

* Update organ page to use layout

* Switch to derived data per tiffany's feedback

* Use height props

* Pull utils/hooks/types into files

* Add a use callback for toc click handler

* Pull find active index into hook

* Consolidate styles

* Use system props

---------

Co-authored-by: John Conroy <[email protected]>

* NickAkhmetov/CAT-776 Dataset Relationships diagram (#3481)

* John conroy/summary bar (#3501)

* Remove placeholder for undefined items

* Create entity header action buttons

* Add workspaces button

* Add copy button

* Always show entity header

* Add basic view select chips

* Fix opacity transition

* Animation progress

* Stash

* Add check icon to view select

* Add datasets relationship graph to summary bar

* Delete unused gene components

* Update summary components

* Different heights for summary and diagram

* Show entity header content when expanded

* Use position sticky

* Fix divider

* Remove version select

* Add citation and consortium to summary for datasets

* Clamp description in expanded summary

* Only show view buttons on large desktops

* Show mapped status

* Dedupe flask data and entity store assay metadata

* Add icons to summary

* Split out cases into components

* Add icons

* Add paper for relationship diagram

* Show summary view for more entities

* Integrate publication summary

* Fix publication summary

* Fix undefined

* Fix tests

* Fix offsets

* Remove fit view

* Fix comments from review

* Fix data-testid propogation

---------

Co-authored-by: John Conroy <[email protected]>

* NickAkhmetov/CAT-777 Processed Data section (#3495)

* make sure sample metadata doesn't get lost

* hide empty dataset relationships box on dataset pages without processed descendants

---------

Co-authored-by: John Conroy <[email protected]>
Co-authored-by: Nikolay Akhmetov <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants