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

John conroy/panel list landing pages #3036

Merged
merged 13 commits into from
Mar 14, 2023
1 change: 1 addition & 0 deletions CHANGELOG-panel-list-landing-pages.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- Refactor panel components for reuse in upcoming publication pages.

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,16 @@ import React from 'react';

import SectionHeader from 'js/shared-styles/sections/SectionHeader';
import { DetailPageSection } from 'js/components/detailPage/style';
import CollectionsPanelList from 'js/components/CollectionsPanelList';
import { buildCollectionsPanelsProps } from 'js/pages/Collections/utils';

import PanelList from 'js/shared-styles/panels/PanelList';

function CollectionsSection({ collectionsData }) {
const panelsProps = buildCollectionsPanelsProps(collectionsData);
return (
<DetailPageSection id="collections">
<SectionHeader>Collections</SectionHeader>
<CollectionsPanelList collectionsData={collectionsData} />
<PanelList panelsProps={panelsProps} />;
</DetailPageSection>
);
}
Expand Down
33 changes: 12 additions & 21 deletions context/app/static/js/pages/Collections/Collections.jsx
Original file line number Diff line number Diff line change
@@ -1,29 +1,20 @@
import React from 'react';
import Typography from '@material-ui/core/Typography';

import CollectionsPanelList from 'js/components/CollectionsPanelList';
import { useSearchHits } from 'js/hooks/useSearchData';
import { getAllCollectionsQuery } from 'js/helpers/queries';
import { PageWrapper, StyledDescription } from './style';
import PanelListLandingPage from 'js/shared-styles/panels/PanelListLandingPage';
import { useCollections } from './hooks';

