From 56290e31bf387348c1c941164bf099844969eadf Mon Sep 17 00:00:00 2001 From: Francois Lehoux Date: Mon, 10 Feb 2025 13:56:55 -0500 Subject: [PATCH] feat: add variant functionality to Card commponent --- packages/odyssey-react-mui/src/Card.tsx | 103 +++++++++++++----- .../odyssey-mui/Card/Card.stories.tsx | 44 ++++++++ 2 files changed, 121 insertions(+), 26 deletions(-) diff --git a/packages/odyssey-react-mui/src/Card.tsx b/packages/odyssey-react-mui/src/Card.tsx index f223b23e0..cf272b257 100644 --- a/packages/odyssey-react-mui/src/Card.tsx +++ b/packages/odyssey-react-mui/src/Card.tsx @@ -36,15 +36,18 @@ import { useOdysseyDesignTokens, } from "./OdysseyDesignTokensContext.js"; import { Heading5, Paragraph, Support } from "./Typography.js"; -import { Box } from "./Box.js"; export const CARD_IMAGE_HEIGHT = "64px"; +export const CARD_IMAGE_HEIGHT_COMPACT = "48px"; + +export const cardVariantValues = ["tile", "stack", "compact"] as const; export type CardProps = { description?: string; image?: ReactElement; overline?: string; title?: string; + variant?: (typeof cardVariantValues)[number]; } & ( | { onClick: MouseEventHandler; @@ -60,31 +63,68 @@ export type CardProps = { const ImageContainer = styled("div", { shouldForwardProp: (prop) => - prop !== "odysseyDesignTokens" && prop !== "hasMenuButtonChildren", + prop !== "odysseyDesignTokens" && + prop !== "hasMenuButtonChildren" && + prop !== "variant", })<{ odysseyDesignTokens: DesignTokens; hasMenuButtonChildren: boolean; -}>(({ odysseyDesignTokens, hasMenuButtonChildren }) => ({ + variant: (typeof cardVariantValues)[number]; +}>(({ odysseyDesignTokens, hasMenuButtonChildren, variant }) => ({ display: "flex", alignItems: "flex-start", - maxHeight: CARD_IMAGE_HEIGHT, - height: CARD_IMAGE_HEIGHT, - marginBlockEnd: odysseyDesignTokens.Spacing5, - paddingRight: hasMenuButtonChildren ? odysseyDesignTokens.Spacing5 : 0, + height: variant === "compact" ? CARD_IMAGE_HEIGHT_COMPACT : CARD_IMAGE_HEIGHT, + maxHeight: + variant === "compact" ? CARD_IMAGE_HEIGHT_COMPACT : CARD_IMAGE_HEIGHT, + marginBlockEnd: variant === "tile" ? odysseyDesignTokens.Spacing5 : 0, + paddingRight: + hasMenuButtonChildren || ["stack", "compact"].includes(variant) + ? odysseyDesignTokens.Spacing5 + : 0, })); const MenuButtonContainer = styled("div", { shouldForwardProp: (prop) => prop !== "odysseyDesignTokens", -})<{ odysseyDesignTokens: DesignTokens }>(({ odysseyDesignTokens }) => ({ +})<{ + odysseyDesignTokens: DesignTokens; + variant: (typeof cardVariantValues)[number]; +}>(({ odysseyDesignTokens, variant }) => ({ position: "absolute", right: odysseyDesignTokens.Spacing3, - top: odysseyDesignTokens.Spacing3, + top: + variant === "compact" + ? odysseyDesignTokens.Spacing4 + : odysseyDesignTokens.Spacing3, + height: variant === "compact" ? CARD_IMAGE_HEIGHT_COMPACT : "auto", + display: "flex", + alignItems: "center", })); const CardContentContainer = styled("div")(() => ({ display: "flex", })); +const CardImageAndContentContainer = styled("div", { + shouldForwardProp: (prop) => prop !== "variant", +})<{ variant: (typeof cardVariantValues)[number] }>(({ variant }) => ({ + display: "flex", + flexDirection: variant === "tile" ? "column" : "row", +})); + +const CardContent = styled("div", { + shouldForwardProp: (prop) => prop !== "odysseyDesignTokens", +})<{ + odysseyDesignTokens: DesignTokens; + variant: (typeof cardVariantValues)[number]; +}>(({ odysseyDesignTokens, variant }) => ({ + "& > .MuiTypography-h5:not(:last-child)": { + marginBlockEnd: `${variant === "compact" ? odysseyDesignTokens.Spacing1 : odysseyDesignTokens.Spacing3} !important`, + }, + "& > *:last-child": { + marginBlockEnd: 0, + }, +})); + const buttonProviderValue = { isFullWidth: true }; const Card = ({ @@ -95,36 +135,43 @@ const Card = ({ onClick, overline, title, + variant = "tile", }: CardProps) => { const odysseyDesignTokens = useOdysseyDesignTokens(); const cardContent = useMemo( () => ( - + {image && ( {image} )} - {overline && {overline}} - {title && {title}} - {description && ( - {description} - )} - - {button && ( - - - {button} - - - )} - + + {overline && {overline}} + {title && {title}} + {description && ( + {description} + )} + + {button && ( + + + {button} + + + )} + + ), [ @@ -135,11 +182,12 @@ const Card = ({ overline, title, odysseyDesignTokens, + variant, ], ); return ( - + {onClick ? ( {cardContent} ) : ( @@ -147,7 +195,10 @@ const Card = ({ )} {menuButtonChildren && ( - + } ariaLabel="Card menu" diff --git a/packages/odyssey-storybook/src/components/odyssey-mui/Card/Card.stories.tsx b/packages/odyssey-storybook/src/components/odyssey-mui/Card/Card.stories.tsx index 6aab655a9..bec55a2b9 100644 --- a/packages/odyssey-storybook/src/components/odyssey-mui/Card/Card.stories.tsx +++ b/packages/odyssey-storybook/src/components/odyssey-mui/Card/Card.stories.tsx @@ -196,3 +196,47 @@ export const ButtonWithoutImage: Story = { ), }; + +export const TileVariant: Story = { + render: (args) => { + return ( + + } + variant="tile" + /> + + ); + }, +}; + +export const StackVariant: Story = { + render: (args) => { + return ( + } + variant="stack" + /> + ); + }, +}; + +export const CompactVariant: Story = { + render: (args) => { + return ( + } + variant="compact" + /> + ); + }, +};