-
Notifications
You must be signed in to change notification settings - Fork 2
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
NickAkhmetov/CAT-777 Processed Data section #3495
Conversation
… support for partial multifetch
…o analysis details, add section descriptions, ensure hash links work with `document.querySelector`, add preliminary logic for scrolling to lazy loaded pieces,
… from dataset page, remove vitessce conf generation from dataset page path, enable providing marker genes and parent uuids to .vitessce.json paths
There was a problem hiding this 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} |
There was a problem hiding this comment.
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')}`}`
?
There was a problem hiding this comment.
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', |
There was a problem hiding this comment.
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> |
There was a problem hiding this comment.
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> |
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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, |
There was a problem hiding this comment.
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
?
There was a problem hiding this comment.
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" />} |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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')), |
There was a problem hiding this comment.
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
?
There was a problem hiding this comment.
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]
There was a problem hiding this comment.
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.', | ||
); |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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; |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
…ra provider, improve table of contents visualization section logic
…ilPageSection` where logical
…cordion actions when collapsed
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. |
There was a problem hiding this 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!
interface CWLPipelineLink { | ||
hash: string; | ||
name: string; | ||
origin: string; | ||
} | ||
|
||
interface IngestPipelineLink { | ||
hash: string; | ||
origin: string; | ||
} | ||
|
||
interface ProvAnalysisDetailsLinkProps { | ||
data: CWLPipelineLink | IngestPipelineLink; | ||
} |
There was a problem hiding this comment.
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?
if (result.assaytype === 'image_pyramid') { | ||
return 'Image Pyramid Generation'; | ||
} |
There was a problem hiding this comment.
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
?
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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') { |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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!
const noWrapStyles = { | ||
overflow: 'hidden', | ||
textOverflow: 'ellipsis', | ||
display: '-webkit-box', | ||
WebkitLineClamp: 3, | ||
WebkitBoxOrient: 'vertical', | ||
}; |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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 😆
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(); | ||
}} |
There was a problem hiding this comment.
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={() => { |
There was a problem hiding this comment.
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.
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'; |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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 😓
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']) { |
There was a problem hiding this comment.
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.
* 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]>
Summary
This PR implements the
Processed Data
section for unified views.TODOs
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.Tooltips for status iconsI 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
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
H&E Stained Microscopy
![image](https://github.com/user-attachments/assets/cfb6938e-0789-4af1-a0eb-04417c96b1ab)
Visium
Donor
Sample
Organ
Publication
Collapsible section demo with disappearing actions:
Screen.Recording.2024-08-16.122217.mp4
Checklist
CHANGELOG-your-feature-name-here.md
is present in the root directory, describing the change(s) in full sentences.Additional Notes
I'm open to ideas re: unit tests to add.