From 0d769fdb34345bd2a18d1418d41e7911c5437535 Mon Sep 17 00:00:00 2001 From: Cody O'Donnell Date: Tue, 20 Feb 2024 17:52:43 -0800 Subject: [PATCH 1/5] Add new collection cards with basic chips. Also add page header to collection page. Update Card component to allow more options. --- src/common/components/Card.module.scss | 58 ++++++++--- src/common/components/Card.tsx | 51 ++++++++-- src/common/components/PageHeader.module.scss | 21 ++++ src/common/components/PageHeader.tsx | 33 +++++++ src/common/components/TextClamp.tsx | 28 ++++++ src/features/collections/CollectionDetail.tsx | 6 +- .../collections/CollectionSidebar.tsx | 9 +- .../collections/Collections.module.scss | 26 +++-- src/features/collections/CollectionsList.tsx | 96 +++++++++++++------ 9 files changed, 265 insertions(+), 63 deletions(-) create mode 100644 src/common/components/PageHeader.module.scss create mode 100644 src/common/components/PageHeader.tsx create mode 100644 src/common/components/TextClamp.tsx diff --git a/src/common/components/Card.module.scss b/src/common/components/Card.module.scss index 35262d9c..391718da 100644 --- a/src/common/components/Card.module.scss +++ b/src/common/components/Card.module.scss @@ -1,26 +1,24 @@ @import "../../common/colors"; -$border: 1px solid use-color("base-lighter"); +$border: 1px solid use-color("silver"); .card { - align-items: center; - align-items: center; background-color: use-color("white"); border: $border; - display: flex; - flex-flow: row nowrap; font-size: 16px; - gap: 10px; + height: 100%; overflow: clip; - padding: 10px; + padding-bottom: 0.5rem; + padding-top: 0.5rem; text-align: left; + transition: 0.25s; &.clickable { cursor: pointer; } &.clickable:hover { - background-color: use-color("base-lightest"); + background-color: use-color("light-gray"); } &.clickable.selected:hover, @@ -37,15 +35,29 @@ $border: 1px solid use-color("base-lighter"); align-self: flex-start; display: flex; flex-flow: column nowrap; - height: 44px; + flex-shrink: 0; justify-content: center; position: relative; - width: 44px; + + &.image-sm { + height: 35px; + width: auto; + } + + &.image-md { + height: 60px; + width: auto; + } + + &.image-lg { + height: 100px; + width: auto; + } > * { display: block; - max-height: 44px; - max-width: 44px; + height: 100%; + width: auto; } } @@ -55,16 +67,34 @@ $border: 1px solid use-color("base-lighter"); display: flex; flex-flow: column nowrap; flex-grow: 1; - gap: 5px; + gap: 0.5rem; justify-content: center; .subtitle { - color: use-color("base-darkest"); + color: use-color("base"); flex-grow: 1; font-size: 0.75em; } } +.header { + padding-left: 0.5rem; + padding-right: 0.5rem; +} + +.content { + flex-grow: 1; + padding-left: 0.5rem; + padding-right: 0.5rem; +} + +.footer { + border-top: 1px solid use-color("silver"); + padding-left: 0.5rem; + padding-right: 0.5rem; + padding-top: 0.5rem; +} + .card-list { border: $border; display: flex; diff --git a/src/common/components/Card.tsx b/src/common/components/Card.tsx index 71e9f413..16f6d2a2 100644 --- a/src/common/components/Card.tsx +++ b/src/common/components/Card.tsx @@ -9,6 +9,7 @@ import { } from 'react'; import { useNavigate } from 'react-router-dom'; import classes from './Card.module.scss'; +import { Stack } from '@mui/material'; /** * Component for rendering a data summary as a card. @@ -23,13 +24,30 @@ export const Card: FC<{ subtitle?: string | ReactElement; /** Image for the card, any react element, but preferably and `` */ image?: ReactElement; + /** Configurable sizes for the image next to the title */ + imageSize?: 'sm' | 'md' | 'lg'; + /** Optional content section to display under the titles */ + content?: string | ReactElement; + /** Optional footer section to display at the bottom of the card */ + footer?: string | ReactElement; /** When true, the card will appear selected */ selected?: boolean; /** An internal link, when defined clicking the Card will navigate to the link */ linkTo?: string; onClick?: (e: React.MouseEvent) => void; className?: string; -}> = ({ title, subtitle, image, onClick, linkTo, selected, className }) => { +}> = ({ + title, + subtitle, + image, + imageSize = 'sm', + content, + footer, + onClick, + linkTo, + selected, + className, +}) => { const navigate = useNavigate(); const rootDiv = useRef(null); @@ -52,6 +70,9 @@ export const Card: FC<{ if (className) classNames += `${className}`; + const imageClasses = + classes['image-wrapper'] + ' ' + classes[`image-${imageSize}`]; + const clickableProps: HTMLAttributes | undefined = clickableRole ? { @@ -68,20 +89,30 @@ export const Card: FC<{ : undefined; return ( -
- {image ?
{image}
: null} -
-
{title}
- {subtitle ? ( -
{subtitle}
- ) : null} -
-
+ + {image ?
{image}
: null} +
+
{title}
+ {subtitle ? ( +
{subtitle}
+ ) : null} +
+
+ {content &&
{content}
} + {footer &&
{footer}
} + ); }; diff --git a/src/common/components/PageHeader.module.scss b/src/common/components/PageHeader.module.scss new file mode 100644 index 00000000..39070f56 --- /dev/null +++ b/src/common/components/PageHeader.module.scss @@ -0,0 +1,21 @@ +@import "../../common/colors"; + +.page-header { + background-color: use-color("white"); +} + +.page-header .page-title { + font-weight: normal; + margin-bottom: 0.5rem; + margin-top: 0; +} + +.page-header .page-subtitle { + color: use-color("base-dark"); + font-size: 1.25rem; + margin: 0; +} + +.page-header .page-description { + margin-top: 1rem; +} diff --git a/src/common/components/PageHeader.tsx b/src/common/components/PageHeader.tsx new file mode 100644 index 00000000..95076a2a --- /dev/null +++ b/src/common/components/PageHeader.tsx @@ -0,0 +1,33 @@ +import { Container } from '@mui/material'; +import { FC, ReactNode } from 'react'; +import classes from './PageHeader.module.scss'; + +interface PageHeaderProps { + title: ReactNode; + subtitle?: ReactNode; + description?: ReactNode; +} + +export const PageHeader: FC = ({ + title, + subtitle, + description, +}) => { + return ( +
+ +

{title}

+ {subtitle &&
{subtitle}
} + {description && ( +
{description}
+ )} +
+
+ ); +}; diff --git a/src/common/components/TextClamp.tsx b/src/common/components/TextClamp.tsx new file mode 100644 index 00000000..11a034dd --- /dev/null +++ b/src/common/components/TextClamp.tsx @@ -0,0 +1,28 @@ +import { FC, PropsWithChildren } from 'react'; +import { Box } from '@mui/material'; + +interface TextClampProps extends PropsWithChildren { + /** Max number of lines to show before clamping */ + lines?: number; +} + +/** + * Clamp inner content to a specified number of lines. + * Shows an ellipsis at the end of the last line. + */ +const TextClamp: FC = ({ lines = 2, children }) => { + return ( + + {children} + + ); +}; + +export default TextClamp; diff --git a/src/features/collections/CollectionDetail.tsx b/src/features/collections/CollectionDetail.tsx index 04f511a3..5dd20659 100644 --- a/src/features/collections/CollectionDetail.tsx +++ b/src/features/collections/CollectionDetail.tsx @@ -110,7 +110,7 @@ export const CollectionDetail = () => { currDataProduct={currDataProduct} showOverview={showOverview} /> -
+

{showOverview @@ -172,7 +172,7 @@ export const CollectionDetail = () => { )}

-
+
{ )}
-
+ {modalView === 'match' ? ( { - const navigate = useNavigate(); usePageTitle('Data Collections'); const collections = listCollections.useQuery(); + + return ( +
+ + + + {collections.isSuccess && + collections.data?.data.map((collection) => ( + + + + ))} + + +
+ ); +}; + +/** + * Variation of the Card component for displaying Collection summary cards + */ +const CollectionCard: FC<{ + collection: Collection; +}> = ({ collection }) => { + const navigate = useNavigate(); + const detailLink = encodeURIComponent(collection.id); + const handleClick = () => navigate(detailLink); + const chipProducts = collection.data_products.filter((dp) => { + return dp.product === 'genome_attribs' || dp.product === 'samples'; + }); + const chips = chipProducts.map((dp) => { + const dpMeta = dataProductsMeta.find((m) => m.product === dp.product); + return dpMeta?.section; + }); return ( -
-
- - {collections.isSuccess - ? collections.data?.data.map((collection) => { - const detailLink = encodeURIComponent(collection.id); - const handleClick = () => navigate(detailLink); - return ( - - } - /> - ); - }) - : null} - -
-
+ {collection.name} + } + subtitle={collection.ver_tag} + onClick={handleClick} + image={ + {`${collection.name} + } + imageSize="md" + content={{collection.desc}} + footer={ + + {chips.map((chip) => ( + + ))} + + } + /> ); }; From e741fc7cc8c6aedd38b8edce8d70d1d371273a37 Mon Sep 17 00:00:00 2001 From: Cody O'Donnell Date: Tue, 20 Feb 2024 18:37:02 -0800 Subject: [PATCH 2/5] Fix navigator background and add border to collections page header --- src/common/components/PageHeader.module.scss | 1 + src/features/navigator/Navigator.module.scss | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/common/components/PageHeader.module.scss b/src/common/components/PageHeader.module.scss index 39070f56..b6b7a1de 100644 --- a/src/common/components/PageHeader.module.scss +++ b/src/common/components/PageHeader.module.scss @@ -2,6 +2,7 @@ .page-header { background-color: use-color("white"); + border-bottom: 1px solid use-color("silver"); } .page-header .page-title { diff --git a/src/features/navigator/Navigator.module.scss b/src/features/navigator/Navigator.module.scss index 49d956b1..2e43037f 100644 --- a/src/features/navigator/Navigator.module.scss +++ b/src/features/navigator/Navigator.module.scss @@ -5,7 +5,9 @@ pre { } .navigator { - background-color: use-color("light-gray"); + background-color: use-color("base-lightest"); + border-left: 1px solid use-color("silver"); + height: 100%; padding: 1rem; } From d5af7729457411cc7db367da4776c61c03073684 Mon Sep 17 00:00:00 2001 From: Cody O'Donnell Date: Wed, 21 Feb 2024 11:01:30 -0800 Subject: [PATCH 3/5] Update collections header text --- src/features/collections/CollectionsList.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/features/collections/CollectionsList.tsx b/src/features/collections/CollectionsList.tsx index a755cdd3..ab8a04b3 100644 --- a/src/features/collections/CollectionsList.tsx +++ b/src/features/collections/CollectionsList.tsx @@ -17,7 +17,7 @@ export const CollectionsList = () => {
From 913c0df91f592faa7f70eb906daee8771363b64c Mon Sep 17 00:00:00 2001 From: Cody O'Donnell Date: Wed, 21 Feb 2024 11:04:08 -0800 Subject: [PATCH 4/5] Expand collection and navigator heights --- src/features/collections/Collections.module.scss | 2 +- src/features/navigator/Navigator.module.scss | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/features/collections/Collections.module.scss b/src/features/collections/Collections.module.scss index ac3e9710..99c39993 100644 --- a/src/features/collections/Collections.module.scss +++ b/src/features/collections/Collections.module.scss @@ -5,7 +5,7 @@ $border: 1px solid use-color("base-lighter"); .collections_main { background-color: use-color("base-lightest"); border-left: 1px solid use-color("silver"); - height: 100%; + min-height: 100%; } .collections_main .inner-container { diff --git a/src/features/navigator/Navigator.module.scss b/src/features/navigator/Navigator.module.scss index 2e43037f..3d07e8b5 100644 --- a/src/features/navigator/Navigator.module.scss +++ b/src/features/navigator/Navigator.module.scss @@ -7,7 +7,7 @@ pre { .navigator { background-color: use-color("base-lightest"); border-left: 1px solid use-color("silver"); - height: 100%; + min-height: 100%; padding: 1rem; } From 39a7692424bebb2f8205ccbe1b37a12ab08a3bd7 Mon Sep 17 00:00:00 2001 From: Cody O'Donnell Date: Thu, 22 Feb 2024 14:39:33 -0800 Subject: [PATCH 5/5] Update collection subtitle --- src/common/components/PageHeader.module.scss | 1 + src/common/components/PageHeader.tsx | 8 ++++++-- src/features/collections/Collections.module.scss | 11 +++++++++-- src/features/collections/CollectionsList.tsx | 11 ++++------- 4 files changed, 20 insertions(+), 11 deletions(-) diff --git a/src/common/components/PageHeader.module.scss b/src/common/components/PageHeader.module.scss index b6b7a1de..b574e8ff 100644 --- a/src/common/components/PageHeader.module.scss +++ b/src/common/components/PageHeader.module.scss @@ -6,6 +6,7 @@ } .page-header .page-title { + font-size: 1.75rem; font-weight: normal; margin-bottom: 0.5rem; margin-top: 0; diff --git a/src/common/components/PageHeader.tsx b/src/common/components/PageHeader.tsx index 95076a2a..7f02d046 100644 --- a/src/common/components/PageHeader.tsx +++ b/src/common/components/PageHeader.tsx @@ -3,11 +3,15 @@ import { FC, ReactNode } from 'react'; import classes from './PageHeader.module.scss'; interface PageHeaderProps { - title: ReactNode; + title?: ReactNode; subtitle?: ReactNode; description?: ReactNode; } +/** + * Header section for app landing pages. + * Not currently in use. + */ export const PageHeader: FC = ({ title, subtitle, @@ -22,7 +26,7 @@ export const PageHeader: FC = ({ padding: '2rem 1rem', }} > -

{title}

+ {title &&

{title}

} {subtitle &&
{subtitle}
} {description && (
{description}
diff --git a/src/features/collections/Collections.module.scss b/src/features/collections/Collections.module.scss index 99c39993..f41e08a8 100644 --- a/src/features/collections/Collections.module.scss +++ b/src/features/collections/Collections.module.scss @@ -2,17 +2,24 @@ $border: 1px solid use-color("base-lighter"); -.collections_main { +.collections-main { background-color: use-color("base-lightest"); border-left: 1px solid use-color("silver"); min-height: 100%; } -.collections_main .inner-container { +.collections-main .inner-container { margin-left: 0; padding: 1rem; } +.collections-subtitle { + font-size: 1.25rem; + font-weight: 500; + margin: 0; + padding: 2rem 1rem 1rem; +} + .collection-card-title { color: use-color("primary"); font-size: 1.15rem; diff --git a/src/features/collections/CollectionsList.tsx b/src/features/collections/CollectionsList.tsx index ab8a04b3..0d32b09c 100644 --- a/src/features/collections/CollectionsList.tsx +++ b/src/features/collections/CollectionsList.tsx @@ -6,7 +6,6 @@ import { Card } from '../../common/components/Card'; import { Chip, Container, Grid, Stack } from '@mui/material'; import TextClamp from '../../common/components/TextClamp'; import { FC } from 'react'; -import { PageHeader } from '../../common/components/PageHeader'; import { dataProductsMeta } from './CollectionSidebar'; export const CollectionsList = () => { @@ -14,12 +13,10 @@ export const CollectionsList = () => { const collections = listCollections.useQuery(); return ( -
- +
+

+ Explore, relate, and integrate curated data collections. +

{collections.isSuccess &&