function Collections() {
const { searchHits: collectionsData } = useSearchHits(getAllCollectionsQuery);
const description =
'Collections of HuBMAP datasets represent data from related experiments—such as assays performed on the same organ—or data that has been grouped for other reasons. In the future, it will be possible to reference collections through Document Object Identifiers (DOIs).';

function Collections() {
const panelsProps = useCollections();
return (
<PageWrapper>
<Typography variant="h2" component="h1">
Collections
</Typography>
<Typography variant="subtitle1" color="primary">
{collectionsData.length > 0 && `${collectionsData.length} Collections`}
</Typography>
<StyledDescription>
Collections of HuBMAP datasets represent data from related experiments—such as assays performed on the same
organ—or data that has been grouped for other reasons. In the future, it will be possible to reference
collections through Document Object Identifiers (DOIs).
</StyledDescription>
{collectionsData.length > 0 && <CollectionsPanelList collectionsData={collectionsData} />}
</PageWrapper>
<PanelListLandingPage
title="Collections"
subtitle={panelsProps.length > 0 && `${panelsProps.length} Collections`}
description={description}
panelsProps={panelsProps}
/>
);
}

Expand Down
10 changes: 10 additions & 0 deletions context/app/static/js/pages/Collections/hooks.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { useSearchHits } from 'js/hooks/useSearchData';
import { getAllCollectionsQuery } from 'js/helpers/queries';
import { buildCollectionsPanelsProps } from './utils';

function useCollections() {
const { searchHits: collectionsData } = useSearchHits(getAllCollectionsQuery);
return buildCollectionsPanelsProps(collectionsData);
}

export { useCollections };
14 changes: 14 additions & 0 deletions context/app/static/js/pages/Collections/utils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import React from 'react';
import Typography from '@material-ui/core/Typography';

function buildCollectionsPanelsProps(collections) {
return collections.map(({ _source }) => ({
key: _source.uuid,
href: `/browse/collection/${_source.uuid}`,
title: _source.title,
secondaryText: _source.hubmap_id,
rightText: <Typography variant="caption">{`${_source.datasets.length} Datasets`}</Typography>,
}));
}

export { buildCollectionsPanelsProps };
9 changes: 4 additions & 5 deletions context/app/static/js/pages/Dataset/Dataset.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,7 @@ import useEntityStore from 'js/stores/useEntityStore';
import CollectionsSection from 'js/components/detailPage/CollectionsSection';
import SupportAlert from 'js/components/detailPage/SupportAlert';
import { DetailPageAlert } from 'js/components/detailPage/style';
import { useSearchHits } from 'js/hooks/useSearchData';
import { getAllCollectionsQuery } from 'js/helpers/queries';

import { ReactComponent as WorkspacesIcon } from 'assets/svg/workspaces.svg';
import { WhiteBackgroundIconButton } from 'js/shared-styles/buttons';
import { SecondaryBackgroundTooltip } from 'js/shared-styles/tooltips';
Expand All @@ -28,8 +27,9 @@ import DetailContext from 'js/components/detailPage/context';
import { getSectionOrder } from 'js/components/detailPage/utils';
import CreateWorkspaceDialog from 'js/components/workspaces/CreateWorkspaceDialog';

import { combineMetadata, getCollectionsWhichContainDataset } from 'js/pages/utils/entity-utils';
import { combineMetadata } from 'js/pages/utils/entity-utils';
import OutboundIconLink from 'js/shared-styles/Links/iconLinks/OutboundIconLink';
import { useDatasetCollections } from './hooks';

function NotebookButton(props) {
return (
Expand Down Expand Up @@ -142,8 +142,7 @@ function DatasetDetail({ assayMetadata, vitData, hasNotebook, visLiftedUUID }) {

const combinedMetadata = combineMetadata(donor, origin_sample, source_sample, metadata);

const { searchHits: allCollections } = useSearchHits(getAllCollectionsQuery);
const collectionsData = getCollectionsWhichContainDataset(uuid, allCollections);
const collectionsData = useDatasetCollections(uuid);

const shouldDisplaySection = {
provenance: entity_type !== 'Support',
Expand Down
11 changes: 11 additions & 0 deletions context/app/static/js/pages/Dataset/hooks.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { useSearchHits } from 'js/hooks/useSearchData';
import { getAllCollectionsQuery } from 'js/helpers/queries';

import { getCollectionsWhichContainDataset } from './utils';

function useDatasetCollections(uuid) {
const { searchHits: collections } = useSearchHits(getAllCollectionsQuery);
return getCollectionsWhichContainDataset(uuid, collections);
}

export { useDatasetCollections };
8 changes: 8 additions & 0 deletions context/app/static/js/pages/Dataset/utils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
function getCollectionsWhichContainDataset(uuid, collections) {
return collections.filter((collection) => {
// eslint-disable-next-line no-underscore-dangle
return collection._source.datasets.some((dataset) => dataset.uuid === uuid);
});
}

export { getCollectionsWhichContainDataset };
10 changes: 1 addition & 9 deletions context/app/static/js/pages/utils/entity-utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,4 @@ function combineMetadata(donor, origin_sample, source_sample, metadata) {

return combinedMetadata;
}

function getCollectionsWhichContainDataset(uuid, collections) {
return collections.filter((collection) => {
// eslint-disable-next-line no-underscore-dangle
return collection._source.datasets.some((dataset) => dataset.uuid === uuid);
});
}

export { combineMetadata, getCollectionsWhichContainDataset };
export { combineMetadata };
21 changes: 21 additions & 0 deletions context/app/static/js/shared-styles/panels/Panel/Panel.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import React from 'react';

import { PanelBox, LeftTextWrapper, TruncatedLink, TruncatedText, RightTextWrapper } from './style';

function Panel({ title, href, secondaryText, rightText }) {
return (
<PanelBox>
<LeftTextWrapper>
<TruncatedLink variant="subtitle1" href={href}>
{title}
</TruncatedLink>
<TruncatedText variant="body2" color="secondary">
{secondaryText}
</TruncatedText>
</LeftTextWrapper>
<RightTextWrapper>{rightText}</RightTextWrapper>
</PanelBox>
);
}

export default Panel;
16 changes: 16 additions & 0 deletions context/app/static/js/shared-styles/panels/Panel/Panel.stories.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import React from 'react';

import PanelComponent from './Panel';

export default {
title: 'Panels/Panel',
component: PanelComponent,
};

export const Panel = (args) => <PanelComponent {...args} />;
Panel.args = {
title: 'Title',
secondaryText: 'Secondary Text',
rightText: 'Right Text',
};
Panel.storyName = 'Panel'; // needed for single story hoisting for multi word component names
3 changes: 3 additions & 0 deletions context/app/static/js/shared-styles/panels/Panel/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import Panel from './Panel';

export default Panel;
47 changes: 47 additions & 0 deletions context/app/static/js/shared-styles/panels/Panel/style.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import styled, { css } from 'styled-components';
import Typography from '@material-ui/core/Typography';

import { LightBlueLink } from 'js/shared-styles/Links';

const overflowCss = css`
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
`;

const PanelBox = styled.div`
padding: 15px 20px;
border-bottom: 1px solid ${(props) => props.theme.palette.divider};
display: flex;
flex-direction: column;

&:hover {
background-color: ${(props) => props.theme.palette.hoverShadow.main};
}
@media (min-width: ${(props) => props.theme.breakpoints.values.md}px) {
flex-direction: row;
justify-content: space-between;
}
`;

const LeftTextWrapper = styled.div`
white-space: nowrap;
min-width: 0px; // needed to handle overflow
margin-right: ${(props) => props.theme.spacing(1)}px;
`;

const TruncatedText = styled(Typography)`
${overflowCss};
`;

const TruncatedLink = styled(LightBlueLink)`
${overflowCss};
display: block; //text-overflow only applies to block elements
`;

const RightTextWrapper = styled.div`
flex-shrink: 0;
padding-left: ${(props) => props.theme.spacing(0.5)}px;
`;

export { PanelBox, LeftTextWrapper, TruncatedText, TruncatedLink, RightTextWrapper };
16 changes: 16 additions & 0 deletions context/app/static/js/shared-styles/panels/PanelList/PanelList.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import React from 'react';

import Panel from 'js/shared-styles/panels/Panel';
import { PanelScrollBox } from './style';

function PanelList({ panelsProps }) {
return (
<PanelScrollBox>
{panelsProps.map((props) => (
<Panel {...props} />
))}
</PanelScrollBox>
);
}

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

import Panel from 'js/shared-styles/panels/Panel';
import PanelListComponent from './PanelList';

export default {
title: 'Panels/PanelList',
component: PanelListComponent,
subcomponents: { Panel },
};

export const PanelList = (args) => <PanelListComponent {...args} />;
PanelList.args = {
panelsProps: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10].map((item, i) => ({
title: `Title ${i}`,
secondaryText: `Secondary Text ${i}`,
rightText: `Right Text ${i}`,
})),
};
PanelList.storyName = 'PanelList'; // needed for single story hoisting for multi word component names
3 changes: 3 additions & 0 deletions context/app/static/js/shared-styles/panels/PanelList/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import PanelList from './PanelList';

export default PanelList;
12 changes: 12 additions & 0 deletions context/app/static/js/shared-styles/panels/PanelList/style.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import styled from 'styled-components';
import Paper from '@material-ui/core/Paper';

const PanelScrollBox = styled(Paper)`
@media (min-width: ${(props) => props.theme.breakpoints.values.md}px) {
flex-grow: 1;
overflow-y: scroll;
margin-top: ${(props) => props.theme.spacing(1)}px;
}
`;

export { PanelScrollBox };
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import React from 'react';
import Typography from '@material-ui/core/Typography';

import PanelList from 'js/shared-styles/panels/PanelList';
import { PageWrapper, StyledDescription } from './style';

function PanelListLandingPage({ title, subtitle, description, panelsProps }) {
return (
<PageWrapper>
<Typography variant="h2" component="h1">
{title}
</Typography>
<Typography variant="subtitle1" color="primary">
{subtitle}
</Typography>
<StyledDescription>{description}</StyledDescription>
{panelsProps.length > 0 && <PanelList panelsProps={panelsProps} />}
</PageWrapper>
);
}

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

import Panel from 'js/shared-styles/panels/Panel';
import PanelList from 'js/shared-styles/panels/PanelList';
import { PanelList as PanelListStory } from 'js/shared-styles/panels/PanelList/PanelList.stories';
import PanelListLandingPageComponent from './PanelListLandingPage';

export default {
title: 'Panels/PanelListLandingPage',
component: PanelListLandingPageComponent,
subcomponents: [PanelList, Panel],
};

const lorem =
'Fugiat irure nisi ea dolore non adipisicing non. Enim enim incididunt ut reprehenderit esse sint adipisicing. Aliqua excepteur reprehenderit tempor commodo anim veniam laboris labore exercitation qui. Adipisicing pariatur est anim nisi cupidatat ea Lorem nostrud labore laborum enim eiusmod.';

export const PanelListLandingPage = (args) => <PanelListLandingPageComponent {...args} />;

PanelListLandingPage.args = {
title: 'Landing Page Title',
subtitle: 'Landing Page Subtitle',
description: lorem,
panelsProps: PanelListStory.args.panelsProps,
};

PanelListLandingPage.storyName = 'PanelListLandingPage'; // needed for single story hoisting for multi word component names
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import PanelListLandingPage from './PanelListLandingPage';

export default PanelListLandingPage;
Loading